后缀数组

poj1743(后缀数组)

一个人想着一个人 提交于 2019-12-27 04:26:32
http://poj.org/problem?id=1743 题意:给出一串字符,求不重合的最长重复子串.......... 我自己的一点想法:编完后发现,其实就是将height值分组,然后记录在二分答案时满足height值>=p的sa[i]的最大最小值,然后要是最大值减去最小值会>=p,这就说明两个子串的lcp值>=p并且它们的坐标也相差>=p,就自然满足题意......... #include<iostream> #include<stdio.h> #include<string.h> using namespace std; #define maxx 20010 int wsf[maxx],wa[maxx],wv[maxx],wb[maxx],s[maxx]; int height[maxx],rank[maxx],sa[maxx]; int cmp(int *r,int a,int b,int k) { return r[a]==r[b]&&r[a+k]==r[b+k]; } void getsa(int *r,int *sa,int n,int m) { int i,j,p,*x=wa,*y=wb,*t; for(i=0;i<m;i++) wsf[i]=0; for(i=0;i<n;i++) wsf[x[i]=r[i]]++; for(i=1;i<m;i++) wsf[i

poj3693(后缀数组)

天涯浪子 提交于 2019-12-27 04:25:02
http://poj.org/problem?id=3693 题意:给出一串字符,需要求这串字符中的最长重复子串,要是有多个,输出字典序最小的......... 我自己的一些想法:这个思路我一开始倒是没有看明白,慢慢的编下去,才懂了它到底是如何操作的......其实就是枚举多少个字符会匹配,然后求出它们的height值,再用这个值去除以长度,得到有多少个循环........具体看代码 #include<iostream> #include<stdio.h> #include<string.h> using namespace std; #define min(x,y) x>y? y:x #define maxn 100010 int dp[maxn][33]; int wa[maxn],wb[maxn],wsf[maxn],wv[maxn],sa[maxn]; int rank[maxn],height[maxn],s[maxn]; char str[maxn]; int cmp(int *r,int a,int b,int k) { return r[a]==r[b]&&r[a+k]==r[b+k]; } void getsa(int *r,int *sa,int n,int m) { int i,j,p,*x=wa,*y=wb,*t; for(i=0;i<m;i++) wsf

后缀数组

旧巷老猫 提交于 2019-12-27 04:24:18
追随蔡大神的脚步,开始后缀数组的学习。 http://www.cnblogs.com/EC-Ecstasy/ //时间不够不定时不定期完善 一、后缀数组的定义 把一个字符串的后缀全部搞出来,比如“aabaaaab”的后缀就是"aabaaaab”,“abaaaab”,“baaaab”,“aaaab”,“aaab”,“aab”,“ab”,“b”,分别编号为1,2,3,4,5,6,7,8。 然后就有两个数组,一个是rank[],一个是sa[]。rank[i]表示第i个后缀排在第几名,sa[i]表示排第i名是哪个后缀。显然这两个数组为逆运算。(sa[rank[i]]=i,rank[sa[i]]=i) 基排倍增写法。 每次倍增,分两个关键字。 模版1(远古写法) var s:ansistring; n,tot:longint; c,x,y,rank,sa:array[0..1000]of longint; procedure first; var i:longint; begin readln(s); n:=length(s); for i:=1 to n do x[i]:=ord(s[i]); fillchar(c,sizeof(c),0); for i:=1 to n do inc(c[x[i]]); for i:=1 to 128 do inc(c[i],c[i-1]); for i:

后缀数组专题与代码模板

南楼画角 提交于 2019-12-27 04:23:47
后缀数组 DA(倍增)算法求 SA[N] 与 Rank[N] (时间O(NlogN),空间O(N)) sa[i] : 表示 排在第i位的后缀 起始下标 rank[i] : 表示后缀 suffix(i)排在第几 height[i] : 表示 sa[i-1] 与 sa[i] 的LCP 值 h[i]: 表示 suffix(i)与其排名前一位的 LCP值 const int N = int(2e5)+10; int cmp(int *r,int a,int b,int l){ return (r[a]==r[b]) && (r[a+l]==r[b+l]); } // 用于比较第一关键字与第二关键字, // 比较特殊的地方是,预处理的时候,r[n]=0(小于前面出现过的字符) int wa[N],wb[N],ws[N],wv[N]; int rank[N],height[N]; void DA(int *r,int *sa,int n,int m){ //此处N比输入的N要多1,为人工添加的一个字符,用于避免CMP时越界 int i,j,p,*x=wa,*y=wb,*t; for(i=0;i<m;i++) ws[i]=0; for(i=0;i<n;i++) ws[x[i]=r[i]]++; for(i=1;i<m;i++) ws[i]+=ws[i-1]; for(i=n-1;i>=0;i--

kuangbin带你飞 后缀数组 题解

ⅰ亾dé卋堺 提交于 2019-12-27 04:23:20
2份模板 DC3 。 空间复杂度O3N 时间复杂度On #define F(x) ((x) / 3 + ((x) % 3 == 1 ? 0 : tb)) #define G(x) ((x) < tb ? (x) * 3 + 1 :((x) - tb) * 3 + 2) const int MAXN = 300010; const int MAXM = 100010; char input[MAXM]; int wa[MAXN],wb[MAXN],ws[MAXN],wv[MAXN],wsd[MAXN],r[MAXN],sa[MAXN]; char str[MAXN]; int dp[MAXM][25]; int c0(int *r,int a,int b) {return r[a] == r[b] && r[a + 1] == r[b + 1] && r[a + 2] == r[b + 2];} int c12(int k,int *r,int a,int b) {if(k == 2) return r[a] < r[b] || r[a] == r[b] && c12(1,r,a + 1,b + 1); else return r[a] < r[b] || r[a] == r[b] && wv[a + 1]< wv[b + 1];} void sort(int *r,int *a

后缀数组小结

馋奶兔 提交于 2019-12-27 04:23:03
目录 原理介绍 倍增算法 基数排序 数组含义 代码解释 height 数组的功能 例题讲解 洛谷P3809【模板】后缀排序 BZOJ : 1717: [Usaco2006 Dec]Milk Patterns 产奶的模式 BZOJ : 4566: [Haoi2016]找相同字符 前言 :Orz ShichengXiao 冬令营的时候就早解决了 字符串算法还是不能随意放弃啊 要认真学了!! 这个算法常用于解决字符串上的 \(\mathrm{LCP}\) 问题 和 一些字符串匹配的问题 这个算法思维难度不是很大 但是代码难度还是有一些的 想学好这个算法 一定要牢牢的记住各个数组的含义 不然容易弄混 原理介绍 还是先简单介绍一下原理吧 : 后缀数组就是将一个字符串的后缀全部进行排序 然后把下标存入一些数组里 用那些数组来进行字符串的一些常用操作 为了后缀排序 我们常常使用 \(O(n \log n)\) 的倍增算法 (而不用 \(O(n)\) 的 \(\mathrm{DC3}\) 因为它常数和空间大,并且十分不好写) 倍增算法 那接下来介绍一下倍增算法qwq 考虑这样一个小问题 我们比较任意两后缀的字典序大小 有没有什么快速比较的方法? 当然有 就是预处理出他们的一个前缀和后缀的大小关系 然后我们就能用另外两个来比较了。 倍增的思路大概就是如此 我们从小到大 每次长度乘二

后缀数组

故事扮演 提交于 2019-12-26 09:53:21
膜了一天,貌似有一点点感觉了?赶紧记下来,免得又忘了。。 $sa_i$是第$i$个后缀的开头在原串的位置。 $rk_i$是$S_{i...|S|}$这一段后缀的排名。 $ht_i$是排名为$i-1$和$i$的后缀的$Longest \; common \; prefix$(最长公共前缀)。 构建$sa$数组,咱膜的是这篇文章, 诱导排序与SA-IS算法 。 ${\color{Red}I}nduced{\color{Red}S}ort$,即诱导排序。 所谓诱导排序,貌似是通过一些信息,获得一些额外的速度? 咱在最后再加一个$\#$,为一个$S$中不会出现且字典序最小的字符。 后缀类型: 对于每一个后缀$suffix(i)$,如果$suffix(i)>suffix(i+1)$,则$suffix(i)$为$L$型后缀;如果$suffix(i)<suffix(i+1)$,则$suffix(i)$为$S$型后缀。特殊的,$suffix(|S|)$为$S$型后缀。 记$t_i$为后缀$suffix(i)$的类型,我们发现$t_i$是可以递推出来的。 $t_i=\begin{cases}L & \text{ if } S_i > S_{i+1} \\ S & \text{ if } S_i < S_{i+1} \\ t_{i+1} & \text{ if } S_i = S_{i+1}\end

NOI2016 优秀的拆分 后缀数组

非 Y 不嫁゛ 提交于 2019-12-23 22:52:31
题目链接 : 洛谷点我:-) UOJ点我:-) 题目描述 : 如果一个字符串可以被拆分为 AABB 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的。 例如,对于字符串 aabaabaa,如果令 A=aab,B=a,我们就找到了这个字符串拆分成 AABB的一种方式。 一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。比如我们令 A=a,B=baa,也可以用 AABB表示出上述字符串;但是,字符串 abaabaa 就没有优秀的拆分。 现在给出一个长度为 n(n <= 30000)的字符串 S,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串中连续的一段。 以下事项需要注意: 1.出现在不同位置的相同子串,我们认为是不同的子串,它们的优秀拆分均会被记入答案。 2.在一个拆分中,允许出现 A=B。例如 cccc 存在拆分 A=B=c。 3.字符串本身也是它的一个子串。 输入格式 : 每个输入文件包含多组数据。输入文件的第一行只有一个整数 T,表示数据的组数。保证 1≤T≤10。 接下来 T行,每行包含一个仅由英文小写字母构成的字符串 S,意义如题所述。 输出格式 : 输出 T行,每行包含一个整数,表示字符串 S 所有子串的所有拆分中,总共有多少个是优秀的拆分。 思路 : 求两个数组:st[i]与en[i]

SA & SAM

删除回忆录丶 提交于 2019-12-23 22:13:54
后缀数组SA \(sa[i]\) 与 \(rk[i]\) \(sa[i]\) 表示排名为 \(i\) 的后缀是哪一个(在原串中开头位置)。 \(rk[i]\) (或 \(rank[i]\) )表示开头位置是 \(i\) 的后缀的排名。 两者是互相映射关系,即 \(sa[rk[i]] = i\) 。 后缀排序(倍增) 假设我们求出了只考虑长度为 \(w\) 的每一个后缀的前缀的 \(sa\) 和 \(rk\) ,怎么求考虑长度为 \(2w\) 的每一个后缀的前缀的 \(sa\) 和 \(rk\) . 对于两个后缀 \(i\) 和 \(j\) , 由于我们求出了在 \(w\) 下的 $sa $ 和 \(rk\) ,实际上可以通过比较两个二元组 \((rk_i,rk_{i+w}),(rk_j, rk_{j+w})\) 来确定大小关系,这里定义一个后缀 \(i\) 的两维度:第一维字符串 \([i...i+w-1]\) ,第二维字符串 \([i+w,i+2w-1]\) 。 为了方便实现以及减小常数,我们开一个辅助数组 \(tmp[i]\) 表示上一轮排序(长度 \(w\) ),排名为 \(i\) 的后缀的长度为 \(w\) 的前缀对应的是现在的哪一个后缀的第二维, \(tmp\) 可以由 \(w\) 下的 \(sa\) 求得。 cnt = 0; for (int i = n - w + 1

【后缀数组】【字符串】【数据结构】后缀数组小结

倾然丶 夕夏残阳落幕 提交于 2019-12-23 18:57:07
后缀数组小结 后缀数组是啥? 简而言之,后缀数组的基础内容就是对于给定的一个字符串,将其后缀按字典序排序后所形成的一个编号序列。 例如: 对于字符串ababa: 其后缀数组为SA[]={5,3,1,4,2} ,因为a < aba < ababa <ba <baba 接下来介绍一下如何求suffix array及其衍生数组(rank,height)等。 后缀数组如何求? 最朴素的想法是直接提取所有的后缀并排序,使用快速排序的复杂度为 \(O(n\log n * T (cmp))\ =\ O (n^2 log n)\) . 考虑如何优化这一过程。 我们观察字符串的比较过程,都是从首位开始进行比较的。 考虑一种比较方式,每次利用每个后缀的第1位先比较,然后确定大致位置,再分组在组内比较第2位,这显然是一种可取的思想,如果使用基数排序是O(n)的,否则是 \(O(n \log n)\) 的。考虑后缀的特性,发现了什么? 我们每一次比较都在做与之前相同的事,通俗的解释一下就是,假设第i个后缀与第j个后缀的首字母相同,事实上我接下来就是在比较第i+1个后缀与第j+1个后缀的首字母,而这个相对顺序,在上一轮排序中,我应该已经知道了才对。利用这一想法,发现第二轮比较的事物可以结合上一轮进行,然后我们又发现了什么?第二轮比较之后,事实上我获得的是每个后缀的前2个字母的相对顺序,那么由此