51 nod 1006 最长公共子序列Lcs
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1006
参考博客 :http://blog.csdn.net/yysdsyl/article/details/4226630
引进一个二维数组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]均已计算出来。此时我们根据X[i] = Y[j]还是X[i] != Y[j],就可以计算出c[i][j]。
计算 LCS 复杂度 O(n*m).由于每次调用至少向上或向左(或向上向左同时)移动一步,故最多调用(m + n)次就会遇到i = 0或j = 0的情况,此时开始返回。返回时与递归调用时方向相反,步数相同,故打印输出算法时间复杂度为Θ(m + n)。
打印序列,非递归。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <set> 5 #include <queue> 6 #include <algorithm> 7 #define MAXN 1111 8 #define MAXM 222222 9 #define INF 1000000000 10 using namespace std; 11 12 char s[MAXN],t[MAXN],res[MAXN]; 13 int dp[MAXN][MAXN],flag[MAXN][MAXN]; 14 15 void LCS(int n,int m) 16 { 17 memset(dp,0,sizeof(dp)); 18 memset(flag,0,sizeof(flag)); 19 for(int i=1;i<=n;i++) 20 for(int j=1;j<=m;j++) 21 { 22 if(s[i-1]==t[j-1]) 23 { 24 dp[i][j]=dp[i-1][j-1]+1; 25 flag[i][j]=1; 26 } 27 else if(dp[i][j-1]>dp[i-1][j]) 28 { 29 dp[i][j]=dp[i][j-1]; 30 flag[i][j]=2; 31 } 32 else 33 { 34 dp[i][j]=dp[i-1][j]; 35 flag[i][j]=3; 36 } 37 } 38 //printf("%d\n",dp[n][m]); 39 } 40 41 void getLCS(int n,int m) 42 { 43 int k=0; 44 while(n>0&&m>0) 45 { 46 if(flag[n][m]==1) 47 { 48 res[k++]=s[n-1]; 49 n--; 50 m--; 51 } 52 else if(flag[n][m]==2) m--; 53 else if(flag[n][m]==3) n--; 54 } 55 for(int i=k-1;i>=0;i--) 56 { 57 printf("%c",res[i]); 58 } 59 printf("\n"); 60 } 61 62 int main() 63 { 64 //freopen("a.txt","r",stdin); 65 scanf("%s %s",s,t); 66 //printf("%s %s\n",s,t); 67 int l1=strlen(s),l2=strlen(t); 68 LCS(l1,l2); 69 /*for(int i=0;i<=l1;i++) 70 { 71 for(int j=0;j<=l2;j++) 72 printf("%d ",flag[i][j]); 73 printf("\n"); 74 } 75 printf("\n");*/ 76 getLCS(l1,l2); 77 return 0; 78 }
递归
1 #include<cstdio> 2 #include<cstring> 3 const int maxn=1111; 4 int dp[maxn][maxn],flag[maxn][maxn]; 5 int n,m; 6 char s[maxn],t[maxn]; 7 8 void LCS(int n,int m) 9 { 10 memset(dp,0,sizeof(dp)); 11 memset(flag,0,sizeof(flag)); 12 for(int i=0;i<n;i++) 13 for(int j=0;j<m;j++) 14 { 15 if(s[i]==t[j]) 16 { 17 dp[i+1][j+1]=dp[i][j]+1; 18 flag[i+1][j+1]=1; 19 } 20 else if(dp[i+1][j]>dp[i][j+1]) 21 { 22 dp[i+1][j+1]=dp[i+1][j]; 23 flag[i+1][j+1]=2; 24 } 25 else 26 { 27 dp[i+1][j+1]=dp[i][j+1]; 28 flag[i+1][j+1]=3; 29 } 30 } 31 // printf("%d\n",dp[n][m]); 32 } 33 34 void printLCS(int n,int m) 35 { 36 if(n==0||m==0) return; 37 38 if(flag[n][m]==1) 39 { 40 printLCS(n-1,m-1); 41 printf("%c",s[n-1]); 42 } 43 else if(flag[n][m]==2) printLCS(n,m-1); 44 else printLCS(n-1,m); 45 } 46 int main() 47 { 48 //freopen("a.txt","r",stdin); 49 scanf("%s %s",s,t); 50 int l1=strlen(s),l2=strlen(t); 51 LCS(l1,l2); 52 printLCS(l1,l2); 53 return 0; 54 }
下面这种是比较简洁的。去掉了标记数组。
1 #include<string> 2 #include<iostream> 3 using namespace std; 4 const int maxn = 1111; 5 6 int dp[maxn][maxn]={0}; 7 string a,b; 8 9 void LCS(int n,int m) 10 { 11 for(int i=1;i<=n;i++) 12 for(int j=1;j<=m;j++) 13 if(a[i-1]==b[j-1]) dp[i][j]=dp[i-1][j-1]+1; 14 else dp[i][j]=max(dp[i][j-1],dp[i-1][j]); 15 } 16 17 int main() 18 { 19 cin>>a>>b; 20 int l1=a.size(),l2=b.size(); 21 LCS(l1,l2); 22 int len=dp[l1][l2]; 23 string ans; 24 int i=l1,j=l2; 25 while(dp[i][j]) 26 { 27 if(dp[i][j]==dp[i-1][j]) i--; 28 else if(dp[i][j]==dp[i][j-1]) j--; 29 else ans.push_back(a[i-1]),i--,j--; 30 } 31 for(int i=len-1;i>=0;i--) 32 cout<<ans[i]; 33 return 0; 34 }