P3426
一篇有严格证明的题解。
称原题中的关系为“
观察到几个重要的性质:
-
若
A 可以被B 表示,则B 为A 的 border。考虑
B 表示A 的第一个串和最后一个串,分别为A 的前缀、后缀,符合 border 的定义。 -
设
S 的两个 border 为\lvert s_1 \rvert \lt \lvert s_2 \rvert 。若S 可被s_1 表示,则s_2 也能被s_1 表示。任取一个
s_1 表示S 的方案。我们能够在其中找到一段长度\in [\max(\lvert s_1 \rvert, \lvert s_2 \rvert - \lvert s_1 \rvert), \lvert s_2\rvert] 的前缀,使得其能被s_1 表示。同理,可以找到长度\in [\max(\lvert s_1 \rvert, \lvert s_2 \rvert - \lvert s_1 \rvert), \lvert s_2\rvert] 的S 的后缀,(因为s_1, s_2, S 互为 border,)这个后缀也是s_2 的后缀。那么这两段总长度\ge \lvert s_2 \rvert ,拼在一起(中间可能有重叠部分)就是s_1 表示s_2 的方案。 -
若
S 能被s_1,s_2 表示,设\lvert s_1 \rvert \lt \lvert s_2 \rvert ,则s_1 是s_2 的 border。前两条性质的直接推论。
下文用前缀的长度代表前缀本身。
回到原题。设
显然若
若
不过我们还需要判断
假设存在一个覆盖到
最后求答案是容易的。只需要对每个值
#include <iostream>
using namespace std;
#define MAXN 500005
string S;
int n;
int pi[MAXN], f[MAXN], las[MAXN];
int main() {
cin >> S;
n = S.size();
f[0] = 1;
for (int i = 1; i < n; i++) {
pi[i] = pi[i - 1];
while (pi[i] && S[i] != S[pi[i]]) pi[i] = pi[pi[i] - 1];
if (S[i] == S[pi[i]]) pi[i]++;
if (pi[i] && las[f[pi[i] - 1]] >= i - pi[i]) f[i] = f[pi[i] - 1];
else f[i] = i + 1;
las[f[i]] = i;
}
cout << f[n - 1] << '\n';
return 0;
}