1 条题解

  • 0
    @ 2025-8-24 23:06:15

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar A6n6d6y6
    QAQ 小A就是我

    搬运于2025-08-24 23:06:15,当前版本为作者最后更新于2025-06-13 17:50:49,作者可能在搬运后再次修改,您可在原文处查看最新版

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

    以下是正文


    问题描述

    传送门。见原题题目描述。

    思路

    你说得对,但是只用哈希、二分、线段树可以轻松通过。

    分为两种情况:

    1. s(l,r)s(l,r) 在原串中不重叠地出现了两次;

    2. s(l,r)s(l,r) 形如 ABAB,如果原串中出现了 AABBAABB,那么就可以将两边的 ABAB 拼接起来。

    对于第一种情况,二分一个长度 xx,对于所有哈希值相同的子串,记录最后一次出现的左端点,判断其他右端点是否在它的左边,就可以处理不重叠的要求了,用了一个 std::map,时间复杂度 O(nlog2n)\mathcal{O}(n\log^2n)

    对于第二种情况,类似于 [NOI2016] 优秀的拆分,先考虑形如 AAAA 的串。不妨设 lil_i 为以 ii右端点最长 AAAA 串的一半长度,不妨设 rir_i 为以 ii左端点最长 AAAA 串的一半长度,答案即为:

    maxi=1n1li+ri+1\max\limits_{i=1}^{n-1}l_i+r_{i+1}

    好,现在考虑如何描述所有 AAAA 串,对于长度为 2i2iAAAA 串:

    如果我们在字符串上每隔 ii 设置一个观察点,那么所有 AAAA必然包含两个观察点

    相邻观察点的 LCP(最长公共前缀) 与 LCS(最长公共后缀) 拼起来的字符串即为经过的两点上的最长 AAAA 串。若 LCP 与 LCS 的和大于 ii,说明它们相交,可以有让一段区间多出长度为 ii 的选择。例如 ABC(AB)CAB\texttt{ABC(AB)CAB},其中括号是重叠的部分,那么出现了三个 AAAA 串:ABCABC,BCABCA,CABCAB\texttt{ABCABC},\texttt{BCABCA},\texttt{CABCAB}

    可以分析,这样的一段区间不会超过 nlognn\log n 个,如果用哈希求 LCP 和 LCS,时间复杂度是 O(nlog2n)\mathcal{O}(n\log^2n) 的。

    现在只需要对于每个左端点右端点,当作一个操作,取一个最大值,有 nlognn\log n 次修改,最后统一查询,可以离线下来对于长度 ii 排序从小到大处理,就成了区间赋值,用线段树处理,时间复杂度也是 O(nlog2n)\mathcal{O}(n\log^2n) 的。

    最后将两种答案取最大值,得到最终答案,总时间复杂度 O(nlog2n)\mathcal{O}(n\log^2n)

    综上,我们在 O(nlog2n)\mathcal{O}(n\log^2n) 的时间复杂度下解决这道缝合问题。

    细节

    自然溢出?被卡了。单哈希?也被卡了。所以只能写双模哈希

    注意多个二分的最大边界条件

    代码

    AC记录。代码轻微压行、卡常。

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    const int maxn=1e5+10,mod1=1e9+7,mod2=1e9+9,base1=19491001,base2=19370707;
    struct SegmentTree{
        struct node{int l,r,w;}t[maxn<<2];
        int ls(int u){return u<<1;}
        int rs(int u){return u<<1|1;}
        void pushdown(int u){
            if(!t[u].w)return;
            t[ls(u)].w=t[u].w;
            t[rs(u)].w=t[u].w;
            t[u].w=0;
        }
        void build(int u,int l,int r){
            t[u]={l,r,0};
            if(l==r)return;
            int mid=(l+r)>>1;
            build(ls(u),l,mid);
            build(rs(u),mid+1,r);
        }
        void modify(int u,int l,int r,int w){
            if(r<t[u].l||t[u].r<l)return;
            if(l<=t[u].l&&t[u].r<=r){t[u].w=w;return;}
            pushdown(u);
            modify(ls(u),l,r,w);
            modify(rs(u),l,r,w);
        }
        int query(int u,int x){
            if(x<t[u].l||t[u].r<x)return 0;
            if(x<=t[u].l&&t[u].r<=x)return t[u].w;
            pushdown(u);
            return query(ls(u),x)^query(rs(u),x);
        }
    }Tl,Tr;//区间赋值线段树
    struct num{
        int h1,h2;num(int a=0,int b=0){h1=a;h2=b;}
        friend bool operator<(const num &a,const num &b){
            if(a.h1!=b.h1)return a.h1<b.h1;
            return a.h2<b.h2;
        }
        friend bool operator ==(const num &a,const num &b){return a.h1==b.h1&&a.h2==b.h2;}
        friend num operator + (const num &a,const num& b){return num((a.h1+b.h1)%mod1,(a.h2+b.h2)%mod2);}
        friend num operator - (const num &a,const num& b){return num((a.h1-b.h1+mod1)%mod1,(a.h2-b.h2+mod2)%mod2);}
        friend num operator * (const num &a,const num& b){return num((a.h1*b.h1)%mod1,(a.h2*b.h2)%mod2);}
    }h[maxn],p[maxn],base(base1,base2);//双哈希结构体
    int n;
    num get(int l,int r){return h[r]-h[l-1]*p[r-l+1];}
    int lcp(int x,int y){
        int l=0,r=x,ans=0;
        while(l<=r){
            int mid=(l+r)>>1;
            if(get(x-mid+1,x)==get(y-mid+1,y))l=mid+1,ans=mid;
            else r=mid-1;
        }
        return ans;
    }//哈希求LCP
    int lcs(int x,int y){
        int l=0,r=n-y+1,ans=0;
        while(l<=r){
            int mid=(l+r)>>1;
            if(get(x,x+mid-1)==get(y,y+mid-1))l=mid+1,ans=mid;
            else r=mid-1;
        }
        return ans;
    }//哈希求LCS
    struct node{int l,r,w;};vector<node>Vr,Vl;
    bool cmp(const node& a,const node& b){
        if(a.w!=b.w)return a.w<b.w;
        if(a.l!=b.l)return a.l<b.l;
        return a.r<b.r;
    }//排序线段
    map<num,int>st;
    bool check(int mid){
        st.clear();
        for(int i=n-mid+1;i>=1;i--){
            const num&& h=get(i,i+mid-1);
            if(!st.count(h))st[h]=i;
            else if(st[h]>=i+mid)return 1;
        }
        return 0;
    }//第一种情况
    signed main(){
        // freopen("str.in","r",stdin);
        // freopen("str.out","w",stdout);
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        string s;cin>>s;s=' '+s;
        n=s.size()-1;p[0]={1,1};
        for(int i=1;i<=n;i++){
            h[i]=h[i-1]*base+num{s[i],s[i]};
            p[i]=p[i-1]*base;
        }//哈希初始化
        Vl.clear();Vr.clear();
        for(int i=1;i<=n;i++){
            for(int j=i,k=i+i;k<=n;j+=i,k+=i){
                int l=max(k-lcp(j,k)+i,k),r=min(k+lcs(j,k)-1,k+i-1);
                if(l>r)continue;
                Vl.push_back({l,r,i});
                Vr.push_back({l-i*2+1,r-i*2+1,i});
            }
        }//求线段
        sort(Vl.begin(),Vl.end(),cmp);
        sort(Vr.begin(),Vr.end(),cmp);
        Tl.build(1,1,n);Tr.build(1,1,n);
        for(auto [l,r,w]:Vl)Tl.modify(1,l,r,w);
        for(auto [l,r,w]:Vr)Tr.modify(1,l,r,w);//离线赋值
        int l=0,r=n/2,ans=0;
        while(l<=r){
            int mid=(l+r)>>1;
            if(check(mid))l=mid+1,ans=mid;
            else r=mid-1;
        }//第一种情况
        for(int i=1;i<n;i++)
            ans=max(ans,Tl.query(1,i)+Tr.query(1,i+1));//第二种情况
        cout<<ans;
        return 0;
    }
    
    • 1

    信息

    ID
    10984
    时间
    1500ms
    内存
    500MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者