扩展KMP(Z函数)
本质仍然是尽量应用串内的信息。
Z函数
-
定义
一个数的 (z) 函数 (z(i)) 表示的是这个字符串和它从 (i) 开始的后缀的最长匹配长度。
即: (z(i)=| lcp(s,s+i-1) |)
(这里的数组下标从 (1) 开始)
-
做法
暴力求 (O(n^2)) ,大部分时候不可接受。
我们可以维护终点为 (r) 的最大匹配子串 ([l , r]),(z_l)就相当于它的长度。
当 (ile r) 时,(z_i) 可以直接从 (min(z_{i-l+1},r-i+1)) 开始,否则从 (0) 开始;
然后继续暴力匹配得到 (z_i)。
判定 ([i+z_i-1>r]) 若为真则将区间更新至 ([i , i-z_i+1])。
注意 (z_1=n) 单独处理。
代码实现:
void zfunc(char *p,int n)//求解z函数
{
for(int i=1;i<=n;i++) z[i]=0;
z[1]=n;
for(int i=2,l=0,r=0;i<=n;i++)
{
if(i<=r) z[i]=min(z[i-l+1],r-i+1);
while(i+z[i]<=n&&p[i+z[i]]==p[z[i]+1]) z[i]++;//i+z[i]<=字符串长度
if(i+z[i]-1>r) l=i,r=i+z[i]-1;
}
}
Extend Z函数
-
定义
模仿Z函数的定义,我们可以定义一个扩展Z函数:
对于文本串 (s) , 和模式串 (p)
(Exz(i)=|lcp(p,s+i)|)
也就是 (p) 与 (s) 的所有后缀的最长公共前缀长度数组。
-
做法
做法也可以模仿Z函数
仍然是维护 (s) 的最长匹配子串 ([l , r]) 。(Exz_l) 即是区间长度。
首先求解模式串 (p) 的z函数。
当 (ile r) 时 (Exz_i) 直接从 (min(z_{i-l+1},r-l+1)) 开始,否则(Exz_i=0)。
然后暴力匹配得到 (Exz_i) 的值。
判定 ([i+Exz_i-1>r]) 若为真则将区间更新至 ([i , i-Exz_i+1])。
过程几乎没什么变化。
void extend_Z(char *s,char *p,int n,int m)
{
zfunc(p,m);
for(int i=2,l=0,r=0;i<=n;i++)
{
if(i<=r) exz[i]=min(z[i-l+1],r-i+1);
while(i+exz[i]<=n&&s[i+exz[i]]==p[1+exz[i]]) exz[i]++;
if(i+exz[i]-1>r) l=i.r=i+exz[i]-1;
}
}
完整代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e7+10;
char s[N],p[N];//文本串,模式串
int z[N];//z函数针对模式串
void zfunc(char *p,int n)//求解z函数
{
for(int i=1;i<=n;i++) z[i]=0;
z[1]=n;
for(int i=2,l=0,r=0;i<=n;i++)
{
if(i<=r) z[i]=min(z[i-l+1],r-i+1);
while(i+z[i]<=n&&p[i+z[i]]==p[z[i]+1]) z[i]++;//i+z[i]<=字符串长度的边界不能忘
if(i+z[i]-1>r) l=i,r=i+z[i]-1;
}
}
int exz[N];
void extend_Z(char *s,char *p,int n,int m)
{
zfunc(p,m);
for(int i=1;i<=n;i++) exz[i]=0;
for(int i=1,l=0,r=0;i<=n;i++)
{
if(i<=r) exz[i]=min(z[i-l+1],r-i+1);
while(i+exz[i]<=n&&s[i+exz[i]]==p[1+exz[i]]) exz[i]++;
if(i+exz[i]-1>r) l=i,r=i+exz[i]-1;
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>s+1>>p+1;
int lens=strlen(s+1),lenp=strlen(p+1);
extend_Z(s,p,lens,lenp);
ll ans=0;
for(int i=1;i<=lenp;i++)
ans^=1ll*i*(z[i]+1);
cout<<ans<<"
";
ans=0;
for(int i=1;i<=lens;i++)
ans^=1ll*i*(exz[i]+1);
cout<<ans;
return 0;
}