算功@LeetCode:LongestCommonPrefix

2017-04-10  本文已影响25人  苏尚君

Log

题目

Longest Common Prefix

【题目类型备注】

字符串

提交

01

【语言】

Python

【时间】

170409

【思路】

  1. 〖复杂度?〗O(mn),其中 m 是字符串的长度,n 是字符串数组 strs 的长度

  2. 〖大致流程?〗

  1. 〖什么时候停?〗
  1. 〖特别注意?〗
    + 要考虑到给定的字符串可能为空串、给定的字符串集合可能是空集、所有的字符串之间没有公共前缀(就是说,所有字符串,至少首位字符是不同的)

  2. 〖能否剪枝?〗暂时看不出剪枝的可能

【代码】

class Solution(object):
    def commonPrefix(self, str_a, str_b):
      #print "str_a: {}, str_b: {}".format(str_a, str_b)
      substr = ""
      if len(str_a)>len(str_b):
        length = len(str_b)
      else:
        length = len(str_a)

      k=0
      while ((k<length) and (str_a[k] == str_b[k])):
        #print "in commonPrefix\nk: {}\nstr_a[k]: {}\nstr_b[k]: {}".format(k, str_a[k], str_b[k])
        #print "substr(before): {}".format(substr)
        substr += str_a[k]
        #print "substr(after): {}".format(substr)
        k += 1

      return substr


    def longestCommonPrefix(self, strs):
        """
        :type strs: List[str]
        :rtype: str
        """
        if 0 == len(strs):
            return ""
        elif 1 == len(strs):
            return strs[0]
        substr_old = self.commonPrefix(strs[0], strs[1])
        for (i, item) in enumerate(strs[1:-1]):
          substr_new = self.commonPrefix(strs[i], strs[i+1])
          if ("" == substr_old) or ("" == substr_new) or (substr_old[0] != substr_new[0]):
            return ""
          #print "{}:\nold: {}\nnew: {}".format(i, substr_old, substr_new)
          if len(substr_old) > len(substr_new):
            substr_old = substr_new

        substr_new = self.commonPrefix(strs[-2], strs[-1])
        if ("" == substr_old) or ("" == substr_new) or (substr_old[0] != substr_new[0]):
            return ""
        if len(substr_old) > len(substr_new):
            substr_old = substr_new

        return substr_old

【结果】

运行时:62 ms

报告链接:https://leetcode.com/submissions/detail/99591515/

不知道其他拥有 LeetCode 帐号的人能不能看到该报告,所以顺便附图如下:

Your runtime beats 28.44% of python submissions.

但是其实这份代码极丑无比,注意到上面使用了多个 print 用于 debug……勉勉强强处理了一些特殊情况后才完成了程序。有待后续重新梳理逻辑或检查细节,看是否能够剪掉一些不必要的代码。

02

【语言】

Python

【时间】

170410

【思路】

检查 01 版的代码,会发现很奇怪的重复:即明明在 for 循环中似乎已经遍历了 strs[1:-1],并在每次循环结束前处理了特殊情况,为何同样的特殊情况处理代码需要在最终 return 前再次运行一遍才不会出错?

例如,以上一次的代码而言,若输入为 ["a", "a", "b"],答案应该是 "",但若删去 for 循环后、return 前的那部分特殊情况处理代码,将返回 "a"。这就让我很奇怪。于是我便在 for 循环内部加入了若干 print 语句,输出 substr_newsubstr_old,惊讶地发现这唯一执行一次的循环(预期应该是 strs[1]"a"strs[2]"b" 之间的比较),居然输出了 "a" 作为公共前缀。于是我多加了几个输出参数,包括输出了 ii+1,然后才明白了:

原来是对 for (i, item) in enumerate(strs[1:-1]): 有了不恰当的预期!

我原本是希望循环开始时,i==1;但实际上 enumerate(obj) 执行后,是不管 obj 从哪里来的,直接把其首位元素作为第 0 位元素,从而第 1 轮循环中 i==0。这也就解释了为什么会出现上述这种怪异的情况。

知道了问题所在,就好办了。处理了下标问题,删去for 之后 return 之前的代码,重新提交。

【代码】

class Solution(object):
    def commonPrefix(self, str_a, str_b):
      substr = ""
      if len(str_a)>len(str_b):
        length = len(str_b)
      else:
        length = len(str_a)

      k=0
      while ((k<length) and (str_a[k] == str_b[k])):
        substr += str_a[k]
        k += 1

      return substr


    def longestCommonPrefix(self, strs):
        """
        :type strs: List[str]
        :rtype: str
        """
        if 0 == len(strs):
            return ""
        elif 1 == len(strs):
            return strs[0]
        substr_old = self.commonPrefix(strs[0], strs[1])

        for (i, item) in enumerate(strs[1:-1]):
          readInd = i+1
          substr_new = self.commonPrefix(strs[readInd], strs[readInd+1])
          if ("" == substr_old) or ("" == substr_new) or (substr_old[0] != substr_new[0]):
            return ""
          if len(substr_old) > len(substr_new):
            substr_old = substr_new

        return substr_old

【结果】

运行时:79 ms

报告链接:https://leetcode.com/submissions/detail/99685590/

不知道其他拥有 LeetCode 帐号的人能不能看到该报告,所以顺便附图如下:

Your runtime beats 10.27% of python submissions.

虽然慢了一点,不过总算是处理完成了

03

【语言】

Python

【时间】

170415

【思路】

基本算法思路与之前一致。这里仅阐述优化思路:

在得到 strs[0]strs[1] 之间的公共子串 commonPrefix后,只要比较 commonPrefixstrs[i] 即可;若提前发现不同,截断到不同位置。最后返回即为所求。

当然,有一些特殊情况也需要处理的,例如输入为空,或者输入为若干单字符,输入仅有 1 个字符等等问题,不再赘述。

【代码】

class Solution(object):
    def longestCommonPrefix(self, strs):
        """
        :type strs: List[str]
        :rtype: str
        """
        if len(strs) == 0:
          return ""
        elif len(strs) == 1:
          return strs[0]
        elif (len(strs[0]) == 0) or (len(strs[1]) == 0):
          return ""

        commonprefix = ""
        (i, j) = (0, 0)
        while (i < len(strs[0])) and (j < len(strs[1])):
          if strs[0][i] == strs[1][j]:
            commonprefix += strs[0][i]
            (i, j) = (i+1, j+1)
          else:
            break
        if (len(strs) == 2) or ("" == commonprefix):
          return commonprefix

        length = len(commonprefix)
        for s in strs:

          if len(s) == 0:
            return ""
          elif len(s) == 1:
            if s[0] == commonprefix[0]:
              commonprefix = s[0]
              length = 1
              continue
            else:
              return ""

          (i, j) = (0, 0)
          while (i < length) and (j < len(s)):
            if commonprefix[i] == s[j]:
              (i, j) = (i+1, j+1)
            else:
              length = i
            commonprefix = commonprefix[:length]
 
        return commonprefix

【结果】

运行时:52 ms

报告链接:https://leetcode.com/submissions/detail/100169585/

不知道其他拥有 LeetCode 帐号的人能不能看到该报告,所以顺便附图如下:

Your runtime beats 53.08% of python submissions.

不是最优解。多尝试了几次,例如下述几个报告(1, 2, 3),这个优化思路似乎没有起到明显的作用。

还是需要研究一下参考答案。

04

【语言】

Python

【时间】

170808

【思路】

空间复杂度为 O(n)(n 为字符串数组长度),时间复杂度为 O(n)。

思路是:最长公共子串,即从首字符开始、在所有字符串中均出现的子串。那么只要:

  1. 从 0 位开始,遍历过字符串的每一位,每一次比较同一位置的所有字符;若在同一位置上只有一个字符,那么显然到当前位置为止,都是公共子串;直到某一位置,出现了 1 个以上的字符,说明最长串到前一位为止
  2. 由于最长子串不会超过所有字符串中最短的那个字符串,因此首先要确定最短字符串的长度,这是遍历字符位的上限

利用 Python 的 list 和 set,能很容易知道所有字符串在同一位置上的独立字符到底有多少。

【代码】

class Solution(object):
    def longestCommonPrefix(self, strs):
        """
        :type strs: List[str]
        :rtype: str
        """
        commons = ""
        maxlens = [len(s) for s in strs]
        if 0 == len(maxlens):
            return commons
        else:
            maxlen = min(maxlens)
            if 0 == maxlen:
                return commons
 
        for i in range(maxlen):
            tset = list(set([s[i] for s in strs]))
            if len(tset) > 1:
                return commons
            else:
                commons += tset[0]

        return commons

【结果】

运行时:35 ms

报告链接:https://leetcode.com/submissions/detail/112975588/

不知道其他拥有 LeetCode 帐号的人能不能看到该报告,所以顺便附图如下:

Your runtime beats 93.91 % of python submissions.

00

参考解法:

自己实现一遍最优解:

+[date-language] 。。。

上一篇 下一篇

猜你喜欢

热点阅读