【洛谷】P8256 [NOI Online 2022 入门组] 字符串(dp)
题意
给定两个由 0,1,- 组成的字符串 \(S\),\(T\),以及一个空串 \(R\)。\(S\) 的长度为 \(n\)。
现在要进行 \(n\) 次操作,每一次操作取出 \(S\) 的第一个字符 \(c\),然后将其在 \(S\) 中删除。如果 \(c\) 是字符 -,那么删除 \(R\) 的开头字符或者结尾字符,否则将 c 插入到字符串 \(R\) 的结尾。
数据范围
\(1\leq |S|,|T| \leq 400\)。
思路
设 \(f{i,j,k}\) 表示现在取出了 \(S\) 中的第 \(i\) 个字符,\(R\) 中开头还要删除 \(j\) 个字符,结尾还要删除 \(k\) 个字符的方案数。最终的答案就是 \(f{n,0,0}\) 。
当 \(s_i=-\) 时,就要从 \(R\) 的开头或结尾删去一个数,有:
\(f{i,j,k}=f{i,j+1,k}+f{i,j,k+1}\) 。
设当前 \(R\) 的长度为 \(now\),由于开头还要删去 \(j\) 个数,当前的 \(s_i\) 就对应到 \(t_{now-j}\),如果它们相等,那么此时结尾就不需要删除字符,就有:
\(f{i,j,k}=f{i-1,j,k}(k=0)\) 。
而如果不相等,那么意味着结尾要多删除一个字符,就有:
\(f{i,j,k}=f{i-1,j,k-1}(k>0)\) 。
只有当 \(R\) 中的目前的字符都会从开头删去时,才可以将当前字符从开头删去,就有:
\(f{i,j,k}=f{i-1,j-1,k}(j=now)\)
最后,多测别忘了清空。
code:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=410,mod=1e9+7;
char s[N],t[N];
int n,m,f[N][N][N];
void solve()
{
scanf("%d%d",&n,&m);scanf("%s%s",s+1,t+1);int tot=0;
for(int i=1;i<=n;i++) tot+=(s[i]=='-');
if(n-2*tot!=m) return puts("0"),void();int now=0;
memset(f,0,sizeof(f));f[0][0][0]=1;
for(int i=1;i<=n;i++)
{
now+=(s[i]=='-'?-1:1);
for(int j=0;j<=tot;j++)
for(int k=0;k<=tot;k++)
{
if(s[i]=='-') f[i][j][k]=(f[i-1][j][k+1]+f[i-1][j+1][k])%mod;
else
{
if(k) f[i][j][k]=f[i-1][j][k-1];
else if(s[i]==t[now-j]) f[i][j][k]=f[i-1][j][k];
if(now==j) f[i][j][k]=(f[i][j][k]+f[i-1][j-1][k])%mod;
}
}
}
printf("%d\n",f[n][0][0]);
}
int main()
{
int T;scanf("%d",&T);while(T--) solve();
return 0;
}

浙公网安备 33010602011771号