zoukankan      html  css  js  c++  java
  • 2019年华南理工大学程序设计竞赛(春季赛)K(后缀自动机)

    传送门

    题意:

    给你一个长度位lenlen的串,一共有tt组询问,每次询问给你一个数ii,你要将原来的串在位置ii处分开,构造出两个不同的字符串str1str_1str2str_2。现在要问你str1str_1str2str_2的公共子串的个数。

    分析:

    讲真这又是一个简化版的原题呀 。(原题poj3415poj3415

    我们发现,在这个题中,询问非常多,字符串的长度并不是非常的大。因此我们可以考虑先把答案预处理出来。

    因为涉及两个串的子串个数问题,因此我们不妨考虑使用后缀自动机预处理。

    我们对其中一个串str1str_1建立后缀自动机,我们用拓扑排序自底向上的对整个自动机的endposendpos集合的大小numnum进行更新(因为对于某个结点pp,他沿着后缀连接走到的结点fa(p)fa(p)必然是结点pp的后缀,故结点pp的答案必定要更新到结点fa(p)fa(p))。

    更新完endposendpos集合的大小numnum后,我们需要维护一个前缀和sum(p)sum(p),代表到达当前结点pp时,终止点在endposendpos的所有子串中,在串str1str_1中出现的次数。

    完成上述工作之后,我们只需要把str2str_2串丢到后缀自动机上去匹配,每次去统计答案并记录下来供tt次询问查询即可。

    整体时间复杂度:O(len2)mathcal{O}(len^2)

    代码:

    #include <bits/stdc++.h>
    #define maxn 4005
    using namespace std;
    char str[maxn];
    typedef unsigned long long ll;
    struct SAM{
        int next[maxn*2][26],fa[maxn*2],len[maxn*2],last,cnt=0;
        ll cntA[maxn*2],A[maxn*2];
        ll num[maxn*2],sum[maxn*2];
        SAM(){clear();}
        void clear(){
            last=cnt=1;
            memset(sum,0,sizeof(sum));
            memset(num,0,sizeof(num));
            memset(next[1],0,sizeof(next[1]));
            memset(len,0,sizeof(len));
            fa[1]=len[1]=0;
        }
        void Insert(int c){
            int p=last;
            int np=++cnt;
            num[np]=1;
            memset(next[cnt],0,sizeof(next[cnt]));
            len[np]=len[p]+1;last=np;
            while(p&&!next[p][c]) next[p][c]=np,p=fa[p];
            if(!p) fa[np]=1;
            else{
                int q=next[p][c];
                if(len[q]==len[p]+1) fa[np]=q;
                else{
                    int nq=++cnt;
                    len[nq]=len[p]+1;
                    memcpy(next[nq],next[q],sizeof next[nq]);
                    fa[nq]=fa[q]; fa[np]=fa[q]=nq;
                    while(next[p][c]==q) next[p][c]=nq,p=fa[p];
                }
            }
        }
        void build(char *s){
            memset(cntA,0,sizeof(cntA));
            memset(A,0,sizeof(A));
            for(int i=1;i<=cnt;i++) cntA[len[i]]++;
            for(int i=1;i<=cnt;i++) cntA[i]+=cntA[i-1];
            for(int i=cnt;i;i--) A[cntA[len[i]]--] =i;
            int tmp=1;
            int sz=strlen(s);
            for(int i=0;i<sz;i++){
                num[tmp=next[tmp][s[i]-'a']]=1;
            }
            for(int i=cnt;i;i--){
                int x=A[i];
                num[fa[x]]+=num[x];
            }
            for(int i=1;i<=cnt;i++){
                int x=A[i];
                sum[x]=sum[fa[x]]+num[x]*(len[x]-len[fa[x]]);
            }
        }
        int query(char *s){
            int sz=strlen(s),now=1,Len=0;
            int res=0;
            for(int i=0;i<sz;i++){
                int c=s[i]-'a';
                if(next[now][c]){
                    Len++;
                    now=next[now][c];
                }
                else{
                    while(now&&!next[now][c]) now=fa[now];
                    if(now){
                        Len=len[now]+1;
                        now=next[now][c];
                    }
                    else{
                        Len=0,now=1;
                    }
                }
                res+=sum[fa[now]]+num[now]*(Len-len[fa[now]]);
            }
            return res;
        }
    }sam;
    ll ans[maxn];
    char tmp1[maxn];
    char tmp2[maxn];
    int main()
    {
        //freopen("in.txt","r",stdin);
        scanf("%s",str);
        int t;
        scanf("%d",&t);
        int len=strlen(str);
        for(int i=0;i<len-1;i++){
            memset(tmp1,0,sizeof(tmp1));
            memset(tmp2,0,sizeof(tmp2));
            for(int j=0;j<=i;j++) tmp1[j]=str[j];
            for(int j=i+1;j<len;j++) tmp2[j-i-1]=str[j];
            sam.clear();
            int sz=strlen(tmp1);
            for(int i=0;i<sz;i++){
                sam.Insert(tmp1[i]-'a');
            }
            sam.build(tmp1);
            ans[i+1]=sam.query(tmp2);
        }
        while(t--){
            int n;
            scanf("%d",&n);
            printf("%lld
    ",ans[n]);
        }
        return 0;
    }
    
    
  • 相关阅读:
    页面跳转方式
    银行账号每4位插入一个空格
    .net 对称加密
    .net 非对称加密
    webpack-dev-server的cli使用
    你不能不知道的 6 个 Web 开发者工具
    String字符串提取指定内容
    SQLServer 复制表结构
    关于OMAPL138烧写程序的说明
    C程序结构
  • 原文地址:https://www.cnblogs.com/Chen-Jr/p/11007145.html
Copyright © 2011-2022 走看看