題目:如果字串一的所有字元按其在字串中的順序出現在另外乙個字串二中,則字串一稱之為字串二的子串。注意,並不要求子串(字串一)的字元必須連續出現在字串二中。請編寫乙個函式,輸入兩個字串,求它們的最長公共子串,並列印出最長公共子串。
例如:輸入兩個字串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] j--;
else
i--;
} 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) //求得三個字串的最大公共子串長度並輸出公共子串
for(i = 0; i < length1+1; i++)
for(i = 0; i < length2+1; i++)
for(i = 0; i < length1+1; i++)
for(i = 1; i < length1+1; i++)
}} }
len=c[length1][length2][length3];
for(i = 1; i < length1+1; i++) //釋放動態申請的三維陣列
delete c;
return len;
}int main(void)
程式的效果圖如下:
本文標題: 深入解析最長公共子串
本文位址:
最長公共子串行 最長公共子串
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...