浅谈Suffix Automaton(后缀自动机)

这是一个强大的automaton——Suffix Automaton==>我学过最强大,最牛犇,最难理解的自动机

现在给你一个问题:

给定一个字符串,要求这个字符串所有子串出现的次数分别是多少

朴素算法

①枚举左端点,枚举右端点,用hash记录一下,统计个数。(注意最好双hash,保证正确率)
预计时间复杂度:\(O(n^2)\)
②可以直接开trie记录,以所有后缀建,就好像这样:aabbabd

每次建的时候就记录每个地方的次数,因为有\(n^2\)个点,那么时间复杂度也就是\(O(n^2)\)

\(n^2\)复杂度很优秀,但是...

n<=100000啊啊啊啊啊....

这样做不了,怎么办?!

这时我们引进Suffix Automaton(后缀自动机)

(集中精神,前方高能)

后缀自动机定义

一个串\(S\)的后缀自动机\((SAM)\)是一个有限状态自动机\((DFA)\),它能且只能接受所有\(S\)的后缀(当然,它能做的事情远远不仅限于后缀).

后缀自动机其实是一个\(DAG\)(有向无环图),其中顶点是状态,边代表了状态之间的转移.

某一状态\(S\)被称为初始状态,由它能够到达其余所有状态.

自动机的所有转移,都是一条有向边,且都被某种符号标记,从某一状态出发的所有转移必须拥有不同的标记.

一个状态被称为终止状态,表示如果我们从初始状态\(S\)经任意一条路径走到某一终止状态,并顺序写出经过边的标记,得到的字符串必定是原串的后缀.

在符合上述条件的所有自动机中,后缀自动机拥有最少的状态与转移,并且后缀自动机的状态数以及转移数都是\(O(|S|)\)的.

Pre的定义

可以说,pre是后缀自动机中最核心的东西,也是最难懂的部分。所以认真看!
pre[x]表示字符串[S~x]的最长后缀[S~pre[x]]的右端点,例如:

绿色的虚线表示pre边。很显然我们可以发现3的pre边连向的是[S~3]的最长后缀[S~1]的右端点1。

Pre的连边

对于刚刚加入的now点,寻找las的pre边,如果没有一条与当前新加的这条边相同的边就加上一条这样的边,直到找到一个有与当前新加的这条边相同的边为止。然后分为两种情况:我们设当前找到的这个点为p,它连向的儿子为q。

①如果p,q两点距离为1,那么就可以直接把当前新节点的pre边指向q。
②如果p,q两点距离大于1,我们会发现如果把当前新节点的pre边指向q,不符合[S~q]为[S~now]的后缀(如下图)

(ab并不是abb的后缀)
我们分析这样做错误的原因,因为我们把原串中的ab当成了b(就是连在下面的那条边),所以把ab误认为成了abb的后缀,那么我们想,我们呢原来是想连b为后缀的,所以这是我们需要进行加点操作。

加点操作

我们在p以后加入一个点,与p及以前所有与q用当前符号为边相连的点,与新加入的点连上一条与当前符号相同的边,例如上图是b,因为这个点在原串中是不存在的,它只是q的一个分身,所以q指出去的所有边它也都要连,然后新加的点的pre边自然就变成了q原来的pre边,然后q和now的pre边也就指向了当前新加节点:

对于“aabbabd”来说它的自动机长这样:

时间复杂度

时间复杂度是:\(O(n)\).
最大的状态数是2n-1,因为除了前面三个点,后面的所有点都可以加点。

应用

1.给定字符串T,每次询问一个p,问p是否为T的子串.

  • 对T建一个后缀自动机.
  • 每次询问就从初始状态ss开始走,然后沿着询问的字符串走.
  • 时间复杂度\(O(|T|+∑ |p|)O(|T|+∑ |p| )\)

2.给定字符串S,问它有多少不同的子串.

  • 还是先建一个后缀自动机.
  • 那么对于后缀自动机中的任何一条路径,都是一个不同的子串.
  • 所以答案就是从S开始出发的不同路径数.

3.给定字符串S,每次询问S的所有不同子串中字典序第k小.

  • 和前两问类似,我们只需要处理从一个状态开始,每种字符的路径条数.
  • 然后从起点S一直找就好了.

4.给定字符串S,找到和它循环同构的字典序最小的字符串.

  • 我们对字符串S+S做一个后缀自动机,然后贪心的找字典序最小就好啦~

5.给定多个字符串,求它们的最长公共子串.

  • 思考...

模板Code

#include<cstdio>
#include<iostream>
#include<cstring>
#define maxN 2000010
using namespace std;
int son[maxN][27],pre[maxN],len[maxN],sum[maxN];
int sz,las,lens,now,q,p;
char s[maxN];
void add(int x)
{
    len[++sz]=len[las]+1,sum[sz]=1,now=sz;
    for (p=las;p&&!son[p][x];p=pre[p]) son[p][x]=now;
    if(p)
    {
        q=son[p][x];
        if(len[q]>1+len[p])
        {
            len[++sz]=len[p]+1;
            memcpy(son[sz],son[q],sizeof(son[q]));
            pre[sz]=pre[q];pre[q]=pre[now]=sz;
            for (;son[p][x]==q;p=pre[p]) son[p][x]=sz;
        }
        else pre[now]=q;
    }
    else pre[now]=1;
    las=now;
}
int main()
{
    scanf("%s",s+1);
    lens=strlen(s+1);sz=las=1;
    for (int i=1;i<=lens;++i) add(s[i]-96);
} 

原文地址:https://www.cnblogs.com/Chandery/p/11332806.html

时间: 2024-09-29 00:57:16

浅谈Suffix Automaton(后缀自动机)的相关文章

浅谈Aho-Corasick automaton(AC自动机)

Aho-Corasick automaton是什么? 要学会AC自动机,我们必须知道什么是Trie,也就是字典树.Trie树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种.典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计. 首先我们要知道trie,而且要知道KMP,这样就可以学AC自动机了! 其实AC自动机就是trie和KMP的结合体.主要构建trie后使用KMP的主导思想构建fail边,每次匹配与KMP相似. 下面我们看看如何构造fa

浅谈对后缀自动机的一点理解

后缀自动机入门详解及模板 后缀自动机 自动机 要想了解后缀自动机,首先得了解自动机. 例如AC自动机,AC自动机可以识别一个字符串为其所匹配的前缀. 而我们今天所介绍的后缀自动机则是识别一个字符串为自动机串的子串. 在接下来的描述中为了方便,简称\(SAM\). 暴力实现 我们知道字典树有着优良的时空复杂度,并且可以支持识别一个字符串的前缀. 如果我们将串中的所有后缀插入进字典树,那么就可以实现这个自动机的功能. 不过,由于忽视了后缀的这个性质,总点数高达\(O(n^2)\). 即使如此,字典树

浅谈后缀自动机SAM

一下是蒟蒻的个人想法,并不很严谨,仅供参考,如有缺误,敬请提出 参考资料: 陈立杰原版课件 litble 某大神 某大神 其实课件讲得最详实了 有限状态自动机 我们要学后缀自动机,我们先来了解一下自动机到底是什么.[虽说以前也学过AC自动机,只是当一个名字罢了] 有限自动机的功能是识别字符串,作用各不相同 如果自动机A能识别串s,那么A(s) = true 自动机有一个初始状态,从初始状态出发能到达多个状态.到达终止状态表示字符串识别 后缀自动机SAM 我们略去建机原理的分析和建机过程,具体原理

浅谈前、中、后缀表达式

浅谈前.中.后缀表达式 前.中.后缀表达式是信息学奥林匹克竞赛中比较鸡肋的知识点.但是知识点在考纲范围内,而且中缀表达式转后缀表达式是比较有用的知识.所以在这里为大家简单介绍一下. 之前在自学前.中.后缀表达式的时候,发现网上的很多博客和讲解的思路都不是很明了,或者就是对新手不是很友好,感谢@JZYShurak的讲解,让我对这个东西建立了一个直观的认识.所以我来补一篇比较基础,比较好理解,语言比较简洁的博客.希望能对各路大佬有所些许的帮助. 中缀表达式 中缀表达式就是我们生活中常用的表达式,简单

后缀自动机(SAM)学习指南

*在学习后缀自动机之前需要熟练掌握WA自动机.RE自动机与TLE自动机* 什么是后缀自动机 后缀自动机 Suffix Automaton (SAM) 是一个用 O(n) 的复杂度构造,能够接受一个字符串所有后缀的自动机. 它最早在陈立杰的 2012 年 noi 冬令营讲稿中提到. 在2013年的一场多校联合训练中,陈立杰出的 hdu 4622 可以用 SAM 轻松水过,由此 SAM 流行了起来. 一般来说,能用后缀自动机解决的问题都可以用后缀数组解决.但是后缀自动机也拥有自己的优点. 1812.

后缀自动机浅析

第一部分 自动机的预备知识 自动机的功能是识别字符串,一个自动机A,若它能识别字符串S,就记为A(S)=true,否则A(S)=false. 自动机由五个部分组成,字符集alpha,状态集合state,初始状态init,结束状态集合end和状态转移函数trans. 令s,t∈state,ch∈alpha,trans(s,ch)=t表示当前状态是s,在经过一条字符ch的边之后,所到达的状态是t. 如果trans(s,ch)这个转移不存在,那么trans(s,ch)=null. 第二部分 后缀自动机

后缀自动机习题合集

(写的都是初中小朋友czl早就切过的题……) http://www.cnblogs.com/Lyush/p/3281546.html POJ-1509 Glass Beads UVA - 719 Glass Beads 题意:一个字符串可以将第一个字符放到最后一位,然后问不断这样做可以得到的字典序最小的字符串 sam模板题,copy一遍建个sam,然后直接在sam中跑一遍就行了. sam记录了字符串的所有后缀(也随便记录了字串),从root开始到每个接受态节点都是一个后缀(或多个),从root开

hihocoder1445 后缀自动机二&#183;重复旋律5

传送门:http://hihocoder.com/problemset/problem/1445 [题解] 大概看了一天的后缀自动机,总算懂了一些 这篇文章写的非常好,诚意安利:Suffix Automaton Tutorial - Hunt Zhan 我就是看了这个大概懂了. 整个过程大概是:每次插入一个state可能会分裂原有的state的transition,就维护这个transition即可. 要用par[x]记录suffix-link是谁. 过程中可以不用记录minlen,因为$min

后缀自动机SAM

终于遇到了一道后缀数组不能过 一定要学SAM的题... (看了半个下午+半个上午) 现在总结一下(是给我自己总结..所以只总结了我觉得重要的 .. 看不太懂的话可以To   http://blog.csdn.net/clover_hxy/article/details/53758535  图文并茂 或者 去看更长更详细的陈立杰PPT   http://wenku.baidu.com/link?url=9YEHHchtr0vyGGDZAcsMYPI3l_Q82UNPuS4KqkfrlG_t5NFk