题目链接
https://www.luogu.org/problem/P5410
分析
国外一般称此算法为Z Algorithm,个人感觉算法思想上和Manacher算法更为接近;
设 n e x t [ i ] next[i] next[i] 表示模式串 S S S 后缀 i . . . n i ... n i...n 与 S S S 的最大公共前缀长度;
若已求 n e x t [ 1... x − 1 ] next[1 ... x - 1] next[1...x−1],接下来要求 n e x t [ x ] next[x] next[x],
记录当前扩展最远的点,即 m a x 1 ≤ k < x ( k + n e x t [ k ] − 1 ) max_{1 \leq k < x} (k + next[k] - 1) max1≤k<x(k+next[k]−1),
由 n e x t next next 数组定义可知, S [ 1... n e x t [ k ] ] = S [ k . . . k + n e x t [ k ] − 1 ] S[1 ... next[k]] = S[k ... k + next[k] - 1] S[1...next[k]]=S[k...k+next[k]−1],
而 S [ 1... n e x t [ x − k + 1 ] ] = S [ x − k + 1... x − k + n e x t [ x − k + 1 ] ] S[1 ... next[x - k + 1]] = S[x - k + 1 ... x - k + next[x - k + 1]] S[1...next[x−k+1]]=S[x−k+1...x−k+next[x−k+1]],
且 S [ x − k + 1... x − k + n e x t [ x − k + 1 ] ] = S [ x . . . x + n e x t [ x − k + 1 ] − 1 ] S[x - k + 1 ... x - k + next[x - k + 1]] = S[x ... x + next[x - k + 1] - 1] S[x−k+1...x−k+next[x−k+1]]=S[x...x+next[x−k+1]−1],
故有 S [ 1... n e x t [ x − k + 1 ] ] = S [ x . . . x + n e x t [ x − k + 1 ] − 1 ] S[1 ... next[x - k + 1]] = S[x ... x + next[x - k + 1] - 1] S[1...next[x−k+1]]=S[x...x+next[x−k+1]−1],
若 x + n e x t [ x − k + 1 ] − 1 x + next[x - k + 1] - 1 x+next[x−k+1]−1 未到达扩展到的最远位置,则 n e x t [ x ] next[x] next[x] 便可确定;
否则,暴力先后扩展,并更新扩展到的最远位置,
仿照KMP算法,利用 n e x t next next 数组求出原问题答案;
扩展到的最远位置要么增加,要么不变,复杂度为 O ( n ) O(n) O(n)。
AC代码
#include <cstdio>
#include <cstring>
const int maxn = 1e5 + 5;
int nxt[maxn], ex[maxn], l1, l2;
char a[maxn], b[maxn];
inline void get_nxt() {
nxt[1] = l2;
for (int i = 1; i < l2 && b[i] == b[i + 1]; ++i) ++nxt[2];
int k = 2;
for (int i = 3; i <= l2; ++i) {
int p = k + nxt[k] - 1;
if (i + nxt[i - k + 1] - 1 < p) nxt[i] = nxt[i - k + 1];
else {
int j = p - i + 1;
if (j < 1) j = 1;
while (i + j - 1 <= l2 && b[i + j - 1] == b[j]) ++j;
nxt[i] = j - 1, k = i;
}
}
}
inline void exkmp() {
get_nxt();
for (int i = 1; i <= l1 && i <= l2; ++i) {
if (a[i] != b[i]) break;
++ex[1];
}
int k = 1;
for (int i = 2; i <= l1; ++i) {
int p = k + ex[k] - 1;
if (i + nxt[i - k + 1] - 1 < p) ex[i] = nxt[i - k + 1];
else {
int j = p - i + 1;
if (j < 1) j = 1;
while (i + j - 1 <= l1 && j <= l2 && a[i + j - 1] == b[j]) ++j;
ex[i] = j - 1, k = i;
}
}
}
int main() {
scanf("%s%s", a + 1, b + 1);
l1 = strlen(a + 1), l2 = strlen(b + 1);
exkmp();
for (int i = 1; i <= l2; ++i) printf("%d ", nxt[i]);
puts("");
for (int i = 1; i <= l1; ++i) printf("%d ", ex[i]);
return 0;
}