每日一道dp题:codeforces:1336C. Kaavi and Magic Spell 区间dp

转载加上自己的理解https://www.cnblogs.com/pigzhouyb/p/12755540.html

题目大意:

  两个字符串S和T,长度分别为n和m,还有一个空串A
  问:每次可以删掉S的第一个字符,然后放到一个初始为空的字符串A的首部或尾部,求有多少种不同的方法使得最后T是A的前缀。
  题目链接:https://codeforces.ml/problemset/problem/1336/C

解题思路:

  可以发现A串的特性是向两边扩展,于是我们可以从A串这个特性来思考问题的解法?(一开始想的是线性直接递推)
  对于不断两边扩展的问题需要想到区间dp!!!

  1. f [ l ] [ r ] f[l][r] f[l][r] S S S的前 l − r + 1 l-r+1 lr+1个字符前后拼接形成的 A A A串, A A A串的任意区间 [ L , R ] , R − L + 1 > = m [L,R],R-L+1>=m [L,R],RL+1>=m任意一个字符符合条件的方案数。这里的字符满足条件当且仅当 i > ∣ T ∣ i>|T| i>T或者 i < = ∣ T ∣ i<=|T| i<=T A A Ai= T T Ti
  2. 我们考虑方程的转移,对于一个l,r和区间的长度len=r-l+1
  3. 对于s[len]=t[i]或者i>m的情况,说明当前新加入的字母足以匹配可以成为开头开头可以选择任意字母,那么就有: f [ i ] [ j ] + = f [ i + 1 ] [ j ] f[i][j]+=f[i+1][j] f[i][j]+=f[i+1][j](当 i < = m i<=m i<=m时, i + 1 i+1 i+1~ j j j都符合的前 j − ( i + 1 ) + 1 j-(i+1)+1 j(i+1)+1个字符)
    (当 i > m i>m i>m时,因为前面都符合题意条件,所以可以直接相加)
  4. 对于s[len]=t[j]或者j>m的情况,同理: f [ i ] [ j ] + = f [ i ] [ j − 1 ] f[i][j]+=f[i][j−1] f[i][j]+=f[i][j1]

AC代码:

#include<bits/stdc++.h>
#define Mod 998244353
using namespace std;  
typedef long long ll;
char s[4000],t[4000];
ll f[4010][4010];
int main() {
	scanf("%s%s",s+1,t+1);
	int ls=strlen(s+1),lt=strlen(t+1);
	for(int i=1; i<=ls; i++) f[i][i]=(i>lt||s[1]==t[i])*2;
	//预处理长度为1的情况,因为第一个放的时候,放前面或者后面都一样但是方式不一样
	//于是要乘2
	for(int len=2; len<=ls; len++)
	    for(int l=1; l+len-1<=ls; l++) {
	    	int r=l+len-1;
	    	if(s[len]==t[l]||l>lt) f[l][r]=(f[l][r]+f[l+1][r])%Mod;
	    	if(s[len]==t[r]||r>lt) f[l][r]=(f[l][r]+f[l][r-1])%Mod;
		}
	ll ans=0;
	for(int i=lt; i<=ls; i++)
	    ans=(ans+f[1][i])%Mod;
	printf("%lld\n",ans);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值