Z函数 & 扩展KMP
Z 函数
求一个字符串str 所有 以第i位开始的后缀与str的最长公共前缀(lcp)
时间复杂度:
理解
使用 z[i]表示第i位开始的后缀与str的最长公共前缀的长度
使用Z Box快速计算Z数组
Z Box是字符串str中的一个区间满足是str的前缀 且满足以下要求:
- 在位置i时,必须包含
- 尽可能大
也就是说 Z Box[i]是包含i的右边界最大的公共前缀的长度。
注意:
- 不是求 包含i的最长的前缀,而是要求包含i的右边界最大的前缀。
- Z Box会随着i的变化而移动。
举例
第8位的c的Z BOX是bacb
第9位的b的Z BOX是ba
求解过程
使用Z Box求Z数组
在知道 位置Z Box的情况下,求 和 位置的Z Box。
- 右边紫色部分表示i-1的 Z Box
- 左边紫色部分表示i-1的 Z Box 在前缀的映射
分三类情况讨论
当时
也就是i在前缀的映像的z函数小于Z Box的右边界时。
因为str[i-l+1,r-l+1] 等价于
显然此时 , Z Box 不变
当时
也就是i在前缀的映像的z函数大于Z Box的右边界时。
和上面类似:
- str[i,r]肯定与前缀相等,因此从r+1逐个往后判断
- Z Box变成
代码:
z[i] = r-i; while(str[ i+z[i] ] == str[ 1+z[i] ]) z[i]++; l = i, r = i+z[i]-1;
当
因为此时和其他前缀没有关联,所以只能暴力算:
- 从i往后逐个判断
- Z Box变成
为什么Z Box的右端点为什么要靠右?
增加的情况。
时间复杂度:
因为:
- 求z[i]需要从1到n遍历,O(n)
- Z Box右端点最多右移n次,O(n)
所以:总体复杂度:O(n)
代码
字符串下标从1开始
void get_z(int n,string str){ int l=0,r=0;//Z Box范围 z[1]=n; for(int i=2;i<=n;i++){ //情况3 if(i>r){ while(str[ i+z[i] ]==str[1+z[i]]) z[i]++; l=i,r=i+z[i]-1; } //情况1 else if(z[i-l+1] < r-i+1){ z[i]=z[i-l+1]; } //情况2 else{ z[i]=r-i; while(str[i+z[i]] == str[1+z[i]]) z[i]++; l=i, r=i+z[i]-1; } } }
模板代码
string t; int tlen; int z[N]; void get_Z(){ int l=0,r=0; z[0]=tlen; for(int i=1;i<tlen;i++){ if(i>=r){ while(i+z[i]<tlen && t[i+z[i]]==t[z[i]]) z[i]++; l=i,r=i+z[i]; } else if(z[i-l]<r-i) z[i]=z[i-l]; else{ z[i]=r-i; while(i+z[i]<tlen && t[i+z[i]]==t[z[i]]) z[i]++; l=i,r=i+z[i]; } } }
扩展KMP
给两个字符串str1,str2,求出str1的每一个后缀与str2的最长公共前缀
也就是说:设extend数组:extend[i]表示T与S[i,n-1]的最长公共前缀,要求出所有extendi。
时间复杂度:
求解过程
类比Z函数
- 如果str=str2,显然就是Z函数
- 如果不相等, 使用类似于Z函数的方法
类似的Box
Extand Box表示:
字符串str1中的一个区间[1,r],满足str1[1,r]是str2的前缀且满足以下要求:
- 在位置i时,[1,r]必须包含i
- r尽可能大
类似的分类讨论:
当时
- ext[i] = z[i-1+1]
- Extand Box不变
当时
- ext[i]从r开始扫描
- Extand Box变成
i > r
- ext[i]从i开始扫描
- Extand Box 变成
为什么Extand Box的右端点为什么要靠右?
增加的情况,节省时间。
代码:
字符串下标从1开始
void get_text(int n,string str1,int m,string str2){ int l=0,r=0;//Box范围 for(int i=1;i<=n;i++){ //情况3 if(i>r){ while(i+ext[i]<=n && 1+ext[i]<=m&& str1[ i+ext[i] ]==str2[1+ext[i]]) ext[i]++; l=i,r=i+ext[i]-1; } //情况1 else if(ext[i-l+1] < r-i+1){ ext[i]=ext[i-l+1]; } //情况2 else{ ext[i]=r-i; while(i+ext[i]<=n && 1+ext[i]<=m&& str1[ i+ext[i] ]==str2[1+ext[i]]) ext[i]++; l=i, r=i+ext[i]-1; } } }
模板代码
const int N=2e7+10; string s,t; int slen,tlen; int z[N]; int ext[N]; void get_Z(){ int l=0,r=0; z[0]=tlen; for(int i=1;i<tlen;i++){ if(i>=r){ while(i+z[i]<tlen && t[i+z[i]]==t[z[i]]) z[i]++; l=i,r=i+z[i]; } else if(z[i-l]<r-i) z[i]=z[i-l]; else{ z[i]=r-i; while(i+z[i]<tlen && t[i+z[i]]==t[z[i]]) z[i]++; l=i,r=i+z[i]; } } } void get_exKMP(){ int l=0,r=0; while (r < slen && r < tlen && s[r] == t[r]) r++; ext[0] = r; // r=0; for(int i=1;i<slen;i++){ if(i>r){ while(i+ext[i]<slen && ext[i]<tlen && s[i+ext[i]]==t[ext[i]]) ext[i]++; l=i,r=i+ext[i]; }else if(z[i-l]<r-i) ext[i]=z[i-l]; else { ext[i]=r-i; while(i+ext[i]<slen && ext[i]<tlen && s[i+ext[i]]==t[ext[i]]) ext[i]++; l=i,r=i+ext[i]; } } }
模板题
代码
#include <bits/stdc++.h> using namespace std; #define int long long const int N=2e7+10; string s,t; int slen,tlen; int z[N]; int ext[N]; void get_Z(){ int l=0,r=0; z[0]=tlen; for(int i=1;i<tlen;i++){ if(i>=r){ while(i+z[i]<tlen && t[i+z[i]]==t[z[i]]) z[i]++; l=i,r=i+z[i]; } else if(z[i-l]<r-i) z[i]=z[i-l]; else{ z[i]=r-i; while(i+z[i]<tlen && t[i+z[i]]==t[z[i]]) z[i]++; l=i,r=i+z[i]; } } } void get_exKMP(){ int l=0,r=0; while (r < slen && r < tlen && s[r] == t[r]) r++; ext[0] = r; // r=0; for(int i=1;i<slen;i++){ if(i>r){ while(i+ext[i]<slen && ext[i]<tlen && s[i+ext[i]]==t[ext[i]]) ext[i]++; l=i,r=i+ext[i]; }else if(z[i-l]<r-i) ext[i]=z[i-l]; else { ext[i]=r-i; while(i+ext[i]<slen && ext[i]<tlen && s[i+ext[i]]==t[ext[i]]) ext[i]++; l=i,r=i+ext[i]; } } } void solve(){ cin>>s>>t; slen=s.length(),tlen=t.length(); get_Z(); get_exKMP(); int ans=0; for(int i = 0; i < tlen; i++) //要注意下标从 0 开始 { ans ^= (i + 1) * (z[i] + 1); // cout<<i<<" "<<z[i]<<" "<<endl; } cout << ans << "\n"; ans = 0; for(int i = 0; i < slen; i++) { ans ^= (i + 1) * (ext[i] + 1); // cout<<i<<" "<<ext[i]<<" "<<endl; } cout << ans; } signed main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int t=1; // cin>>t; while(t--) solve(); return 0; }
习题
本文作者:kingwzun
本文链接:https://www.cnblogs.com/kingwz/p/16658336.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步