模板
- 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;
}
}
- 扩展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)
{
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);
}
- 二维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;
}
}
性质
- 时间复杂度O(n+m)
- 最小循环节
如果len%(len-nxt[x])=0,则存在循环节,最小循环节长度为len-nxt[x]
题目
- 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;
}
- 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;
}