1. 程式人生 > 實用技巧 >最長公共子序列問題|動態規劃

最長公共子序列問題|動態規劃

Longest Common Subsequence Problem
序列X和Y,找到Z為X和Y的最大公共子序列

蠻力列舉
從X的長度為1序列開始列舉,在Y中查詢是否有該序列

列舉觀察,長度為x-1的子序列是長度為x的子序列的一部分
存在最優子結構和重疊子問題,適合動態規劃

1、問題結構分析
C[i,j]記錄X[1..i]和Y[1..j]的最長公共子序列長度

2、通過考察末尾元素來尋找遞推關係
有Xi=Yj和Xi≠Yj兩種情況
Xi≠Yj時,C[i,j]= max(C[i-1,j],C[i, j-1])
Xi=Yj時,C[i,j]=max(C[i-1, j-1]+1,C[i-1, j],C[i,j-1])


由於C[i-1, j-1]+1>=max{C[i,j-1],c[i-1,j]}
則 Xi=Yj時,C[i,j]= C[i-1, j-1]+1

3、自底向上計算:確定計算順序
初始化 C[i,0]=C[0,j]=0
遞推公式 得出計算順序自上而下自左而右


4、追蹤陣列rec[i,j]=LU / U/ L


時間複雜度O(n*m),n、m分別為序列X、Y的長度

#include<stdio.h>
#define MAX 1000
int x[MAX], y[MAX], z[MAX][MAX], rec[MAX][MAX]; 
int xl, yl;
int longestSub(){
    
int i, j, res; // for(i=0; i<=xl; i++) z[i][0]=0;//初始化 // for(i=0; i<=yl; i++) z[0][i]=0; for(i=1; i<=xl; i++){ for(j=1; j<=yl; j++){ if(x[i]==y[j]){ z[i][j]=z[i-1][j-1]+1; rec[i][j]=1; } else if(z[i-1][j]>=z[i][j-1
]){ z[i][j]=z[i-1][j]; rec[i][j]=2; } else{ z[i][j]=z[i][j-1]; rec[i][j]=3; } } } return z[xl][yl]; } void printSub(int i, int j){ if(i==0||j==0) return ; if(rec[i][j]==1){ printSub(i-1, j-1); printf("%d ", x[i]); } else if(rec[i][j]==2) printSub(i-1, j); else printSub(i, j-1); } int main(){ int i, j, res; scanf("%d%d", &xl, &yl); for(i=1; i<=xl; i++) scanf("%d", &x[i]); for(i=1; i<=yl; i++) scanf("%d", &y[i]); res=longestSub(); printf("最長公共子序列長度為%d:\n",res); printSub(xl, yl); return ; }