zoukankan      html  css  js  c++  java
  • SPOJ1812

    PortalPortal to 洛谷

    Description

    给出(n(nleq10))个仅包含小写字母的字符串(s_1..s_n(|s_i|leq10^5)),求这些字符串的最长公共子串长度。

    Solution

    对第一个串建立SAM,然后依次跑剩下的串。
    记录(maxL[i])表示对于目前做过的所有串,状态(i)最长能匹配的长度。跑一个串(x)时,记录(tmp[i])表示对于串(x),状态(i)最长能匹配的长度,跑完之后将(maxL)(tmp)(min)。答案就是(maxL)中的最大值。
    如何在SAM上跑一个串呢?如果当前处于状态(p),下一个字符是(x),那么若有(ch[p][x])则转移,否则在(parent)树上回溯(p)直至存在(ch[p][x])并转移。如果直到根也不存在(ch[p][x]),那么令(p=rt),从头开始匹配。在(parent)上回溯就相当于不断减小匹配长度(len)
    不过只是跑一遍可并不能求出所有的(tmp),有的状态因为不够优而被跳过了。所以跑完之后,我们应当在(parent)树上从底向上更新出所有状态的(tmp),即tmp[fa[p]]=max(tmp[fa[p]],min(len[fa[p],tmp[p]))。但由于(len[fa[p]] < tmp[p] leq len[p], tmp[fa[p]]leq len[fa[p]]),所以后面的一堆在(p)被匹配到(存在(tmp[p]))的情况下必然等于(len[fa[p]])

    时间复杂度(O(sum|s|))

    Code

    //Longest Common Substring
    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    int const N=2e5+10;
    int const INF=0x7FFFFFFF;
    char s0[N>>1],s[N>>1];
    int ndCnt,rt,last;
    int fa[N],ch[N][26],len[N];
    void ins(int x)
    {
        int p=last,np=++ndCnt;
        last=np,len[np]=len[p]+1;
        for(p;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;
        if(!p) {fa[np]=rt; return;}
        int q=ch[p][x];
        if(len[q]==len[p]+1) {fa[np]=q; return;}
        int nq=++ndCnt; len[nq]=len[p]+1;
        for(int i=0;i<26;i++) ch[nq][i]=ch[q][i];
        fa[nq]=fa[q]; fa[q]=fa[np]=nq;
        for(p;p&&ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
    }
    int cnt[N],ord[N];
    void buildSAM(char s[])
    {
        last=rt=++ndCnt;
        for(int i=1;s[i];i++) ins(s[i]-'a');
        memset(cnt,0,sizeof cnt);
        for(int i=1;i<=ndCnt;i++) cnt[len[i]]++;
        for(int i=ndCnt-1;i>=0;i--) cnt[i]+=cnt[i+1];
        for(int i=ndCnt;i>=1;i--) ord[cnt[len[i]]--]=i;
    }
    int maxL[N],tmp[N];
    void query(char s[])
    {
        memset(tmp,0,sizeof tmp);
        for(int p=rt,L=0,i=1;s[i];i++)
        {
            int x=s[i]-'a';
            if(ch[p][x]) L++,p=ch[p][x];
            else
            {
                while(p&&!ch[p][x]) p=fa[p];
                if(!p) L=0,p=rt;
                else L=len[p]+1,p=ch[p][x];
            }
            tmp[p]=max(tmp[p],L);
        }
        for(int i=1;i<=ndCnt;i++)
        {
            int p=ord[i]; maxL[p]=min(maxL[p],tmp[p]);
            if(fa[p]&&tmp[p]) tmp[fa[p]]=len[fa[p]];
            //等价于tmp[fa[p]]=max(tmp[fa[p]],min(len[fa[p],tmp[p]))
        }
    }
    int main()
    {
        scanf("%s",s0+1); buildSAM(s0);
        for(int p=1;p<=ndCnt;p++) maxL[p]=len[p];
        while(scanf("%s",s+1)!=EOF) query(s);
        int ans=0;
        for(int p=1;p<=ndCnt;p++) ans=max(ans,maxL[p]);
        printf("%d
    ",ans);
        return 0;
    }
    

    P.S.

    昨天就A了今天才发题解真是对不起...

  • 相关阅读:
    了解 NoSQL 的必读资料
    关于什么时候用assert(断言)的思考
    这次见到了一些大侠
    NetBeans 时事通讯(刊号 # 87 Jan 12, 2010)
    动态链接库dll,静态链接库lib, 导入库lib
    新女性十得 写得了代码,查得出异常
    记录系统乱谈
    新女性十得 写得了代码,查得出异常
    fullpage.js禁止滚动
    RunningMapReduceExampleTFIDF hadoopclusternet This document describes how to run the TFIDF MapReduce example against ascii books. This project is for those who wants to experiment hadoop as a skunkworks in a small cluster (110 nodes) Google Pro
  • 原文地址:https://www.cnblogs.com/VisJiao/p/SPOJ1812.html
Copyright © 2011-2022 走看看