W
e
l
c
o
m
e
: )

[字符串] EXKMP + Manacher

Manacher 学习笔记:

我们想要得到 \(\forall i<|S|\),以 \(i\) 为回文中心的最长回文子串的半径 \(p_i\)

为了不进行复杂的分类讨论,我们在每个字符左右都放上一个 #,这样使得串长必然为奇数,为了避免边界问题,在串头再放一个 ~

类似于 KMP,我们希望通过之前得到的 \(p_{1 \dots i-1}\) 来转移 \(p_i\),这里有个玄学的操作,记已经找到的最靠右的回文子串的右端点 \(R\) 和对应的对称中心 \(mid\),对于我们当前的 \(i\),分类讨论:

(这里借用了 Lstdo 大佬的图片,侵删)

\(i\) 的拓展还在 \([mid, R]\) 内,此时必然存在一个 \(j\) 使得 \([j-p_j+1, j+p_j-1]=[i-p_i+1, i+p_i-1]\),直接根据中点公式得到 \(j\) 的坐标即可。

但还有可能是这样的。。。

此时并不能判断多出去的是否同样回文,没关系,我会暴力,先对 \((R-i \text{(最大的半径)}, p_j)\)\(\min\),然后暴力在 \(p_i\) 的基础上拓展,同时更新 \(R, mid\),由于本身是塞了多出来几个,所以 \(p_i\) 就是匹配半径长度。不需要除 2,如果 \(i\)# 那么就是以 \(i-1, i\) 为中心,长度为 \(p_i\) 的偶回文串,否则是以 \(i\) 为中心,长度为 \(p_i\) 的奇回文串。

复杂度:

根据字符串算法均可线性定理所以它是 O(n),由于 \(R\) 只会不断往右移,当我们基础的 \(p_i+i = R'\) 时才会产生移动,而 \(R\) 移到 \(n\) 就不动了,也就是说,我们“暴力”只会拓展 \(n\) 格,所以复杂度自然而然就是 \(O(n)\) 了。

Code:

	scanf("%s", s+1);n=strlen(s+1);
	nw[0]='~', nw[1]='#';
	for(int i=1; i<=n; i++)
		nw[i<<1]=s[i], nw[i<<1|1]='#';
	for(int i=1; i<=n*2+1; i++){
		if(i<R) p[i]=min(p[mid*2-i], R-i);
		while(nw[i-p[i]-1]==nw[i+p[i]+1]) p[i]++;
		if(i+p[i]>R) R=i+p[i], mid=i;
		ans=max(ans, p[i]);
	}

EXKMP

求数组 \(z_i=|\operatorname{LCP}(s_{[1, n]}, s_{i, n})|\)

同样跟 Manacher 与 KMP 一致,假设我们正在处理 \(z_x\),我们希望通过 \(z_{[1, x)}\) 来得到快速转移。

考虑 \(z\) 数组的本质:\(s_{[1, z_i]}=s_{[i, i+z_i-1]}\),考虑记当前所能匹配最靠长的后缀起点和末尾分别为 \(l, r\),若 \(x \le r\),则 \(i \in [l, r]\)\(s_{[1, r-l+1]}=s_{[l, r]}\),当前的 \(z_x\) 最多能是 \(r-x+1\)(其实是有可能超过的,因为我们假定了 \(r\) 是最大值,这样初始化对后面的复杂度分析有很大帮助)(珂以看下面的图,侵删)

进一步分析,考虑 \(s_{[1, z_{x-l+1}]}\) 这一段与 \(s_{[x-l+1, x-l+1+z_{x-l+1}-1]}\) 这一段相等(这是 \(z\) 的定义!)
,同时上图两个红色部分是一样的,所以这个是从 \(x\) 的后缀珂以匹配到的最长段,那么珂以初始化 \(z_x=\min(r-x+1, z_{x-l+1})\).

接下来,考虑直接暴力匹配,与 Manacher 一样,\(r\) 的上界是 \(n\),每次暴力起码 +1,故复杂度为 \(O(n)\)

求数组 \(EK_i=|\operatorname{LCP}(b_{[1, n]}, a_{i, n})|\)

先求出 \(b\)\(z\),考虑类似于上面的匹配即可,只不过初始化的值应该是 \(z_{z-l+1}\)

画个图给你们体会一下

如上图,根据定义,\(b_{[1, EK_l]}=a_{l, r}\),我们仍然考虑 \(b_{[1, i-l+1]}\) 这一段,

同上,蓝色这四段都相等,我们同样珂以初始化为 \(EK_i=\min(z_{i-l+1}, r-i+1)\),其余暴力。

Code:

	Z[1]=m
	for(int i=2, l=0, r=0; i<=m; i++){
		if(i<=r) Z[i]=min(Z[i-l+1], r-i+1);
		while(i+Z[i]<=m&&b[i+Z[i]]==b[1+Z[i]])
			Z[i]++;
		if(i+Z[i]-1>r) l=i, r=i+Z[i]-1;
	}
	for(int i=1, l=0, r=0; i<=n; i++){
		if(i<=r) EK[i]=min(Z[i-l+1], r-i+1);
		while(i+EK[i]<=n&&a[i+EK[i]]==b[1+EK[i]]) EK[i]++;
		if(i+EK[i]-1>r) l=i, r=i+EK[i]-1;
	}
posted @ 2022-01-18 21:40  127_127_127  阅读(39)  评论(0)    收藏  举报