扩展KMP(Z函数)

扩展KMP(Z函数)

本质仍然是尽量应用串内的信息。


Z函数

  • 定义

    一个数的 \(z\) 函数 \(z(i)\) 表示的是这个字符串和它从 \(i\) 开始的后缀的最长匹配长度。

    即: \(z(i)=|\ lcp(s,s+i-1)\ |\)

    (这里的数组下标从 \(1\) 开始)

  • 做法

    暴力求 \(O(n^2)\) ,大部分时候不可接受。

    我们可以维护终点为 \(r\) 的最大匹配子串 \([l\ ,\ r]\)\(z_l\)就相当于它的长度。

    \(i\le 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函数。

    \(i\le 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<<"\n";
	ans=0;
	for(int i=1;i<=lens;i++)
		ans^=1ll*i*(exz[i]+1);
	cout<<ans;
	return 0;

}
posted @ 2021-01-30 20:35  RemilaScarlet  阅读(197)  评论(0编辑  收藏  举报