P6640 [BJOI2020] 封印 SAM

题意:

戳这里

分析:

  • 暴力:

对于每一次询问,在t里面哈希找最长总共子串,单次复杂度(O(|t|log |t|)) ,总复杂度 (O(q|t|log |t|)),期望得分:50pts

  • 正解:

对于最长公共子串问题考虑后缀字符串算法

我们发现,对于暴力做法单次查询的复杂度过高,所以考虑使用SAM进行优化,我们可以直接求出每一个i,求出最长的 (len[i]) 使得 (s[i-len[i]+1,i]) 为 t 的一个子串,那么我们的答案就是 (max{i-max(l,i-len[i]+1)+1})

对于第二个 max 我们分类讨论:

  1. (l> i-len[i]+1)(ans=max(i-l+1))
  2. (lle i-len[i]+1)(ans=max(len[i]))

仔细思考一下我们会发现,(i-len[i]+1) 是单调不减的,感性理解,最长公共子串的左端点只会不断右移

所以我们可以对于询问按 l 升序排序,然后双指针每次找到分界点 i,查询区间最值就可以了

tip: 树状数组查询区间最值的时候,需要一部分跳(lowbit),一部分暴力取max

代码:

#include<bits/stdc++.h>
#define inl inline
#define reg register

using namespace std;

namespace zzc
{
    inl int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
        return x*f;
    }

    const int maxn = 6e5+5;
    int n,m,qt;
    int length[maxn],ans[maxn];
    char s[maxn],t[maxn];
    struct que
    {
        int l,r,id;
        bool operator <(const que &b)const
        {
            return l<b.l;
        }
    }q[maxn];

    struct suffix_automaton
    {
        int tot,lst;
        int len[maxn],trans[maxn][26],link[maxn];
        suffix_automaton(){tot=lst=1;}

        inl void insert(int x)
        {
            int cur=++tot,tmp=lst;lst=tot;
            len[cur]=len[tmp]+1;
            for(;tmp&&!trans[tmp][x];tmp=link[tmp]) trans[tmp][x]=cur;
            if(!tmp) link[cur]=1;
            else
            {
                int q=trans[tmp][x];
                if(len[tmp]+1==len[q]) link[cur]=q;
                else
                {
                    int clone=++tot;
                    len[clone]=len[tmp]+1;
                    link[clone]=link[q];
                    for(reg int i=0;i<=25;i++) trans[clone][i]=trans[q][i];
                    link[cur]=link[q]=clone;
                    for(;tmp&&trans[tmp][x]==q;tmp=link[tmp]) trans[tmp][x]=clone;
                }
            }
        }
        
        inl void build()
        {
            for(reg int i=1;i<=m;i++) insert(t[i]-'a');
            for(reg int i=1,now=1,tmp=0;i<=n;i++)
            {
                while(now&&!trans[now][s[i]-'a']) now=link[now],tmp=len[now];
                if(!now) now=1,tmp=0;
                else now=trans[now][s[i]-'a'],tmp++;
                length[i]=min(tmp,i);
            }
        }
    }sam;
    
    struct bit
    {
        int c[maxn];
		inl int lowbit(int x) {return x&(-x);}
		inl void add(int x,int k) {for(reg int i=x;i<=n;i+=lowbit(i)) c[i]=max(c[i],k);}
		inl int query(int l,int r)
		{
			int res=0;
			while(l<=r)
			{
				while(r-lowbit(r)>=l) res=max(res,c[r]),r-=lowbit(r);
				res=max(res,length[r--]);
			}
			return res;
		}
    }tr;

    void work()
    {
        scanf("%s",s+1);n=strlen(s+1);
        scanf("%s",t+1);m=strlen(t+1);
        sam.build();
        for(reg int i=1;i<=n;i++) tr.add(i,length[i]);
        qt=read();
        for(reg int i=1;i<=qt;i++) 
        {
            q[i].l=read();q[i].r=read();
            q[i].id=i;
        }
        sort(q+1,q+qt+1);
        for(reg int i=1,j=1,l=1;i<=n&&j<=qt;l++)
        {
            while(i<=n&&i-length[i]+1<l) i++;
            while(j<=qt&&q[j].l==l)
            {
                if(i-1>=l) ans[q[j].id]=min(i-1,q[j].r)-l+1;
                if(i<=q[j].r) ans[q[j].id]=max(ans[q[j].id],tr.query(max(i,l),q[j].r));
                j++;
            }
        }
        for(reg int i=1;i<=qt;i++) printf("%d
",ans[i]);
    }

}

int main()
{
    zzc::work();
    return 0;
}
原文地址:https://www.cnblogs.com/youth518/p/14411522.html