【字符串数据结构后缀系列Part3】后缀自动机的性质和应用

学会了构建SAM之后,我们要开始学如何使用SAM来处理各种问题了.

我们先来整体看一下SAM的性质(引自2015国家集训队论文集张天扬《后缀自动机及其应用》):

1.每个状态s代表的串的长度是区间(lenfas,lens].

2.对于每个状态s,它代表的所有串在原串中出现次数和每次出现的右端点相同.

3.在后缀自动机的Parent树中,每个状态的right集合都是其父状态right集合的子集.

4.后缀自动机的Parent树是原串的反向前缀树 .

5.两个串的最长公共后缀,位于这两个串对应状态在Parent树上的最近公共祖先状态.

———————————————–线 割 分 是 我 >w<—————————————-

我们都说SAM能够完全代替SA.

因此我们先来看一下SA的应用.SA主要有以下几方面的功能(对每个功能我只侧重讲一下SAM的做法):

1.多模式串的匹配.

显然作为一个自动机,字符串的匹配功能是与生俱来的…因此SAM能够做字符串的匹配在显然不过…

2.最长公共前缀(LCP)

SA在处理这个问题时候往往是建立一个height数组,其实也算是比较好实现了,但是比起SAM来略有不足.

首先我们将两个串反过来,那么求最长公共前缀就变成了求最长公共后缀.

在观察SAM的性质5,显然这个问题变成裸题了…

可能有人会疑问该性质的正确性,那么我们就来做一个简要的分析:

首先我们知道,对一个状态s,其父状态对应的串必定是s对应的串的后缀.这个根据SAM的构建方式想一想是很显然的.

所以找到两个串在SAM上的对应状态,再做LCA就能得到那个最长公共后缀也就是原问题的最长公共前缀了.

3.最长回文子串

我们对原串构建SAM,求出每个点right集合里的最大值rmax.

对原串取反然后代入后缀自动机进行匹配,假设当前匹配串对应原串的子串是[l,r],那么如果这个区间覆盖到了当前匹配到状态rmax,则[l,rmax]是个回文串.(这东西看起来挺对的但是我不太会证,您自己画画图看一看也会发现这东西确实挺对的…)

同样的这种方法我们可以求出所有的回文子串来应对不同的问题>_<

4.最长公共子串

对某一个串建立SAM,把其他串放到SAM上运行,时刻记录一下子串的长度,最后每个状态都把其他所有匹配串在此处的匹配长度取min,然后对所有状态的min取max即可.

—————————————————————–线 割 分 是 我 >w<—————————————————

这些都是比较常见的问题了.可能还有一些不常见的问题,然后我们又不太会用SAM处理,这种时候怎么办?

其实SAM可以线性构造后缀树和后缀数组.

我们知道SA的线性构造(DC3算法)不但常数大而且不好理解不好写,但是SAM线性构造SA其实是很简单的,而且常数小.

我们知道跟后缀数组对应的还有后缀树,在后缀树上做一遍DFS即可得到后缀数组.

那么如何利用SAM线性构造后缀树呢?

看性质四.

先看看什么是反向前缀树吧:

把每一个前缀的反串插入一个trie中,并把没有分支的链合并.

好吧其实这货可以看成原串的反串的后缀树..至于为什么大家可以自己看看论文或者脑补一下,我不多说了= =

所以我们对原串的反串建立后缀自动机,其parent树就是原串的后缀树了.

然后用前面说到的方法对后缀树DFS一下,得到的就是SA了.

这样看常数真的很小啊…基本上就是一个构建SAM,扫一下parent树,然后一个DFS的问题了…

并不会比DC3慢吧.

终于得到了我们满意的结果.

其实SAM的功能还有很多,大家都说他可以完全代替SA,而且好像功能更加丰富,但是由于我刚学会SAM没多久,对他还没有太深的理解,现在差不多只会这些了QAQ请大家见谅QAQ

更多的应用和上面那些应用的具体实现我以后会在题解里通过代码体现的.

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-08 01:32:52

【字符串数据结构后缀系列Part3】后缀自动机的性质和应用的相关文章

poj 2774 最长公共子串--字符串hash或者后缀数组或者后缀自动机

http://poj.org/problem?id=2774 想用后缀数组的看这里:http://blog.csdn.net/u011026968/article/details/22801015 本文主要讲下怎么hash去找 开始的时候写的是O(n^2 logn)算法 果断超时...虽然也用了二分的,, 代码如下: //hash+二分 #include <cstdio> #include <cstring> #include <algorithm> #include

学渣乱搞系列之后缀数组

学渣乱搞系列之后缀数组 by 狂徒归来 后缀数组,其nlogn的构造方法,比较麻烦,十几个循环,基数排序?计数排序?各种排序,各种凌乱,学渣表示鸭梨很大啊!学渣从<挑战程序设计竞赛>中偷学了一点nlog2n的构造方法. 字符串后缀(Suffix)是指从字符串的某个位置开始到其末尾的字符串子串.我们认为原串和空串也是后缀. 后缀数组(Suffix Array)指的是将某个字符的所有后缀按字典序排序后得到的数组.排序方式很多,时间复杂度也不同.有基数排序的倍增法o(nlogn),有DC3构造方法o

poj 2752 求一个字符串所有的相同前后缀

求一个字符串所有的相同前后缀Sample Input ababcababababcababaaaaaSample Output 2 4 9 181 2 3 4 5 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <stack> 5 using namespace std; 6 7 const int N = 400010; 8 int next[N]; 9 c

字符串前缀,真前缀,后缀,真后缀,及前缀函数

举个例子,如字符串 ababc 首先,不考虑空字符,所有的前缀有a, ab, aba, abab, ababc,其中真前缀有a, ab, aba, abab 同理可以理解后缀,真前(后)缀就是指不包含自身的前(后)缀 前缀函数next[j]是指某个字符串的最长真后缀同时也是它的前缀的子串长度.不太理解可以看下面的例子 a -> 0 ab -> 0 aba -> 1 abab -> 2 ababc -> 0 前缀函数在字符串的匹配中用的较多,如KMP等.它主要是表明在一次匹配失

[TJOI2015]弦论(后缀数组or后缀自动机)

解法一:后缀数组 听说后缀数组解第k小本质不同的子串是一个经典问题. 把后缀排好序后第i个串的本质不同的串的贡献就是\(n-sa[i]+1-LCP(i,i-1)\)然后我们累加这个贡献,看到哪一个串的时候,这个贡献的和大于等于k,然后答案就在这个串里了,然后枚举就行了. 那么第k小子串该怎么办? 我们考虑二分答案,我们按字典序大小二分一个子串(具体就是二分第k小的本质不同子串,因为这个串可以\(O(n)\)求),然后看看比这个串小的串有多少个?然后改变上下界就行了. 那么我们如何求出比一个串小的

【数据结构&amp;&amp;算法系列】KMP算法介绍及实现(c++ &amp;&amp; java)

KMP算法如果理解原理的话,其实很简单. KMP算法简介 这里根据自己的理解简单介绍下. KMP算法的名称由三位发明者(Knuth.Morris.Pratt)的首字母组成,又称字符串查找算法. 个人觉得可以理解为最小回溯算法,即匹配失效的时候,尽量少回溯,从而缩短时间复杂度. KMP算法有两个关键的地方,1)求解next数组,2)利用next数组进行最小回溯. 1)求解next数组 next数组的取值只与模式串有关,next数组用于失配时回溯使用. 在简单版本的KMP算法中,每个位置 j 的 n

利用后缀数组构造后缀树

由于蒟蒻azui前段时间忙着准备省选,并在省选中闷声滚大粗,博客停更了好久.. 省选过后整个人各种颓,整天玩玩泥巴什么的... 前段时间学后缀数组的时候上网查相关资料,看到说后缀数组和后缀树是可以相互转化的,并且uoj上有大量通过后缀自动机建出后缀树然后dfs遍历获得后缀数组的模板,但是通过后缀数组来建后缀树的资料确实稀缺. 也许大牛们都觉得这xjbYY一下就可以写了,所以网上没找到对应的代码,那么我来补个坑吧.大牛勿喷.. 先谈谈我的理解吧.. 讲道理后缀数组和后缀树应该是完全等价的,但前两者

GTK+ and Glade3--中文系列-Part3

http://blog.csdn.net/xbwee/article/details/4034314 原文链接: Micah Carrick www.micahcarrick.com/01-01-2008/gtk -glade-tutorial -part-3.html Part 3 Writing a Basic Program to Implement the Glade File 在这一部分, 我将示范一个非常简单的程序, 用来解析我们在 part1 中用 Glade3 创建的 GUI 文

数据结构之图 Part3 – 2 遍历

BFS using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace LH.GraphConsole { class Program { private static bool[] visited; private static Queue<int> rootVertexQueue = new Queue<int>(); static void Main