「LeetCode」14.最长公共前缀

205 阅读2分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。

题目描述🌍

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

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

示例 1

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

示例 2

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

提示

  • 1 <= strs.length <= 200
  • 0 <= strs[i].length <= 200
  • strs[i] 仅由小写英文字母组成

最大差异比较法🚀

解题思路

利用 sort() 排序方法对字符串数组进行排序,这样越靠近的两个字符串之间的差异就越小(也就是公共前缀越长),反之,离得越远的两个字符串之间的差异越大。

借助这个思想,我们只需要比较字符串数组排序后的首个字符串 strs[0] 与最后一个字符串 strs[strs.length-1] 即可,这样就可以精确地找出数组中的最长公共前缀。

代码

Java

class Solution {
    public String longestCommonPrefix(String[] strs) {
        Arrays.sort(strs);

        String firstStr = strs[0];
        String lastStr = strs[strs.length - 1];

        StringBuilder common = new StringBuilder("");
        // 必须是 firstStr 的长度, 不懂可以试着举两个例子: fliwer flow | flo flow
        int length = firstStr.length();
        // 直接比较第一个字符串与最后一个字符串即可直接判断出最长公共前缀(最大差异比较)
        for (int i = 0; i < length && firstStr.charAt(i) == lastStr.charAt(i); i++) {
            common.append(firstStr.charAt(i));
        }

        return common.toString();
    }
}

C++

Beats 100%

class Solution {
public:
    string longestCommonPrefix(vector<string> &strs) {
        std::sort(strs.begin(), strs.end());

        string firstStr = strs[0];
        string lastStr = strs[strs.size() - 1];

        int length = firstStr.length();
        string s;
        for (int i = 0; i < length && firstStr[i] == lastStr[i]; ++i) {
            // s[i] = firstStr[i] ❌
            s += firstStr[i];
        }
        return s;
    }
};

时间复杂度:O(nlogn)O(n·logn),排序算法所耗费的时间

空间复杂度:O(1)O(1)

纵向扫描🍀

解题思路

低情商:暴力求解
高情商:纵向扫描

开个玩笑。言归正传,其实这种解法是最容易想到的,即我们按顺序逐列比较每个字符串同一列的字符。这里不再采用 sort() 排序方法,不然极大地简化了循环终止的判断条件。

⭐唯一需要注意的点:什么时候会终止循环?

  1. 同一列出现不同的字符
  2. 比较到某一列时,已经超出某一行字符串的长度了

所以判断条件为: column == strs[row].length() || strs[0].charAt(column) != strs[row].charAt(column)

如果文字抽象,可以参考下图(图片来源于 LeetCode 官网)

代码

Java

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

        int count = strs.length;
        int length = strs[0].length();
        for (int column = 0; column < length; column++) {
            for (int row = 1; row < count; row++) {
                if (column == strs[row].length() || strs[0].charAt(column) != strs[row].charAt(column)) {
                    // substring(): [左闭右开]区间
                    return strs[0].substring(0, column);
                }
            }
        }
        return strs[0];
    }
}

C++

class Solution {
public:
    string longestCommonPrefix(vector<string> &strs) {
        if (strs.empty()) {
            return "";
        }

        int count = strs.size();
        int length = strs[0].length();
        for (int column = 0; column < length; ++column) {
            for (int row = 0; row < count; ++row) {
                if (column == strs[row].length() || strs[0][column] != strs[row][column]) {
                    return strs[0].substr(0, column);
                }
            }
        }
        // strs[0] happens to be the longest common prefix
        return strs[0];
    }
};

时间复杂度:O(mn)O(m·n),m 是字符串数组中的平均长度,n 是字符串的个数。

空间复杂度:O(1)O(1)

横向扫描🚢

解题思路

首先求出前两个字符串的最长公共前缀,然后求出其 (前两者的最长公共前缀) 与第三个字符串的最长公共前缀,对于每个遍历到的字符串,就更新一次最长公共前缀。直到遍历完所有字符串,即可得到字符串数组中的最长公共前缀了。

同样,下图简明扼要地描绘了横向扫描的过程。

代码

Java

class Solution {
	public String longestCommonPrefix(String[] strs) {
        if (strs.length == 0) {
            return "";
        }
        String tempLongestStr = strs[0];
        for (int i = 0; i < strs.length - 1; i++) {
            tempLongestStr = searchLongestPrefix(tempLongestStr, strs[i + 1]);
            // 如果过程中出现空字符串, 则不存在最长公共前缀, 即 "" (这一步为了减少判断)
            if ("".equals(tempLongestStr))
                return "";
        }
        return tempLongestStr;
    }

    public String searchLongestPrefix(String str1, String str2) {
        if ("".equals(str1) || "".equals(str2)) {
            return "";
        }
        int minLength = Math.min(str1.length(), str2.length());
        int i = 0;
        while (i < minLength && str1.charAt(i) == str2.charAt(i)) {
            i++;
        }
        return str1.substring(0, i);
    }
}

C++

class Solution {
public:
    string longestCommonPrefix(vector<string> &strs) {
        if (strs.empty()) {
            return "";
        }
        string tempLongestStr = strs[0];
        int length = strs.size();
        for (int i = 1; i < length; ++i) {
            tempLongestStr = searchLongestPrefix(tempLongestStr, strs[i]);
            if (tempLongestStr.empty())
                return "";
        }
        return tempLongestStr;
    }

    // 返回 str1 & str2 的最长公共前缀
    string searchLongestPrefix(string str1, string str2) {
        if (str1.empty() || str2.empty())
            return "";
        int min_length = min(str1.length(), str2.length());
        int i = 0;
        while (i < min_length && str1[i] == str2[i])
            ++i;
        return str1.substr(0, i);
    }
};

时间复杂度:O(mn)O(m·n),m 是字符串数组中的平均长度,n 是字符串的个数。

空间复杂度:O(1)O(1)

分治法❓

这题用「分治法」解决倒也是个不错的选择,后期补上代码。

图解分治

最后🌅

该篇文章为 「LeetCode」 系列的 No.6 篇,在这个系列文章中:

  • 尽量给出多种解题思路
  • 提供题解的多语言代码实现
  • 记录该题涉及的知识点

👨‍💻争取做到 LeetCode 每日 1 题,所有的题解/代码均收录于 「LeetCode」 仓库,欢迎随时加入我们的刷题小分队!