C語言求解最長公共子字串問題及相關的演算法分析

2022-10-04 01:39:09 字數 3348 閱讀 8249

題目:如果字串一的所有字元按其在字串中的順序出現在另外乙個字串二中,則字串一稱之為字串二的子串。注意,並不要求子串(字串一)的字元必須連續出現在字串二中。請編寫乙個函式,輸入兩個字串,求它們的最長公共子串行,並列印出最長公共子串行。

例如:輸入兩個字串bdcaba和abcbdab,字串bcba和bdab都是是它們的最長公共子串行,則輸出它們的長度4,並列印任意乙個子串行。

分析:求最長公共子串行(longest common subsequence, lcs)是一道非常經典的動態規劃題,因此一些重視演算法的公司像microstrategy都把它當作面試題。

完整介紹動態規劃將需要很長的篇幅,因此我不打算在此全面討論動態規劃相關的概念,只集中對lcs直接相關內容作討論。如果對動態規劃不是很熟悉,請參考相關演算法書比如演算法討論。

考慮最長公共子串行問題如何分解成子問題,設a=「a0,a1,…,am-1」,b=「b0,b1,…,bn-1」,並z=「z0,z1,…,zk-1」為它們的最長公共子串行。不難證明有以下性質:

(1) 如果am-1==bn-1,則zk-1=am-1=bn-1,且「z0,z1,…,zk-2」是「a0,a1,…,am-2」和「b0,b1,…,bn-2」的乙個最長公共子串行;

(2) 如果am-1!=bn-1,則若zk-1!=am-1時,蘊涵「z0,z1,…,zk-1」是「a0,a1,…,am-2」和「b0,b1,…,bn-1」的乙個最長公共子串行;

(3) 如果am-1!=bn-1,則若zk-1!=bn-1時,蘊涵「z0,z1,…,zk-1」是「a0,a1,…,am-1」和「b0,b1,…,bn-2」的乙個最長公共子串行。

這樣,在找a和b的公共子串行時,如果有am-1==bn-1,則進一步解決乙個子問題,找「a0,a1,…,am-2」和「b0,b1,…,bm-2」的乙個最長公共子串行;如果am-1!=bn-1,則要解決兩個子問題,找出「a0,a1,…,am-2」和「b0,b1,…,bn-1」的乙個最長公共子串行和找出「a0,a1,…,am-1」和「b0,b1,…,bn-2」的乙個最長公共子串行,再取兩者中較長者作為a和b的最長公共子串行。

求解:引進乙個二維陣列c,用c[i][j]記錄x[i]與y[j] 的lcs 的長度,b[i][j]記錄c[i][j]是通過哪乙個子問題的值求得的,以決定輸出最長公共字串時搜尋的方向。

我們是自底向上進行遞推計算,那麼在計算c[i,j]之前,c[i-1][j-1],c[i-1][j程式設計客棧]與c[i][j-1]均已計算出來。此時我們根據x[i] == y[j]還是x[i] != y[j],就可以計算出c[i][j]。

問題的遞迴式寫成:

回溯輸出最長公共子串行過程:    

演算法分析:

由於每次呼叫至少向上或向左(或向上向左同時)移動一步,故最多呼叫(m + n)次就會遇到i = 0或j = 0的情況,此時開始返回。返回時與遞迴呼叫時方向相反,步數相同,故演算法時間複雜度為(m + n)。

完整的實現**如下:

/**

找出兩個字串的最長公共子串行的長度

** author :liuzhiwei

** data :2011-08-15

**/

#include "stdio.h"

#include "string.h"

#include "stdlib.h"

int lcslength(char* str1, char* str2, int **b)

else if(c[i-1][j]>c[i][j-1])

else

} }

/* for(i= 0; i < length1+1; i++)

*/ len=c[length1][length2];

for(i = 0; i < length1+1; i++) //釋放動態申請的二維陣列

delete c[i];

delete c;

return len;

} void printlcs(int **b, char *str1, int i, int j)

else if(b[i][j]==1)

printlcs(b, str1, i-1, j);

else

printlcs(b, str1, i, j-1); }

int main(void)

程式的效果圖如下:

第二種方法為:

/**

找出兩個字串的最長公共子串行的長度

** author :liuzhiwei

** data :2011-08-15

**/

#include "stdio.h"

#include "string.h"

#include "stdlib.h"

int lcslength(char* str1, char* str2) //求得兩個字串的最大公共子串長度並輸出公共子串

} //輸出公共子串

char s[100];

int len,k;

len=k=c[length1][length2];

s[k--]='\0';

i=length1,j=length2;

while(i>0 && j>0)

else if(c[i-1][j]jfbele printf("最長公共子串為:");

puts(s);

for(i = 0; i < length1+1; i++) //釋放動態申請的二維陣列

delete c[i];

delete c;

return len; }

int main(void)

問題拓展:設a、b、c是三個長為n的字串,它們取自同一常數大小的字母表。設計乙個找出三個串的最長公共子串行的o(n^3)的時間演算法。

思路:跟上面的求2個字串的公共子串行是一樣的思路,只不過這裡需要動態申請乙個三維的陣列,三個字串的尾字元不同的時候,考慮的情況多一些而已。

/**

找出三個字串的最長公共子串行的長度

** author :liuzhiwei

** data :2011-08-15

**/

#include "stdio.h"

#include "string.h"

#include "stdlib.h"

int max1(int m,int n)

int max2(int x,int y,int z,int k,int m,int n)

int lcslength(char* str1, char* str2, char* str3) //求得三個字串的最大公共子串行長度並輸出公共子串行

程式的效果圖如下:

本文位址:

最長公共子字串

關於題目理解,請注意和最長公共子串行的區別,最長公共子字串的解法是動態規劃,但是比較難想到表的構造方法。注意到,設給定字串為str1 和 str2 二者的長度分別是 len1 和 len2 那麼解空間大小之多是len1 len2?假設最長公共子字串為substr common,那麼substr co...

最長公共子字串

描述 求兩個輸入序列的最長的公共子字串的長度。子字串中的所有字元在源字串中必須相鄰。如字串 21232523311324和字串312123223445,他們的最長公共子字串為21232,長度為5。輸入格式 兩行,第一行為第乙個字串x,第二行為第二個字串y,字串不含空格並以回車標示結束。x和y的串長都...

最長公共子串,字串

目前,在各大 或者是,有著不少的關於這個問題的講解,但是目前覺得都不是很清晰明白!現在普遍的解法都是用動態規劃的方式來做這道筆試題。或者是經典的面試題。這裡我做乙個自認為比較清楚的講解。說到動態規劃很多人都不會感到很陌生。但是有多少知道動態規劃什麼?精髓在 這裡僅僅提供一些個人見解,首先,動態規劃不...