LeetCode小白菜笔记[5]:Longest Common Prefix

LeetCode小白菜笔记[5]:Longest Common Prefix

14. Longest Common Prefix [Easy]

题目:Write a function to find the longest common prefix string amongst an array of strings.

这个题目要求在一组字符串中所有字符串的最长公共前缀,如 {‘abc’,‘abdf’,‘abandon’} 的返回值应该是‘ab’。解决该方法最直接的方法之一就是逐次比较,并记录下当前已经比较过的字符串的最长公共前缀,直到所有的字符串都比较完成。这样实际上最长公共前缀的长度随着比较的字符串数量的增加而变短。用这种方法写代码如下:(注意各种特殊情况,如空组,即没有字符串,返回“”,即空字符串;如果只有一个字符串,那么应该返回自身;如果有两个及以上,则应该比较后返回最长公共前缀)

 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) == 2:
            cnt = 0
            minlen = min(len(strs[0]),len(strs[1]))
            while (cnt < minlen and strs[0][cnt] == strs[1][cnt]):
                cnt += 1
            if cnt == 0 :
                return ""
            else:
                return strs[0][:cnt]
        else:
            prefix = strs[0]
            for idx in range(1,len(strs)):
                cnt = 0
                while (cnt < len(strs[idx]) and cnt < len(prefix) and strs[idx][cnt] == prefix[cnt]):
                    cnt += 1
                if cnt == 0 :
                    return ""
                else:
                    prefix = prefix[:cnt]
            return prefix

结果如下:

这里写图片描述

运行通过,这是修改过几次之后的代码,因为之前没有考虑到有些特殊情况,如空list或空str之类。

这道题目的Solution里面提供了几种方法,主要有 水平扫描,垂直扫描,分治算法,二分查找

  1. 水平扫描(Horizontal Scanning)

    水平扫描的基本思路就是上面的代码的思路,即两两比较,把找最长公共前缀的操作称为LCP的化,实际上该方法就是:

    LCP(S1…Sn) = LCP(LCP(LCP(S1,S2),S3),…Sn)。


    这里写图片描述

    时间复杂度为 O(S), 其中 S 为所有string中的所有char的数目。空间复杂度为常数复杂度,因为没有用到变长的空间。

  2. 垂直扫描(Vertical Scanning)

    垂直扫描是为了避免水平扫描的worst-case,即如果前面的n-1项都是一样的,而最后一项只有很短,这样来说用水平扫描的方法就不适当。考虑每个string的相同位置上的元素,遍历所有string,取第一个元素,看是否都相同,如果相同则属于LCP,以此类推,直到发现一个不common的位置。

    时间复杂度worst-case和上面一样也是O(S),但是最好的情况为 n * minlen,其中minlen时string的list中的最短的stirng的长度。空间为常数复杂度。

  3. 分治算法(Divide and Conquer)

    分治算法基本思路:

    LCP(S1…Sn) = LCP(LCP(S1…Sk),LCP(Sk+1…Sn)),将所有的string分成 left 和 right 两部分。每一部分继续分割,直到全部分完。然后 conquer ,即从叶子两两计算,得到的返回上一层的 LCPleft 和 LCPright ,从而通过递归调用,得到结果。


    这里写图片描述

    T(n)=2T(n/2)+O(m),故时间复杂度为 O(S) 。其中 S = m * n。best-case 为 O(minlen * n)

    空间复杂度 O(m * logn)。 ​

  4. 二分查找(Binary Search)

    二分查找找到最短的sring,从中间分开,只看左边,如果所有的string都有这些char,那么说明左边是LCP的子串,故将右边二分,继续查找;如果不是所有的都有左边这一串,那么说明右边不可能有被包含在LCP中的char,因此扔掉右边,对左边二分。


    这里写图片描述

    时间复杂度 worst-case 为O(S * log(m)),S = m*n

    空间复杂度为常数项。

这个问题的一个变形即给定一组string S,新来一个string q,找到 q 和 S 中的LCP。常见的自动补全功能本质上即为此类问题。涉及到 Prefix trie 的结构,即每个node都表示一个所有下面的node所共用的LCP。

总结:

掌握递归函数的复杂度计算,了解Prefix trie。

THE END

2017/12/15 Fri 17:38

posted @ 2017-12-15 17:45  毛利小九郎  阅读(83)  评论(0编辑  收藏  举报