后缀自动机构造后缀数组。

因为有个SB题洛谷5115,它逼迫我学习后缀数组...(边分树合并是啥?)。

一些定义:sa[i]表示字典序排第i的后缀是从哪里开始的。Rank[i]表示后缀i的排名。height[i]表示排名i和i - 1的后缀的最长公共前缀。

首先我们可以建出后缀树,然后按字典序DFS即可获得sa数组和rank数组。

接下来要求height,使用经典后缀数组的求法即可。

说一下关于后缀数组经典倍增构造方法的一些理解。关于网上流传的那个锯齿形图,其实就是把上一次的两个排名拼接起来进行排序。

height数组的构建也很神奇。按照下标求,可以发现sa[Rank[i]](它自己)和j = sa[Rank[i] - 1]和i + 1的关系:

若i和j的[1, x]这些位相同,那么i + 1和j的[2, x]这些位相同。所以height[Rank[i]]至少有x - 1。

拍过的模板......暂时没找到模板题(板子字符集居然是62...是想卡爆SAM吧)

  1 #include <bits/stdc++.h>
2
3 const int N = 200010;
4
5 int n, pw[N], ST[N][20];
6 int tr[N][26], len[N], fail[N], tot = 1, last = 1, ed[N], Lp[N];
7 int tr2[N][26], sa[N], Rank[N], height[N], num;
8 char str[N];
9
10 inline void insert(char c, int id) {
11 int f = c - 'a', p = last, np = ++tot;
12 last = np;
13 ed[np] = 1;
14 Lp[np] = id;
15 len[np] = len[p] + 1;
16 while(p && !tr[p][f]) {
17 tr[p][f] = np;
18 p = fail[p];
19 }
20 if(!p) {
21 fail[np] = 1;
22 }
23 else {
24 int Q = tr[p][f];
25 if(len[Q] == len[p] + 1) {
26 fail[np] = Q;
27 }
28 else {
29 int nQ = ++tot;
30 Lp[nQ] = Lp[Q];
31 len[nQ] = len[p] + 1;
32 fail[nQ] = fail[Q];
33 fail[Q] = fail[np] = nQ;
34 memcpy(tr[nQ], tr[Q], sizeof(tr[Q]));
35 while(tr[p][f] == Q) {
36 tr[p][f] = nQ;
37 p = fail[p];
38 }
39 }
40 }
41 return;
42 }
43
44 void DFS(int x) {
45 if(ed[x]) {
46 sa[++num] = Lp[x];
47 Rank[Lp[x]] = num;
48 }
49 for(int i = 0; i < 26; i++) {
50 if(!tr2[x][i]) continue;
51 DFS(tr2[x][i]);
52 }
53 return;
54 }
55
56 inline void getsa() {
57 for(int i = 2; i <= tot; i++) { /// build suffix tree
58 char c = str[Lp[i] + len[fail[i]]];
59 tr2[fail[i]][c - 'a'] = i;
60 }
61 DFS(1); /// DFS suffix tree to get SA and Rank
62 for(int i = 1, j, k = 0; i <= n; i++) { /// get height
63 j = sa[Rank[i] - 1];
64 if(!j) continue;
65 if(k) k--;
66 while(i + k <= n && j + k <= n && str[i + k] == str[j + k]) {
67 k++;
68 }
69 height[Rank[i]] = k;
70 }
71 return;
72 }
73
74 inline void prework() {
75 for(int i = 2; i <= n; i++) pw[i] = pw[i >> 1] + 1;
76 for(int i = 1; i <= n; i++) ST[i][0] = height[i];
77 for(int j = 1; j <= pw[n]; j++) {
78 for(int i = 1; i + (1 << j) - 1 <= n; i++) {
79 ST[i][j] = std::min(ST[i][j - 1], ST[i + (1 << (j - 1))][j - 1]);
80 }
81 }
82 return;
83 }
84
85 inline getSmall(int l, int r) {
86 if(l > r) std::swap(l, r);
87 l++;
88 int t = pw[r - l + 1];
89 return std::min(ST[l][t], ST[r - (1 << t) + 1][t]);
90 }
91
92 int main() {
93 scanf("%s", str + 1);
94 n = strlen(str + 1);
95 for(int i = n; i >= 1; i--) {
96 insert(str[i], i);
97 }
98 getsa();
99 prework();
100
101 int m;
102 scanf("%d", &m);
103 for(int i = 1; i <= m; i++) {
104 int x, y;
105 scanf("%d%d", &x, &y);
106 if(x == y) {
107 printf("%d ", n - x + 1);
108 }
109 else {
110 int t = getSmall(Rank[x], Rank[y]);
111 printf("%d ", t);
112 }
113 }
114
115 return 0;
116 }

SAM build SA求lcp

后缀数组的第X种求法的更多相关文章

  1. POJ 3261 Milk Patterns 后缀数组求 一个串种 最长可重复子串重复至少k次

    Milk Patterns   Description Farmer John has noticed that the quality of milk given by his cows varie ...

  2. 1402 后缀数组 (hash+二分)

    描述 后缀数组 (SA) 是一种重要的数据结构,通常使用倍增或者DC3算法实现,这超出了我们的讨论范围.在本题中,我们希望使用快排.Hash与二分实现一个简单的 O(n log^2⁡n ) 的后缀数组 ...

  3. CH 1402 - 后缀数组 - [字符串hash]

    题目链接:传送门 描述 后缀数组 (SA) 是一种重要的数据结构,通常使用倍增或者DC3算法实现,这超出了我们的讨论范围. 在本题中,我们希望使用快排.Hash与二分实现一个简单的 $O(n \log ...

  4. CH1402 后缀数组【Hash】【字符串】【二分】

    1402 后缀数组 0x10「基本数据结构」例题 描述 后缀数组 (SA) 是一种重要的数据结构,通常使用倍增或者DC3算法实现,这超出了我们的讨论范围.在本题中,我们希望使用快排.Hash与二分实现 ...

  5. 后缀数组:倍增法和DC3的简单理解

    一些定义:设字符串S的长度为n,S[0~n-1]. 子串:设0<=i<=j<=n-1,那么由S的第i到第j个字符组成的串为它的子串S[i,j]. 后缀:设0<=i<=n- ...

  6. hdu 1403 Longest Common Substring(最长公共子字符串)(后缀数组)

    http://acm.hdu.edu.cn/showproblem.php?pid=1403 Longest Common Substring Time Limit: 8000/4000 MS (Ja ...

  7. hdu3518 Boring counting(后缀数组)

    Boring counting 题目传送门 解题思路 后缀数组.枚举每种长度,对于每个字符串,记录其最大起始位置和最小起始位置,比较是否重合. 代码如下 #include <bits/stdc+ ...

  8. 2019牛客多校第四场 I题 后缀自动机_后缀数组_求两个串de公共子串的种类数

    目录 求若干个串的公共子串个数相关变形题 对一个串建后缀自动机,另一个串在上面跑同时计数 广义后缀自动机 后缀数组 其他:POJ 3415 求两个串长度至少为k的公共子串数量 @(牛客多校第四场 I题 ...

  9. 后缀数组SA学习笔记

    什么是后缀数组 后缀数组\(sa[i]\)表示字符串中字典序排名为\(i\)的后缀位置 \(rk[i]\)表示字符串中第\(i\)个后缀的字典序排名 举个例子: ababa a b a b a rk: ...

随机推荐

  1. vue2.0生命周期

    https://www.cnblogs.com/goloving/p/8616989.html(copy )

  2. zabbix模板

    https://github.com/xm-y/zabbix-community-repos  https://monitoringartist.github.io/zabbix-searcher/ 

  3. Java 8 函数式接口

    函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口. 函数式接口可以被隐式转换为 lambda 表达式. Lambda 表达式和方法引用 ...

  4. git在实际开发中的应用

    PS: git操作实例(https://learngitbranching.js.org/?demo) 一, 创建分支,合并分支到master 1. 在远程仓库中创建master和test分支 2.本 ...

  5. Lodop控件NewPage();测试输出空白页

    LODOP.NewPage();和LODOP.NewPageA();是强制分页语句,两者的区别可查看本博客的相关博文:Lodop强制分页LODOP.NewPage()和LODOP.NewPageA() ...

  6. AtCoder Beginner Contest 120 D - Decayed Bridges(并查集)

    题目链接:https://atcoder.jp/contests/abc120/tasks/abc120_d 题意 先给m条边,然后按顺序慢慢删掉边,求每一次删掉之后有多少对(i,j)不连通(我应该解 ...

  7. React 学习(五) ---- 条件和列表渲染

    条件渲染 React中的条件渲染和我们平常写的js 代码一样,都是用的if else, 只不过在if else 中它的返回值是jsx, 根据不同的条件渲染不同的UI. 先写两个组件 //登录的用户显示 ...

  8. 1、linux下对绝对路径和相对路径

    cd /  回到根目录         cd  /etc 回到根目录下的etc 目录下  绝对路径  路径写法是从根目录/ 写起来的. cd . 当前目录 cd .. 上层目录 cd ~回到自家的根目 ...

  9. Nginx 安装与详解

    nginx简介 nginx是一个开源的,支持高性能,高并发的www服务和代理服务软件.它是一个俄罗斯人lgor sysoev开发的,作者将源代码开源出来供全球使用.nginx比它大哥apache性能改 ...

  10. LOJ2269 [SDOI2017] 切树游戏 【FWT】【动态DP】【树链剖分】【线段树】

    题目分析: 好题.本来是一道好的非套路题,但是不凑巧的是当年有一位国家集训队员正好介绍了这个算法. 首先考虑静态的情况.这个的DP方程非常容易写出来. 接着可以注意到对于异或结果的计数可以看成一个FW ...