最長公共子串行和最長公共子串

2021-06-27 01:22:49 字數 2777 閱讀 7832

問題定義

最長公共子串行,序列的意思是順序對就可以,並不需要是連續的。例如:

abcde

oalblcldle

其中abcde就是這兩個字串的最長公共子串行。

容易知道乙個長度為n的字串的子串行有2

n 個,假設兩個字串的長度都為n,直接去求解兩個字串的最長公共子串行需要用這2

n 個序列串去匹配另外乙個字串,匹配一次的時間複雜度為o(n

) ,則總的時間複雜度為o(n

⋅2n)

,顯然效率非常之低。

最長公共子串,和上面最長公共子串行不同的地方在於需要匹配連續的字串。直接求解的話,需要找到所有的公共子串,保留最長的即可。乙個長度為n的字串的子串個數可以有:1+

2+3+

...+

n=(n

+1)n

/2(1)

上式每項分別對應於子串長度為n,n

−1,n

−2,n

−3,.

..,1

的子串個數。每個子串和另乙個字串進行匹配時間複雜度為o(m

n),m為子串長度,總時間複雜度約為o(n

4).這樣按子串長度排序,從最長開始匹配,如果匹配上了就退出。

簡單的優化就是可以簡單的通過記錄子串的開始位置,然後依次進行比較找到最長,這樣並不需要取出所有子串,如果不考慮字串比較的時間,時間複雜度為o(n

2), 但是進行串比較時,時間並不是o(1

) 所以總的時間應該在o(n

2)和o(n

3),最壞情況出現在兩個字串完全相同的情況,這種情況下還是要比較完所有子串,每次子串比較耗時也最長。 上面這種考慮的**:

//from ider's blog

int longestcommonsubstring_n3(const string& str1, const string& str2)

if (longest < length)}}

#ifdef ider_debug

cout<< "(first, second, comparisions) = ("

<< start1 << ", " << start2 << ", " << comparisons

<< ")" << endl;

#endif

return longest;

}

最長公共子串行和最長公共子串的直接解法,顯然不可取.時間複雜度都到4次方了。這兩個問題都可以通過動態規劃的思想來進行求解。

動態規劃解法

使用動態規劃的時候首先要找到最優子結構,即原問題的解可分解為求解子問題得到。這樣就可以通過遞迴求解子問題而得到原問題的最優解。所以說這裡是可以使用遞迴演算法的,但是動態規劃法比遞迴快的地方就是使用動態規劃表記錄子問題的解,因為較大規模子問題的求解包涵了對較小規模子問題的重複求解,通過子子問題的解可以求出子問題的解。

最長公共子串行

在最長公共子串行和最長公共子串中,用動態規劃方法就要換個角度找到問題的最優子結構。前面的暴力法是從字串的左邊開始的,現在從字串的右邊開始考慮。

符號約定,s1,s2是兩個字串,lcs(s1,s2)表示s1,s2的最長公共子串行長度,c1是s1的最右側字元,c2是s2的最右側字元,s1』是從s1中去除c1的部分,s2』是從s2中去除c2的部分。

如果最右邊字元相等,則lcs(s1,s2)=lcs(s1』,s2』)+1,如果不相等 lcs(s1,s2)=max(lcs(s1』,s2),lcs(s1,s2』)) ,當任意字串長度為0時,返回0;

則lcs(s1,s2)等於:

所以這裡的結果需要使用乙個二維表來記錄,dp[x,y] ,假設字串s1,s2長度分別為m,n 則在程式中只需要開闢乙個m*n的二維陣列.dp[x,y]就表示字串s1(1,x),s2(1,y)的最長公共子串行.則dp[x,y]:

if(x==0||y==0)

dp[x,y]=0;

else if(c1==c2)

dp[x,y]=dp[x-1,y-1]= +1;

else

dp[x,y]=max(dp[x-1,y],dp[x,y-1])

這樣,可以先求解最小的子問題,然後查表求出較大子問題,最後求出最優解,消除遞迴。

最長公共子串

最長公共子串和上面最長公共子串行思想基本一樣,只是最優子結構稍微不同。因為最長公共子串要求字元連續,所以當最後乙個字元不同時以這個字元結尾的最長公共子串長度就變為0;還是簡單的使用乙個二維表來儲存,lsub(x,y)表示s1中以x位置字元結尾,s2中以y位置結尾的最長公共子串長度(這裡的區別是,最長公共子串要以這最後乙個字元結尾!)即二維表儲存的是以當前字元結尾的最長公共子串的長度,所以在最後需要遍歷二維表找到最長長度。

最後遍歷二維表,得到最大公共子串長度。

動態規劃時間複雜度

從上面看出動態規劃的整個求解過程就是對二維表進行填表,每個填表步驟都可以在o(1

) 內完成,則總的時間複雜度為o(n

2),空間複雜度也為o(n

2).通過進一步壓縮狀態表,可以減少空間複雜度。可行的原因是,每次填表我們只用到了周邊的**資料,所以可以通過只儲存需要用到的狀態來壓縮空間。

寫的比較好的文章:

從優化到再優化,最長公共子串

最長公共子串行

最長公共子串行 最長公共子串

1 最長公共子串行 採用動態規劃的思想,用乙個陣列dp i j 記錄a字串中i 1位置到b字串中j 1位置的最長公共子串行,若a i 1 b j 1 那麼dp i j dp i 1 j 1 1,若不相同,那麼dp i j 就是dp i 1 j 和dp i j 1 中的較大者。class lcs el...

最長公共子串行 最長公共子串

1.區別 找兩個字串的最長公共子串,這個子串要求在原字串中是連續的。而最長公共子串行則並不要求連續。2 最長公共子串 其實這是乙個序貫決策問題,可以用動態規劃來求解。我們採用乙個二維矩陣來記錄中間的結果。這個二維矩陣怎麼構造呢?直接舉個例子吧 bab 和 caba 當然我們現在一眼就可以看出來最長公...

最長公共子串 最長公共子串行

子串要求連續 子串行不要求連續 之前的做法是dp求子序列 include include include using namespace std const int inf 0x3f3f3f3f const int mod 1000000007 string s1,s2 int dp 1010 10...