题解 CF494B Obsessive String
传送门。
题意
给定一个字符串 $S$,和一个字符串 $T$。
在 $S$ 中取出任意多个不重叠的子串,使得每个子串都包含 $T$,求方案数。
分析
先分析一下题目,显然,我们使用 DP 来解决这道题。
设计一个状态:$f_i$,$f_i$ 表示最后一个串的最后一位是 $i$ 的方案数。
我们可以枚举最后一个子串的区间,进行转移。
首先要考虑的是,如何判断区间是否包含 $T$。
我们考虑使用字符串哈希,将整一个匹配串哈希,再标记每一个区间,最后,预处理出当前左侧的第一个匹配串,令其为 $L_i$,因此只要左端点处于 $1\sim L_i-m+1$,我们的区间就可以包含 $T$。
p[0]=1;
for(int i=1; i<=n; ++i) p[i]=p[i-1]*P;
for(int i=1; i<=n; ++i) a[i]=a[i-1]*P+s[i];
for(int i=1; i<=m; ++i) wh=wh*P+t[i];
for(int i=m; i<=n; ++i) if(query(i-m+1,i)==wh) mark[i]=1;
for(int i=m; i<=n; ++i) L[i]=mark[i]?i:L[i-1];
枚举了我们这一个区间后,考虑继承前面的 $f$。
令枚举的左端点为 $j$,右端点为 $i$。
我们的 $f_i$ 就可以继承 $\forall k\in \left [ 1,j-1 \right ] $ 的 $f$。
for(int i=1; i<=n; ++i) {
if(!L[i]) continue;
for(int j=1; j<=L[i]-m+1; ++j) {
f[i]++;
for(int k=1; k<=j-1; ++k) f[i]=(f[i]+f[k])%MOD;
}
}
for(int i=1; i<=n; ++i) tot=(tot+f[i])%MOD;
时间复杂度:$O(n^3)$。
接下来考虑优化这个 DP,首先将 $k$ 优化掉。
怎么优化呢,使用前缀和。
for(int i=1; i<=n; ++i) {
if(!L[i]) continue;
for(int j=1;j<=L[i]-m+1;++j) f[i]=(f[i]+qzh[j-1]+1)%MOD;
qzh[i]=(qzh[i-1]+f[i])%MOD;
}
时间复杂度:$O(n^2)$。
显然,我们依旧使用前缀和来优化。
for(int i=1; i<=n; ++i) {
if(!L[i]) continue;
f[i]=(sum[L[i]-m]+L[i]-m+1)%MOD;
qzh[i]=(qzh[i-1]+f[i])%MOD;
sum[i]=(sum[i-1]+qzh[i])%MOD;
}
时间复杂度:$O(n)$。
此时就可以愉快的 A 掉此题了。

浙公网安备 33010602011771号