zoukankan      html  css  js  c++  java
  • 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集合,这样缩短字符串长度,却能增加后续匹配的可能性,及时更新答案即可。

    后缀自动机。

  • 相关阅读:
    【Leetcode】【Easy】Remove Duplicates from Sorted List
    【Leetcode】【Easy】Pascal's Triangle II
    【Leetcode】【Easy】Pascal's Triangle
    【Leetcode】【Easy】Binary Tree Level Order Traversal II
    【Leetcode】【Easy】Binary Tree Level Order Traversal
    【Leetcode】【Easy】Maximum Depth of Binary Tree
    【Leetcode】【Easy】Minimum Depth of Binary Tree
    【Leetcode】【Easy】Balanced Binary Tree
    【Leetcode】【Easy】Symmetric Tree
    如何使用Action.Invoke()触发一个Storyboard
  • 原文地址:https://www.cnblogs.com/OYzx/p/5546049.html
Copyright © 2011-2022 走看看