扩展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,k−1]的所有 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,k−1]使得 L = p 0 + z p 0 − 1 \\rm L=p_0+z_{p_0}-1 L=p0+zp0−1最大
Ⅰ.当 k + z k − p 0 + 1 − 1 < L k+\\rm z_{k-p_0+1}-1<L k+zk−p0+1−1<L
令 x = k − p 0 + 1 x=k-p_0+1 x=k−p0+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+zx−1]
所有得到对应关系为 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+zk−p0+1−1>=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的主要内容,如果未能解决你的问题,请参考以下文章