最长公共子序列(LCS)问题 Longest Common Subsequence 与最长公告字串 longest common substr...
問題描述:字符序列的子序列是指從給定字符序列中隨意地(不一定連續(xù))去掉若干個字符(可能一個也不去掉)后所形成的字符序列。令給定的字符序列X=“x0,x1,…,xm-1”,序列Y=“y0,y1,…,yk-1”是X的子序列,存在X的一個嚴格遞增下標序列<i0,i1,…,ik-1>,使得對所有的j=0,1,…,k-1,有xij=yj。例如,X=“ABCBDAB”,Y=“BCDB”是X的一個子序列。
考慮最長公共子序列問題如何分解成子問題,設A=“a0,a1,…,am-1”,B=“b0,b1,…,bm-1”,并Z=“z0,z1,…,zk-1”為它們的最長公共子序列。不難證明有以下性質(zhì):
(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的最長公共子序列。
?
?
求解:
?
引進一個二維數(shù)組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]均已計算出來。此時我們根據(jù)X[i] = Y[j]還是X[i] != Y[j],就可以計算出c[i][j]。
問題的遞歸式寫成:
?
?
回溯輸出最長公共子序列過程:
?
算法分析:
由于每次調(diào)用至少向上或向左(或向上向左同時)移動一步,故最多調(diào)用(m + n)次就會遇到i = 0或j = 0的情況,此時開始返回。返回時與遞歸調(diào)用時方向相反,步數(shù)相同,故算法時間復雜度為Θ(m + n)。
?
?
http://blog.csdn.net/yysdsyl/article/details/4226630
?
一切沒有code的分析都是耍流氓。。。 上code
void printLCS(string str1, string str2, vector<vector<int> >flag, int idx1, int idx2) {if(idx1 == 0 || idx2 == 0 ) return;if(flag[idx1][idx2] == 1){ printLCS(str1, str2,flag, idx1-1, idx2-1);cout << idx1 <<"\t"<< idx2 <<"\t";cout << str1[idx1-1] <<"\t"<<endl;} else if(flag[idx1][idx2] == 2)printLCS(str1, str2,flag, idx1, idx2-1);else if(flag[idx1][idx2] == 3)printLCS(str1, str2,flag, idx1-1, idx2); }int lcs(string str1, string str2) {const size_t len1 = str1.size();const size_t len2 = str2.size();if(len1 == 0 || len2 == 0)return 0;int f[len1 + 1][len2 + 1];vector<vector<int> >flag;vector<int> tmp;tmp.resize(len2+1);for(size_t i = 0; i<= len1; i++)flag.push_back(tmp);//memset(flag,0,sizeof(flag)); // 1: leftup; 2: left; 3: upfor(size_t i = 0; i <= len1; i++){f[i][0] = 0;}for(size_t i = 0; i <= len2; i++){f[0][i] = 0;}for(size_t i = 1; i <= len1; i++){for(size_t j = 1; j <= len2; j++){if(str1[i-1] == str2[j-1]){f[i][j] = f[i-1][j-1] + 1;flag[i][j] = 1;}else{f[i][j] = max(f[i][j-1], f[i-1][j]);if(f[i][j-1] > f[i-1][j])flag[i][j] = 2;elseflag[i][j] = 3;}}} #if 0for(size_t i = 1; i <= len1; i++){for(size_t j = 1; j <= len2; j++){//cout << "f["<<j<<"][" <<i<<"]" << f[j][i] <<"\n";cout << f[i][j] <<"\t";}cout << endl;}cout << endl;for(size_t i = 1; i <= len1; i++){for(size_t j = 1; j <= len2; j++){//cout << "f["<<j<<"][" <<i<<"]" << f[j][i] <<"\n";cout << flag[i][j] <<"\t";}cout << endl;} #endifprintLCS(str1, str2, flag, len1, len2);return f[len1][len2];}
?
最長公共字串:
找兩個字符串的最長公共子串,這個子串要求在原字符串中是連續(xù)的。其實這又是一個序貫決策問題,可以用動態(tài)規(guī)劃來求解。我們采用一個二維矩陣來記錄中間的結果。這個二維矩陣怎么構造呢?直接舉個例子吧:"bab"和"caba"(當然我們現(xiàn)在一眼就可以看出來最長公共子串是"ba"或"ab")
b a b
c 0 0 0
a 0 1 0
b 1 0 1
a 0 1 0
我們看矩陣的斜對角線最長的那個就能找出最長公共子串。
不過在二維矩陣上找最長的由1組成的斜對角線也是件麻煩費時的事,下面改進:當要在矩陣是填1時讓它等于其左上角元素加1。
b a b
c 0 0 0
a 0 1 0
b 1 0 2
a 0 2 0
這樣矩陣中的最大元素就是 最長公共子串的長度。
在構造這個二維矩陣的過程中由于得出矩陣的某一行后其上一行就沒用了,所以實際上在程序中可以用一維數(shù)組來代替這個矩陣。
? ? 與Subsequence問題不同的是,Substring問題不光要求下標序列是遞增的,還要求每次
?? 遞增的增量為1, 即兩個下標序列為:
?? <i, i+1, i+2, ..., i+k-1> 和 <j, j+1, j+2, ..., j+k-1>
??? 類比Subquence問題的動態(tài)規(guī)劃解法,Substring也可以用動態(tài)規(guī)劃解決,令
??? c[i][j]表示Xi和Yi的最大Substring的長度,比如
?? X = <y, e, d, f>
???Y = <y, e, k, f>
?? c[1][1] = 1
?? c[2][2] = 2
?? c[3][3] = 0
?? c[4][4] = 1
???動態(tài)轉(zhuǎn)移方程為:
?? 如果xi == yj, 則 c[i][j] = c[i-1][j-1]+1
?? 如果xi ! = yj,? 那么c[i][j] = 0
?? 最后求Longest Common Substring的長度等于
?? max{? c[i][j],? 1<=i<=n, 1<=j<=m}
?完整的代碼如下:
?
/** 找出兩個字符串的最長公共連續(xù)子串的長度 ** author :liuzhiwei ** data :2011-08-16 **/ #include "stdio.h" #include "string.h" #include "stdlib.h"int longest_common_substring(char *str1, char *str2) {int i,j,k,len1,len2,max,x,y;len1 = strlen(str1);len2 = strlen(str2);int **c = new int*[len1+1];for(i = 0; i < len1+1; i++)c[i] = new int[len2+1];for(i = 0; i < len1+1; i++)c[i][0]=0; //第0列都初始化為0for(j = 0; j < len2+1; j++)c[0][j]=0; //第0行都初始化為0 max = -1;for(i = 1 ; i < len1+1 ; i++){for(j = 1; j < len2+1; j++){if(str1[i-1]==str2[j-1]) //只需要跟左上方的c[i-1][j-1]比較就可以了c[i][j]=c[i-1][j-1]+1;else //不連續(xù)的時候還要跟左邊的c[i][j-1]、上邊的c[i-1][j]值比較,這里不需要c[i][j]=0;if(c[i][j]>max){max=c[i][j];x=i;y=j;}}}//輸出公共子串char s[1000];k=max;i=x-1,j=y-1;s[k--]='\0';while(i>=0 && j>=0){if(str1[i]==str2[j]){s[k--]=str1[i];i--;j--;}else //只要有一個不相等,就說明相等的公共字符斷了,不連續(xù)了break;}printf("最長公共子串為:");puts(s);for(i = 0; i < len1+1; i++) //釋放動態(tài)申請的二維數(shù)組 delete[] c[i];delete[] c;return max; } int main(void) {char str1[1000],str2[1000];printf("請輸入第一個字符串:");gets(str1);printf("請輸入第二個字符串:");gets(str2);int len = longest_common_substring(str1, str2);printf("最長公共連續(xù)子串的長度為:%d\n",len);system("pause");return 0; }
?
轉(zhuǎn)載于:https://www.cnblogs.com/diegodu/p/4248242.html
總結
以上是生活随笔為你收集整理的最长公共子序列(LCS)问题 Longest Common Subsequence 与最长公告字串 longest common substr...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 求一个俩个字的qq网名
- 下一篇: CodeForces Round #28