后缀数组

简(kun)单(nan)到让我开(jue)心(wang)的后缀自动机全家桶(普通后缀、广义后缀、子序列)

半世苍凉 提交于 2019-11-28 15:01:58
目录 参考文献 后缀自动机是什么神仙玩意? 例题 right集合、等价类以及Parent树 定义 等价类性质 Trie?DAG? 自动机?自动鸡? 自动机的基本性质 一定存在后缀自动机吗? 后缀自动机是唯一的吗? 后缀自动机的几个性质 边的数量级 构造方法 代码 部分1 部分2 部分3 部分4 部分5 思路 代码 时间复杂度 广义后缀自动机 思路 练习 子序列自动机 参考文献 咕咕日报上的,就没有一个是差品: https://www.luogu.org/blog/Kesdiael3/hou-zhui-zi-dong-ji-yang-xie,同时,带luogu水印的图也是一律采用这个博客的,因为我太弱了,不会画图QAQ 。 时间复杂度的证明https://blog.csdn.net/qq_35649707/article/details/66473069 广义后缀数组就是在这学的: https://blog.csdn.net/litble/article/details/78997914 字符集的相关内容: https://oi.men.ci/suffix-automaton-notes/ 套B-树: https://www.cnblogs.com/hyghb/p/8445112.html stO 后缀自动机怎么能少了陈立杰大佬的论文了呢? Orz 博主是真的臭不要脸

hdu6704 K-th occurrence(后缀数组+RMQ+主席树)

て烟熏妆下的殇ゞ 提交于 2019-11-28 13:51:10
题意 给定一个长度为 \(n\) 的字符串,有 \(q\) 次询问,每次询问求字符串的 \([l, r]\) 所形成的子串第 \(k\) 次出现时的位置。 传送门 思路 \([l, r]\) 形成的子串,它是一些后缀的前缀,并且这些前缀在后缀数组中的rk是相邻的,因此,对于 \([l, r]\) 我们可以通过 rmq/lcp 求出他的区间公共前缀长度,之后二分长度大于等于 \(r-l+1\) 的左右端点,只需要求出这些端点的第 \(k\) 小就可以,对于这些端点,可以通过主席树维护 \(sa[i]\) ,之后就可以 \(O(log)\) 查询第K小了 。 Code #include <bits/stdc++.h> using namespace std; const int maxn = 1e5+10; const int maxm = maxn*20; int n; char str[maxn]; struct node { int l, r, val; }tr[maxm]; namespace HJTree { int tot = 0; void init() { tot = 0; } void build(int l, int r, int &x) { x = ++tot; tr[x].val = 0; if(l==r) return; int mid = l+r>>1;

luogu p2582(后缀数组)

别说谁变了你拦得住时间么 提交于 2019-11-28 13:46:17
传送门 题意: 给你一个字符串 \(str\) ,问出现次数为 \(k\) 的最长的子串的长度。 分析: 首先我们先将字符串 \(str\) 的所有后缀进行排序,并求出他们两两的 \(height\) 数组。 根据 \(height\) 数组的含义, \(height[i]=lcp(i,i-1)\) ,我们知道,倘若存在一个子串出现了k次,那么必定存在一个连续的区间 \([l,r],(r-l+1 \ge k-1)\) ,使得 \(lcp(l,r) !=0\) 。那么我们他们 \(lcp\) 中的最小值就是答案。因此我们发现,我们现在要求的是 \(height\) 数组中,长度至少为 \(k-1\) 的最小值,并要使得最小值最大化。而这个显然是一个经典的划窗问题,我们可以通过单调队列在 \(\mathcal{O}(n)\) 的时间复杂度中求出答案。 故整体的复杂度为 \(\mathcal{O}(nlogn)\) 代码: #include <bits/stdc++.h> #define maxn 20010 using namespace std; int rk[maxn],sa[maxn],height[maxn],tmp[maxn],cnt[maxn],n,k; int str[maxn],tot=0; unordered_map<int,int>mp; unordered_map

poj3415_Common Substrings

给你一囗甜甜゛ 提交于 2019-11-28 13:45:02
题意 给定两个字符串,求长度大于等于k的公共子串数。 分析 将两个字符串中间加个特殊字符拼接,跑后缀数组。 将题目转化为对每一个后缀求 \(\sum_{j=1}^{i-1}lcp(i,j)\) ,且后缀 \(i\) 和 \(j\) 属于不同字符串。 由于 \(lcp\) 只跟 \(h\) 数组的区间最小值有关,因此对于单调递减的 \(h[i]\) 我们可以合并贡献和个数,维护一个单调栈。 分别统计 \(a\) 串对 \(b\) 的贡献和 \(b\) 串对 \(a\) 的贡献。 代码 #include <cstdio> #include <algorithm> #include <cstring> using namespace std; typedef long long ll; const int N=3e5+50; char a[N],b[N],s[N]; int sa[N],rk[N],h[N]; int t[N],t2[N],c[N]; int al,bl,n,k; void build(int n,int m){ n++; int *x=t,*y=t2; for(int i=0;i<m;i++){ c[i]=0; } for(int i=0;i<n;i++){ c[x[i]=s[i]]++; } for(int i=1;i<m;i++){ c[i]+=c[i-1]; }

HDU-6704 K-th occurrence(后缀数组+RMQ+主席树)

北战南征 提交于 2019-11-28 13:20:19
http://acm.hdu.edu.cn/showproblem.php?pid=6704 Time Limit: 3000/3000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others) Total Submission(s): 829 Accepted Submission(s): 259 Problem Description You are given a string S consisting of only lowercase english letters and some queries. For each query ( l , r , k ), please output the starting position of the k-th occurence of the substring S l S l +1... S r in S. Input The first line contains an integer T (1≤ T ≤20), denoting the number of test cases. The first line of each test case contains two integer N (1≤ N ≤10^5), Q (1≤ Q ≤10^5),

洛谷 P3809 【模板】后缀排序

情到浓时终转凉″ 提交于 2019-11-28 11:16:32
题意 后缀数组板子题 传送门 Code #include <bits/stdc++.h> using namespace std; const int maxn = 1e6+10; int n; char str[maxn]; struct SuffixArray { int x[maxn], y[maxn], c[maxn]; int sa[maxn], rk[maxn], height[maxn]; void SA() { int m = 127; for (int i=0; i<=m; ++i) c[i] = 0; for (int i=1; i<=n; ++i) ++c[x[i]=str[i]]; for (int i=1; i<=m; ++i) c[i] += c[i-1]; for (int i=n; i; --i) sa[c[x[i]]--] = i; for (int p, k=1; k<=n; k<<=1) { p = 0; for (int i=n; i>n-k; --i) y[++p] = i; for (int i=1; i<=n; ++i) { if(sa[i] > k) y[++p] = sa[i] - k; } for (int i=0; i<=m; ++i) c[i] = 0; for (int i=1; i<=n; ++i) ++c[x[y[i]]

后缀数组学习

不羁岁月 提交于 2019-11-28 11:02:28
后缀数组一个优化是利用$hash$, 直接$O(nlog^2n)$按照定义模拟 #include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #define REP(i,a,n) for(int i=a;i<=n;++i) #define PER(i,a,n) for(int i=n;i>=a;--i) using namespace std; typedef long long ll; const int P = 1e9+7, INF = 0x3f3f3f3f; const int N = 1e6+10; char s[N]; int n, fac[N], f[N]; int Hash(int l, int r) { int ret = (f[r]-(ll)f[l-1]*fac[r-l+1])%P; if (ret<0) ret+=P; return ret; } struct _ { int l,r,id; } a[N]; int cmp(_ a, _ b) { int l=1,r=min(a.r-a.l+1,b.r-b.l+1),ret=0; while (l<=r) { int mid=(l+r)/2; if (Hash(a.l,a.l+mid-1)==Hash(b.l,b.l

HDU-6704 K-th occurrence(后缀数组+主席树)

*爱你&永不变心* 提交于 2019-11-28 08:53:12
题意 给一个长度为n的字符串,Q次询问,每次询问 \((l,r,k)\) , 回答子串 \(s_ls_{l+1}\cdots s_r\) 第 \(k\) 次出现的位置,若不存在输出-1。 \(n\le 1e5,Q\le 1e5\) 分析 查询子串第 k 次出现的位置,很容易想到要用处理字符串的有力工具——后缀数组。 那么该怎么用呢?我们先把样例的字符串的每个后缀排个序,然后对样例进行模拟 原串:aaabaabaaaab 排名 后缀 位置 1 aaaab 8 2 aaab 9 3 aaabaabaaab 1 4 aab 10 5 aabaaaab 5 6 aabaabaaab 2 7 ab 11 8 abaaaab 6 9 abaabaaaab 3 10 b 12 11 baaaab 7 12 baabaaaab 4 查询:[3,3], k = 4 [3,3]表示子串为 \(a\) ,我们可以找到起始位置为 3 的后缀 \(t = abaabaaab\) ,该后缀的第一个字符代表了当前要查询的子串,惊奇的发现,该子串又同时出现在了其他的一些后缀中,而这些后缀与 \(t\) 的LCP(最长公共前缀)大于等于 1 。在这个例子中我们可以发现排名在9之前的后缀与 t 的LCP都大于1,所以只需要在这些后缀的开始位置中找第 k 大的即可。也就是在 [8,9,1,10,5,2,11,6,3]

SuffixArray

三世轮回 提交于 2019-11-28 06:05:41
SuffixSort 后缀数组前置知识 后缀排序 \((SuffixSort)\) ,是后缀数组的核心部分,但我不知道这玩意儿到底是怎么想出来的......(毕竟我不是神仙).然后,这个东西 并不是很难理解,我认为难的地方其实是在 \(height_i\) 的理解.所以.... \(height\) 我到现在也没理解. 先说一说后缀排序. 后缀排序是对后缀进行排序,这有啥用呢?如果没有 \(height\) ,后缀数组就失去了它的魅力.但单纯的后缀排序也并非没有用.哎?不对,好像确实没啥用...但是这不影响我们学它. 后缀排序是对一个串的所有后缀进行排序,排序依据就是字典序. 一个显然的想法是把所有后缀都拿出来排一遍序,但显然这亚子太慢了,复杂度高达 \(O(nl \log_2{n})\) 最好也只能是 \(O(nl)\) .因为字符串的比较是 \(O(L)\) 的.那么考虑怎样去优化这个排序. 我们可以发现,每次字符串的比较,如果我们知道这两个字符串相对应的两半的大小关系,那么我们可以直接得到这两个字符串的大小关系(类比线段的两个关键字的排序).这样是可以做到 \(O(1)\) 判断大小的. 那么我们这里就可以比较自然的想到使用倍增算法.我们先对所有的单个字符进行排序,这是 \(O(n \log_2{n})\) 的,这个复杂度可以接受,然后我们再对长度为 \(2\) 的串进行排序

后缀数组(理解)

半世苍凉 提交于 2019-11-28 01:24:35
一、基本概念    后缀: 用 s u f f [ i ] 表示, 是指从某个 位置 i 开始 到整个串 末尾 结束的一个 子串 。    后缀数组: 用 s a [ i ] 表示,是指 所有后缀在排完序后,排名为 i 的后缀在原串中的位置。 sa[排名]=位置    名次数组: 用 r a n k [ i ] 表示,是指 所有后缀在排序完后,原字符串中第 i个后缀 现在的排名。 rank[位置]=排名       比如字符串aabaaaab$,(我们习惯在字符串后面加一个特殊字符$,表示字符串的结尾)他的所有后缀、位置、排名如下:   后缀           位置      排名   suff[ 1 ] : aabaaaab$    sa[ 4 ] = 1    rank[ 1 ]=4   suff[ 2 ] : abaaaab$     sa[ 6 ] = 2    rank[ 2 ]=6   suff[ 3 ] : baaaab$     sa[ 8 ] = 3     rank[ 3 ]=8   suff[ 4 ] : aaaab$      sa[ 1 ] = 4     rank[ 4 ]=1   suff[ 5 ] : aaab$      sa[ 2 ] = 5     rank[ 5 ]=2   suff[ 6 ] : aab$      sa[ 3 ] = 6