多個字串的最長公共字串

2021-05-28 15:17:02 字數 3253 閱讀 3852

如果所有字串的長度之和是l,則下面介紹的這個演算法的平均效率o(l * logl),但是最壞情況下可能會再乘以o(l),l是每個字串的平均長度。

首先對於每個字串,取出以每個字元開頭,到字串尾的子串。比如字串「acb」,從中取出的子串有「acb」、「cb」和「b」。如果所有字串的總長度為l,則總共就有l個子串。我們把這些子串存在乙個名為sub的陣列中。(注意,最好用c風格的字元,這樣可以直接引用每個子串的首位址,不用把這些子串另外轉存。)

接下來就是主要花時間的步驟:把這l個子串排序。如果用快速排序的話時間就是o(l * logl)。比如共有三個字串「acb」、「cbb」和「dcba」,從它們中取出的子串有:「acb」、「cb」、「b」、「cbb」、「bb」、「b」、「dcba」、「cba」、「ba」和「a」。把它們排序後的結果如下(為了看得清楚,我把字串堅著放):

sub: 0 1 2 3 4 5 6 7 8 9

aab

bbbc

c

c

dca

bbb

b

c

ba

b

b

a

但是這裡有個問題,這個排序中的每次比較都是字串的比較,它們會不會花很多時間呢。其實比較兩個字串的時間取決於它們開頭有幾個字元是相同的,如果相同的少,則很快就可以比完了。假設字串的每個字元都是26個英文本母之一,則兩個字串開頭有1個字元相同的概率是1 / 26,有2個字元相同的概率是1 / 26

2;學過概率論就知道,這樣兩個字串開頭相同的字元數的期望值為1 / 26 + 2 / 26

2 + 3 / 26

3 ... < 1。也就是說比較兩個字串的平均時間是常數極的。

但以上說的只是平均情況。在最最極端的情況下(雖然概率上不幾乎不可能出現,但是出題的人一定會出這種極限資料),可能所有字串都只含有同樣的字元(比如要你求「aaaa」、「aaaa」和「aaaa」和最大公共子串),那麼在排序演算法中比較每兩個子串時,都至少要把其中乙個子串從頭讀到尾,時間數量級大約就是所有字串的平均長度,也就是本文一開始說的,間時複雜度會上公升到o(l * l * logl)。

把所有子串排好了序,就離答案很近了。不難想象,我們要求的最大公共子串一定是陣列sub中相鄰的幾項的最大公共字首。比如在上面的例子中,最大公共子串是「cb」,它就是陣列sub中下標6、7和8這三項的最大公共字首。

其實陣列sub中需要存放的不只是每個子串的首位址,還需要存放每個子串屬於第幾個原字串(在上面的例子中用顏色來表示),這在最後乙個步驟中需要用到。

下面是最後的尋找步驟:在陣列sub中,對於每一段相鄰的且覆蓋了所有原字串的元素(而且只要最小段,也就是去掉該段的首、尾任意乙個元素,它就不能覆蓋所有原字串了),求出該段首尾兩個元素的最大公共字首,即找到了所有原字串的乙個公共子串。列舉所有符合要求的段,就可以找出所有原字串的最大公共子串。在前面的例子中,符合要求的段有[0,2]、[2,4]、[3,5]、[4,6]、[5,7]和[6,8],經過比較,在[6,8]這一段就找到了我們要求的最大公共子串「cb」。

這一步驟所花的時間是o(l),具體流程雖然不難,但是用文字說起來有點麻煩,所以還是詳見後面的**吧。可見這一步花的時間比起總的o(l * logl)算不了什麼。

到這裡算是講完了。在

uva 11107有乙個類似的問題,雖然有點不一樣,但是道理是完全一樣的。為了彌補我上面沒有講清楚的最後乙個步驟,下面附上我這題的**。

#include

#include

#include

using namespace std;

const int max_len = 1004;//notice, the test data is wrong.

//the bound is 1004 but not 1000.

const int max_str = 100;

struct substr ;

char g_str[max_str][max_len + 1];

int g_strcnt;

substr g_substr[max_str * max_len];

int g_substrcnt;

int substrcmp(const void* a, const void* b)

int commonlen(const substr& a, const substr& b)

return len;

}void printstr(const char* str, int len)

printf("\n");

}void initsubstr()

}qsort(g_substr, g_substrcnt, sizeof(substr), substrcmp);

}int findlongest()

cover[head->num]++;

head++;

}while (covercnt > half)

tail++;

}if (covercnt == half) }}

return longest;

}//the work flow of this function is just like "findlongest()".

void printcommon(int longest)

cover[head->num]++;

head++;

}while (covercnt > half)

tail++;

}if (covercnt == half) }}

}bool input()

}return hasnext;

}void solve()

else

}int main()

solve();

cnt++;

}return 0;

}

求兩個字串最長公共字串

package com.test 求兩個字串最長公共字串,演算法 兩個字串形成乙個矩陣,將兩個字元不匹配的位置標記為0,c i j 中兩個字元相匹配的位置標記為n,其中n c i 1 j 1 1,其中沿正對角線方向最長的序列為兩個字串的最長公共子串行 public class lcs public ...

java求兩個字串最長公共字串

思路 將str1的字元用兩個for迴圈用substring i,j 逐段截出,再與str2內字串比較,從而選出最長公共的字串,從而輸出字元內串 public class similarstring for int i 0 i str1.length 1 i system.out.println 兩字...

最長公共字串

include include include include include customer.h using namespace std 最長公共字串 動態規劃 假設需要求得字串為str1,str2。函式f m,n 分別為 str1 m str2 n 結尾的公共字串長度。有以下遞推公式 遞推邊界...