这题题意真的是玄学==读了半年
题目在这里
题意:给两个串s,t,0<|s|,|t|<=1e5,现在要把s分割成不重合的k段,每段的起点是a[i],终点是b[i].也就是串s[a[i]]s[a[i]+1]s[a[i]+2].....s[b[i]]这一段。要求每段都有一个子串是t。求有多少种分割的方案
思路:首先不难想到这样暴力:dp[i]表示把i作为最后一段子串终止点的方案数dp[i] = dp[j] *(si - j) + dp[j + 1] * (si - j - 1).....+dp[si-1]*(1)
si是最后一段子串想要包含t的最后面的开始点。然后观察等式右边发现这个公式可以维护一个前缀答案和tmp和前缀和sum,然后si每向右边移动一位。sum就加上dp[si],tmp就加上sum;
每个点的si可以用KMP来求==具体见代码:
代码:

#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5 + 10; const ll mod = 1e9 + 7; char s[maxn] , t[maxn]; int nxt[maxn]; bool mat[maxn]; ll dp[maxn]; int lastmat[maxn]; void get_next(){ int lent = strlen(t + 1)+1; nxt[0] = 0; nxt[1] = 0; int i = 2 , j = 0; while(i <= lent){ if(j != 0 && t[i - 1] != t[j]) j = nxt[j]; else nxt[i++] = ++j; } for(int i = 1; i <= lent;i++){ int xt = nxt[i]; while(t[i] == t[xt]&&xt != 0) nxt[i] = nxt[xt] ,xt = nxt[xt]; } } void KMP(){ memset(mat,0,sizeof(mat)); int lent = strlen(t + 1) + 1; int lens = strlen(s + 1); int j = 1; for(int i = 1; i <= lens;i++){ while(j != 0 && s[i] != t[j]) j = nxt[j]; j++; if(j == lent) mat[i] = 1 , j = nxt[j]; } } void init(){ int si = 0; int lens = strlen(s + 1); int lent = strlen(t + 1); for(int i = 1 ; i <= lens;i++){ if(mat[i]){ si = i - lent + 1; } lastmat[i] = si; } } int main() { while(~scanf("%s%s",s+1,t+1)){ get_next(); KMP(); init(); memset(dp,0,sizeof(dp)); dp[0] = 1; ll sum = 0; ll tmp = 0; int lens = strlen(s + 1); ll ans = 0; int si = 0; for(int i = 1; i <= lens;i++){ while(si < lastmat[i]){ sum += dp[si++]; if(sum >= mod) sum -= mod; tmp += sum; if(tmp >= mod) tmp -= mod; } dp[i] = tmp; ans += tmp; if(ans >= mod) ans -= mod; } printf("%I64d ",ans); } }