1 条题解

  • 0
    @ 2025-8-24 22:50:03

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar ZHR100102
    kipfel 可爱喵

    搬运于2025-08-24 22:50:03,当前版本为作者最后更新于2024-12-10 23:48:39,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    哈希

    思路

    显然正着做一遍哈希,倒着做一遍哈希,然后枚举回文中心即可。

    时间复杂度 O(n)O(n)

    代码

    #include <bits/stdc++.h>
    #define fi first
    #define se second
    #define lc (p<<1)
    #define rc ((p<<1)|1)
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> pi;
    const int N=400005;
    const ull base=13331;
    ull phash[N],shash[N],pw[N];
    int n,ans;
    char s[N];
    void dohash()
    {
        pw[0]=1;
        for(int i=1;i<N;i++)pw[i]=pw[i-1]*base;
        for(int i=1;i<=n;i++)phash[i]=phash[i-1]*base+s[i];
        for(int i=n;i>=1;i--)shash[i]=shash[i+1]*base+s[i];
    }
    ull gethash(int op,int l,int r)
    {
        if(op==0)return (phash[r]-phash[l-1]*pw[r-l+1]);
        return (shash[l]-shash[r+1]*pw[r-l+1]);
    }
    int main()
    {
        ios::sync_with_stdio(0);
        cin.tie(0);
        cout.tie(0);
        cin>>n>>s+1;
        ans=n-1;
        if(n==1)
        {
            cout<<0;
            return 0;
        }
        dohash();
        for(int i=1;i<=n;i++)
        {
            if(i-1>=n-(i+1)+1)
            {
                int len=n-(i+1)+1;
                if(gethash(0,i-1-len+1,i-1)==gethash(1,i+1,i+1+len-1))
                {
                    ans=min(ans,i-1-len);
                }
            }
            if(i>=n-(i+1)+1)
            {
                int len=n-(i+1)+1;
                if(gethash(0,i-len+1,i)==gethash(1,i+1,i+1+len-1))
                {
                    ans=min(ans,i-len);
                }
            }
        }
        cout<<ans;
        return 0;
    }
    

    哈希的代码又臭又长,肯定不是这题的最优解。

    KMP

    思路

    我们考虑这题要求的本质是什么,显然是求字符串 ss 最长的回文后缀的长度 ll,那么答案就是 nln-l。因为剩下的那些必须对称过去。

    那么如何求最长的回文后缀呢?我们可以先把字符串 ss 反转为 ss',把后缀转化为前缀便于处理。

    由于回文串正着读和反着读都一样,可以发现回文后缀满足是字符串 ss' 的一段前缀,也是字符串 ss 的一段后缀

    因此如果字符串 ss' 的一段前缀与字符串 ss 的一段后缀相等,就说明这是一个合法的回文后缀。

    如果要让这个后缀最长,那么显然是 KMP 的 nextnnext_n,就求出来了。

    实现上,我们可以将最后的主串设置为 ss' 与任意一个分隔符与 ss 拼接起来的字符串,然后做一遍 KMP 即可。

    时间复杂度 O(n)O(n)

    代码

    非常简短,注意字符串开两倍的长度。

    #include <bits/stdc++.h>
    using namespace std;
    const int N=800005;
    int n,ne[N];
    char s[N];
    int main()
    {
        ios::sync_with_stdio(0);
        cin.tie(0);
        cout.tie(0);
        cin>>n>>s+n+2;
        s[n+1]='%';
        for(int i=1,j=2*n+1;i<=n;i++,j--)s[i]=s[j];
        int now=0;
        for(int i=2;i<=2*n+1;i++)
        {
            while(now&&s[now+1]!=s[i])now=ne[now];
            if(s[now+1]==s[i])now++;
            ne[i]=now;
        }
        cout<<n-ne[2*n+1];
        return 0;
    }
    
    • 1

    信息

    ID
    8863
    时间
    1000ms
    内存
    128MiB
    难度
    4
    标签
    递交数
    0
    已通过
    0
    上传者