[bzoj2423][HAOI2010]最长公共子序列
来自FallDream的博客,未经允许,请勿转载,谢谢。
--------------------------------------------------------------------------------------------------------
给定两个长度为n,m字符串,求最长公共子序列长度和方案数$\mod 10^{8}$ $n,m\leqslant 5000$
题解:第一问相信大家都会...f[i][j]表示第一个字串前i个,第二个字串前j个最长的长度$f[i][j]=\begin{equation}
\left\{
\begin{aligned}
\max(f[i][j-1],f[i-1][j]) \\
f[i-1][j-1]+1(a[i]=b[j]) \\
\end{aligned}
\right.
\end{equation}$
然后第二个问,我们考虑f[i][j]=k,那么就从加上f[i-1][j]和f[i][j-1]中=k的方案数。
如果a[i]=b[j],那么还要算上f[i-1][j-1]的方案数;但是如果a[i]!=b[j]且f[i-1][j-1]=k,就要减去它的方案数了。
#include<iostream> #include<cstdio> #include<cstring> #define mod 100000000 #define ll long long using namespace std; inline int read() { int x = 0 , f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x * f; } int f[2][5005],r[2][5005]; char s1[5005],s2[5005]; int n,m; int main() { scanf("%s",s1+1);n=strlen(s1+1)-1; scanf("%s",s2+1);m=strlen(s2+1)-1; int now=1,pre=0; for(int k=0;k<=m;k++)r[0][k]=1;r[1][0]=1; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { f[now][j]=max(f[pre][j],f[now][j-1]);r[now][j]=0; if(s1[i]==s2[j])f[now][j]=max(f[now][j],f[pre][j-1]+1); if(s1[i]==s2[j]&&f[now][j]==f[pre][j-1]+1) r[now][j]+=r[pre][j-1]; if(f[pre][j]==f[now][j]) r[now][j]+=r[pre][j]; if(f[now][j-1]==f[now][j]) r[now][j]+=r[now][j-1]; if(f[pre][j-1]==f[now][j]) r[now][j]-=r[pre][j-1]; r[now][j]=(r[now][j]+mod)%mod; } now=pre;pre=1-pre; } printf("%d\n%d",f[pre][m],r[pre][m]); return 0; }
FallDream代表秋之国向您问好!
欢迎您来我的博客www.cnblogs.com/FallDream