KMP与扩展KMP(Z函数)

模板

  1. KMP
void getfail()
{
	int i,j;
	j=0;
	for(i=2;i<=n;i++)
	{
		while(j&&s[i]!=s[j+1])
			j=nxt[x][j];
		if(s[i]==s[j+1])
			j++;
		nxt[i]=j;
	}
 } 
  1. 扩展KMP
    a[1……m], b[1……n]
    s = a + ‘*’ + b
    nxt[i]表示S[1……n+m]与s[i……n+m+1]的最长公共前缀
    f[i]表示b[i……n]与a[1……m]的最长公共前缀
void get_next(int len) // len = n + m + 1
{
    int l,r,i;
    nxt[1]=len;
    l=1;
    r=2;
    for(i=2;i<=len;i++)
    {
        nxt[i]=0;
        if(i<=r)
            nxt[i]=min(r-i,nxt[i-l+1]);
        while(s[i+nxt[i]]==s[1+nxt[i]]) nxt[i]++;
        if(i+nxt[i]>r)
        {
            l=i;
            r=i+nxt[i];
        }
    }
}
void get_f(int n,int m)
{
    int i;
    for(i=m+2;i<=n+m+1;i++)
        f[i-m-1]=min(nxt[i],m-1);
}
  1. 二维KMP(我也不知道叫啥)
    nxt[i][j]表示s[i……n]中j的失配位置
void getfail(int x)
{
	int i,j;
	j=x-1;
	nxt[x][x]=x-1;
	for(i=x+1;i<=n;i++)
	{
		while(j>=x&&s[i]!=s[j+1])
			j=nxt[x][j];
		if(s[i]==s[j+1])
			j++;
		nxt[x][i]=j;
	}
 } 

性质

  1. 时间复杂度O(n+m)
  2. 最小循环节
    如果len%(len-nxt[x])=0,则存在循环节,最小循环节长度为len-nxt[x]

题目

  1. Dishonest Driver
    from (2018-2019 ICPC Southwestern European Regional Programming Contest (SWERC 2018))
    题意: 压缩字符串,求压缩后最短的字符长度。
    题解:区间DP+kmp求最小循环节
    nxt[i][j]表示由i开始的后缀中,j的nxt值
    f[l][r] = min(f[l][r], f[l][k-1] + f[k][k + len - ( nxt[k][r] - k + 1) - 1] );
    p.s. 附一组数据
    input
    8
    ababcabc
    output
    5
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=710;
const int inf=1e8;
char s[N];
int f[N][N],nxt[N][N];
int n;
void getfail(int x)
{
	int i,j;
	j=x-1;
	nxt[x][x]=x-1;
	for(i=x+1;i<=n;i++)
	{
		while(j>=x&&s[i]!=s[j+1])
			j=nxt[x][j];
		if(s[i]==s[j+1])
			j++;
		nxt[x][i]=j;
	}
 } 
int main()
{
	int i,j,len,l,r,k;
	scanf("%d",&n);
	scanf("%s",s+1);
	for(i=1;i<=n;i++)
		getfail(i);
	for(i=1;i<=n;i++)
		f[i][i]=1;
	for(i=2;i<=n;i++)
		for(l=1;l<=n-i+1;l++)
		{
			r=i+l-1;
			f[l][r]=inf;
			for(k=l;k<=r;k++)
			{
				len=r-k+1;
				if(len%(len-(nxt[k][r]-k+1))==0)
					f[l][r]=min(f[l][r],f[l][k-1]+f[k][k+len-(nxt[k][r]-k+1)-1]);
			}
		}
	printf("%d",f[1][n]);
	return 0;
}
/*
8
ababcabc
*/
  1. CF 1313 E
    题解:扩展KMP+树状数组
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=2e6+10;
const int M=1e6+10;
int f[2][M],nxt[N];
char a[M],b[M],ss[N],s[N*2];
long long tree[M][2];
void get_next(int n)
{
    int l,r,i;
    nxt[1]=n;
    l=1;
    r=2;
    for(i=2;i<=n;i++)
    {
        nxt[i]=0;
        if(i<=r)
            nxt[i]=min(r-i,nxt[i-l+1]);
        while(s[i+nxt[i]]==s[1+nxt[i]]) nxt[i]++;
        if(i+nxt[i]>r)
        {
            l=i;
            r=i+nxt[i];
        }
    }
}
void get_f(int opt,int n,int m)
{
    int i;
    for(i=m+2;i<=n+m+1;i++)
        f[opt][i-m-1]=min(nxt[i],m-1);
}
void insert(int x,long long y,int t,int n)
{
    int i;
    if(x==0)
    {
        tree[0][0]+=t;
        return;
    }
    for(i=x;i<=n;i+=(i&(-i)))
        tree[i][1]+=y,tree[i][0]+=t;
}
long long query(int opt,int x)
{
    long long ans=0;
    int i;
    for(i=x;i>0;i-=(i&(-i)))
        ans+=tree[i][opt];
    return ans;
}
int main()
{
    int n,m,cnt,l,r,i;
    long long ans=0;
    scanf("%d%d",&n,&m);
    scanf("%s",a+1);
    scanf("%s",b+1);
    scanf("%s",ss+1);
    cnt=0;
    for(i=1;i<=m;i++)
        s[++cnt]=ss[i];
    s[++cnt]='z'+1;
    for(i=1;i<=n;i++)
        s[++cnt]=a[i];
    get_next(n+m+1);
    get_f(0,n,m);
    cnt=0;
    for(i=m;i>0;i--)
        s[++cnt]=ss[i];
    s[++cnt]='z'+1;
    for(i=n;i>0;i--)
        s[++cnt]=b[i];
    get_next(n+m+1);
    get_f(1,n,m);
    for(i=1;i<=n/2;i++)
        swap(f[1][i],f[1][n-i+1]);
    l=1;
    r=0;
    for(i=1;i<=n;i++)
    {
        while(r<min(n,i+m-2))
        {
            r++;
            insert(m-f[1][r]-1,(long long)(f[1][r]-m+1),1,m);
        }
        while(l<i)
        {
            insert(m-f[1][l]-1,(long long)(m-f[1][l]-1),-1,m);
            l++;
        }
        ans+=query(1,f[0][i])+f[0][i]*(query(0,f[0][i])+tree[0][0]);
    }
    cout<<ans;
    return 0;
}

你可能感兴趣的:(字符串)