因為比較有用,所以就轉來了,原文地址http://www.nocow.cn/index.php/%E6%9C%80%E9%95%BF%E5%85%AC%E5%85%B1%E5%AD%90%E5%BA%8F%E5%88%97
概述
最長公用子序列:字元序列的子序列是指從給定字元序列中隨意地(不一定連續)去掉若干個字元(可能一個也不去掉)後所形成的字元序列。
令給定的字元序列X="x0,x1,...,xm -1",序列Y="y0,y1,...,yk-1"是X的子序列,
存在X的一個嚴格遞增下標序列,使得對所有的j=0,1,...,k-1,有xij= yj。例如,X="ABCBDAB",Y="BCDB"是X的一個子序列。
給定兩個序列A和B,稱序列Z是A和B的公用子序列,是指Z同是A和B的子序列。
問題要求已知兩序列A和B的最長公用子序列。
分析
本題初看有些類似線形動態規劃,但經過仔細審題發現用矩陣類動態規劃更好理解,
如所顯示,我們把兩個字串看作矩陣的橫縱軸:
首先讀入資料時,把兩個字元相同的標記成1,不相同的地方標記成0;
很容易證明,如果能形成公用子序列,則最長子序列一定是從某個位置開始的對角線的長度,所以我們需要做的就是統計對角線的長度,DP方程就為if a[i,j]=1 then d[i,j]:=d[i-1,j-1]+1;
經過迴圈我們就可以把上錶轉化成:
因此我們只需記錄在迴圈中的最大值就是我們要的最長公用子序列的長度;
代碼:
d[0,0]:=0; for i:=1 to n do d[i,0]:=0; for j:=1 to m do d[0,j]:=0; //初始化 for i:=1 to n do for j:=1 to m do if s1[i]=s2[j] then //等價於 if a[i,j]=1 then d[i,j]:=d[i-1,j-1]+1 else begin if d[i-1,j]>d[i,j-1] then d[i,j]:=d[i-1,j] else d[i,j]:=d[i,j-1]; end;
最佳化:但是在0和1的矩陣中找最長的1對角線序列又要花去一定的時間。通過改進矩陣的產生方式和設定標記變數,可以省去這部分時間。
當字元匹配的時候,我們並不是簡單的給相應元素賦上1,而是賦上其左上方元素的值加一。
我們用兩個標記變數來標記矩陣中值最大的元素的位置,在矩陣產生的過程中來判斷當前產生的元素的值是不是最大的,據此來改變標記變數的值,那麼到矩陣完成的時候,
最長相符子串的位置和長度就已經出來了.這樣做速度比較快,但是花的空間太多。我們注意到在改進的矩陣產生方式當中,每產生當前一層,前面的那一層就已經沒有用了。
因此我們只需使用一維數組即可。可以自己寫一下。
變種之一:字串距離
順便附上HDOJ
Common Subsequence AC代碼一份
#include <cstdio><br />#include <cstring><br />int a[1000][1000];<br />int main(){<br />char x[1000], y[1000];<br />int n, m, i, j, ans;<br />while(~scanf("%s%s",x,y)){<br />n = strlen(x);<br />m = strlen(y);<br />ans = -1;<br />memset(a,0,sizeof(a));<br />for(i = 0; i < n; i++){<br />for(j = 0; j < m; j++){<br />if(x[i] == y[j])<br />a[i+1][j+1] = 1;<br />else<br />a[i+1][j+1] = 0;<br />if(a[i+1][j+1] > ans)<br />ans = a[i+1][j+1];<br />}<br />}<br />for(i = 1; i <= n; i++){<br />for(j = 1; j <= m; j++){<br />if(a[i][j])<br />a[i][j] = a[i-1][j-1] + 1;<br />else{<br />a[i][j] = (a[i-1][j]>a[i][j-1])?a[i-1][j]:a[i][j-1];<br />}<br />if(a[i][j] > ans)<br />ans = a[i][j];<br />}<br />}<br />/*for(i = 1; i <= n; i++){<br />for(j = 1; j <= m; j++){<br />if(j) putchar(' ');<br />printf("%d",a[i][j]);<br />}<br />putchar(10);<br />}<br />*/printf("%d/n",ans);<br />}<br />return 0;<br />}