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);
}