题目:CF 494 B.
题目大意:给定两个字符串
S
S
S和
T
T
T,在
S
S
S中抽取几个不相交的子串,使得
T
T
T均是这些子串的子串,问有多少种方案.
1
≤
∣
S
∣
,
∣
T
∣
≤
1
0
5
1\leq |S|,|T|\leq 10^5
1≤∣S∣,∣T∣≤105.
仔细一看感觉题目好像比较难,但看完题解后感觉这也是一道比较容易的套路题,KMP与DP结合的题做的少啊…
首先很容易想到第一个串的子串必须包含第二个串,就很自然想到用KMP将第二个串在第一个串中出现的位置都找出来,那么所有的子串都必须包含这些位置.
考虑计算出一个数组 v a l [ i ] val[i] val[i]表示第 i i i个字符前最近的匹配点(第 i i i个点不一定要匹配),发现这个数组可以很容易用KMP求出匹配点后稍微处理一下求出.
考虑应用
v
a
l
val
val数组,设
f
[
i
]
f[i]
f[i]表示到前
i
i
i个字符的方案数(所选子串中不一定要包含第
i
i
i个字符),然后很容易发现这个状态的转移有两种情况:
1.去掉第
i
i
i个字符,也就是
f
[
i
−
1
]
f[i-1]
f[i−1].
2.找到最近的匹配点
v
a
l
[
i
]
val[i]
val[i],考虑
f
[
1..
v
a
l
[
i
]
−
1
]
f[1..val[i]-1]
f[1..val[i]−1],发现可以只选串
[
v
a
l
[
i
]
.
.
i
]
[val[i]..i]
[val[i]..i]或者在
f
[
1..
v
a
l
[
i
]
−
1
]
f[1..val[i]-1]
f[1..val[i]−1]后面直接加入这个串.
所以转移就是:
f
[
i
]
=
f
[
i
−
1
]
+
v
a
l
[
i
]
+
∑
j
=
1
v
a
l
[
i
]
−
1
f
[
j
]
f[i]=f[i-1]+val[i]+\sum_{j=1}^{val[i]-1}f[j]
f[i]=f[i−1]+val[i]+j=1∑val[i]−1f[j]
转移出来之后,发现答案就是 f [ n ] f[n] f[n]了,所以直接输出就好.
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=100000;
const LL mod=1000000007;
char s[N+9],t[N+9];
int n,m,nxt[N+9];
LL f[N+9],sum[N+9],val[N+9];
void add(LL &a,LL b){a+=b;while (a>=mod) a-=mod;}
void self_mate(){
int j=0;
nxt[1]=0;
for (int i=2;i<=m;++i){
while (t[i]^t[j+1]&&j>0) j=nxt[j];
if (t[i]==t[j+1]) ++j;
nxt[i]=j;
}
}
void mate(){
int j=0;
for (int i=1;i<=n;++i){
while (s[i]^t[j+1]&&j>0) j=nxt[j];
if (s[i]==t[j+1]) ++j;
if (j==m){
val[i]=i-m+1;
j=nxt[j];
}
}
}
void solve(){
for (int i=1;i<=n;++i)
if (val[i]==0) val[i]=val[i-1];
for (int i=1;i<=n;++i){
f[i]=f[i-1];
if (val[i]) add(f[i],sum[val[i]-1]+val[i]);
add(sum[i],sum[i-1]+f[i]);
}
}
Abigail into(){
scanf("%s",s+1);
scanf("%s",t+1);
n=strlen(s+1);
m=strlen(t+1);
}
Abigail work(){
self_mate();
mate();
solve();
}
Abigail outo(){
printf("%I64d\n",f[n]);
}
int main(){
into();
work();
outo();
return 0;
}