题意:给你两个字符串,问第一个字符串可以有多少种方式拆分出子串,使得得到的所有子串都包含第二个字符串。
思路:dp[i]表示前在第一个字符串中前i个字符可以拆分出来的方式。对于第r个字符,如果至少为l-r使得其包含第二个字符串,那么dp[r]=dp[i-1]+ dp[l-1]+1 +dp[l-2]+1 +...+dp[0]+1;每次表示最后的那些字符组成一个拆分出来的子串。合并之后为dp[r]=sum[l-1]+l;
AC代码如下:
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
char str1[100010];
char str2[100010];
int f[100010],val[200010];
int n,m;
ll dp[100010],sum[100010],MOD=1e9+7;
void find()
{ int i,j=0;
for(i=0;i<n;i++)
{ while(j && str2[j]!=str1[i])
j=f[j];
if(str2[j]==str1[i])
j++;
if(j==m)
val[i+1]=i-m+2;
}
}
void getfail()
{ int i,j;
memset(f,0,sizeof(f));
for(i=1;i<m;i++)
{ j=f[i];
while(j && str2[j]!=str2[i])
j=f[j];
if(str2[j]==str2[i])
f[i+1]=j+1;
else
f[i+1]=0;
}
}
int main()
{
int t,i,j,k;
scanf("%s",str1);
scanf("%s",str2);
n=strlen(str1);
m=strlen(str2);
getfail();
find();
for(i=1;i<=n;i++)
if(val[i]==0)
val[i]=val[i-1];
for(i=1;i<=n;i++)
{
dp[i]=dp[i-1];
if(val[i]>0)
dp[i]+=sum[val[i]-1]+val[i];
dp[i]%=MOD;
sum[i]=(sum[i-1]+dp[i])%MOD;
}
printf("%I64d\n",dp[n]);
}