給定一個字串,求出其最長的重複子串的長度
阿新 • • 發佈:2018-12-24
題目來源:這是程式設計珠璣上的一道題目
概念
字串的字首和字尾
例如字串 s =“abcdeabc”
則s的字首:
“a”,“ab”,“abc”,“abcd”,“abcde”,“abcdea”,“abcdeab”,“abcdeabc”其中除了s字串本身(“abcdeabc”)之外,其他的字首為s的真字首。
s的字尾:
“abcdeabc”,“bcdeabc”,“cdeabc”,“deabc”,“eabc”,”abc”,“bc”,“c”其中除了s字串本身,其他字首為s的真字尾。
注:這裡字首和字尾中不包括空字串
為什麼用字尾
對於字串s,它所有可能的子串為:
其中,1,2,3,4, 5, 6,7,8標識的是字串s的字尾。
通過觀察可知:
字尾字串的字首已經包含了字串s的部分子串(例如:abcdeabc 的字首有
a,ab,abc,abcd,abcde,abcdea,abcdeab;由圖上可知字串s的子串也包括abcdeabc的字首);因此可知,只需要求出字串s的所有後綴就可間接的表示了s所有的子串;(因為我們的目的就是求出s所有子串中最長的重複子串的長度)
那麼,可以通過比較字串的s的字尾中相同的字首就可以求出s中最長的重複子串。
解題思路
- 儲存s字串的所有後綴
- 對所有後綴進行排序(自然排序)
- 比較排序後的相鄰的字尾的最長公共子串(兩個字尾從第一個字元開始的就相等得到公共子串),求出最長的公共子串
程式碼
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
size_t getCommLen(string str1, string str2) {
size_t i;
for (i = 0; i < str1.size() && i < str2.size(); i++) {
if (str1[i] != str2[i])
break ;
}
return i;
}
int main()
{
string str;
cin >> str;
vector<string> strs;
for (size_t i = 0; i < str.size(); i++) {
strs.push_back(str.substr(i));
}
sort(strs.begin(), strs.end());
size_t maxLen = 0;
for (size_t i = 0; i < strs.size()-1; i++) {
size_t len = getCommLen(strs[i], strs[i+1]);
maxLen = max(maxLen, len);
}
cout << maxLen << endl;
return 0;
}
問題
對於字串s = “abcdefabcdefabc”求出的最長重複子串的長度為 9。
注意字尾字串:”abcdefabc”和“abcdefabcdefabc”,很明顯這兩字串中有重疊的部分。
所以這個演算法並不適用所有的字串。
時空複雜度
空間複雜度:求長度為n的字串的字尾,需要O(n)的空間複雜度
時間複雜度:sort呼叫排序的時間複雜度為O(nlogn),其他的操作都為O(n),因此該演算法的時間複雜度為O(nlogn)。