题解:P13790 「CZOI-R6」Border

题目传送门

给出 \(z\) 函数线性做法。


考虑从大到小枚举可能的 border 长度。

假设当前枚举到了长度 \(x\),先求一下这两个前后缀的 \(\operatorname{LCP}\),这样我们就知道了下一个字符一定是要修改的。

求前后缀 \(\operatorname{LCP}\) 刚好在 \(z\) 函数射程内。当然,如果原串本来就有 \(x\) 长度的 border 的话,就不用继续处理了,正好我们用 \(z\) 函数就可以判断是否有这样的 border,不用再做 KMP。

这里要做分讨:

  1. \(x\le \frac{n}{2}\)

则不同位置之后的部分要相同,为了快速判断是否相同,我们使用哈希。这一部分是平凡的。

  1. \(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;
}
posted @ 2026-04-10 17:52  TP2010  阅读(22)  评论(0)    收藏  举报