后缀自动机----一种将字符串变成DAG的方法
后缀自动机 (suffix automaton, SAM) 是一个能解决许多字符串相关问题的有力的数据结构。(否则我们也不会用它)
举几个例子,以下的字符串问题都可以在线性时间内通过 SAM 解决
1.在另一个字符串中搜索一个字符串的所有出现位置。(诶?KMP好像能做)
2.计算给定的字符串中有多少个不同的子串。(诶?好像也能做)
3.统计一个子串所有不同子串的总长度 (额,线性复杂度?以前的知识好像就不行了)
4.字典序第k大/小子串(字典树好像可以做,但线性?不会!)
5.给定一个字符串 。找出字典序最小的循环移位。(这个我会!最小表示法!)
6.对于一个给定的文本串T ,有多组询问,每组询问给一个模式串S ,回答模式串S在字符串T中作为子串出现了多少次。(AC自动机!也可以)
7.给定一个字符串S 和一个特定的字符集,我们要找一个长度最短的没有在S中出现过的字符串。(以前的知识做不到我太菜了)
8.两个字符串的最长公共子串(线性??!以前的知识做不到我太菜了)
9.多个字符串的最长公共子串(我不会!!)
这么多东西都可以线性时间内解决,厉害吧;
厉害的前提是你先要学会后缀自动机
接下来进入正题:
SAM是什么?
1.SAM 是一张有向无环图。结点被称作 状态 ,边被称作状态间的 转移 。
2.图存在一个源点root称作 初始状态 ,其它各结点均可从root出发到达。每个 转移 都标有一些字母。从一个结点出发的所有转移均 不同 。
3.存在一个或多个 终止状态 。如果我们从初始状态出发,最终转移到了一个终止状态,则路径上的所有转移连接起来一定是字符串S的一个后缀。
4.S的每个后缀均可用一条从到某个终止状态的路径构成。
5.在所有满足上述条件的自动机中,SAM 的结点数是最少的。
具体可以证明,SAM的点数最多是2*n-1,边数最多时3*n-4; (暂时为了方便理解并不证明,你只要露出"哇!这是上限吗","我靠,这SAM的复杂度也太***的强了"的表情就好了);
SAM的性质:
SAM 最简单、也最重要的性质是,它包含关于字符串的所有子串的信息。任意从初始状态开始的路径,如果我们将转移路径上的标号写下来,都会形成S的一个子串 。反之每个S的子串对应从初始状态开始的某条路径。
为了更好的理解SAM(背模板,水经验),我们定义一些奇怪的东西;(不要一看到不认识的概念就跳过去,否则建图会看不懂的)
1.endpos等价类
考虑字符串S的任意非空子串t,我们记endpos(t)为在字符串S中t的所有结束位置.
让我们举个栗子:(假设对字符串中字符的编号从零开始)
对于S=“cbacbaba” endpos(ba)="2,5,7" endpos(a)="2,5,7" endpos(cba)="2,5";
我们发现,对于"ba"和"a"的endpos等价类的数值是完全相同的,那么这对与SAM来说是个好东西,我们可以将所有endpos相同的子串放到SAM的一个节点上,大大节省了空间与时间复杂度;
那么一个重要的结论出现了:SAM上的每个节点都代表一个endpos等价类;(比如刚才的栗子中,子串"ba","a"在SAM上用一个节点表示)
我们发现一个不是引理的性质:对于一个子串S,不断的删除它开头的元素,那么它的endpos一定是在已有的旧元素基础上加入一些新元素(或者不加),大致理解一下对于下面的引理很好理解
引理1:字符串S的两个非空子串x和y的endpos相同,当且仅当字符串x在S中的每次出现,都是以y后缀的形式存在的。(x<=y) (很显然的对吧?)
引理2:两个非空子串x,y, 他们的endpos的不可能存在交集 (很显然的对吧)
引理3:考虑一个endpos等价类,将类中的所有子串按长度递减的顺序排序。每个子串是它前一个子串的后缀。
换句话说,对于同一等价类的任一两子串,较短者为较长者的后缀,且该等价类中的子串长度恰好覆盖整个区间;
粗略证明感性理解:对于一个endpos等价类中的最长子串x,和最短子串y,若y的开头加一个元素,使得新形成的字符串z是S的子串,那么如果z的长度<=x,则endpos(z)
一定=endpos(y)=endpos(x); 使用数学归纳法可以证明所有的y<=z<=x;z的endpos都与x,y相同;
2.后缀链接 link:
考虑 SAM 中某个不是root的一个状态 。我们已经知道,状态v对应于具有相同endpos的等价类。我们如果定义x为这些字符串中最长的一个,则所有其它的字符串都是x的后缀。
一个后缀链接link连接到对应于状态v的最短子串的所有后缀中长度最大的那个endpos等价类。
引理1:所有后缀链接构成一棵根节点为root的树 (和造一颗随机树的原理相同:对于一个endpos等价类v,只会连到比v的最短子串小的endpos等价类)
下面放两张图:对于字符串"abcbc",前者是表示边的转移状态,后者表示link的连接方式;

好了,定义暂时告一段落了;接下来就是构造环节了(代码):(别人家的构造过程 (逃~)
!!极度建议结合之前的定义来阅读!!

代码实现就是:
class node{
public:
int ch[28];
int link;
int len;
}SAM[3000010];
int ans[3000010];
int last=0,size=1;
void SAM_build()
{
SAM[0].len=0;
n=strlen(s+1);
SAM[0].link=-1;
inc(u,1,n){
int c=s[u]-'a';
int cur=size++;
ans[cur]=1;
SAM[cur].len=SAM[last].len+1;
int p=last;
while(p!=-1&&!SAM[p].ch[c]){
SAM[p].ch[c]=cur;
p=SAM[p].link;
}
if(p==-1){
SAM[cur].link=0;
}
else{
int q=SAM[p].ch[c];
if(SAM[q].len==SAM[p].len+1){
SAM[cur].link=q;
}
else{
int newq=size++;
SAM[newq]=SAM[q];
SAM[newq].len=SAM[p].len+1;
while(p!=-1&&SAM[p].ch[c]==q){
SAM[p].ch[c]=newq;
p=SAM[p].link;
}
SAM[q].link=newq;
SAM[cur].link=newq;
}
}
last=cur;
}
}
后缀自动机----一种将字符串变成DAG的方法的更多相关文章
- 用c++后缀自动机实现最大公共字符串算法,并封装成Python库
后缀自动机的C++代码转自https://e-maxx.ru/algo/suffix_automata,其余封装为自写. 在C++文件同级目录建立setup.py文件,代码如下: # !/usr/bi ...
- 【文文殿下】后缀自动机(SAM)求最长公共子串的方法
首先,在A 串上建立一个SAM,然后用B串在上面跑.具体跑的方法是: 从根节点开始,建立一个指针 p ,指着B串的开头,同步移动指针,沿着SAM的边移动,如果可以移动(即存在边)那么万事皆好,直接le ...
- 一文读懂后缀自动机 Suffix_Automata
原论文(俄文)地址:suffix_automata 原翻译(中文)地址:后缀自动机详解(DZYO的博客) Upd:强推浅显易懂(?)的SAM讲解 后缀自动机 后缀自动机(单词的有向无环图)--是一种强 ...
- 牛客多校第四场 I string 后缀自动机/回文自动机
这个回文自动机的板有问题,它虽然能过这道题,但是在计算size的时候会出锅! 题意: 求一个字符串中本质不同的连续子串有几个,但是某串和它反转后的字符串算一个. 题解: 要注意的是,一般字符串题中的“ ...
- BZOJ 3473: 字符串 [广义后缀自动机]
3473: 字符串 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 354 Solved: 160[Submit][Status][Discuss] ...
- [BJWC2018]Border 的四种求法(后缀自动机+链分治+线段树合并)
题目描述 给一个小写字母字符串 S ,q 次询问每次给出 l,r ,求 s[l..r] 的 Border . Border: 对于给定的串 s ,最大的 i 使得 s[1..i] = s[|s|-i+ ...
- bzoj 3277 串 && bzoj 3473 字符串 && bzoj 2780 [Spoj]8093 Sevenk Love Oimaster——广义后缀自动机
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3277 https://www.lydsy.com/JudgeOnline/problem.p ...
- 【专题】字符串专题小结(AC自动机 + 后缀自动机)
AC自动机相关: $fail$树: $fail$树上以最长$border$关系形成父子关系,我们定一个节点对应的串为根到该节点的路径. 对于任意一个非根节点$x$,定$y = fa_{x}$,那$y$ ...
- BZOJ 3926: [Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机 后缀自动机 字符串
https://www.lydsy.com/JudgeOnline/problem.php?id=3926 广义后缀自动机是一种可以处理好多字符串的一种数据结构(不像后缀自动机只有处理一到两种的时候比 ...
随机推荐
- JetBrains IDE 基本快捷键
转载自:https://nextfe.com/jetbrains-ide-shortcuts/ 一个好的手艺人很熟悉他的工具.软件开发者也不例外.所以,在编程的过程中,值得了解一些键盘快捷键,以免因为 ...
- 在chrome开发者模式中查找你的js文件
在chrom开发者模式中按ctrl+o查找你的js文件
- [vbs]脚本启动
Set ws = CreateObject("Wscript.Shell") ws.run "cmd.exe /c start tool.exe config_tence ...
- Nginx之进程间的通信机制(信号、信号量、文件锁)
1. 信号 Nginx 在管理 master 进程和 worker 进程时大量使用了信号.Linux 定义的前 31 个信号是最常用的,Nginx 则通过重定义其中一些信号的处理方法来使用吸纳后,如接 ...
- Python——私有化 和 属性property
Python——私有化 和 属性property 一.私有化 xx: 公有变量 _x: 单前置下划线,私有化属性或方法,from somemodule import *禁止导入,类对象和子类可以访问 ...
- Go项目的测试代码3(测试替身Test Double)
上一篇文章介绍了项目中测试代码的写法. Go项目的测试代码2(项目运用) 这里简单的共享一下测试替身. 当我们写测试代码的时候,经常遇到一个问题.跟别的模块或服务有依赖性,可是功能还没开发完.或是因为 ...
- MySQL(逻辑分层,存储引擎,sql优化,索引优化以及底层实现(B+Tree))
一 , 逻辑分层 连接层:连接与线程处理,这一层并不是MySQL独有,一般的基于C/S架构的都有类似组件,比如连接处理.授权认证.安全等. 服务层:包括缓存查询.解析器.优化器,这一部分是MySQL核 ...
- VLAN和VXLAN的区别
VLAN ·概况 VLAN (Virtual Local Area Network)意为虚拟局域网,是在交换机实现过程中涉及到的概念,由802.1Q标准所定义.由于交换机是工作在链路层的网络设备,连接 ...
- Ubuntu配置zsh
安装zsh sudo apt-get install zsh chsh -s /bin/zsh 配置 curl -L https://raw.github.com/robbyrussell/oh-my ...
- ElasticSearch——分词
前言: 最近在使用elasticSearch中发现有些数据查不出来,于是研究了一下,发现是分词导致的,现梳理并总结一下. ElasticSearch 5.0以后,string类型有重大变更,移除了st ...