這是自動判題系統的描述
描述:
一個給定序列的子序列是在該序列中刪去若干元素後得到的序列。確切地說,若給定序列X=<x1, x2,…,
xm>,則另一序列Z=<z1, z2,…, zk>是X的子序列是指存在一個嚴格遞增的下標序列 <i1, i2,…,
ik>,使得對於所有j=1,2,…,k有:
Xij =
Zj
如果一個序列S即是A的子序列又是B的子序列,則稱S是A、B的公用子序列。
求A、B所有公用子序列中最長的序列的長度。
輸入:
輸入共兩行,每行一個由字母和數字組成的字串,代表序列A、B。A、B的長度不超過200個字元。
輸出:
一個整數,表示最長各個子序列的長度。
格式:printf("%d/n");
輸入範例:
programming
contest
輸出範例:
2
代碼:
import java.util.Scanner;<br />public class Main {</p><p>public static void main(String[] args) {<br />Scanner sc=new Scanner(System.in);</p><p>String str1=sc.nextLine();<br />String str2=sc.nextLine();<br />System.out.println(lcsLength(str1,str2));</p><p>}<br />//關於此問題,需要遞迴公式,c[i][j]記錄序列Xi和yj的最長<br />//公用子序列的長度,當i=0或j=0時,空序列是兩個字串的最長<br />//公用子序列,故此時c[i][j]=0<br />//當Xi=yi時,c[i][j]=c[i-1][j-1]+1;<br />//else max{c[i][j-1],c[i-1][j]}<br />public static int lcsLength(String str1,String str2)<br />{<br />int m=str1.length();<br />int n=str2.length();<br />int c[][]=new int[m+1][n+1];<br />for(int i=1;i<=m;i++)<br />c[i][0]=0;<br />for(int i=1;i<=n;i++)<br />c[0][i]=0;<br /> for(int i=1;i<=m;i++)<br /> for(int j=1;j<=n;j++)<br /> {<br /> if(str1.charAt(i-1)==str2.charAt(j-1))<br /> c[i][j]=c[i-1][j-1]+1;<br /> else if(c[i-1][j]>=c[i][j-1])<br /> c[i][j]=c[i-1][j];<br /> else<br /> c[i][j]=c[i][j-1];</p><p> }<br /> return c[m][n];<br />}<br />}<br />
但是這個程式只能計算出最長公用子序列的長度,那要是還想要知道是什麼序列該怎麼辦呢?
import java.util.Scanner;<br />public class Main {</p><p>static int b[][];//此數組用於計算最長公用子序列中每個字元時怎麼樣得到的</p><p>public static void main(String[] args) {<br />Scanner sc=new Scanner(System.in);<br />String str1=sc.nextLine();<br />String str2=sc.nextLine();<br />b=new int[str1.length()+1][str2.length()+1];</p><p>System.out.println(lcsLength(str1,str2,b));<br />lcs(str1.length(),str2.length(),str1,b);</p><p>}<br />//關於此問題,需要遞迴公式,c[i][j]記錄序列Xi和yj的最長<br />//公用子序列的長度,當i=0或j=0時,空序列是兩個字串的最長<br />//公用子序列,故此時c[i][j]=0<br />//當Xi=yi時,c[i][j]=c[i-1][j-1]+1;<br />//else max{c[i][j-1],c[i-1][j]}<br />public static int lcsLength(String str1,String str2,int [][]b)<br />{<br />int m=str1.length();<br />int n=str2.length();<br />int c[][]=new int[m+1][n+1];<br />for(int i=1;i<=m;i++)<br />c[i][0]=0;<br />for(int i=1;i<=n;i++)<br />c[0][i]=0;<br /> for(int i=1;i<=m;i++)<br /> for(int j=1;j<=n;j++)<br /> {<br /> if(str1.charAt(i-1)==str2.charAt(j-1))<br /> {<br /> c[i][j]=c[i-1][j-1]+1;<br /> b[i][j]=1;<br /> }<br /> else if(c[i-1][j]>=c[i][j-1])<br /> {<br /> c[i][j]=c[i-1][j];<br /> b[i][j]=2;<br /> }<br /> else<br /> {<br /> c[i][j]=c[i][j-1];<br /> b[i][j]=3;<br /> }</p><p> }<br /> return c[m][n];<br />}<br />//構造最長公用子序列<br />public static void lcs(int i,int j,String str,int b[][])<br />{<br />if(i==0||j==0)<br />return;<br />if(b[i][j]==1)<br />{<br />lcs(i-1,j-1,str,b);<br />System.out.print(str.charAt(i-1));<br />}<br />else if(b[i][j]==2)<br />lcs(i-1,j,str,b);<br />else<br />lcs(i,j-1,str,b);</p><p>}<br />}<br />
這個演算法可以解決剛才說的問題。
下面由我來進一步解釋一下最長公用子序列問題的解法,當然,這不是我發明的,從書上學來的。
最長公用子序列問題具有最優子結構性質。
設序列X={x1,x2,...,xm},Y={y1,y2,...,yn}的最長公用子序列是Z={z1,z2,...zk},則
(1)如果xm=yn,則zk=xm=yn,且Zk-1是Xm-1和Yn-1的最長公用子序列
(2)else if(zk不等於xm),則Z是Xm-1和Y的最長公用子序列
(3)else if(zk不等於yn),則Z是X和Yn-1的最長公用子序列
由此德奧遞推公式
公用子序列,故此時c[i][j]=0 i=0 or j=0
當Xi=yi時,c[i][j]=c[i-1][j-1]+1;
else max{c[i][j-1],c[i-1][j]}
用數組b[i][j]記錄c[i][j]的值是由哪一個子問題的解得到的,在構造最長公用子序列時用到了。
在構造最長公用子序列時,首先從b[m][n]開始,依其值在數組b中搜尋,當b[i][j]=1,表示Xi和Yj的最長公用子序列是由Xi-1和Yj-1的最長公用子序列尾部加上xi得到的序列。當b[i][j]=2時,表示Xi和Yj的最長公用子序列是由Xi-1和Yj的最長公用子序列相同,當b[i][j]=3時,表示Xi和Yj的最長公用子序列是由Xi和Yj-1的最長公用子序列相同,由此可找出最長公用子序列的具體內容。