题解 2014 年湖北省队互测 Week2 似乎在梦中见过的样子
前排提醒:fail
函数 / 数组 和 next
函数 / 数组 是同一个东西,叫法存在差异,是用于解决失配情况的。个人在这篇题解里使用了 next
的说法,若有差异,敬请理解包涵。
题目大意
求长度为 \(n\) 的字符串 \(\rm S\) 中所有不同的形如 \(\rm ABA\) 的子串,满足 \(\operatorname{len}(A)\ge k\),\(\operatorname{len}(B)\ge 1\)。
「不同的」的定义是位置不同、长度不同,拆分方式可以相同。
大致思路
由于其数据规模并不是很大,可以通过循环解决。
枚举子串的左端点,边跑 next
数组边计算答案(移除覆盖的情况、判断答案)。
详细做法
此题重难点在于「转化问题」和「移除覆盖情况」。
· 转化问题
求长度为 \(n\) 的字符串 \(\rm S\) 中所有不同的形如 \(\rm ABA\) 的子串,要满足一定条件。
如果你有做过一本通题目列表中此题的上一道题「POI 2006 OKR-Periods of Words」,这道题会相对好做一些。
上一道题是只移动右端点,跑一遍 next
,右端点往右动,字符串从短到长跑,记忆化搜索,时间复杂度 \(O(n)\);
这道题我们可以参考一下,不仅仅移动右端点,还要移动左端点,字符串从短到长跑,因为数据范围并不大,时间复杂度可以接受 \(O(n^2)\)。可以使用边跑 next
边算答案。
· 移除覆盖情况
这道题不需要求最短公共前后缀,也不能用最长公共前后缀。这道题需要你判断子串是否有一个长度合适(合规)的公共前后缀。
详细的,我们用一个样例:aaaaa
(下标从 \(1\) 开始),\(k\) 为 \(1\);
跑完 next
的结果是 0 1 2 3 4
。
- 左端点指向 \(1\),右端点指向 \(1\),为了算
next
需要经历的过程,不满足基本条件,不通过 - 左端点指向 \(1\),右端点指向 \(2\),也不满足基本条件,不通过;
- 左端点指向 \(1\),右端点指向 \(3\),满足基本条件,但是存在问题:在
aaa
中最大公共前后缀为aa
,构成重叠,我们将指针 \(P = 3\) 赋值为 \(next_P = 2\),得到aaa
的最大公共前后缀aa
,发现字符串aa
的最大公共前后缀a
是合法的。
这个时候我们需要用上一题求最短公共前后缀的思路,往前回溯(x = next[x]
)。
也并不是所有样例都像上述一般,有的时候存在回溯了就非法的情况:字符串 ababa
,\(k = 2\) 时,指针 \(P = 3\) 赋值为 \(next_P = 1\),不重叠了,但是长度为 \(1\),小于 \(2\)。
最后将所有的左端点循环一遍就好了,从 \(1\) 到 \(len - k * 2\)。
代码
Click to show code
#include <bits/stdc++.h>
using namespace std;
int nxt[111111]; // next 数组
int k, lp, lptmp; // 给定 k,字符串长度(常量),字符串长度(变量)
long long ans = 0; // 答案
void exec (string s) {
// 我命名为 exec 是因为它已经不是单纯的一个跑 next 了。
// 边跑 next 边统计答案,你可以发现 for 里面前两句是纯纯的求 next
// 第三句是融合一下 第四第五句就是统计答案
nxt[0] = nxt[1] = 0;
int j = 0;
for (int i = 1; i < lptmp; i++){
while (j && s[j] != s[i]) j = nxt[j]; // 求 next 第一步
if(s[j] == s[i]) j++; // 求 next 第二步
int P = nxt[i + 1] = j; // 求 next 第三步,但是记录指针
while (P > i / 2) P = nxt[w]; // 递归求满足最大长度要求的公共前后缀
if (P >= k) ans++; // 若满足最小长度要求 记录答案
}
}
signed main () {
string p;
cin >> p >> k;
lp = p.length (); // 之后就不能变了
lptmp = lp;
for (int i = 1; i <= lp - k * 2; i++) {
exec (p);
// 这里使用了一种很激进的方案,直接删字符串
p.erase (0, 1); // 这里的 erase () 函数是从下标为 0 开始移除 1 个
lptmp--; // 字符串长度 - 1
}
cout << ans;
}