后缀数组Da模板+注释 以及 dc3模板
后缀数组Da模板:
1 /*
2 后缀数组倍增法Da板子
3 */
4 #include <cstdlib>
5 #include <cstring>
6 #include <cstdio>
7 #include <algorithm>
8 using namespace std;
9 const int N = 200000+9;
10 int c[N];
11 int rank[N], height[N];
12 int sa[N],s[N],n;
13 /*
14 rank[i]数组的下标表示后缀是[i...n],它的值是这个后缀的排名
15 sa[i]数组的下标表示这个后缀的排名,它的值是这个后缀在这个串中的起始位置。譬如sa[i]表示的后缀就是[sa[i]...n]
16 c是辅助数组
17 height[i]数组表示排名第i的后缀和排名第i-1后缀的最长公共前缀
18 s数组放的是输入的数
19 */
20 bool pan(int *x,int i,int j,int k,int n)
21 {
22 int ti=i+k<n?x[i+k]:-1;
23 int tj=j+k<n?x[j+k]:-1;
24 return x[i]==x[j]&&ti==tj;
25 }
26 //下面这个代码是未优化版代码讲解
27 //void build_SA(int m)
28 //{
29 // int i,j,k;
30 // //清空数组,m就是一个范围。是根据你输入字符大小来决定
31 // for(i=1; i<=m; ++i) c[i]=0;
32 // //将原数组里面内容复制一份
33 // for(i=0; i<n; ++i) x[i]=s[i];
34 // //这个就是统计输入数组里面某一个字符出现次数
35 // for(i=0; i<n; ++i) c[x[i]]++;
36 // //求前缀和
37 // for(i=2; i<=m; ++i) c[i]+=c[i-1];
38 // //这行代码就是按照每一个后缀的第一个字母排序
39 // for(i=n-1; i>=0; i--) sa[--c[x[i]]]=i;
40 // //k表示关键字的长度
41 // for(int k=1; k<=n; k<<=1)
42 // {
43 // int num=0;
44 // //当k==1的时候是对每一个后缀得第二个字符排序
45 // for(i=n-k; i<n; ++i)//当k==1的时候很明显最后一个后缀(即[(n-1)...(n-1)])没有第二个字符
46 // y[num++]=i; //所以仅仅按照第二字符排序的时候他的排名必定靠前
47 //
48 // //下面这个for循环就是对剩下的后缀进行排序
49 // for(i=0; i<n; ++i)
50 // {
51 // if(sa[i]>=k) //他的排序借助了我们对第一个字符排序后的数组
52 // y[num++]=sa[i]-k; //这里为啥要减去k,因为我们要对每一个后缀得第二个字符排序,可是
53 // //y数组的值代表这个后缀从哪开始
54 // }
55 // for(i=1; i<=m; ++i) c[i]=0;
56 // for(i=0; i<=n; ++i) c[x[i]]++;
57 // for(i=2; i<=m; ++i) c[i]+=c[i-1]; //这三行和上面一样,因为在排序过程中c数组的值改变了,所以我们还要重新弄一遍
58 // //下面一行代码就将每一个后缀得第一个字符和第二个字符综合起来给每一个后缀排序
59 // for(i=n-1; i>=0; --i) sa[--c[x[y[i]]]]=y[i],y[i]=0;
60 // /*
61 // 上面一行代码的作用就是用结合两个关键字把总的排序搞出来
62 // 我们应该做的,就是先根据第一关键字排序,第一关键字相等时根据第二关键字大小排序。
63 // 但是看上去,只进行了一次计数排序啊。
64 // 还记得这个计数排序的特点:先根据x的值排序,x值相等时根据出现先后次序排序。
65 // x里面存了上次关键字的排序,在本次排序中即是第一关键字的排序,x的值排序==第一关键字排序,这里的计数排序做的是对的。那么第二关键字呢?
66 // 前面对第二关键字进行了排序,在这里x[y[i]]就是根据第二关键字的顺序重新改变了第一关键字的顺序,也就是说在本次计数排序中,出现先后次序排序==第二关键字大小排序。
67 // 换句话说,我们先单独对第二关键字排序,根据这个顺序改变第一关键字的顺序,由于在计数排序时首先按照第一关键字的值排序,而第一关键字的值没有改变所以首先还是根据第一关键字排序,
68 // 改变的是第一关键字相同的时候,出现在前面的第二关键字排在前面。
69 //
70 // y[i]的值就是排名第i的后缀是【y[i]...(n-1)】,那么x[y[i]]就代表了y[i]这个位置上的字符,这就做到了
71 // “先根据第一关键字排序,第一关键字相等时根据第二关键字大小排序。”
72 // */
73 // /*
74 // 做到这里就完成了第一第二关键字的合并,得到了合并以后的关键字顺序,它可以用于下次迭代。
75 // 对于下面代码每次新的迭代要用到rank数组x,由于有了刚求的关键字排序数组sa,要得到rank数组也很容易。
76 // 但是对于相同的值,rank应该相同,所以要判断一下合并以后的关键字是否相同。
77 // */
78 // swap(x,y);
79 // num=1;
80 // x[sa[0]]=1;
81 // for(i=1; i<n; ++i)
82 // {
83 // if(y[sa[i]]!=y[sa[i-1]] || y[sa[i]+k]!=y[sa[i-1]+k])
84 // x[sa[i]]=++num;
85 // else x[sa[i]]=num;
86 // }
87 // if(num>=n) break;
88 // m=num;
89 // }
90 //}
91 void build_SA(int n,int r)
92 {
93 int *x=rank,*y=height;
94 //先清空辅助数组
95 for(int i=0; i<r; i++)c[i]=0;
96 //因为后面要排序,所以先统计一下各数字个数
97 for(int i=0; i<n; i++)c[s[i]]++;
98 //求前缀和,可能你不知道为啥要求前缀和。这个一言两语说不清。建议模拟一下
99 for(int i=1; i<r; i++)c[i]+=c[i-1];
100 //前面辅助工作做完后,下面这一行代码就对每一个后缀按第一个关键字排序了(模拟一下)
101 //第一关键字?比如后缀是abbcd那么a就是第一关键字,c就是第四关键字,d就是第五关键字,二三关键字都是b
102 for(int i=n-1; i>=0; i--)sa[--c[s[i]]]=i;
103
104 r=1;
105 x[sa[0]]=0;
106 for(int i=1; i<n; i++) //这个x数组就是整理一下对每一个后缀第一个关键字排序的结果
107 //怎么整理?就是如果两个后缀第一关键字相同,那么排名也一样,否则就不一样,排名是从0到n
108 x[sa[i]]=s[sa[i]]==s[sa[i-1]]?r-1:r++;
109
110 for(int k=1; r<n; k<<=1) //这个里面内容和未优化代码中的差不多
111 {
112 int yn=0;
113 //y数组里面保存着对每一个后缀的第二个关键字排序的结果
114 //注意这个时候仅仅是按照第二个关键字排序,第一个关键字没有影响这个排序
115 for(int i=n-k; i<n; i++)y[yn++]=i;
116 for(int i=0; i<n; i++)
117 if(sa[i]>=k)y[yn++]=sa[i]-k;
118 //这一部分是对每一个后缀按照第一二关键字同时排序
119 //这样的话就是先判断第一关键字相同不,相同的话再按照第二关键字排序
120 for(int i=0; i<r; i++)c[i]=0;
121 for(int i=0; i<n; i++)++c[x[y[i]]];
122 for(int i=1; i<r; i++)c[i]+=c[i-1];
123 for(int i=n-1; i>=0; i--)sa[--c[x[y[i]]]]=y[i];
124 swap(x,y);
125 r=1;
126 x[sa[0]]=0;
127 for(int i=1; i<n; i++) //这个就是将第一第二关键字排序结果当作新的第一关键字,继而进行倍增
128 x[sa[i]]=pan(y,sa[i],sa[i-1],k,n)?r-1:r++;
129 }
130 for(int i=0; i<n; i++)rank[i]=x[i];
131 }
132 //height[i]数组表示排名第i的后缀和排名第i-1后缀的最长公共前缀
133 /*
134 能够线性计算height[]的值的关键在于h[](height[rank[]])的性质,即h[i]>=h[i-1]-1,下面具体分析一下这个不等式的由来。
135 我们先把要证什么放在这:对于第i个后缀,设j=sa[rank[i] – 1],也就是说j是i的按排名来的上一个字符串,按定义来i和j的最
136 长公共前缀就是height[rank[i]],我们现在就是想知道height[rank[i]]至少是多少,而我们要证明的就是至少是
137 height[rank[i-1]]-1。好啦,现在开始证吧。
138
139 首先我们不妨设第i-1个字符串(这里以及后面指的“第?个字符串”不是按字典序排名来的,是按照首字符在字符串中的位置来的)
140 按字典序排名来的前面的那个字符串是第k个字符串,注意k不一定是i-2,因为第k个字符串是按字典序排名来的i-1前面那个,
141 并不是指在原字符串中位置在i-1前面的那个第i-2个字符串。
142 这时,依据height[]的定义,第k个字符串和第i-1个字符串的公共前缀自然是height[rank[i-1]],现在先讨论一下第k+1个字符串
143 和第i个字符串的关系。
144 第一种情况,第k个字符串和第i-1个字符串的首字符不同,那么第k+1个字符串的排名既可能在i的前面,也可能在i的后面,但没有
145 关系,因为height[rank[i-1]]就是0了呀,那么无论height[rank[i]]是多少都会有height[rank[i]]>=height[rank[i-1]]-1,也
146 就是h[i]>=h[i-1]-1。
147 第二种情况,第k个字符串和第i-1个字符串的首字符相同,那么由于第k+1个字符串就是第k个字符串去掉首字符得到的,第i个字
148 符串也是第i-1个字符串去掉首字符得到的,那么显然第k+1个字符串要排在第i个字符串前面,要么就产生矛盾了。同时,第k个
149 字符串和第i-1个字符串的最长公共前缀是height[rank[i-1]],那么自然第k+1个字符串和第i个字符串的最长公共前缀就是
150 height[rank[i-1]]-1。
151 到此为止,第二种情况的证明还没有完,我们可以试想一下,对于比第i个字符串的字典序排名更靠前的那些字符串,谁和
152 第i个字符串的相似度最高(这里说的相似度是指最长公共前缀的长度)?显然是排名紧邻第i个字符串的那个字符串了呀,
153 即sa[rank[i]-1]。也就是说sa[rank[i]]和sa[rank[i]-1]的最长公共前缀至少是height[rank[i-1]]-1,那么就有
154 height[rank[i]]>=height[rank[i-1]]-1,也即h[i]>=h[i-1]-1。
155 证明完这些之后,下面的代码也就比较容易看懂了。
156
157 我们说了这么多就是为了证明height[i]>=height[i-1]-1,证明了这个我们就不用一个后缀和好多后缀比较。可以直接从
158 height[i-1]-1来比较
159 */
160 void get_height(int n)
161 {
162 int i,j,k=0;
163 for(i=1; i<=n; i++)rank[sa[i]]=i;
164 for(i=0; i<n; i++)
165 {
166 if(k)k--;
167 else k=0;
168 j=sa[rank[i]-1];
169 while(s[i+k]==s[j+k])k++;
170 height[rank[i]]=k;
171 }
172 }
173 int main()
174 {
175 scanf("%d",&n);
176 for(int i=0; i<n; i++)
177 scanf("%d",&s[i]),s[i]++;
178 s[n]=0;
179 build_SA(n+1,N);
180 get_height(n);
181 /*
182 注意,用这个板子的时候都要在输入的最后新加一个长度(必须加,要不然会跑不出来结果),然后像这一个板子尽量新加的
183 那个数比所有输入的数都要小。
184 这样的话排名第0的后缀肯定是[n...n]
185
186 然后主串的排名是1到n,它的height数组范围就是2到n了(这里的范围都是闭区间)
187 */
188 for(int i=2;i<=n;++i)
189 {
190 printf("%d ",height[i]);
191 }
192 printf("\n");
193 return 0;
194 }
学习后缀数组链接:
https://www.cnblogs.com/nietzsche-oier/articles/6621881.html
https://blog.csdn.net/qq_37774171/article/details/81776029
后缀数组dc3模板:
//后缀数组dc3模板
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 3000010;
#define F(x) ((x) / 3 + ((x) % 3 == 1 ? 0 : tb))
#define G(x) ((x) < tb ? (x) * 3 + 1 : ((x) - tb) * 3 + 2)
int wa[maxn], wb[maxn], Ws[maxn], wv[maxn], sa[maxn];
int Rank[maxn], height[maxn],r[maxn];
char s[maxn];
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);
return r[a] < r[b] || r[a] == r[b] && wv[a + 1] < wv[b + 1];
}
void Rsort(int *r, int *a, int *b, int n, int m)
{
for (int i = 0; i < n; i++) wv[i] = r[a[i]];
for (int i = 0; i < m; i++) Ws[i] = 0;
for (int i = 0; i < n; i++) Ws[wv[i]]++;
for (int i = 1; i < m; i++) Ws[i] += Ws[i - 1];
for (int i = n - 1; i >= 0; i--) b[--Ws[wv[i]]] = a[i];
}
void dc3(int *r,int *sa,int n, int m)
{
int i, j, *rn = r + n, *san = sa + n, ta = 0, tb = (n + 1) / 3, tbc = 0, p;
r[n] = r[n + 1] = 0;
for (i = 0; i < n; i++) if (i % 3 != 0) wa[tbc++] = i;
Rsort(r + 2, wa, wb, tbc, m);
Rsort(r + 1, wb, wa, tbc, m);
Rsort(r, wa, wb, tbc, m);
for (p = 1, rn[F(wb[0])] = 0, i = 1; i < tbc; i++)
rn[F(wb[i])] = c0(r, wb[i - 1], wb[i]) ? p - 1 : p++;
if (p < tbc) dc3(rn, san, tbc, p);
else for (i = 0; i < tbc; i++) san[rn[i]] = i;
for (i = 0; i < tbc; i++) if (san[i] < tb) wb[ta++] = san[i] * 3;
if (n % 3 == 1) wb[ta++] = n - 1;
Rsort(r, wb, wa, ta, m);
for (i = 0; i < tbc; i++) wv[wb[i] = G(san[i])] = i;
for (i = 0, j = 0, p = 0; i < ta && j < tbc; p++)
sa[p] = c12(wb[j] % 3, r, wa[i], wb[j]) ? wa[i++] : wb[j++];
for (; i < ta; p++) sa[p] = wa[i++];
for (; j < tbc; p++) sa[p] = wb[j++];
}
void get_height(int n)
{
int i, j, k = 0;
for (i = 1; i <= n; i++) Rank[sa[i]] = i;
for (i = 0; i < n; height[Rank[i++]] = k)
for (k ? k-- : 0, j = sa[Rank[i] - 1]; r[i + k] == r[j + k]; k++);
}
int main()
{
while(~scanf("%s",s))
{
int n=strlen(s);
if(n==1 && s[0]=='.') break;
for(int i=0;i<n;++i)
r[i]=s[i];
r[n]='0';
/*
这个用法和后缀数组Da模板一样,后面要加一个小于输入所有字符的字符
*/
dc3(r,sa,n+1,200);
get_height(n);
for(int i=2;i<=n;++i)
printf("%d ",height[i]);
printf("\n");
}
return 0;
}
dc3模板学习链接:
https://www.cnblogs.com/jianglangcaijin/p/6035937.html
后缀数组Da模板+注释 以及 dc3模板的更多相关文章
- UOJ35 后缀数组(模板)
#35. 后缀排序 这是一道模板题. 读入一个长度为 nn 的由小写英文字母组成的字符串,请把这个字符串的所有非空后缀按字典序从小到大排序,然后按顺序输出后缀的第一个字符在原串中的位置.位置编号为 1 ...
- Power Strings POJ - 2406 后缀数组
Given two strings a and b we define a*b to be their concatenation. For example, if a = "abc&quo ...
- poj 1743 后缀数组 求最长不重叠重复子串
题意:有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复的主题. “主题”是整个音符序列的一个子串,它需要满足如下条件:1 ...
- poj2406(后缀数组)
poj2406 题意 给出一个字符串,它是某个子串重复出现得到的,求子串最多出现的次数. 分析 后缀数组做的话得换上 DC3 算法. 那么子串的长度就是 \(len - height[rnk[0]]\ ...
- POJ 2774 Long Long Message (后缀数组模板)
借用罗大神的模板,开始搞后缀数组 #include <cstdio> #include <iostream> #include <cstring> #include ...
- 【后缀数组】洛谷P3809模板题
题目背景 这是一道模板题. 题目描述 读入一个长度为 n n n 的由大小写英文字母或数字组成的字符串,请把这个字符串的所有非空后缀按字典序从小到大排序,然后按顺序输出后缀的第一个字符在原串中的位置. ...
- POJ 2774 Long Long Message 后缀数组模板题
题意 给定字符串A.B,求其最长公共子串 后缀数组模板题,求出height数组,判断sa[i]与sa[i-1]是否分属字符串A.B,统计答案即可. #include <cstdio> #i ...
- 洛谷:P3809 【模板】后缀排序(后缀数组模板)
P3809 [模板]后缀排序 题目链接:https://www.luogu.org/problemnew/show/P3809 题目背景 这是一道模板题. 题目描述 读入一个长度为 nn 的由大小写英 ...
- 【UOJ #35】后缀排序 后缀数组模板
http://uoj.ac/problem/35 以前做后缀数组的题直接粘模板...现在重新写一下模板 注意用来基数排序的数组一定要开到N. #include<cstdio> #inclu ...
随机推荐
- ip访问本机vs调试项目
环境:win10 vs2019 webapi F5启动调试. 问题:localhost可以访问,127.0.0.1和本机ip访问不了.比如想让别人浏览一下看效果,或者测试人员测试功能,每次修改都有重新 ...
- kubernets之pod的删除方式
一 删除单个pod 1 删除指定命名空间的指定名称的pod k delete po kubia-manual -n defaultpod "kubia-manual" delet ...
- ctfshow—web—web4
打开靶机 发现与web3很相似,测试文件包含未成功 此题有两种解决方法 一.日志注入 查看日志的默认目录,得到了日志文件 ?url=/var/log/nginx/access.log 进行日志注入 & ...
- oracle rac搭建单实例DG步骤(阅读全篇后再做)
环境介绍 主库: 主机名 rac01 rac02 实体IP 10.206.132.232 10.206.132.233 私有IP 192.168.56.12 192.168.56.13 虚拟IP 10 ...
- BAPI_SALESORDER_CREATEFROMDAT2 条件 定价元素
用函数 BAPI_SALESORDER_CREATEFROMDAT2创建SO的时候,遇见个问题,就是如图: 会多出来一个类型,搞了半天,发现是一个函数里的一个参数,我没有设置: LOGIC_SWITC ...
- 1.5V转5V的最少电路的芯片电路图
PW5100满足1.5V转5V的很简洁芯片电路,同时达到了最少的元件即可组成DC-DC电路1.5V转5V的升压转换器系统. PW5100在1.5V转5V输出无负载时,输入效率电流极低,典型值10uA. ...
- django ajax应用
ajax: 什么是ajax,有什么作用: 以前我们在页面向后台提交数据的时候都是使用from表单,这样的提交会在提交的时候将整个页面全部刷新,如果你在填写表单的时候提交之后发现某个数据不对,但是你已提 ...
- CoeMonkey少儿编程第4章 变量
点击这里,现在就开启CodeMonkey的趣味编程之旅. 目标 了解什么是变量 了解变量的命名规则 掌握如何使用变量 变量 什么是变量?顾名思义,变量就是可以变化的量. 和变量相对的是常量,即不可变化 ...
- Typora+PicGo+Gitee打造图床
前言 自己一直使用的是Typora来写博客,但比较麻烦的是图片粘贴上去后都是存储到了本地,写好了之后放到博客园等地,图片不能直接访问,但如今Typora已经支持图片上传,所以搞了一波图片上传到Gi ...
- ES数据库高可用配置
ES高可用集群部署 1.ES高可用架构图 2.创建ES用户组 1.Elasticsearch不能在 root 用户下启动,我们需要在三台机器上分创建一个普通用户# 创建elastic用户 userad ...