字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列。令给定的字符序列X=“x0,x1,…,xm-1”,序列Y=“y0,y1,…,yk-1”是X的子序列,存在X的一个严格递增下标序列<i0,i1,…,ik-1>,使得对所有的j=0,1,…,k-1,有xij = yj。例如,X=“ABCBDAB”,Y=“BCDB”是X的一个子序列。对给定的两个字符序列,求出他们最长的公共子序列长度,以及最长公共子序列个数。
Solution
这题其实就是让求两个序列的LCS和方案数。
LCS的求法:
dp[i][j]=dp[i-1][j-1]+1(s[i]==s[j])
dp[i][j]=max(dp[i][j-1],dp[i-1][j])(s[i]!=s[j])
这个非常简单,但计数部分需要一些思考。
第一种情况时,我们当前的方案数位不管i位和j位的方案数(因为i和j匹配了),所以要把g[i-1][j]和g[i][j-1]讨论一下。
第二种情况,当dp[i-1][j]==dp[i][j-1]时,我们的g[i-1][j-1]的方案都被算了一次,所以要减掉。
这道题可以加深对LCS的理解。
Code
#include<iostream> #include<cstdio> #include<cstring> #define N 5004 #define mod 100000000 using namespace std; int dp[2][N],n1,n2,now; long long g[2][N]; char s1[N],s2[N]; int main(){ scanf("%s%s",s1+1,s2+1); n1=strlen(s1+1)-1; n2=strlen(s2+1)-1; for(int i=0;i<=n1;++i)g[0][i]=g[1][i]=1; for(int i=1;i<=n1;++i){ now^=1; for(int j=1;j<=n2;++j){ int f=(s1[i]==s2[j]); if(f){ dp[now][j]=dp[now^1][j-1]+f; g[now][j]=g[now^1][j-1]; if(dp[now^1][j]==dp[now][j])(g[now][j]+=g[now^1][j])%=mod; if(dp[now][j-1]==dp[now][j])(g[now][j]+=g[now][j-1])%=mod; } else{ g[now][j]=0; dp[now][j]=max(dp[now^1][j],dp[now][j-1]); if(dp[now^1][j]==dp[now][j])(g[now][j]+=g[now^1][j])%=mod; if(dp[now][j-1]==dp[now][j])(g[now][j]+=g[now][j-1])%=mod; if(dp[now^1][j-1]==dp[now][j])(g[now][j]-=g[now^1][j-1])%=mod; g[now][j]=(g[now][j]+mod)%mod; } } } printf("%d %d",dp[now][n2],g[now][n2]); return 0; }