• spoj1811 Longest Common Substring


     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <cmath>
     5 #include <algorithm>
     6 #define maxn 500005
     7 #define maxl 250005
     8 using namespace std;
     9 
    10 int n,m,ans,last,len,tot,root,son[maxn][26],fa[maxn],dist[maxn];
    11 char s1[maxl],s2[maxl];
    12 struct Tsegment{
    13     void prepare(){last=tot=root=1;}
    14     int newnode(int x){dist[++tot]=x;return tot;}
    15     void add(int x){
    16         int p=last,np=newnode(dist[p]+1); last=np;
    17         for (;p&&!son[p][x];p=fa[p]) son[p][x]=np;
    18         if (p==0) fa[np]=root;
    19         else{
    20             int q=son[p][x];
    21             if (dist[p]+1==dist[q]) fa[np]=q;
    22             else{
    23                 int nq=newnode(dist[p]+1);
    24                 memcpy(son[nq],son[q],sizeof(son[q]));
    25                 fa[nq]=fa[q],fa[q]=fa[np]=nq;
    26                 for (;p&&son[p][x]==q;p=fa[p]) son[p][x]=nq;
    27             }
    28         }
    29     }
    30 }SAM;
    31 int main(){
    32     scanf("%s",s1+1),n=strlen(s1+1);
    33     scanf("%s",s2+1),m=strlen(s2+1);
    34     SAM.prepare();
    35     for (int i=1;i<=n;i++) SAM.add(s1[i]-'a');
    36     ans=0,len=0,last=root;
    37     for (int i=1;i<=m;i++){
    38         int x=s2[i]-'a';
    39         if (son[last][x]) len++,last=son[last][x];
    40         else{
    41             for (;last&&!son[last][x];) last=fa[last];
    42             if (last==0) len=0,last=root;
    43             else{
    44                 len=dist[last]+1,last=son[last][x];
    45             }
    46         }
    47         ans=max(ans,len);
    48     }
    49     printf("%d
    ",ans);
    50     return 0;
    51 }
    View Code

    题目链接:http://begin.lydsy.com/JudgeOnline/problem.php?id=2796

    题目大意:给定两个字符串,长度<=2.5*10^5,询问这两个字符串的最长公共子串的长度,这题以前用后缀数组写过,比较基础,这次是用SAM写的,比较坑爹。

    做法:最近学习了后缀自动机,讲讲我的理解:

    后缀自动机可以识别一个字符串中的所有子串,写过AC自动机的同学都知道Trie树,如果按照那种方法建树的话,空间复杂度为n^2,但是我们发现有很多重复的状态,我们会发现,有很多个子串的右端点集合完全相同,那么这些字符串向后匹配的能力是相同的,故可将其缩成一个状态。

    我先介绍几个性质:

    1.right集合要么没有交集,要么真包含,用反证法易得。

    2.对于某一个right集合中字符串,其长度有一个区间,即为【min,max】,若大于这个长度区间,|right|会减小,反之增大。

    3.由于性质1,我们可以发现利用right集合能建立一棵树,满足:父亲的right集合是真包含儿子节点right集合中max最大的 ,且满足父亲的max+1=儿子的min。这三条性质在建立后缀自动机的时候有用。

    如何建立后缀自动机呢?

    建立过程比较麻烦,大家画个图理解理解吧,orzclj……

    具体看代码。。。。。细节太多。。

    这于这题的做法:

    对第一个字符串建立SAM,第二个字符串在SAM上匹配即可,若失配,就跳fa,因为fa的right集合真包含于自己的right集合,这样缩短字符串长度,却能增加后续匹配的可能性,及时更新答案即可。

    后缀自动机。

  • 相关阅读:
    LUOGU P1654 OSU! (概率期望)
    poj 3682 King Arthur's Birthday Celebration (期望dp)
    CF148D Bag of mice (期望dp)
    LUOGU P1514 引水入城 (bfs)
    LUOGU P4281 [AHOI2008]紧急集合 / 聚会 (lca)
    LUOGU P1313 计算系数 (组合数学)
    LUOGU P2949 [USACO09OPEN]工作调度Work Scheduling (贪心)
    LUOGU P1613 跑路 (倍增floyd)
    LUOGU P1291 [SHOI2002]百事世界杯之旅 (期望dp)
    poj 3208--Apocalypse Someday(数位dp)
  • 原文地址:https://www.cnblogs.com/OYzx/p/5546049.html
Copyright © 2020-2023  润新知