学习笔记/String:Z-Box 笔记

BASIC INFORMATION

TOPIC: \(\small\text{Z--Box}\) 算法。

REFERENCE: 基本说明和常用符号参考字符串系列目录&&说明

UPDATE: 暂无

\(\text{Z}\) 算法,又称 \(\text{Z-Box}\) 算法,国内一般称为扩展 \(\text{KMP}\)

其实我也没搞清楚它有什么别的算法做不到的功能,就当成增量法的思维练习吧(怎么又是你)。

对自身的 \(\text{Z-Box}\) 算法

\(\text{Z-Box}\) 算法,用于求出原串 \(s\) 与其从字符 \(s[i]\) 开始的后缀的最长公共前缀的长度 \(z[]\)(也称 \(\text{Z}\) 函数)。

给出形式化定义:即 \(z[i]=\operatorname{lcp}(s,s[i:])\),特别地,\(z[1]=0\)

Question: 请写出 \(s=\texttt{abacaba}\)\(z[]\)\(\text{Z}\) 函数)。

Answer: \(z[]=\{0,0,1,0,3,0,1\}\)

同样先看朴素做法:枚举 \(i\),暴力往后匹配即可,时间复杂度 \(O(n^2)\),给出实现:

// force.cpp
int n,z[N];
char s[N];
z[1]=0;
for(int i=2;i<=n;i++){
	while(s[1+z[i]]==s[i+z[i]]) z[i]++;
}

考虑用增量法求出 \(z[]\),这一次我们可以利用的信息是 \(\operatorname{lcp}\) 带来的相同,类似 \(\text{Manacher}\),维护一个最大的匹配过的右端点 \(r\) 以及该右端点对应的对应的后缀的起始点 \(o\),接着考虑 \(i\le r\) 时如何利用已知信息

\(z[]\) 的定义有 \(s[:z[o]]=s[o:r]\),又 \(i \in [o,r]\),所以 \(s[i:r]=s[i-o+1:z[o]]=s[i-o+1:r-o+1]\),那就是说 \(s[i:]\) 的前 \(r-i+1\) 位与 \(s[i-o+1:]\) 的前 \(r-i+1\) 位相等,于是 \(z[i]\) 转换为 \(z[i-o+1]\),那么 \(z[i]\gets\min(z[i-o+1],r-i+1)\)

\(\bm{p.s.}\)\(r-i+1\)\(\min\) 则是因为我们只知道它们前 \(r-i+1\) 位相等,但 \(z[i-o+1]\) 可能更多,但是第 \(r-i+1\) 位后面的我们是只能暴力判断的,因为我们没有匹配过 \(r\) 后面,所以要取 \(\min\)

给出实现:

int n,z[N];
char s[N];
void Z_Box(){
	z[1]=0;
	for(int i=2,o=0,r=0;i<=n;i++){
		z[i]=(i>r?0:min(z[i-o+1],r-i+1));
		while(s[1+z[i]]==s[i+z[i]]) z[i]++;
		if(i+z[i]-1>r) o=i,r=i+z[i]-1;
	}
}

如果你对 \(\text{Manacher}\) 很熟悉的话,学习 \(\text{Z-Box}\) 应该如鱼得水,那么我们就基本把 \(\text{Z-Box}\) 解决了。

两个串之间的 \(\text{Z-Box}\) 算法

咕咕咕。

posted @ 2024-08-07 15:29  godmoo  阅读(24)  评论(0编辑  收藏  举报