• [BZOJ2946][Poi2000]公共串 后缀自动机


    2946: [Poi2000]公共串

    Time Limit: 3 Sec  Memory Limit: 128 MB
    Submit: 1367  Solved: 612
    [Submit][Status][Discuss]

    Description

     
           给出几个由小写字母构成的单词,求它们最长的公共子串的长度。
    任务:
    l        读入单词
    l        计算最长公共子串的长度
    l        输出结果
     

    Input

     
    文件的第一行是整数 n,1<=n<=5,表示单词的数量。接下来n行每行一个单词,只由小写字母组成,单词的长度至少为1,最大为2000。
     

    Output

    仅一行,一个整数,最长公共子串的长度。
     

    Sample Input

    3
    abcb
    bca
    acbc

    Sample Output

    HINT

     

    Source

    第一次写后缀自动机,感觉非常神奇。

    对第一个串建立后缀自动机,多串进行匹配。

    每次匹配的时候对于后缀自动机中的每个节点维护ans,ls,表示的是到达该节点所能匹配上的最大后缀长度(每个节点都可能是多个节点的儿子,所以从根到该点的路径长度可能是不同的)。

    我们还需要按照step从大到小,用每个节点取更新他的link节点,因为如果匹配到一个状态,那么实际上他link链上的所有状态都匹配了,for(int i=cnt;i;i--) ls[link[pos[i]]]=max(ls[pos[i]],ls[link[pos[i]]]);

    然后对于每个串匹配后得到的每个位置的ls数组取min得到数组ans,ans的最大值极为答案。 

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdlib>
     4 #include<cstdio>
     5 #include<cmath>
     6 #include<algorithm>
     7 #define maxn 4005
     8 using namespace std;
     9 char ch[maxn];
    10 struct data {
    11     int son[maxn][30],step[maxn],link[maxn],last,cnt;
    12     int ans[maxn],v[maxn],pos[maxn],ls[maxn];
    13     data() {last=cnt=1;}
    14     void extend(int c) {
    15         int p=last,np=last=++cnt;step[np]=step[p]+1;
    16         while(!son[p][c]&&p) son[p][c]=np,p=link[p];
    17         if(!p) link[np]=1;
    18         else {
    19             int q=son[p][c];
    20             if(step[q]==step[p]+1) link[np]=q;
    21             else {
    22                 int nq=++cnt;
    23                 step[nq]=step[p]+1;
    24                 memcpy(son[nq],son[q],sizeof(son[q]));
    25                 link[nq]=link[q];
    26                 link[np]=link[q]=nq;
    27                 while(son[p][c]==q) son[p][c]=nq,p=link[p];
    28             }
    29         }
    30     }
    31     void pre() {
    32         for(int i=1;i<=cnt;i++) {ans[i]=step[i];v[step[i]]++;}
    33         for(int i=1;i<=cnt;i++) v[i]+=v[i-1];
    34         for(int i=cnt;i;i--) pos[v[step[i]]--]=i;
    35     }
    36     void solve() {
    37         memset(ls,0,sizeof(ls));
    38         scanf("%s",ch+1);
    39         int l=strlen(ch+1),p=1,tmp=0;
    40         for(int i=1;i<=l;i++) {
    41             int c=ch[i]-'a';
    42             while(!son[p][c]&&p) p=link[p];
    43             if(!p) p=1,tmp=0;
    44             else tmp=min(tmp,step[p])+1,p=son[p][c];
    45             ls[p]=max(ls[p],tmp);
    46         }
    47         for(int i=cnt;i;i--) ls[link[pos[i]]]=max(ls[pos[i]],ls[link[pos[i]]]);
    48         for(int i=1;i<=cnt;i++) ans[i]=min(ans[i],ls[i]);
    49     }
    50     void getans() {
    51         int an=0;
    52         for(int i=1;i<=cnt;i++) an=max(an,ans[i]);
    53         printf("%d
    ",an);
    54     }
    55 }sam;
    56 int main() {
    57     int n;
    58     scanf("%d",&n);
    59     scanf("%s",ch+1);
    60     int l=strlen(ch+1);
    61     for(int i=1;i<=l;i++) sam.extend(ch[i]-'a');
    62     sam.pre();
    63     for(int i=2;i<=n;i++) sam.solve();
    64     sam.getans();
    65 }
    View Code
    O(∩_∩)O~ (*^__^*) 嘻嘻…… O(∩_∩)O哈哈~
  • 相关阅读:
    全程图解】ADSL+笔记本电脑 组建WIFI网络让5800实现WIFI上网(更新完毕)
    JSP用户管理系统【上学应付作业用】
    c++按位操作符
    F#: .NET中的函数编程语言
    Visual Studio OpenGL 配置方法
    Linux下挂载U盘方法
    开发者该以什么为骄傲
    POSIX约定与GNU长选项
    修复移动硬盘"文件或目录损坏且无法读取"
    某国外论坛关于什么是Computer Science的争论,你怎么看?
  • 原文地址:https://www.cnblogs.com/wls001/p/8244968.html
Copyright © 2020-2023  润新知