后缀数组

hdu 4691 后缀数组+rmq 求子串lcp

匿名 (未验证) 提交于 2019-12-02 23:56:01
利用RMQ求子串lcp的基础题,这个板子是我之前的,比较蠢的一种实现 后面试试用最新的板子AC一下 #include<bits/stdc++.h> #define ll long long #define rep(ii,a,b) for(int ii=a;ii<=b;++ii) #define per(ii,a,b) for(int ii=b;ii>=a;--ii) using namespace std; const int maxn=2e5+10,maxm=2e6+10; const ll INF=0x3f3f3f3f,mod=1e9+7; int casn,n,m,k; const int csize=131; char s[maxn]; class suffix{public: int sa[maxn],rank[maxn],h[maxn]; int wa[maxn],wb[maxn],wc[maxn],wd[maxn]; char *str; void geth(int n){ int i,j,k=0; for(i=1;i<=n;++i) rank[sa[i]]=i; for(i=0;i<n;h[rank[i++]]=k) for(k?k--:0,j=sa[rank[i]-1];str[i+k]==str[j+k];++k); } void getsa(char *_s

后缀数组

匿名 (未验证) 提交于 2019-12-02 23:52:01
后缀数组 给出一个长度为n的字符串 \(\{s_i\}\) ,定义它的后缀为一个i,表示子串 \(s[i\sim n]\) ,现在请维护出 \(SA[i]\) 为存储后缀的数组,但是按照其对应的子串的字典序排序, \(Height[i]\) 为第i个后缀和第i+1个后缀对应的子串公共前缀的长度, \(n\leq 3\times 10^5\) 。 最直接的思路是对后缀数组直接排序,问题在于时间复杂度为 \(O(n^2log(n))\) ,我们可以尝试优化其中的 \(O(n)\) 的比较,因为子串的比较,维护出前缀hash值,以后,可以通过二分找到第一个前缀不相等的位置,注意不存在这个位置需要特判,然后向后比较一个,就能知道两个子串的大小了,这样比较就优化到了 \(log(n)\) 。 而至于最长公共前缀同样的方法二分,于是可以做到时间复杂度 \(nlog(n)^2\) 。 参考代码: #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define il inline #define ri register #define ll long long #define ull unsigned ll #define Size 300500 #define jzm 19260817

AC自动机&amp;后缀自动机

匿名 (未验证) 提交于 2019-12-02 23:47:01
理解的不够深 故只能以此来加深理解 。我这个人就是蠢没办法 学长讲的题全程蒙蔽。可能我字符串就是菜吧,哦不我这个人就是菜吧。 AC自动机的名字 AC 取自一个大牛 而自动机就比较有讲究了 不是寻常的东西呢。 自动机由5部分组成 1 字符集 2 状态集合 3 初始状态 4 结束状态集合 5 状态转移函数。 字符集 是指自动机字符的集合。 当然以上有点深奥,我们只需要其能识别字符串即可。 显然的是 KMP做单字符串对单字符串的匹配使用 而AC自动机则是多个字符串在一个字符串上的匹配。 构建trie 大家都会 但是如何求fail 指针呢 思考一下我们求的fail指针其实是指向和当前这个串能匹配的最长后缀。前缀是没有必要的因为我是利用答案串进行匹配的,因为我们得到一个最长前缀再继续这样匹配还是一样效果我们既然答案串都匹配到了现在 也就是当前局面已经不可逆了我们只能不断的跳 跳到一个合法地方让其能继续匹配下去 所以跳到一个最长后缀的位置这样如果还失配的话我们还有机会继续跳 如果跳到一个较短的后缀上的话我们把那些长的都扔了这显然是非常不好做法。 那么现在就有了基础思路构造fail指针 然后失配就不断跳 就行了。在构造fail指针的时候我们是先构造了一颗trie树在trie树搞fail指针显然最长后缀<当前匹配的位置 至于那些比当前位置还要深的位置一定不可能出现,那么就是看深度咯

[BJWC2010] 外星联络 - 后缀数组

被刻印的时光 ゝ 提交于 2019-12-02 18:09:41
[BJWC2010] 外星联络 Description 求一个 \(01\) 串中所有重复出现次数大于 \(1\) 的子串所出现的次数,按照字典序排序输出。 Solution 预处理出后缀数组和高度数组。 对于每一个后缀 \(i\) ,如果 \(h[i+1]>h[i]\) ,我们就去找到在这之后对任意 \(j \in (h[i], h[i+1]]\) ,第一次出现 \(h[k]<j\) 的 \(k\) ,那么 \(k-i\) 就是这个子串出现的次数。 很不优美的解法,但是好像又没有别的办法。还不如直接建字典树。 #include <bits/stdc++.h> using namespace std; int n,m=256,sa[1000005],y[1000005],u[1000005],v[1000005],o[1000005],r[1000005],h[1000005],T; char str[1000005]; long long ans; int main() { ios::sync_with_stdio(false); cin>>n; cin>>str+1; for(int i=1; i<=n; i++) u[str[i]]++; for(int i=1; i<=m; i++) u[i]+=u[i-1]; for(int i=n; i>=1; i--) sa[u

模板——后缀数组

守給你的承諾、 提交于 2019-12-02 13:00:02
后缀数组的详解参见此博客: https://www.cnblogs.com/victorique/p/8480093.html 这里主要理一下思路和注意点 后缀数组基本介绍: 后缀数组就是对一个字符串的$n$个后缀进行排序,但是考虑到每一个字符串都有一个长度,一位位比下来肯定炸飞,所以要优化。 先对于每一个字符进行离散化,每次按照倍增和前面的字符合并,然后合并后进行双关键字排序,再离散化,直至没有完全相同的值为止 总复杂度为$O(nlog(n))$ 代码思路:第一次先预处理离散化,再塞进桶里。后面每次都是二维桶排序,再离散化合并,更新桶,每次用一个pair数组维护双关键字排序的数组信息 (之前没用pair,全用离散化数组num,结果第二关键字就被吃了。。) 代码: #include <bits/stdc++.h> using namespace std; const int N=1020000; int sa[N],rk[N],n; char s[N]; pair<int,int> t[N]; int totb=0,tota=0; int a[N],b[N],num[N];//now:桶的个数 int pre[N],ord[N],cnt[N];//排名为i的id bool base_sort() { int bl=1; for(int i=2;i<=n;i++) if(num

temp

十年热恋 提交于 2019-12-02 11:26:29
luogu博客看不了 从百度快照把文字扒了下来 然后latex没法看 借用博客园latex,看完就删 史上最通俗的后缀自动机详解 $by\space KesdiaelKen$ 网上的解析都太巨佬了,搞得本蒟蒻看了2天才看懂…… 感觉本文会比网上大多数文章通俗易懂很多,并且会对程序作具体解析,适合初学者阅读。 本文共分四节,内容大致符合政治答题思路如下:①干什么以及基本定义②基本思想以及特殊定义③构造方法以及程序具体解析④基本应用以及类比。 虽然这篇文章是针对初学者,但是读这篇文章之前,请务必了解 $trie$ 、基本的集合知识、基本字符串知识和基本图论知识。如果需要看懂第四节的一些内容,还需要了解后缀数组。 本文只是对后缀自动机最基本的思想与构造方法进行详细解析,对于更高级的扩展层面不会多加分析,想了解此方面的同学请自行查找资料(clj的ppt等)。 本文很多的表述会在上文中解释,所以如果需要完全理解本文,请务必仔细按顺序阅读。 本文借鉴了许多其他文章的表述方法,如有雷同,请见谅。 1. 后缀自动机要干什么 如果要在一个DAG(有向无环图)上表示出一个字符串的所有子串,应该怎么办? 很显然,一个最简单的方法是建立一个trie(字典树),如图。(对于 $aabab$ 建trie,红色为根,黄色为终止节点,边的方向未画出) 方法是将原串( $n$ 为其长度,下文默认

[SDOI2016] 生成魔咒 - 后缀数组,平衡树,STL,时间倒流

时光怂恿深爱的人放手 提交于 2019-12-02 08:24:19
[SDOI2016] 生成魔咒 Description 初态串为空,每次在末尾追加一个字符,动态维护本质不同的子串数。 Solution 考虑时间倒流,并将串反转,则变为每次从开头删掉一个字符,即每次从后缀集合中删掉一个后缀。 预处理出后缀数组和高度数组后,用平衡树维护所有后缀集合(按照后缀排序),要删除一个后缀 \(S[sa[p],n]\) 时,找到它在平衡树上的前驱 \(u\) 和后继 \(v\) ,如果都存在,那么这一步的贡献就是 \[(n-sa[p]+1) - Max(h[p],h[v])\] 约定 \(h[p]\) 表示 \(S[sa[p],n]\) 与 \(S[sa[p-1],n]\) 的 LCP 长度。 如果 \(u\) 或 \(v\) 不存在,则当作 \(LCP\) 为零处理,仍然成立。 求 \(LCP\) 可以暴力用 ST 表维护。但考虑到这里每次删除操作最多只会再影响一个元素,我们可以顺便记录一下,即当我们删除 \(p\) 的时候令 \(h[v] = Min(h[p],h[v])\) 即可。 #include <bits/stdc++.h> using namespace std; #define int long long int n,m,sa[1000005],y[1000005],u[1000005],v[1000005],o[1000005],r

P3809 【模板】后缀排序

别说谁变了你拦得住时间么 提交于 2019-12-02 03:08:26
P3809 【模板】后缀排序 1 #include <bits/stdc++.h> 2 using namespace std; 3 const int maxn = 1e6+5; 4 char s[maxn]; 5 int sa[maxn], t[maxn], t2[maxn], c[maxn]; 6 int n; 7 //构造字符串s的后缀数组, 每个字符值必须为0 ~ m-1 8 void build_sa(int m) { 9 int *x = t, *y = t2; 10 //基数排序 11 for(int i = 0; i < m; i++) c[i] = 0; 12 for(int i = 0; i < n; i++) c[x[i] = s[i]]++; 13 for(int i = 1; i < m; i++) c[i] += c[i-1]; 14 for(int i = n-1; i >= 0; i--) sa[--c[x[i]]] = i; 15 for(int k = 1; k <= n; k <<= 1) { 16 int p = 0; 17 //直接利用sa数组排序第二关键字 18 for(int i = n-k; i < n; i++) y[p++] = i; 19 for(int i = 0; i < n; i++) if(sa[i] >= k) y

[HEOI2016] 字符串 - 后缀数组,主席树,ST表,二分

时光总嘲笑我的痴心妄想 提交于 2019-12-02 02:22:48
[HEOI2016] 字符串 Description 给定一个字符串 \(S\) , 有 \(m\) 个询问,每个询问给定参数 \((a,b,c,d)\) ,求 \(s[a..b]\) 的子串与 \(s[c..d]\) 的最长公共前缀长度的最大值。 Solution 读懂题意以后就很简单。把后缀数组和高度数组都搞出来,并对高度数组建立 ST 表,然后对于每个询问找到 \(s[c..d]\) 在后缀排序中的位置,二分一个 \(LCP\) 长度,检验只需要查询某一段下标区间内有没有 \(rank\) 在某个区间内的值,对 \(rank\) 数组建一个主席树即可。 我太菜了,写了一个多小时,瞎优化主席树才卡过了常。正思索着也没哪里常数太大,后来发现原来是 \(log\) 的锅…… Code #include <bits/stdc++.h> using namespace std; const int N = 100005; int fastlog[N]; namespace st { int a[N][21]; void build(int *src,int n) { for(int i=1; i<=n; i++) a[i][0]=src[i]; for(int i=1; i<=20; i++) for(int j=1; j<=n-(1<<i)+1; j++) a[j][i]=min(a

CodeForces - 113B Petr# (后缀数组)

五迷三道 提交于 2019-12-01 17:00:17
应该算是远古时期的一道题了吧,不过感觉挺经典的。 题意是给出三一个字符串s,a,b,求以a开头b结尾的本质不同的字符串数。 由于n不算大,用hash就可以搞,不过这道题是存在复杂度$O(nlogn)$的做法的。 由于要求本质不同,所以可以考虑使用后缀数组来不重复地枚举字符串。 首先用两个不同的其他字符将s,a,b拼起来求后缀数组,这样就可以知道任意两个后缀的lcp了。然后将s中所有b出现的末尾位置置1,求个后缀和suf。将s中所有后缀按名次从小到大存到一个vector里。对于s中的每个后缀,设其名次为x,a的长度为la,b的长度为lb,若$lcp(x,rnk[ia])=la$,则其对答案的贡献为$suf[sa[i]+max(lcp(x,vec[i-1]),la-1,lb-1)]$。其中la-1和lb-1是为了保证字符串长度比a和b都大,lcp(x,vec[i-1])是为了保证不重复枚举,相当于没有将s与a,b拼起来时的height[x]。特别地,当x=0时为0。 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=1e4+10; 5 char buf[N]; 6 int s[N],sa[N],buf1[N],buf2[N],c[N],n,rnk[N],ht[N]