CF494B - Obsessive String(dp+kmp)
题目
题解
dp苦手表示不会做qwq
看题解看老半天才理解。关键就是有次序条理地计数。
设\(a_i\)为包含以位置i为结尾的区间的所有取法。
用kmp找出所有子串t在母串结尾的位置,称这样的位置为好位置。
显然,区间[l,r]的所有方案即为\(\sum_{i=l}^{r}{a_i}\)。所以关键是求\(a_i\)。
- 如果位置i不是好位置,那么\(a_i=a_{i-1}\),因为为了包含子串,它只能从\(a_{i-1}\)扩展。
- 如果位置i是好位置,那么从i开始,只取一个区间包含当前位置的子串,有i-|t|+1总方案;或者把区间分成[1, j]和[j+1, i],这样的方案数为\(\sum_{i=1}^{j}{a_i}\),又由于不能重叠,j的范围为[1, i-|t|]。这样总方案数为\(\sum_{j=1}^{i-|t|}{\sum_{k=1}^{j}{a_k}}+i-|t|+1\)。
所以维护一个前缀和的前缀和即可。
#include <bits/stdc++.h>
#define endl '\n'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define FILE freopen(".//data_generator//in.txt","r",stdin),freopen("res.txt","w",stdout)
#define FI freopen(".//data_generator//in.txt","r",stdin)
#define FO freopen("res.txt","w",stdout)
#define pb push_back
#define mp make_pair
#define seteps(N) fixed << setprecision(N)
typedef long long ll;
using namespace std;
/*-----------------------------------------------------------------*/
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f
const int N = 3e5 + 10;
const int M = 1e9 + 7;
const double eps = 1e-5;
char s[N];
char t[N];
int nt[N];
bool flag[N];
void getnext() {
int i = 0, j = -1;
nt[i] = j;
while(t[i]) {
if(j == -1 || t[i] == t[j]) {
i++, j++;
nt[i] = j;
} else {
j = nt[j];
}
}
}
void work() {
int i = 0, j = 0;
while(s[i]) {
if(j == -1 || s[i] == t[j]) {
i++, j++;
if(!t[j]) {
flag[i - 1] = 1;
j = nt[j];
}
} else {
j = nt[j];
}
}
}
int sum1[N];
int sum2[N];
int dp[N];
int main() {
IOS;
cin >> s >> t;
int n = strlen(t);
getnext();
work();
for(int i = 0; s[i]; i++) {
if(flag[i]) {
dp[i] = (i - n >= 0 ? sum2[i - n] : 0) + i - n + 2;
dp[i] %= M;
} else {
if(i > 0) {
dp[i] = dp[i - 1];
} else dp[i] = 0;
}
if(i > 0) {
sum1[i] += sum1[i - 1] + dp[i];
sum1[i] %= M;
sum2[i] += sum2[i - 1] + sum1[i];
sum2[i] %= M;
} else sum1[i] = sum2[i] = dp[i];
}
int ans = 0;
for(int i = 0; s[i]; i++) {
ans += dp[i];
ans %= M;
}
cout << ans << endl;
}