P3426

· · 题解

一篇有严格证明的题解。

称原题中的关系为“A 可被 B 表示”,即可以用若干个 B 拼出 A

观察到几个重要的性质:

下文用前缀的长度代表前缀本身。

回到原题。设 f_i 表示前缀 i 的答案,即最短能表示 S_{[1, i]} 的串的长度。设 \pi_i 表示 i 的最长 border(即 KMP 中求出来的失配指针)。则我们可以说明 f_i \in \{i, f_{\pi_i}\}

显然若 f_i \ne i,则 f_i \le \pi_i。并且,由第二个性质,f_i \ge f_{\pi_i}。我们可以说明此时必有 f_i = f_{\pi_i}

f_i \ne f_{\pi_i},则 f_i \in (f_{\pi_i}, \pi_i]。由 border 的树性得到 f_i\pi_i 的 border。仍由性质 2f_i 可被 f_{\pi_i} 表示,必然不是最小值。因此性质得证。

不过我们还需要判断 f_{\pi_i} 是否能表示 i。它最后一次覆盖的位置是 S_{[i - f_{\pi_i} + 1, i]},因此一个充分条件是 \exists j \in [i - f_{\pi_i}, i], f_j = f_{\pi_i}。我们能说明这也是必要的。

假设存在一个覆盖到 j \in [i - f_{\pi_i}, i] 的解(即 f_{\pi_i} 能表示 j),我们能说明 f_j = f_{\pi_i}。首先肯定有 f_j \le f_{\pi_i}。另一方面,由第三个性质,f_jf_{\pi_i} 的 border,由第二个性质知 f_{\pi_i} 能被 f_j 表示,进而推出 \pi_i 能被 f_j 表示,所以 f_{\pi_i} \le f_j(这个不等式是从 f 本身的定义出发的,而这句话前面别的 f_{\pi_i} 是将 f_{\pi_i} 视作一个已知的定值),这两个数只能相等。

最后求答案是容易的。只需要对每个值 f 维护最大的下标 i 就行了,复杂度线性。


#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;
}