[SDOI2017] 文本校正
henryhu2006 · · 题解
本文的所有情况的时间复杂度均为
题意
多测,每次给定两个(正整数)串
这一部分较为容易,可以将
const ull B=1e8+7; ull h[N*2];
bool solve2(){
ull H=0,P=1;
for(int i=1;i<=n;++i)
t[n+i]=t[i],H=B*H+s[i],P*=B;
for(int i=1;i<=2*n;++i) h[i]=B*h[i-1]+t[i];
for(int l=2;l<=n;++l)
if(H==h[l+n-1]-h[l-1]*P){
if(l>2) return print(l,n,1,1,2,l-1);
else return print(l,n-1,n,n,1,l-1);
}
return 0;
}
132,213
我们考虑
首先可以求出
设
-
zs_{j+1}\ge i -
zt_{i+1}\ge j -
i+j\ge n-p
如果打算
枚举
因此为了第三个条件尽可能满足,我们可以在对
于是时间复杂度是
int z1[N],z[N],nxt[N],mx[N];
void exkmp(int *z,int *Z,int *t,int *s){
memset(z+1,0,4*n);
for(int i=1+(z==Z),l=0,r=0;i<=n;++i){
if(r>=i) z[i]=min(r-i+1,Z[i-l+1]);
while(i+z[i]<=n&&t[z[i]+1]==s[i+z[i]]) ++z[i];
if(i+z[i]-1>r) l=i,r=i+z[i]-1;
}
}
bool solve3(bool rev){
memset(nxt+1,0,4*n);
exkmp(z1,z1,s,s),exkmp(z,z1,s,t);
int p=0; while(s[n-p]==t[n-p]) ++p;
if(!p) return 0;
iota(mx+1,mx+n,1);
for(int i=2,j=0;i<=n;++i){
while(j&&t[i]!=t[j+1]) j=nxt[j];
if(t[i]==t[j+1]) ++j; nxt[i]=j;
if(j) mx[i]=(mx[j]+z[mx[j]+1]>i+z[i+1]?mx[j]:i);
}
for(int i=1,j=0;i<n;++i){
while(j&&s[i]!=t[j+1]) j=nxt[j];
if(t[j+1]==s[i]) ++j;
if(mx[j]+z[mx[j]+1]>=i&&i>=n-p){
int r=mx[j];
if(!rev) return print(r+1,i,1,r,i+1,n);
else return print(1,n-i,n-r+1,n,n-i+1,n-r);
}
}
return 0;
}
321
将
然后就是各种各样的巨型后缀数据结构,但有一个非常 Ad-Hoc 的构造:将
这个构造的充分和必要性都是容易证明的。于是问题就变成了判断一个字符串是否能划分成三个偶回文串。
显然需要 Manacher,但是后面的问题看似简单,但是要做到线性非常困难,保底可以用 NTT 做到
枚举第一个回文串,则要将后缀划分成两个回文串。此时有一个非常难发现也难证明的性质。
考虑一个字符串,如果有三种方案对其进行划分(不一定成立),分别为
如果
不妨考虑
以此类推,辗转相除,到
可以得出
于是可以得到
回到原问题,我们只需要考虑
所以只需要考虑这两种情况,算出所有回文后缀,直接用指针扫,可以解决
时间复杂度
代码中,
int c[N*2],w[N*2],ra[N*2],st[N],top;
void cmax(int &x,int y){if(x<y) x=y;}
bool palin(int l,int r){return w[l+r>>1]>=(r-l+1)/2;}
bool solve4(){
memset(ra+1,0,8*n);
memset(w+1,0,8*n);
for(int i=1;i<=n;++i) c[2*i-1]=t[i],c[2*i]=s[i];
for(int i=1,mx=0,id;i<n*2;++i){
if(i<mx) w[i]=min(mx-i,w[id*2-i]);
while(i+w[i]+1<=n*2&&i-w[i]&&c[i-w[i]]==c[i+w[i]+1]) ++w[i];
if(i+w[i]>=mx) mx=i+w[i],id=i;
cmax(ra[i-w[i]+1+((i+w[i])&1)],i);
}
top=0;
for(int i=1;i<=n*2;i+=2)
if(palin(i,n*2)) st[++top]=i;
for(int i=3;i<=n*2;++i) cmax(ra[i],ra[i-2]);
for(int i=2,j=1;i<n*2;i+=2)
if(palin(1,i)){
while(j<=top&&st[j]<=i) ++j;
if(j<=top){
if(palin(i+1,st[j]-1))
return print((st[j]+1)/2,n,(i+2)/2,st[j]/2,1,i/2);
if(ra[i+1]>=i+1&&palin(ra[i+1]*2-i+1,n*2))
return print(((ra[i+1]*2-i+1)+1)/2,n,(i+2)/2,(ra[i+1]*2-i+1)/2,1,i/2);
}
}
return 0;
}
完整代码见 提交记录 #1931412。