扩展KMP(Z函数),线性LCP

Posted issue是fw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了扩展KMP(Z函数),线性LCP相关的知识,希望对你有一定的参考价值。

下面解释的并不完整,下次来补充

求串 s s s的每个后缀和串 p p p的最长公共前缀 ( l c p ) (lcp) (lcp)

设我们求出了一个数组 z \\rm z z, z i \\rm z_i zi表示 l c p ( p [ 1... p l ] , p [ i . . . p l ] ) \\rm lcp(p[1...pl],p[i...pl]) lcp(p[1...pl],p[i...pl])

首先考虑我们求出了 i ∈ [ 1 , k − 1 ] i\\in[1,k-1] i[1,k1]的所有 e x l c p i \\rm exlcp_i exlcpi,考虑求 e x l c p k \\rm exlcp_k exlcpk

也就是求 l c p ( p [ 1... p l ] , s [ k . . . s l ] ) \\rm lcp(p[1...pl],s[k...sl]) lcp(p[1...pl],s[k...sl])

首先我们保存一个 p 0 ∈ [ 1 , k − 1 ] p_0\\in[1,k-1] p0[1,k1]使得 L = p 0 + z p 0 − 1 \\rm L=p_0+z_{p_0}-1 L=p0+zp01最大

Ⅰ.当 k + z k − p 0 + 1 − 1 < L k+\\rm z_{k-p_0+1}-1<L k+zkp0+11<L

x = k − p 0 + 1 x=k-p_0+1 x=kp0+1,也就是在 p 0 p_0 p0开始的后缀中, s [ k ] s[k] s[k]位置对应 p [ x ] p[x] p[x]

因为 s [ p 0 . . . L ] = p [ 1... e x l c p p 0 ] \\rm s[p_0...L]=p[1...exlcp_{p_0}] s[p0...L]=p[1...exlcpp0],得到 s [ k . . . L ] = p [ x . . . L ] s[k...L]=p[x...L] s[k...L]=p[x...L]

p [ 1... z x ] = p [ x . . . x + z x − 1 ] p[1...z_{x}]=p[x...x+z_x-1] p[1...zx]=p[x...x+zx1]

所有得到对应关系为 s [ k . . . ] s[k...] s[k...] p [ 1... ] p[1...] p[1...] l c p lcp lcp z x z_x zx

显然这个 l c p lcp lcp不能再扩大,因为 z x z_x zx只有这么多,下一个字符匹配不上了

Ⅱ.当 k + z k − p 0 + 1 − 1 > = L k+z_{k-p_0+1}-1>=L k+zkp0+11>=L

这个时候显然 l c p lcp lcp至少是 z x z_x zx,但是往后就不知道了,因为丧失了对应关系

所以从 s [ L + 1 ] s[L+1] s[L+1]开始和 p [ z x + 1 ] p[z_x+1] p[zx+1]开始匹配


#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e7+10;
char p[maxn],s[maxn];
int pl,sl,z[maxn],exlcp[maxn];
void getZ()
{
	z[0] = pl;
	int now = 0, p0 = 1;
	while( now+1<pl && p[now]==p[now+1] )	now++;
	z[1] = now;
	for(int i=2;i<pl;i++)
	{
		if( i+z[i-p0]<p0+z[p0] )
			z[i] = z[i-p0];
		else
		{
			now = max( p0+z[p0]-i,0 );
			while( now+i<pl && p[now]==p[now+i] )	now++;
			z[i] = now; p0 = i;
		}
	}
}
void exkmp()
{
	getZ();
	int now = 0, p0 = 0;
	while( now<pl && now<sl && p[now]==s[now] )	now++;
	exlcp[0] = now;
	for(int i=1;i<sl;i++)
	{
		//设x=z[i-p0],有p[0..x-1]==p[i-p0..i-p0+x-1]
		//s[p0,p0+exlcp[p0]-1]==p[0,exlcp[p0]-1]
		if( i+z[i-p0]<p0+exlcp[p0] )
			exlcp[i] = z[i-p0];
		else
		{
			now = max( p0+exlcp[p0]-i,0 );
			while( now<pl && now+i<sl && p[now]==s[now+i] )	now++;
			exlcp[i] = now; p0 = i;
		}	
	} 
}
int main()
{
	scanf("%s%s",&s,&p);
	pl = strlen( p ), sl = strlen( s );
	exkmp();
	long long ans1 = 0, ans2 = 0;
	for(int i=0;i<pl;i++)	ans1 ^= 1ll*(i+1)*(z[i]+1);
	for(int i=0;i<sl;i++)	ans2 ^= 1ll*(i+1)*(exlcp[i]+1);
	cout << ans1 << "\\n" << ans2;
	return 0; 
}

下标从 1 1 1起(下面的代码是错的)

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e7+10;
char s[maxn],p[maxn];
int sl,pl,z[maxn],ex[maxn];
void Z()
{
	z[1] = pl;
	int now = 1, p0 = 2;
	while( now+1<=pl && p[now]==p[now+1] )	now++;
	z[2] = now-1;
	for(int i=3;i<=pl;i++)
	{
		int x = i-p0+1, L = p0+z[p0]-1;
		if( i+z[x]-1<L )	z[i] = z[x];
		else
		{
			now = max( 1,L-i+2 );
			while( now+i<=pl && p[now+1]==p[now+i] )	now++;
			z[i] = now; p0 = i;
		}
	}	
	for(int i=1;i<=pl;i++)	cout << i << ": " << z[i] << endl;
}
void exkmp()
{
	int now = 0, p0 = 1, mi = min( pl,sl );
	while( now+1<=mi && s[now+1]==p[now+1] )	now++;
	ex以上是关于扩展KMP(Z函数),线性LCP的主要内容,如果未能解决你的问题,请参考以下文章

Z 函数(扩展 KMP)

扩展KMP

Z函数(扩展KMP)学习笔记

扩展KMP

126B Password[扩展kmp学习]

扩展KMP模板