CF R 635 div1 C Kaavi and Magic Spell 区间dp

LINK:Kaavi and Magic Spell

一打CF才知道自己原来这么菜 这题完全没想到.

可以发现 如果dp f[i][j]表示前i个字符匹配T的前j个字符的方案数 此时转移变得异常麻烦 状态转移一次变成了O(n).

会超时 而且这个状态的转移也是不正确的 可能当前的S字符串后面放了一些不能匹配的东西 但是此时却体现不出来.

那我们如何描述每次增加一个字符且和T匹配多少这种状态呢.

一个思路 先对于S串的某个i暴力枚举 其对应在T中的位置 这样的话dp就变成了

f[i][j][k]表示前i个字符 匹配了T中j~k区间的方案数 这样就没有刚才的问题了。

不过 这个状态还是不行 因为存在重复 的方案被统计到了.

仔细思考 对于刚才的那个状态 之所以会重复是因为两种方案的重叠都被我们枚举到了。

考虑最终的答案的方案数 不难发现对于i号点来说 其位置是不固定的。

我们完全可以只枚举1的位置在哪然后进行上述的区间dp.

这样的话就既没有加到队首 整体右移的问题 也没有状态数重复的问题.

有状态 f[i][j]表示 前j-i+1个字符 匹配了T的 i~j的区间的方案数.

考虑转移.

对于 第j-i+2个字符 可以判断一下能否转移即可。

由于每次使用的字符固定 不需要再枚举决策。

所以复杂度为n^2.

const int MAXN=3010;
int T,n,ans,m;
int f[MAXN][MAXN];
char a[MAXN],b[MAXN];
int main()
{
	freopen("1.in","r",stdin);
	gc(a);gc(b);
	n=strlen(a+1);
	m=strlen(b+1);
	rep(1,n,i)if(a[1]==b[i]||i>m)f[i][i]=1;
	for(int len=2;len<=n;++len)
	{
		for(int i=1;i<=n-len+1;++i)
		{
			int j=i+len-1;
			if(a[len]==b[i]||i>m)f[i][j]=(f[i][j]+f[i+1][j])%mod;
			if(a[len]==b[j]||j>m)f[i][j]=(f[i][j]+f[i][j-1])%mod;
		}
	}
	rep(m,n,i)ans=(ans+f[1][i])%mod;
	put(ans*2%mod);
	return 0;
}
posted @ 2020-04-17 07:47  chdy  阅读(194)  评论(0编辑  收藏  举报