写在前面
本专栏专注于分析与讲解【面试经典150】算法,两到三天更新一篇文章,欢迎催更……
专栏内容以分析题目为主,并附带一些对于本题涉及到的数据结构等内容进行回顾与总结,文章结构大致如下,部分内容会有增删:
- Tag:介绍本题牵涉到的知识点、数据结构;
- 题目来源:贴上题目的链接,方便大家查找题目并完成练习;
- 题目解读:复述题目(确保自己真的理解题目意思),并强调一些题目重点信息;
- 解题思路:介绍一些解题思路,每种解题思路包括思路讲解、实现代码以及复杂度分析;
- 知识回忆:针对今天介绍的题目中的重点内容、数据结构进行回顾总结。
Tag
【数组】【字符串】
题目来源
![](https://i-blog.csdnimg.cn/blog_migrate/d04f09a2259398ec49146cf8d4593be6.png)
解题思路
方法一:横向扫描
思路
我们需要统计字符串数组中所有字符串的公共前缀的最长字符。为此我们可以先统计字符串数组中前两个字符串的最长公共前缀,然后将得到的最长公共前缀字符串与第三个字符串在一起统计最长公共前缀…,如此循环下去,直到统计与最后一个字符串的最长公共前缀。
代码
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
if (!strs.size()) { // 特判
return "";
}
string prefix = strs[0];
int n = strs.size();
for (int i = 1; i < n; ++i) {
prefix = longestCommonPrefix(prefix, strs[i]);
if (!prefix.size()) { // 如果某个时刻得到的最长公共前缀为空,可以提前退出
break;
}
}
return prefix;
}
// longestCommonPrefix 重载函数,统计两个字符串的最长公共前缀
string longestCommonPrefix(const string& str1, const string& str2) {
int m = min(str2.size(), str2.size());
int idx = 0;
while (idx < m && str1[idx] == str2[idx]) {
++idx;
}
return str1.substr(0, idx);
}
};
复杂度分析
时间复杂度: O ( m n ) O(mn) O(mn), m m m 是字符串数组中字符串的平均长度, n n n 字符串数组的长度。
空间复杂度: O ( 1 ) O(1) O(1)。
方法二:纵向扫描
思路
纵向扫描是比较容易想到的方法。纵向扫描时,从前往后遍历所有字符串的每一列,比较相同列上的字符是否相同,如果相同则继续对下一列进行比较,如果不相同则当前列不再属于公共前缀,当前列之前的部分为最长公共前缀。
代码
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
if (!strs.size()) { // 特判
return "";
}
int n = strs[0].size();
int m = strs.size();
for (int i = 0; i < n; ++i) {
char c = strs[0][i];
for (int j = 1; j < m; ++j) {
if (i == strs[j].size() || strs[j][i] != c) {
return strs[0].substr(0, i);
}
}
}
return strs[0];
}
};
复杂度分析
时间复杂度: O ( m n ) O(mn) O(mn), m m m 是字符串数组中字符串的平均长度, n n n 字符串数组的长度。
空间复杂度: O ( 1 ) O(1) O(1)。
方法三:分治
思路
还可以利用分治的思想来解决本题。计算字符串数组的最长公共前缀,可以先计算前半部分字符串的最长公共前缀,再计算后半部分的最长公共前缀,最后计算这两部分最长公共前缀的公共前缀。对于前半部分和后半部分各自的最长公共前缀也可以通过先分再计算的方法。
具体实现见代码。
代码
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
if (!strs.size()) { // 特判
return "";
}
return longestCommonPrefix(strs, 0, strs.size() - 1);
}
// longestCommonPrefix 的重载函数,分与调用计算公共前缀
string longestCommonPrefix(const vector<string>& strs, int start, int end) {
if (start == end) {
return strs[start];
}
int mid = (start + end) / 2;
string lcpLeft = longestCommonPrefix(strs, start, mid);
string lcpRight = longestCommonPrefix(strs, mid + 1, end);
return commonPrefix(lcpLeft, lcpRight);
}
// 计算两字符的公共前缀
string commonPrefix(const string& str1, const string& str2) {
int n = min(str1.size(), str2.size());
for (int i = 0; i < n; ++i) {
if (str1[i] != str2[i]) {
return str2.substr(0, i);
}
}
return str1.substr(0, n);
}
};
复杂度分析
时间复杂度: O ( m n ) O(mn) O(mn), m m m 是字符串数组中字符串的平均长度, n n n 字符串数组的长度。时间复杂度的递推式为 T ( n ) = 2 ⋅ T ( n 2 ) + O ( m ) T(n) = 2 \cdot T(\frac{n}{2}) + O(m) T(n)=2⋅T(2n)+O(m),计算得 T ( n ) = O ( m n ) T(n)=O(mn) T(n)=O(mn)。
空间复杂度: O ( m l o g n ) O(mlogn) O(mlogn)。
写在最后
如果您发现文章有任何错误或者对文章有任何疑问,欢迎私信博主或者在评论区指出 💬💬💬。
如果大家有更优的时间、空间复杂度的方法,欢迎评论区交流。
最后,感谢您的阅读,如果有所收获的话可以给我点一个 👍 哦。