题目来源
题目描述
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
}
};
题目解析
横向扫描
用 L C P ( S 1 . . . S n ) LCP(S_1... S_n) LCP(S1...Sn)表示 S 1 . . . S n S_1...S_n S1...Sn的最长公共前缀。
可以得出如下结论:
基于该结论,可以得到一种查找字符串数组中的最长公共前缀的简单方法。依次遍历字符串数组中的每个字符串,对于每个遍历到的字符串,更新最长公共前缀,当遍历完所有的字符串以后,即可得到字符串数组中的最长公共前缀。
如果在尚未遍历完所有的字符串时,最长公共前缀已经是空串,则最长公共前缀一定是空串,因此不需要继续遍历剩下的字符串,直接返回空串即可。
class Solution{
public:
std::string helper(const std::string& str1, const std::string & str2){
int length = std::min(str1.size(), str2.size());
int idx = 0;
while (idx < length && str1[idx] == str2[idx]){
++idx;
}
return str1.substr(0, idx);
}
std::string longestCommonPrefix(std::vector<std::string> & strs){
if(strs.empty()){
return "";
}
std::string prefix = strs[0];
for (int i = 1; i < strs.size(); ++i) {
prefix = helper(prefix, strs[i]);
if(prefix.empty()){
break;
}
}
return prefix;
}
};
纵向扫描
方法一是横向扫描,依次遍历每个字符串,更新最长公共前缀。另一种方法是纵向扫描。纵向扫描时,从前往后遍历所有字符串的每一列,比较相同列上的字符是否相同,如果相同则继续对下一列进行比较,如果不相同则当前列不再属于公共前缀,当前列之前的部分为最长公共前缀。
class Solution{
public:
std::string longestCommonPrefix(std::vector<std::string> & strs){
if(strs.empty()){
return "";
}
for (int i = 0; i < strs[0].size(); ++i) {
char c = strs[0][i];
for (int j = 1; j < strs.size(); ++j) {
if(j == i || c != strs[j][i]){
return strs[0].substr(0, i);
}
}
}
return strs[0];
}
};
分治
注意到LCP的计算满足结合律,有以下结论
用 L C P ( S 1 . . . S n ) LCP(S_1... S_n) LCP(S1...Sn)表示 S 1 . . . S n S_1...S_n S1...Sn的最长公共前缀, 1 < k < n 1 < k < n 1<k<n
基于上面结论,可以使用分治法得到字符串数组中的最长公共前缀。
- 对于问题 L C P ( S i . . . S j ) LCP(S_i... S_j) LCP(Si...Sj),可以分解为两个子问题 L C P ( S i . . . S m i d ) LCP(S_i... S_{mid}) LCP(Si...Smid)与 L C P ( S m i d + 1 . . . S j ) LCP(S_{mid+1}... S_{j}) LCP(Smid+1...Sj),其中 m i d = ( i + j ) / 2 mid = (i + j) / 2 mid=(i+j)/2。
- 对两个子问题分别求解,然后对两个子问题的解计算最长公共前缀,就是原问题的解
class Solution{
std::string getPrefix(std::string lcpLeft, std::string lcpRight){
int length = std::min(lcpLeft.length(), lcpRight.length());
for (int i = 0; i < length; ++i) {
if(lcpLeft[i] != lcpRight[i]){
return lcpLeft.substr(0, i);
}
}
return lcpLeft.substr(0, length);
}
std::string process(std::vector<std::string> & strs, int start, int end){
if(start == end){
return strs[start];
}
int mid = (start + end) / 2;
std::string lcpLeft = process(strs, start, mid);
std::string lcpRight = process(strs, mid + 1, end);
return getPrefix(lcpLeft, lcpRight);
}
public:
std::string longestCommonPrefix(std::vector<std::string> & strs){
if(strs.empty()){
return "";
}
return process(strs, 0, strs.size() - 1);
}
};