公共子序列
Description
我們稱序列Z = < z1, z2, ..., zk >是序列X = < x1, x2, ..., xm >的子序列當且僅當存在 嚴格上升 的序列< i1, i2, ..., ik >,使得對j = 1, 2, ... ,k, 有xij = zj。比如Z = < a, b, f, c > 是X = < a, b, c, f, b, c >的子序列。現在給出兩個序列X和Y,你的任務是找到X和Y的最大公共子序列,也就是說要找到一個最長的序列Z,使得Z既是X的子序列也是Y的子序列。
Input
輸入包括多組測試數據。每組數據包括一行,給出兩個長度不超過200的字符串,表示兩個序列。兩個字符串之間由若干個空格隔開。Output
對每組輸入數據,輸出一行,給出兩個序列的最大公共子序列的長度。Sample Input
abcfbc abfcab programming contest abcd mnp
Sample Output
4 2 0
解題
看到這道題的時候,我發現這道題可以用3種方案,分別是深搜(TLE)、動態規劃(AC)、記憶化搜索(AC)。
方法I:深搜
#include<bits/stdc++.h>
using namespace std;
string x,y;
int lenx,leny,maxn;
void dfs(int s,int t,int g)
{
if(t>=lenx||g>=leny) return ;
for(int i=t;i<lenx;i++)
{
int j;
for(j=g;j<leny;j++)
if(x[i]==y[j])
break;
if(j==leny) continue;
dfs(s+1,i+1,j+1);
if(s>maxn) maxn=s;
}
}
main()
{
while(cin>>x>>y)
{
maxn=0;
lenx=x.length();
leny=y.length();
dfs(1,0,0);
cout<<maxn<<endl;
}
}
方法II:動態規劃
#include<bits/stdc++.h>
using namespace std;
int ans,A[201][201],lenx,leny,flag[201][201];
string x,y;
void DP()
{
memset(A,0,sizeof(A));
for(int i=1;i<=lenx;++i)
flag[i][0]=1;
for(int i=1;i<=leny;++i)
flag[0][i]=-1;
for(int i=1;i<=lenx;++i)
for(int j=1;j<=leny;++j)
{
if(x[i-1]==y[j-1])
{
A[i][j]=A[i-1][j-1]+1;
flag[i][j]=0;
}
else if(A[i-1][j]>=A[i][j-1])
{
A[i][j]=A[i-1][j];
flag[i][j]=1;
}
else
{
A[i][j]=A[i][j-1];
flag[i][j]=-1;
}
}
}
void print(int x,int y)
{
if(!x&&!y)
return ;
if(flag[x][y]==0)
{
print(x-1,y-1);
ans++;
}
else if(flag[x][y]==1)
print(x-1,y);
else
print(x,y-1);
}
int main()
{
while(cin>>x>>y)
{
ans=0;
lenx=x.size();
leny=y.size();
DP();
print(lenx,leny);
printf("%d\n",ans);
}
}
方法III:記憶化搜索
#include<bits/stdc++.h>
using namespace std;
string x,y;
int lenx,leny,A[201][201];
int search(int i,int j)
{
if(A[i][j]+1) return A[i][j];
if(!i&&!j)
{
if(x[i]==y[j])
return A[i][j]=1;
else
return A[i][j]=0;
}
if(!i)
{
for(int k=j;k>=0;k--)
if(x[i]==y[k])
return A[i][j]=1;
return A[i][j]=0;
}
if(!j)
{
for(int k=i;k>=0;k--)
if(x[k]==y[j])
return A[i][j]=1;
return A[i][j]=0;
}
if(x[i]==y[j]) return A[i][j]=1+search(i-1,j-1);
else return A[i][j]=max(search(i,j-1),search(i-1,j));
}
int main()
{
while(cin>>x>>y)
{
memset(A,-1,sizeof(A));
cout<<search(x.length()-1,y.length()-1)<<endl;
}
}
通過這道題我們可以發現,其實記憶化搜索和動態規劃的關係就像遞歸和遞推的關係一樣,大多數時候可以相互轉換。