算法:最长公共前缀(longest-common-prefix)。

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 ""

示例

输入: ["flower","flow","flight"]
输出: "fl"

示例

输入: ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀。

说明

所有输入只包含小写字母 a-z 。

方法一:横向扫描


LCP(S1 …Sn) 表示字符串 S1 …Sn 的最长公共前缀。

可以得到以下结论:

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

基于该结论,可以得到一种查找字符串数组中的最长公共前缀的简单方法。依次遍历字符串数组中的每个字符串,对于每个遍历到的字符串,更新最长公共前缀,当遍历完所有的字符串以后,即可得到字符串数组中的最长公共前缀。

如果在尚未遍历完所有的字符串时,最长公共前缀已经是空串,则最长公共前缀一定是空串,因此不需要继续遍历剩下的字符串,直接返回空串即可。

class Solution {
    public String longestCommonPrefix(String[] strs) {
        if (strs == null || strs.length == 0) {
            return "";
        }
        String prefix = strs[0];
        int count = strs.length;
        for (int i = 1; i < count; i++) {
            prefix = longestCommonPrefix(prefix, strs[i]);
            if (prefix.length() == 0) {
                break;
            }
        }
        return prefix;
    }

    public String longestCommonPrefix(String str1, String str2) {
        int length = Math.min(str1.length(), str2.length());
        int index = 0;
        while (index < length && str1.charAt(index) == str2.charAt(index)) {
            index++;
        }
        return str1.substring(0, index);
    }
}

复杂度分析

  • 时间复杂度:O(mn),其中 m 是字符串数组中的字符串的平均长度,n 是字符串的数量。最坏情况下,字符串数组中的每个字符串的每个字符都会被比较一次。
  • 空间复杂度:O(1)。使用的额外空间复杂度为常数。

方法二:纵向扫描

方法一是横向扫描,依次遍历每个字符串,更新最长公共前缀。另一种方法是纵向扫描。纵向扫描时,从前往后遍历所有字符串的每一列,比较相同列上的字符是否相同,如果相同则继续对下一列进行比较,如果不相同则当前列不再属于公共前缀,当前列之前的部分为最长公共前缀。

class Solution {
    public String longestCommonPrefix(String[] strs) {
        if (strs == null || strs.length == 0) {
            return "";
        }
        int length = strs[0].length();
        int count = strs.length;
        for (int i = 0; i < length; i++) {
            char c = strs[0].charAt(i);
            for (int j = 1; j < count; j++) {
                if (i == strs[j].length() || strs[j].charAt(i) != c) {
                    return strs[0].substring(0, i);
                }
            }
        }
        return strs[0];
    }
}

复杂度分析

  • 时间复杂度:O(mn),其中 m 是字符串数组中的字符串的平均长度,n 是字符串的数量。最坏情况下,字符串数组中的每个字符串的每个字符都会被比较一次。
  • 空间复杂度:O(1)。使用的额外空间复杂度为常数。

方法三:分治


注意到 LCP 的计算满足结合律,有以下结论:

                                           LCP(S1…Sn)=LCP(LCP(S1…Sk),LCP(Sk+1…Sn))

其中 LCP(S1…Sn) 是字符串 S1…Sn的最长公共前缀,1<k<n。

基于上述结论,可以使用分治法得到字符串数组中的最长公共前缀。对于问题 LCP(Si⋯Sj),可以分解成两个子问题 LCP(Si…Smid)LCP(Smid+1…Sj),其中 mid= (i+j)/2。对两个子问题分别求解,然后对两个子问题的解计算最长公共前缀,即为原问题的解。

class Solution {
    public String longestCommonPrefix(String[] strs) {
        if (strs == null || strs.length == 0) {
            return "";
        } else {
            return longestCommonPrefix(strs, 0, strs.length - 1);
        }
    }

    public String longestCommonPrefix(String[] strs, int start, int end) {
        if (start == end) {
            return strs[start];
        } else {
            int mid = (end - start) / 2 + start;
            String lcpLeft = longestCommonPrefix(strs, start, mid);
            String lcpRight = longestCommonPrefix(strs, mid + 1, end);
            return commonPrefix(lcpLeft, lcpRight);
        }
    }

    public String commonPrefix(String lcpLeft, String lcpRight) {
        int minLength = Math.min(lcpLeft.length(), lcpRight.length());       
        for (int i = 0; i < minLength; i++) {
            if (lcpLeft.charAt(i) != lcpRight.charAt(i)) {
                return lcpLeft.substring(0, i);
            }
        }
        return lcpLeft.substring(0, minLength);
    }
}

复杂度分析

  • 时间复杂度:O(mn),其中 m 是字符串数组中的字符串的平均长度,n 是字符串的数量。时间复杂度的递推式是 T(n)=2⋅T( n/2)+O(m),通过计算可得 T(n)=O(mn)。
  • 空间复杂度:O(m log n),其中 m 是字符串数组中的字符串的平均长度,n 是字符串的数量。空间复杂度主要取决于递归调用的层数,层数最大为 log n,每层需要 m 的空间存储返回结果。

方法四:二分查找

显然,最长公共前缀的长度不会超过字符串数组中的最短字符串的长度。用 minLength 表示字符串数组中的最短字符串的长度,则可以在 [0,minLength] 的范围内通过二分查找得到最长公共前缀的长度。每次取查找范围的中间值 mid,判断每个字符串的长度为 mid 的前缀是否相同,如果相同则最长公共前缀的长度一定大于或等于 mid,如果不相同则最长公共前缀的长度一定小于 mid,通过上述方式将查找范围缩小一半,直到得到最长公共前缀的长度。

class Solution {
    public String longestCommonPrefix(String[] strs) {
        if (strs == null || strs.length == 0) {
            return "";
        }
        int minLength = Integer.MAX_VALUE;
        for (String str : strs) {
            minLength = Math.min(minLength, str.length());
        }
        int low = 0, high = minLength;
        while (low < high) {
            int mid = (high - low + 1) / 2 + low;
            if (isCommonPrefix(strs, mid)) {
                low = mid;
            } else {
                high = mid - 1;
            }
        }
        return strs[0].substring(0, low);
    }

    public boolean isCommonPrefix(String[] strs, int length) {
        String str0 = strs[0].substring(0, length);
        int count = strs.length;
        for (int i = 1; i < count; i++) {
            String str = strs[i];
            for (int j = 0; j < length; j++) {
                if (str0.charAt(j) != str.charAt(j)) {
                    return false;
                }
            }
        }
        return true;
    }
}

复杂度分析

  • 时间复杂度:O(mn log m),其中 m 是字符串数组中的字符串的最小长度,n 是字符串的数量。二分查找的迭代执行次数是 O(log m),每次迭代最多需要比较 mn 个字符,因此总时间复杂度是 O(mn log m)。
  • 空间复杂度:O(1)。使用的额外空间复杂度为常数。
最长公共前缀Longest Common Prefix,简称LCP)是指在一组字符串中,所有字符串共有的、最长前缀子串。 C++实现LCP的一种简单方法是使用字符数组,遍历字符串数组中的每个字符串的每个字符,直到出现不同的字符或到达某个字符串的结尾。代码如下: ```c++ #include <iostream> #include <string> #include <vector> using namespace std; string longestCommonPrefix(vector<string>& strs) { if (strs.empty()) { return ""; } int n = strs.size(); int len = strs[0].size(); for (int i = 0; i < len; i++) { char c = strs[0][i]; for (int j = 1; j < n; j++) { if (strs[j][i] != c || i == strs[j].size()) { return strs[0].substr(0, i); } } } return strs[0]; } int main() { vector<string> strs = {"flower", "flow", "flight"}; string lcp = longestCommonPrefix(strs); cout << "Longest Common Prefix: " << lcp << endl; return 0; } ``` 在这个示例中,我们使用了一个字符串数组`strs`来存储所有的字符串。然后我们定义一个函数`longestCommonPrefix`来计算最长公共前缀。如果字符串数组为空,我们直接返回一个空字符串。接着,我们遍历第一个字符串中的每个字符。对于每个字符,我们检查所有其他字符串在该位置上是否与它相同。如果有任何一个字符串在该位置上与第一个字符串不同,或者该字符串已经到达了某个字符串的结尾,我们就返回第一个字符串的前缀子串。如果我们遍历完了第一个字符串,那么第一个字符串就是所有字符串的最长公共前缀。 在这个示例中,我们使用了`substr`函数来截取字符串的前缀子串。如果最长公共前缀为空字符串,该函数会返回一个空字符串。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值