后缀数组

后缀数组

為{幸葍}努か 提交于 2020-03-26 02:19:45
前一段时间看了后缀数组,也是对着大佬们的博客研究了半天,有几个点理解了好久才明白(这就是弱者的世界吗_(:з」∠)_,再加上后来敲题时也遇到了一些坑,正好今天有时间所以写个博客来记录一下吧,方便日后写bug时查阅 本文算是自己的一篇简略的小笔记吧,如果想正经学习的话建议去看大佬们的博客哦,附上一个写的很好的链接: https://blog.csdn.net/yxuanwkeith/article/details/50636898 后缀数组求出来两个数组 sa[i] 和 rank[i] , sa[i] 表示按字典序排序排名第i的后缀的起始位置下标, rank[i] 表示以第i位为起始位置的后缀的排名,两者互逆,还有一个神奇的数组 height[i] 它表示的是排名第i的后缀和排名第i-1的后缀的LCP,有了这三个数组就可以解决大部分问题了 求法的话就是利用倍增(还有一个更优秀的DC3算法,但是好像很复杂的样子就没有看_(:з」∠)_ 首先要注意的一点就是该算法涉及的数组最好都以下标为1作为数组的第一位也就是起始位, (鬼知道为什么我敲第一道题的时候以0为起始位置怎么改怎么wa,第二天以1为起始位置重新敲了一遍就莫名其妙的a了... 实现原理概述:用 str 来表示整个字符串,我们先处理每个长度为1的子串 str[i] 对应的 sa 、 rank 的信息,然后利用 str[i] 和

URAL 1297 Palindrome 后缀数组+RMQ

血红的双手。 提交于 2020-03-26 01:43:48
本题是利用后缀数组求最长的回文串。 方法是将字符串反转之后拼接到原来的字符串末尾,中间用一个没有出现过的分割符隔开,原因是防止最长公共前缀横跨两个串。 之后分别枚举回文串的中点,以及回文串长度是奇数还是偶数,看一下对应位置的最长公共前缀即可。 这里的求最长公共前缀要处理RMQ问题,线段树固然可以解决,但是显然ST 算法更加快一些。 #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <stack> #include <map> #include <set> #include <climits> #include <string> using namespace std; #define MP make_pair #define PB push_back typedef long long LL; typedef unsigned long long ULL; typedef vector<int> VI; typedef pair<int, int> PII; typedef pair<double, double> PDD; const int INF = INT_MAX / 3; const double eps = 1e-8; const LL LINF =

URAL 1297:后缀数组求最长回文串

若如初见. 提交于 2020-03-26 01:40:49
求最长回文串 策略是枚举中心位置往两边拓展,这里要分长度为奇数、偶数的情况 n^2的复杂度无法接受,枚举中心后,这里用后缀数组来快速求回文长度 首先在字符串末尾插入一个未出现过的字符,目的是避免字符串翻转后拼接到末尾时的首部和原字符串末尾连接成新的后缀 比如说abac->abaccaba,这里的acca后缀是不存在的,所以应该变成abac#caba,阻隔开来 然后就是枚举中间点 首先说长度为奇数的情况 因为回文串至少长度为1,我们从下标1开始枚举至n-1 例如 0 1 2 3 4 5 6 7 8    a b a c # c a b a   枚举到1时,我用2号位置起始的后缀和8号位置的后缀求最长公共前缀。因为8号位置的字符相当于是原串中的0号位置,相当于中心对称。以此类推 再来说偶数的情况 从0开始枚举至n-1。看看下面的例子理解一下为何从0开始 例如 0 1 2 3 4 5 6 7 8 9 10    a b b a c # c a b b a   枚举到1时,我用2号位置的后缀和9号位置的后缀求最长公共前缀,9、10号对应原串1、0号,相当于轴对称。以此类推 不断维护长度最大值和起始位置,最后输出就行了 #include"cstdio" #include"queue" #include"cmath" #include"stack" #include"iostream"

字典树(前缀树/后缀树)

你离开我真会死。 提交于 2020-03-22 18:15:14
用途 有人说是为了统计字频,可我觉得 HashMap 就可以完成。有人说比 HashMap 占用内存要小,但我感觉小也小不到哪里去。 有人说为了查询字符,还是那句话,HashSet 表示我也可以。 也许在 Hash 没有出来前,它也许在这些领域占有一席之地。目前,从数据结构来看,我认为它的作用也许在以下方面比较突出: 也被称为 前缀树,就是剔除相同的前缀操作,这里看不懂很正常,后面慢慢说 搜索提示,讲了下面结构你就理解了 结构 这就是字典树,我们沿着最左边走一遭,那就是 JOHNNY。 先来说下之前提到的第一个用途,前缀树。比如有两个字符串, JOHNNY 和 JOHN。可以发现两者拥有前缀,JOHN,这有什么作用呢?数据压缩的时候也许会用到,可以看这题。 820. 单词的压缩编码 接下来,说说第二个功能。比如你在搜索框中输入了 JO,那我根据字典树,我猜测你要找的也许是 JOHNNY 或者 JOE,然后我会给你提示。 定义数据结构 讲一个数据结构,主要就讲,如何去定义一个数据结构,然后涉及相关的操作,这里就是添加(搭建)、删除操作。 因为这也是一个树的结构,只不过这里不一定是二叉树,上面的图有点误导,我们如果要表示英文字符的话,那么从根节点出发,应该有26个字符可供选择。 class NodeTree{ // 用数组编号代替字符,会减小搜索查询时间复杂度 NodeTree[]

字符串匹配算法-KMP

耗尽温柔 提交于 2020-03-09 04:50:15
文章目录 字符串匹配问题 KMP算法 简介 前缀/后缀/部分匹配表 甲的疑问1:k = next[k-1]是什么鬼? 结论 得到部分匹配表后匹配过程 算法特点 字符串匹配问题 引用知乎用户 灵茶山艾府 的举例,假设我们有两个角色,甲和乙 甲 : abbaabbaaba 乙 : abbaaba 一天清晨,乙对甲说,你心里到底有没有我,告诉一下我在你心中的位置。甲心中一紧,从头开始一一与乙的字符进行比较。 但是,前面的六位都能匹配,当比较到第七位时,不匹配。甲试着像往常一样暴力匹配,回退到自己的第二个字符,从乙的开头开始继续比较。这一位匹配,则继续匹配下一位,不匹配再回退到上一次开始匹配的下一个字符重头开始匹配。如下图所示: 这样没过一会儿,甲就对乙说,我找到了,放心吧,我心里一直有你呢,然后骄傲地告诉了乙在自己心中的具体位置。乙表示还不错,一是觉得心里面有它,二是甲的查询速度这么快,说明甲没有其他相好的再心里(甲心里存放的字符数目较少). 但终有一天,甲心中想的相好的越来越多,而且这些相好的很大一部分还和乙很像,这样,甲的字符串就变得越来越长。 又是一个清晨,乙又对甲说,你心里到底还有没有我。甲JH一紧,捏了一把冷汗,心想自己心里现在不是那么干净,相好的那么多,要是还像以前那么暴力搜索,不知道要找到什么时候。果然,乙又开口了,快告诉我在你心中的位置。不过,乙又说,今天天气好

算法竞赛进阶指南——后缀数组

允我心安 提交于 2020-03-09 04:47:09
后缀数组 后缀数组 (SA) 是一种重要的数据结构,通常使用倍增或者DC3算法实现,这超出了我们的讨论范围。 在本题中,我们希望使用快排、Hash与二分实现一个简单的O(nlog2n)的后缀数组求法。 详细地说,给定一个长度为 n 的字符串S(下标 0~n-1),我们可以用整数 k(0≤k<n) 表示字符串S的后缀 S(k~n-1)。 把字符串S的所有后缀按照字典序排列,排名为 i 的后缀记为 SA[i]。 额外地,我们考虑排名为 i 的后缀与排名为 i-1 的后缀,把二者的最长公共前缀的长度记为 Height[i]。 我们的任务就是求出SA与Height这两个数组。 输入格式 输入一个字符串,其长度不超过30万。 字符串由小写字母构成。 输出格式 第一行为数组SA,相邻两个整数用1个空格隔开。 第二行为数组Height,相邻两个整数用1个空格隔开,我们规定Height[1]=0。 输入样例: ponoiiipoi 输出样例: 9 4 5 6 2 8 3 1 7 0 0 1 2 1 0 0 2 1 0 2 说实话看到这道题的时候真的是一脸懵,这东西咋用hash,二分,快排。 后来找了些博客看,终于理解其中的思路了, 一、先记录整条的hash值。 二、用一个sort函数,自定义cmp。 三、通过二分确定两个后缀字符之间的前缀相同字母。 这道题写这篇题解的时候又重新去写了一遍

「kuangbin带你飞」专题十八 后缀数组

一笑奈何 提交于 2020-03-04 07:06:28
layout: post title: 「kuangbin带你飞」专题十八 后缀数组 author: "luowentaoaa" catalog: true tags: - kuangbin - 字符串 - 后缀数组 传送门 倍增法 struct DA{ bool cmp(int *r,int a,int b,int l){ return r[a]==r[b]&&r[a+l]==r[b+l]; } int t1[maxn],t2[maxn],c[maxn]; int rank[maxn],height[maxn],RMQ[maxn],mm[maxn]; int best[20][maxn]; int r[maxn]; int sa[maxn]; int str[maxn]; void da(int n,int m){ n++; int i,j,p,*x=t1,*y=t2; for(i=0;i<m;i++)c[i]=0; for(i=0;i<n;i++)c[x[i]=str[i]]++; for(i=1;i<m;i++)c[i]+=c[i-1]; for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i; for(j=1;j<=n;j<<=1){ p=0; for(i=n-j;i<n;i++)y[p++]=i; for(i=0;i<n;i++)if(sa[i]>=j)y

后缀数组小结

让人想犯罪 __ 提交于 2020-03-04 07:05:54
后缀数组果然是个神奇的东西…看起来非常巧妙的样子。话说我花了很长时间终于把罗穗骞大神论文里的代码弄懂了。。。 论文在这里: http://wenku.baidu.com/view/ed1be61e10a6f524ccbf85fd.html (OrzOrzOrzOrz) 关于后缀数组的一些小应用: POJ3693 给定一个字符串,求重复次数最多的连续重复子串。如果存在多个,则输出字典序最小的一个。 枚举长度 l, 然后求长度为 l 的子串最多能连续出现几次。 http://www.cnblogs.com/wangziyun/archive/2013/03/18/2966628.html POJ1743 给定一列数,求最长重复子串,且这两个子串不能重叠。 注意:这道题中的“重复”定义为整个子串加上或减去一个整数后和另一子串相同。 转化为差分序列后二分答案 k,然后把height数组分组,每组的后缀之间的height值不少于 k。判断每组中sa的最大值与最小值之差是否不小于 k。如果有一组满足,那么该答案就是满足条件的。这种分组的思想似乎十分常用。 View Code 1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 using namespace std; 5 #define maxn 20010 6

五分钟搞懂后缀数组!

≡放荡痞女 提交于 2020-03-04 07:04:20
为什么学后缀数组 后缀数组是一个比较强大的处理字符串的算法,是有关字符串的基础算法,所以必须掌握。 学会后缀自动机(SAM)就不用学后缀数组(SA)了?不,虽然SAM看起来更为强大和全面,但是有些SAM解决不了的问题能被SA解决,只掌握SAM是远远不够的。 …… 有什么SAM做不了的例子? 比如果求一个串后缀的lcp方面的应用,这是SA可以很方便的用rmq来维护,但是SAM还要求lca,比较麻烦,还有就是字符集比较大的时候SA也有优势。 现在这里放道题,看完这个blog可能就会做了!: 你可想想这道题:你有一个01串S,然后定义一个前缀最右边的位置就是这个前缀的结束位置。现在有q多个询问,每个询问结束位置在l~r中不同前缀的最长公共后缀是多长? | S | , q ≤ 100000 |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是互逆运算) 好,来形象的理解一下

后缀数组学习笔记

若如初见. 提交于 2020-03-03 19:03:23
后缀数组,即SA,是一种简单实用的字符串算法。当我们要处理一些关于子串的问题时,我们发现字符串中任意的一个子串都可以表示成该字符串 一个后缀的前缀 ,因此我们只需要维护所有后缀的信息即可。基于这个思想,人们发明了后缀数组。 把所有的后缀按字典序排序 这是后缀数组算法的第一个任务。 我们定义: \(\text{suffix}(i)\) :以位置 \(i\) 开头的后缀; \(sa[i]\) :排名为 \(i\) 的后缀的开头位置; \(rk[i]\) :以位置 \(i\) 开头的后缀的排名。 当我们在对所有后缀按字典序排序时,我们实际上以每个后缀的第一个字母为第一关键字,第二个字母为第二关键字......对所有后缀进行比较。 因此我们先把所有的后缀按其第一个字母排序,当然,这其中肯定有首字母相同的情况,对于这种情况,我们暂时以后缀的开头位置为第二关键字。这样我们就得到了一个初步的排名。 接下来该处理每个后缀的第二个字母了,我们发现 \(\text{suffix}(i)\) 的第二个字母就是 \(\text{suffix}(i+1)\) 的第一个字母。即: \(\text{suffix}(i)\) 的第二关键字就是 \(\text{suffix}(i+1)\) 在上一轮排序中的第一关键字。于是我们在这一轮排序中把 \(\text{suffix}(i)\) 和 \(\text