给定两个序列X,Y,求最长公共子序列。
参考:《算法设计与分析》张德富 g
例如:
X = { a , b , c , b , d , a , b }
Y = { b , d , c , a , b , a }
最长公共子序列为
LSC = { b , c , b , a }
分析:
最长公共子序列问题具有最优子结构性质
设X = { x1 , ... , xm } Y = { y1 , ... , yn }
及它们的最长子序列Z = { z1 , ... , zk }
则
1、若 xm = yn , 则 zk = xm = yn,且Z[k-1] 是 X[m-1] 和 Y[n-1] 的最长公共子序列
2、若 xm != yn ,且 zk != xm , 则 Z 是 X[m-1] 和 Y 的最长公共子序列
3、若 xm != yn , 且 zk != yn , 则 Z 是 Y[n-1] 和 X 的最长公共子序列
由性质导出子问题的递归结构:
用c[i,j]表示X[1...i]与Y[1...j]公共子序列长度。
当 i = 0 or j = 0 时 , c[i,j] = 0
当 i , j > 0 ; xi = yj 时 , c[i,j] = c[i-1,j-1] + 1
当 i , j > 0 ; xi!= yj 时 , c[i,j] = max { c[i,j-1] , c[i-1,j] }
实现:
c[m,n]数组存放公共子序列长度。b[m,n]存放由哪个子问题决定c[i,j]?是c[i-1,j-1],c[i,j-1]还是c[i-1,j].用于最后输出公共子序列。
求公共子序列长度函数dpLCS时间复杂度O(mn),根据结果构造公共子序列genLCS的时间复杂度为O(m+n)
- /**
- * Dynamic Programming, DP
- *
- */
- public class DPLCS {
- /**
- * @param args
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- String x = "abcbdab";
- String y = "bdcaba";
- String cls = "bdab";
- DPLCS dp = new DPLCS(x.toCharArray(),y.toCharArray());
- System.out.println(dp.getDPLCSLength());
- System.out.println(dp.getDPLCS());
- }
- private int[][] c = null;
- private int[][] b = null;
- private char[] x = null;
- private char[] y = null;
- private int lcsLength;
- private char[] lcs;
- public DPLCS(char[] x, char[] y, int m, int n){
- c = new int[m+1][n+1];
- b = new int[m+1][n+1];
- for (int i = 0; i < m+1; i++) c[i][0]=0;
- for (int j = 0; j < n+1; j++) c[0][j]=0;
- this.x = x;
- this.y = y;
- dpLcs(x, y, m, n);
- lcs = new char[lcsLength];
- //生成公共子序列
- genLCS(m, n, lcsLength-1);
- }
- public DPLCS(char[] x, char[] y){
- this(x, y, x.length, y.length);
- }
- private void dpLCS(char[] x, char[] y, int m, int n) {
- // TODO Auto-generated method stub
- for (int i = 1; i < m+1; i++) {
- for (int j = 1; j < n+1; j++) {
- if(x[i-1]==y[j-1]){//注意index
- c[i][j]=c[i-1][j-1]+1;
- b[i][j]=1;//表示由子问题i-1,j-1得来
- }else{
- if(c[i][j-1]>=c[i-1][j]){
- c[i][j]=c[i][j-1];
- b[i][j]=2;//表示由子问题i,j-1得来
- }
- else{
- c[i][j]=c[i-1][j];
- b[i][j]=3;//表示由子问题i-1,j得来
- }
- }
- }
- }
- lcsLength = c[m][n];
- }
- private void genLCS(int i, int j, int index) {
- // TODO Auto-generated method stub
- if(index<0)
- return;
- if(b[i][j]==1){
- lcs[index]= x[i-1];
- genLCS(i-1,j-1,index-1);
- }
- else if(b[i][j]==2)
- genLCS(i,j-1,index);
- else
- genLCS(i-1,j,index);
- }
- public int getDPLCSLength(){
- return lcsLength;
- }
- public char[] getDPLCS(){
- return lcs;
- }
- }
缺陷:最长公共子序列可能有多条,该程序只求出其中一条
转载于:https://blog.51cto.com/dongdong1314/397481