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

因为有个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. php foreach跳出本次/当前循环与终止循环方法

    continue:跳出本次循环 break:终止循环 exit:用来结束程序执行 return: 用来结束一段代码     $arr= array('le','yang','jun','lecode' ...

  2. CLOUD流程设置

    流程-反写规则 允许超额

  3. js对json解析获取对应属性的值,JSON.stringify()和JSON.parse()

    JSON.stringify() 该方法,将一个JSON对象转化为字符串string JSON.parse() 该方法,将一个字符串转化为JSON对象object 对于JSON对象,获取其对应键值 可 ...

  4. 用dbexpress连接sqlserver数据库

    SQLConnection1.Params.clearSQLConnection1.Params.Values['ServerName'] := '192.168.0.112'; SQLConnect ...

  5. Oracle minus用法详解及应用实例

    本文转载:https://blog.csdn.net/jhon_03/article/details/78321937 Oracle minus用法 “minus”直接翻译为中文是“减”的意思,在Or ...

  6. Windows & RabbitMQ:安装

    可以参考:在 Windows 上安装Rabbit MQ 指南 本文RabbitMQ版本为3.6.1 Step 1:安装Erlang Step 2:安装RabbitMQ Step 3:启用Managem ...

  7. HttpRequest,WebRequest,HttpWebRequest,WebClient,HttpClient 之间的区别

    HttpRequest,WebRequest,HttpWebRequest,WebClient,HttpClient 今天我们来聊一下他们之间的关系与区别. HttpRequest 类 .NET Fr ...

  8. 【C/C++】实现龙贝格算法

    1. 复化梯形法公式以及递推化 复化梯形法是一种有效改善求积公式精度的方法.将[a,b]区间n等分,步长h = (b-a)/n,分点xk = a + kh.复化求积公式就是将这n等分的每一个小区间进行 ...

  9. linux shell系列10 判断某个月中的星期六和星期天

    #!/bin/bashread -p "请输入月份:" month #输入要查找的月份 mon=`date -d "0 month ago" +%m` #计算本 ...

  10. BZOJ4477[Jsoi2015]字符串树——可持久化trie树

    题目描述 萌萌买了一颗字符串树的种子,春天种下去以后夏天就能长出一棵很大的字符串树.字符串树很奇特,树枝上都密密麻麻写满了字符串,看上去很复杂的样子.[问题描述]字符串树本质上还是一棵树,即N个节点N ...