1 条题解

  • 0
    @ 2025-8-24 21:44:17

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Graphcity
    循此苦旅,终抵繁星。

    搬运于2025-08-24 21:44:17,当前版本为作者最后更新于2021-11-16 22:00:35,作者可能在搬运后再次修改,您可在原文处查看最新版

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

    以下是正文


    看到题面,首先可以想到一个 DP:设 f(i)f(i) 为剪出第二封信的前 ii 个字符所需要的最小次数,显然有 f(i)=1+minivalij<if(j)f(i)=1+\min_{i-val_i\le j<i} f(j),其中 valival_i 为以 ii 结尾能够剪出的最大后缀长度。这个 DP 可以利用线段树优化成 O(nlogn)O(n\log n),所以关键在于如何求 valival_i

    根据 “子串是前缀的后缀”,valival_i 即为第二封信的前缀 ii 与第一封信的任意前缀的 最大公共后缀。将两封信翻转,就变成了求第二封信的一个后缀与第一封信的任意后缀的 最大公共前缀

    将第一封信与第二封信拼在一起,中间用特殊字符连接,跑一遍后缀数组,求出 height(i)height(i),根据 lcp(sai,saj)=mini+1kjheight(k)lcp(sa_i,sa_j)=\min_{i+1\le k\le j}height(k),建立 ST 表就可以用 O(nlogn)O(1)O(n\log n)-O(1) 的时间复杂度求出任意两个后缀的最大公共前缀。

    根据上面的定理,若 saisa_isajsa_j 距离越远,它们的最大公共前缀就越小。所以,在求 valival_i 时,寻找 sasa 数组上左右距离 rkirk_i 最近的两个点求最大公共前缀,然后取最大值即可。

    每一步的时间复杂度均为 O(nlogn)O(n\log n),所以总时间复杂度为 O(nlogn)O(n\log n)

    #include<bits/stdc++.h>
    #define For(i,a,b) for(i=(a);i<=(b);++i)
    #define Rof(i,a,b) for(i=(a);i>=(b);--i)
    using namespace std;
    const int Maxn=2e5,inf=1e9;
    
    int m1,m2,n,i,j,m,p,t,seg[Maxn*4+5]; char s1[Maxn+5],s2[Maxn+5],s[Maxn+5],str[Maxn+5];
    int sa[Maxn+5],rk[Maxn+5],ht[Maxn+5],cnt[Maxn+5],id[Maxn+5],px[Maxn+5],old[Maxn+5];
    int f[Maxn+5],val[Maxn+5],pos[Maxn+5][2],lg[Maxn+5],st[Maxn+5][20],vis[Maxn+5];
    inline int cmp(int x,int y) {return (old[x]==old[y] && old[x+t]==old[y+t]);}
    #define ls(x) (x<<1)
    #define rs(x) (x<<1|1)
    
    inline void Init()
    {
        int res=0;
        scanf("%d%d",&m1,&m2); n=m1+m2+1,m=200;
        while(1)
        {
            scanf("%s",str+1); int siz=strlen(str+1);
            For(i,1,siz) s1[res+i]=str[i];
            res+=siz; if(res==m1) break;
        }
        res=0;
        while(1)
        {
            scanf("%s",str+1); int siz=strlen(str+1);
            For(i,1,siz) s2[res+i]=str[i];
            res+=siz; if(res==m2) break;
        }
        For(i,1,m1) s[i]=s1[m1-i+1];
        s[m1+1]='#';
        For(i,1,m2) s[m1+1+i]=s2[m2-i+1];
    }
    inline void SA()
    {
        For(i,1,n) cnt[rk[i]=s[i]]++;
        For(i,1,m) cnt[i]+=cnt[i-1];
        Rof(i,n,1) sa[cnt[rk[i]]--]=i;
        for(t=1;t<=n;t<<=1,m=p)
        {
            for(p=0,i=n;i>n-t;--i) id[++p]=i;
            For(i,1,n) if(sa[i]>t) id[++p]=sa[i]-t;
            memset(cnt,0,sizeof(cnt));
            For(i,1,n) cnt[px[i]=rk[id[i]]]++;
            For(i,1,m) cnt[i]+=cnt[i-1];
            Rof(i,n,1) sa[cnt[px[i]]--]=id[i];
            memcpy(old,rk,sizeof(rk));
            for(p=0,i=1;i<=n;++i) rk[sa[i]]=(cmp(sa[i-1],sa[i])?p:++p);
        }
    }
    inline void Build()
    {
        int res=0;
        For(i,1,n)
        {
            while(s[sa[rk[i]-1]+res]==s[i+res]) res++;
            ht[rk[i]]=res,res=max(0,res-1);
        }
    }
    inline void BuildST()
    {
        For(i,2,n) lg[i]=lg[i>>1]+1;
        For(i,1,n) st[i][0]=ht[i];
        For(j,1,19) For(i,1,n)
        {
            st[i][j]=st[i][j-1];
            if(i+(1<<j-1)<=n) st[i][j]=min(st[i][j],st[i+(1<<j-1)][j-1]);
        }
    }
    inline int Count(int l,int r)
    {
        int len=lg[r-l+1];
        return min(st[l][len],st[r-(1<<len)+1][len]);
    }
    inline void Tag()
    {
        For(i,0,n+1) pos[i][0]=0,pos[i][1]=n+1;
        For(i,1,m1) vis[rk[i]]=1;
        For(i,1,n) pos[i][0]=(vis[i]?i:pos[i-1][0]);
        Rof(i,n,1) pos[i][1]=(vis[i]?i:pos[i+1][1]);
    }
    inline void GetVal(int x)
    {
        int l=pos[rk[n-x+1]][0],r=pos[rk[n-x+1]][1];
        if(l) val[x]=max(val[x],Count(l+1,rk[n-x+1]));
        if(r!=n+1) val[x]=max(val[x],Count(rk[n-x+1]+1,r));
    }
    inline void push_up(int p) {seg[p]=min(seg[ls(p)],seg[rs(p)]);}
    inline void Update(int l,int r,int p,int pos,int k)
    {
        if(l==r) {seg[p]=k; return;}
        int mid=(l+r)>>1;
        if(pos<=mid) Update(l,mid,ls(p),pos,k);
        else Update(mid+1,r,rs(p),pos,k);
        push_up(p);
    }
    inline int Count(int nl,int nr,int l,int r,int p)
    {
        if(l<=nl && nr<=r) return seg[p];
        int mid=(nl+nr)>>1,res=inf;
        if(l<=mid) res=min(res,Count(nl,mid,l,r,ls(p)));
        if(r>mid) res=min(res,Count(mid+1,nr,l,r,rs(p)));
        push_up(p); return res;
    }
    inline void GetAns()
    {
        For(i,1,m2)
        {
            if(val[i]==i) f[i]=1;
            else f[i]=Count(1,n,i-val[i],i-1,1)+1;
            Update(1,n,1,i,f[i]);
        }
        printf("%d\n",f[m2]);
    }
    
    int main()
    {
        Init(),SA(),Build(),BuildST(),Tag();
        For(i,1,m2) GetVal(i);
        GetAns();
        return 0;
    }
    
    • 1

    信息

    ID
    2066
    时间
    1000ms
    内存
    125MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者