https://vjudge.net/problem/CodeForces-494B
题目
给字符串s和t,你可以选择一些字符串s的子串,问有多少种方法满足以下条件
- 这些字串不重叠
- 这些字串中都包含t
1 ≤ |s|, |t| ≤ 105
题解
设dp[i]为以i开头有多少种方法可以选择
如果s[i]不是t的开头,要保证包含t又以i开头,那么只能在dp[i+1]的选择方法前面加上一格,dp[i]=dp[i+1]
如果s[i]是t的开头,那么就可以只选择t、选择t后再选择后面的、选择t多一格再选择后面的,选择t多2格再选择后面的……
dp[i]=1+dp[i+|t|]+2*dp[i+|t|+1]+...+?*dp[|s|]
答案是$sum dp[i]$
AC代码
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<map> #define REP(i,a,b) for(register int i=(a); i<(b); i++) #define REPE(i,a,b) for(register int i=(a); i<=(b); i++) #define PERE(i,a,b) for(register int i=(a); i>=(b); i--) using namespace std; typedef long long ll; #define MO 1000000007 char s[100007], t[100007]; int ls, lt; int dp[100007], ss[100007], s2[100007]; int f[100007]; bool g[100007]; inline void getf(char *s) { int t=f[0]=-1; int l=strlen(s); REP(i,0,l) { while(t>=0 && s[i]!=s[t]) t=f[t]; t++; f[i+1]=(s[i+1]!=s[t]?t:f[t]); } } inline void sear() { int j=0; REP(i,0,ls) { while(~j && s[i]!=t[j]) j=f[j]; j++; if(j==lt) { g[i-lt+1]=1; } } } int main() { scanf("%s%s", s, t); ls=strlen(s), lt=strlen(t); getf(t); memset(g,0,sizeof g); sear(); dp[ls]=0; memset(ss,0,sizeof ss); ss[ls]=0; s2[ls]=0; PERE(i,ls-1,0) { if(g[i]) { dp[i]=(ls-i-lt+1)%MO; dp[i]=(dp[i]+s2[i+lt]) % MO; } else { dp[i]=dp[i+1]; } ss[i]=(ss[i+1]+dp[i]) % MO; s2[i]=(s2[i+1]+ss[i]) % MO; } int ans=0; REP(i,0,ls) ans=(ans+dp[i])%MO; printf("%d ", ans); }