题解:P13790 「CZOI-R6」Border
给出 \(z\) 函数线性做法。
考虑从大到小枚举可能的 border 长度。
假设当前枚举到了长度 \(x\),先求一下这两个前后缀的 \(\operatorname{LCP}\),这样我们就知道了下一个字符一定是要修改的。
求前后缀 \(\operatorname{LCP}\) 刚好在 \(z\) 函数射程内。当然,如果原串本来就有 \(x\) 长度的 border 的话,就不用继续处理了,正好我们用 \(z\) 函数就可以判断是否有这样的 border,不用再做 KMP。
这里要做分讨:
- \(x\le \frac{n}{2}\)。
则不同位置之后的部分要相同,为了快速判断是否相同,我们使用哈希。这一部分是平凡的。
- \(x> \frac{n}{2}\)。
此时,前后缀有重叠。

假设 \(z_{n-x}=y\),则要改的位置为 \(y\)。
继续分讨,若 \(y+n-x\in [n-x,x-1]\)
如果使 \(S_{y+n-x}\) 改为 \(S_y\),前提就是 \(S_y=S_{y+2n-2x}\)。如果使 \(S_{y}\) 改为 \(S_{y+n-x}\),前提就是 \(S_{y+n-x}=S_{y+2n-2x}\) 和 \(y<n-x\)(因为 \(S[0,y-1]=S[n-x,y+n-x-1]\),如果 \(y\in [n-x,y+n-x-1]\) 的话,是不能改的)。然后还要 \(S[y+1,y+n-x-1]=S[y+n-x+1,y+2n-2x-1],S[y+n-x+1,x-1]=S[y+2n-2x+1,n-1]\)。

若 \(y+n-x > x-1\),此时 \(y+2n-2x\) 不存在,且 \(y+n-x\) 一定是修改 \(S_{y+n-x}\),然后再让 \(S[y+1,x-1]=S[y+n-x+1,n-1]\)。
时空复杂度 \(O(|S|)\)。
#include <bits/stdc++.h>
using namespace std;
const int N=1000005,B=131,mod=1e9+7;
char S[N];
int n,z[N],h[N],mi[N];
inline void getz(){
z[0]=n;
for (int i=1,L,R=-1;i<n;i++){
if (R>=i) z[i]=min(z[i-L],R-i+1);
while (S[z[i]]==S[i+z[i]]) z[i]++;
if (i+z[i]-1>R) R=i+z[i]-1,L=i;
}
}
inline int H(int l,int r){
if (l>r) return 0;
if (l==0) return h[r];
return (h[r]-1llu*h[l-1]*mi[r-l+1]%mod+mod)%mod;
}
inline int solve(){
for (int x=n-1;x>n/2;x--)
if (z[n-x]==x) return x;
else {
int y=z[n-x];
if (y+n-x<=x-1){
if (S[y]!=S[y+n*2-x*2]&&(y>=n-x||S[y+n-x]!=S[y+n*2-x*2])) continue;
if (H(y+1,y+n-x-1)!=H(y+n-x+1,y+n*2-x*2-1)) continue;
if (H(y+n-x+1,x-1)!=H(y+n*2-x*2+1,n-1)) continue;
}
else {
if (H(y+1,x-1)!=H(y+n-x+1,n-1)) continue;
}
return x;
}
for (int x=n/2;x>=1;x--)
if (z[n-x]==x) return x;
else {
int y=z[n-x];
if (H(y+1,x-1)==H(n-x+y+1,n-1)) return x;
}
return 0;
}
int main(){
scanf("%s",S),n=strlen(S),getz();
h[0]=S[0]-'a',mi[0]=1;
for (int i=1;i<n;i++) h[i]=(1llu*h[i-1]*B+S[i]-'a')%mod;
for (int i=1;i<=n;i++) mi[i]=1llu*mi[i-1]*B%mod;
printf("%d\n",solve());
return 0;
}

浙公网安备 33010602011771号