1. 程式人生 > >給定一個字串,求出其最長的重複子串的長度

給定一個字串,求出其最長的重複子串的長度

題目來源:這是程式設計珠璣上的一道題目

概念

字串的字首和字尾

例如字串 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中最長的重複子串。

解題思路

  1. 儲存s字串的所有後綴
  2. 對所有後綴進行排序(自然排序)
  3. 比較排序後的相鄰的字尾的最長公共子串(兩個字尾從第一個字元開始的就相等得到公共子串),求出最長的公共子串

程式碼

#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)。

參考題目