【洛谷】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;
}
posted @ 2022-10-11 17:12  曙诚  阅读(91)  评论(0)    收藏  举报