五分钟搞懂后缀数组!后缀数组解析以及应用(附详解代码) |
您所在的位置:网站首页 › 前缀树和后缀树的区别 › 五分钟搞懂后缀数组!后缀数组解析以及应用(附详解代码) |
为什么学后缀数组
后缀数组是一个比较强大的处理字符串的算法,是有关字符串的基础算法,所以必须掌握。 学会后缀自动机(SAM)就不用学后缀数组(SA)了?不,虽然SAM看起来更为强大和全面,但是有些SAM解决不了的问题能被SA解决,只掌握SAM是远远不够的。 …… 有什么SAM做不了的例子? 比如果求一个串后缀的lcp方面的应用,这是SA可以很方便的用rmq来维护,但是SAM还要求lca,比较麻烦,还有就是字符集比较大的时候SA也有优势。 现在这里放道题,看完这个blog可能就会做了!: 你可想想这道题:你有一个01串S,然后定义一个前缀最右边的位置就是这个前缀的结束位置。现在有q多个询问,每个询问结束位置在l~r中不同前缀的最长公共后缀是多长? |S|,q≤100000 时限4s 而下面是我对后缀数组的一些理解 构造后缀数组——SA 先定义一些变量的含义Str :需要处理的字符串(长度为Len) Suffix[i] :Str下标为i ~ Len的连续子串(即后缀) Rank[i] : Suffix[i]在所有后缀中的排名 SA[i] : 满足Suffix[SA[1]] < Suffix[SA[2]] …… < Suffix[SA[Len]],即排名为i的后缀为Suffix[SA[i]] (与Rank是互逆运算) 好,来形象的理解一下 倍增算法的主要思想 :对于一个后缀Suffix[i],如果想直接得到Rank比较困难,但是我们可以对每个字符开始的长度为
2k
的字符串求出排名,k从0开始每次递增1(每递增1就成为一轮),当
2k
大于Len时,所得到的序列就是Rank,而SA也就知道了。O(logLen)枚举k 这样做有什么好处呢? 设每一轮得到的序列为rank(注意r是小写,最终后缀排名Rank是大写)。有一个很美妙的性质就出现了!第k轮的rank可由第k - 1轮的rank快速得来! 为什么呢?为了方便描述,设SubStr(i, len)为从第i个字符开始,长度为len的字符串我们可以把第k轮SubStr(i,
2k
)看成是一个由SubStr(i,
2k−1
)和SubStr(i +
2k−1
,
2k−1
)拼起来的东西。类似rmq算法,这两个长度而
2k−1
的字符串是上一轮遇到过的!当然上一轮的rank也知道!那么吧每个这一轮的字符串都转化为这种形式,并且大家都知道字符串的比较是从左往右,左边和右边的大小我们可以用上一轮的rank表示,那么……这不就是一些两位数(也可以视为第一关键字和第二关键字)比较大小吗!再把这些两位数重新排名就是这一轮的rank。 我们用下面这张经典的图理解一下: Heigth[i] : 表示Suffix[SA[i]]和Suffix[SA[i - 1]]的最长公共前缀,也就是排名相邻的两个后缀的最长公共前缀 H[i] : 等于Height[Rank[i]],也就是后缀Suffix[i]和它前一名的后缀的最长公共前缀 而两个排名不相邻的最长公共前缀定义为排名在它们之间的Height的最小值。 跟上面一样,先形像的理解一下: 如果一个一个数按SA中的顺序比较的话复杂度是O( N2 )级别的,想要快速的得到Height就需要用到一个关于H数组的性质。 H[i] ≥ H[i - 1] - 1! 如果上面这个性质是对的,那我们可以按照H[1]、H[2]……H[Len]的顺序进行计算,那么复杂度就降为O(N)了! 让我们尝试一下证明这个性质 : 设Suffix[k]是排在Suffix[i - 1]前一名的后缀,则它们的最长公共前缀是H[i - 1]。都去掉第一个字符,就变成Suffix[k + 1]和Suffix[i]。如果H[i - 1] = 0或1,那么H[i] ≥ 0显然成立。否则,H[i] ≥ H[i - 1] - 1(去掉了原来的第一个,其他前缀一样相等),所以Suffix[i]和在它前一名的后缀的最长公共前缀至少是H[i - 1] - 1。 仔细想想还是比较好理解的。H求出来,那Height就相应的求出来了,这样结合SA,Rank和Height我们就可以做很多关于字符串的题了! 代码——Code建议复制到自己的编程软件上看 /* Problem: JZOJ1598(询问一个字符串中有多少至少出现两次的子串) Content: SA's Code and Explanation Author : YxuanwKeith */ #include #include #include using namespace std; const int MAXN = 100005; char ch[MAXN], All[MAXN]; int SA[MAXN], rank[MAXN], Height[MAXN], tax[MAXN], tp[MAXN], a[MAXN], n, m; char str[MAXN]; //rank[i] 第i个后缀的排名; SA[i] 排名为i的后缀位置; Height[i] 排名为i的后缀与排名为(i-1)的后缀的LCP //tax[i] 计数排序辅助数组; tp[i] rank的辅助数组(计数排序中的第二关键字),与SA意义一样。 //a为原串 void RSort() { //rank第一关键字,tp第二关键字。 for (int i = 0; i |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |