后缀数组

【字符串】后缀数组SA

荒凉一梦 提交于 2019-11-28 00:49:51
后缀数组 概念 实际上就是将一个字符串的所有后缀按照字典序排序 得到了两个数组 \(sa[i]\) 和 \(rk[i]\) ,其中 \(sa[i]\) 表示排名为 i 的后缀, \(rk[i]\) 表示后缀 i 的排名 注意到 \(rk\) 和 \(sa\) 是互逆的,即 \(sa[rk[i]]=rk[sa[i]]=i\) 先讨论几个关于 \(lcp\) 的性质,令 \(lcp(i,j)\) 表示 \(sa[i]\) 和 \(sa[j]\) 的最长公共前缀 \(lcp(l,r)=min(lcp(l,i),lcp(i,r)),l\le i\le r\) ,注意这里只需要枚举任意一个 i 即可 证明: 令 \(p=min(lcp(l,i),lcp(i,r)),sa[l]=u,sa[r]=v,sa[i]=w\) 由于 \(u\) 和 \(w\) 的前 \(p\) 位相同, \(v\) 和 \(w\) 的前 \(p\) 位相同 所以 \(u\) 和 \(v\) 的 \(lcp\) 至少为 \(p\) 假设 \(u\) 和 \(v\) 的 \(lcp>p\) ,不妨设其为 \(q=p+k\) 我们知道 \(u[q]\neq w[q],v[q] \neq w[q]\) ,并且 \(w[q]\ge u[q],v[q]\ge w[q]\) 那么 \(w[q]\) 只能是 \(>u[q]\) ,且

『后缀自动机和后缀树』

那年仲夏 提交于 2019-11-27 21:32:51
前言 本博客仅记录个人对后缀自动机的一些理解,没有入门详细推导等内容。 可以参考这两篇博客: 后缀自动机 (SAM) 学习笔记 后缀自动机 (SAM) 理解 后缀自动机到底记录了什么?由于一个字符串的任意一个子串可以表达为某个前缀的后缀这样的形式,所以后缀自动机其实以一种高度压缩的形式保存了字符串所有子串的信息。 一点性质,后缀自动机最多只有 \(2*n-1\) 个状态, \(3n-4\) 条转移,这可能在开数组大小,或者多组测试数据清空的时候要注意到。 回顾一些知识: \(1.\) \(SAM\) 的状态和转移构成了一张有向无环图,称为 \(DAWG\) 。 \(2.\) \(SAM\) 的状态和后缀链接构成了一棵树,称为 \(parent\) 树。 \(3.\) 后缀自动机有唯一一个起始节点 \(s\) ,代表空串,起始节点 \(s\) 到任意一个终止状态路径上的所有转移恰好可以表示原串的一个后缀,原串的每一个后缀同样都可以被表示。 \(4.\) 后缀自动机每一个状态本质上代表了一个 \(endpos\) 等价类,又称为 \(right\) 集合。 \(5.\) 引理: 两个非空子串 \(|u|\) 和 \(|w|\) (假设 \(|u|<|w|\) )的 \(endpos\) 相同,当且仅当字符串 \(u\) 是 \(w\) 的后缀。 \(6.\) 引理: 两个非空子串 \(

ZROI 19.07.30 简单字符串/ll

巧了我就是萌 提交于 2019-11-27 20:30:25
写在前面:今天下午药丸……不会字符串,全程掉线/ll 给出字符串 \(S\) , \(q\) 次询问,每次给出 \(a,b,c,d\) ,询问 \(S[a,b]\) 的所有子串和 \(S[c,d]\) 最长公共前缀的最大值。 \(|S|,q \leq 10^5\) 。 取反建个SAM,每次二分答案。如果存在,合法串的右端点一定在 \([a+len-1,b]\) ,建个主席树维护一下这些后缀在不在对应串的子树里就可以。 有一个字符串 \(S\) ,初始为空。 \(m\) 次操作,每次操作在第 \(x_i\) 次操作之后的字符串后面加上一个不超过 \(k\) 的字符 \(c_i\) ,并求出新串最短的循环节,强制在线。 \(m, k \leq 10^5\) 。 Sol1: 发现我们要维护的就是kmp的 \(next\) 数组,然而kmp复杂度是均摊的,如果可持久化的话,对某个多次跳 \(next\) 的位置多次询问就会gg。 我们可以考虑给它严格化一下。 设 \(trans_{x,c}\) 表示从 \(x\) 后面添加一个字符 \(c\) ,会转移到的位置。 考虑 \(trans_{x,c}\) 和 \(trans_{next_{x},c}\) 有什么不同。 如果 \(next_{x}\) 后面的字符不是 \(c\) ,显然它们相等。否则 \(trans_{x,c}=next_x+1\

trie上构建后缀数组

穿精又带淫゛_ 提交于 2019-11-27 18:07:46
例题: 往事太多,有时候忘了就忘了吧。 如果有非记不可的,就只能用点附加手段啦! 我们定义一棵往事树是一个 n 个点 n-1 条边的有向无环图,点编号为 1到 n,其中 1 号点被称为是根结点,除根结点以外, 每个点都恰有一条出边(即以其作为起点的边)。 每条边上有 1 个字符(这里我们实际上用一个不大于 300的非负整数代表不同的字符), 对于任意一个点 u,u 的所有入边(即以其为终点的边)上的字符互不相同。 接下来我们定义往事,点 u 对应的往事为 u 到根的路径(显然有且只有一条)上的所有边按照从 u 到根经过的顺序排序后边上的字符依次拼接形成的字符串,简记为 r(u)。 一棵往事树的联系度取决于它包含的所有往事之中最相近的一对的相似度。具体的,我们定义 2 个点 u 和点 v 对应的往事的相似度 f(u,v)如下。 \(f(u,v)=Lcp(r(u),r(v))+Lcs(r(u),r(v))\) 其中 Lcp(a,b)表示字符串 a 和 b 的最长公共前缀的长度, Lcs(a,b)表示字符串a 和 b 的最长公共后缀的长度。 定义一棵往事树的联系度为所有满足 1<=u<v<=n 的 f(u,v)的最大值。 现在,给出一棵往事树,请你给出这棵往事树的联系度。 题解: 首先,将所有r(u)排序,这样,lcp就是相邻的r(u)的lcp的RMQ。(原理同后缀数组) 所以

关于的字符串的总结:

和自甴很熟 提交于 2019-11-27 16:13:14
字符串: 群: ​ 群是一种只有一个运算的,简单的线性结构,可用来建立许多其他代数系统的一种基本结构. ​ 设G是一个非空集合,a,b,c为它的任意元素.如果对G所定义的一种代数运算"."满足: 封闭性:a.b属于G 结合律:(ab)c=a(bc) 对于G中的任意元素a,b在G中存在唯一一个元素x,y,使得ax=b,ya=b,则称G对于所定义的运算构成一个群. 满足交换律是交换群 子群: ​ 设H是群<G,.>的非空子集,则H是G的子群当且仅当H满足以下条件: 对任意的a,b属于H,a.b也是属于H,a^(-1)属于H; 对任意的a,b属于H,a.b^(-1)属于H; 任何群<G,.>有两个平凡子群:G和e,e是G的幺元. 1.字符串的实现: 1.1基本实现问题和技术: ​ 字符串是字符的线性序列,可以采用线性表的各种实现技术实现,用顺序表或者链接表的形式表示.不论如何表示,实现的基础都是基于顺序存储或链接存储. ​ 考虑字符串的表示时,有两个重要的方面必须确定: 字符串内容的存储(两个极端的存储方式) 1.把一个字符串的全部内容存储在一块连续存储区里 2.把串中的每个字符单独存入一个独立存储块,并将这些块连接在一起. 弊端: 连续存储需要很大的连续空间,极长的字符串可能会带来问题,而一个字符一块存储,需要附带链接域,增大存储开销.实际中可以采用某种中间方式

算法4第6章后缀数组讲解

↘锁芯ラ 提交于 2019-11-27 15:59:07
最长重复子字符串/后缀数组 给定字符串怎样找到它的最长重复子字符串,如字符串atobeornottobe,最长重复子字符串是tobe 使用一般的方法速度很慢,时间复杂度是O(N^2),而使用后缀数组可以巧妙而高效的解决此问题。 首先找出字符的所有后缀字符串组成后缀字符串数组,对数组进行排序,然后遍历数组,最长重复子字符串就在相邻的两个后缀字符串中 如字符串tobeornottobe,后缀字符串是atobeornottobe,tobeornottobe,obeornottobe,beornottobe,eornottobe,ornottobe,rnottobe,nottobe,ottobe,ttobe,tobe,obe,be,e 排序后就是 atobeornottobe be beornottobe e eornottobe nottobe obeornottobe ornottobe ottobe rnottobe ttobe tobe tobeornottobe 该算法在一般情况下时间复杂度是O(NlogN),代码如下 public class LongestRepeatedSubstring { // Do not instantiate. private LongestRepeatedSubstring() { } /** * Returns the longest

POJ - 2406 Power Strings (后缀数组 最大重复次数)

回眸只為那壹抹淺笑 提交于 2019-11-27 15:39:55
Power Strings Given two strings a and b we define a*b to be their concatenation. For example, if a = "abc" and b = "def" then a*b = "abcdef". If we think of concatenation as multiplication, exponentiation by a non-negative integer is defined in the normal way: a^0 = "" (the empty string) and a^(n+1) = a*(a^n). Input Each test case is a line of input representing s, a string of printable characters. The length of s will be at least 1 and will not exceed 1 million characters. A line containing a period follows the last test case. Output For each s you should print the largest n such that s = a^n

[数据结构]后缀自动机

偶尔善良 提交于 2019-11-27 12:33:44
前言 对于字符串 \(s\) , \(|s|\) 表示s的长度 对于字符集 \(A\) , \(|A|\) 表示 \(A\) 的大小 本文字符串下标一律从0开始 。 本文字数较多,如有错别字或者概念性错误,请联系博主或在下方回复。 SAM 后缀自动机 (suffix automaton, SAM) 是一种解决多种字符串问题的数据结构。 SAM基于一个字符串构建的,是给定字符串的所有子串的压缩形式。 标准定义为: 字符串 \(s\) 的SAM是一个接受 \(s\) 的所有后缀的最小 \(\texttt{DFA}\) (确定性有限自动机或确定性有限状态自动机) 构造SAM 我们记 \(t_0\) 为字符串的一个虚拟源点,事实上这种操作(构造虚拟节点)应用非常广泛。 那么SAM应当是: 有向无环图,节点为状态,边叫做转移 所有节点都可以由 \(t_0\) 到达 每个转移代表一个字母,且任意一个状态的出边的字母不同 存在一个或多个 终止状态 ,使得从 $t_0 $ 到终止状态上的所有转移依访问顺序排列,对应原字符串 \(s\) 的某个后缀,且 \(s\) 的任何后缀均可以上述方式描述。 SAM是满足以上的条件节点数最小的自动机 简单来说,没有后缀链接的SAM是一棵以 \(t_0\) 为源点的无向图。这个图的名字叫做后缀链接树。 没有后缀链接的SAM的样子 目前您暂且不需要知道后缀链接是什么

快速字符串匹配一: 看毛片算法(KMP)

喜欢而已 提交于 2019-11-27 02:57:47
前言 由于需要做一个快速匹配敏感关键词的服务,为了提供一个高效,准确,低能耗的关键词匹配服务,我进行了漫长的探索。这里把过程记录成系列博客,供大家参考。 在一开始,接收到快速敏感词匹配时,我就想到了 KMP 翻译过来叫“看毛片“的算法,因为大学的时候就学过它。听说到它的效率非常高。把原本字符串匹配效率 O(n*m) 缩短到了O(n+m),把✖️变成了➕,真是了不得。 每次我回顾 KMP 算法时,都会发现自己是个小白,或者每次回顾时,都发现上次因为回顾而写的总结居然是错的!所以为了学习快速字符串匹配,并再次温故 KMP ,所以我决定使用 KMP 算法试一试。如果以后在面试的时候,可以将KMP 完整的写出来,那岂不是很牛逼? 孔子说过的“温故而知新” 真的是很有道理的,经过这次回顾,我觉得是时候为此写一篇全新的博客了,因为这次的理解肯定是正确的! KMP 快是因为啥呢?是因为利用了 字符串 公共前后缀的特性,加快了匹配速度,但是转念一想,敏感关键词公共前后缀相等的情况可是很少的呀。那还有必要用KMP 吗? 当然有必要了,所谓技多不压身,了解掌握一种算法准没坏处,而且还可以比较 KMP 和 C# 中 String.Contains() 的效率,开拓自己的眼界。 KMP 以前在学习 KMP 的时候,我也看了网上很多博客,关于这个算法讲解的博客是非常多的,而且讲解的都很细致。奈何我每看过一次

最长公共子串

╄→гoц情女王★ 提交于 2019-11-27 01:21:16
题目链接: https://cn.vjudge.net/contest/318888#problem/H 题意: 求两个子串的字符串的最长公共子串 思路: "最长公共子串"解法(摘自罗穗骞的国家集训队论文): 字符串的任何一个子串都是这个字符串的某个后缀的前缀。 求A和B的最长公共子串等价于求A的后缀和B的后缀的最长公共前缀的最大值。如果枚举A 和B的所有的后缀, 那么这样做显然效率低下。 由于要计算A的后缀和B的后缀的最长公共前缀, 所以先将第二个字符串写在第一个字符串后面, 中间用一个没有出现过的字符隔开,再求这个新的字符串的后缀数组。 观察一下,看看能不能从这个新的字符串的后缀数组中找到一些规律。以A=“ aaaba”, B=“ abaa” 为例,如图所示。 那么是不是所有的height值中的最大值就是答案呢?不一定!有可能这两个后缀是在同一个字符串中的, 所以实际上只有当 suffix(sa[i-1])和suffix(sa[i])不是同一个字符串中的两个后缀时, height[i]才是满足条件的。而这其中的最大值就是答案。 记字符串A和字符串B的长度分别为|A|和|B|。 求新的字符串的后缀数组和height数组的时间是O(|A|+|B|),然后求排名相邻但原来不在同一个字符串中的两个后缀的 height 值的最大值,时间也是O(|A|+|B|)