后缀数组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 ...
随机推荐
- Spark学习进度11-Spark Streaming&Structured Streaming
Spark Streaming Spark Streaming 介绍 批量计算 流计算 Spark Streaming 入门 Netcat 的使用 项目实例 目标:使用 Spark Streaming ...
- IDEA 常用的一些 (就几个) 快捷键
快捷键 说明 Ctrl + P 提示类参数 Ctrl + Q 提示类的属性和方法包名 Ctrl + D 复制一行到下一行 Ctrl + F 查找 Ctrl + R 替换 Ctrl + Z 撤销 Ctr ...
- 【Linux】if中的逻辑运算符怎么在linux的帮助中看到
今天在写shell的时候,突然想查看下if相关的一些逻辑运算的,像-f -d之类的这种 于是man if 或者if --help 可是返回的信息却都无济于事,一点帮助都没有 回想一下,if中调的判断 ...
- rac双节点+物理DG
注:以下文章均是看了黄伟老师的视频,记录为博客供以后使用. 双节点RAC搭建: http://blog.csdn.net/imliuqun123/article/details/76171289 RA ...
- 【ORA】 ORA-01031:权限不足的问题
今天创建一个用户,赋予dba权限,在plsql中选择sysdba登录,但是报错 ORA-01031 在网上找了好久最后的解决办法是 不仅仅要有dba权限 还要有这个权限: grant all priv ...
- mysql—group_concat函数
MySQL中的group_concat函数的使用方法,比如select group_concat(name) . 完整的语法如下: group_concat([DISTINCT] 要连接的字段 [Or ...
- ctfshow—web—web签到题
打开靶机,发现只有一句话 查看源码 发现一段字符串,猜是base64加密 拿到flag
- 利用sql_tuning_Advisor调优sql
1.赋权给调优用户 grant ADVISOR to xxxxxx; 2.创建调优任务 使用sql_text创建 DECLARE my_task_name VARCHAR2 (30); my_sqlt ...
- 翻译 - ASP.NET Core 基本知识 - 中间件(Middleware)
翻译自 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-5.0 中间件是集成 ...
- kettle数据质量统计
1.利用Kettle的"分组","JavaScript代码","字段选择"组件,实现数据质量统计.2.熟练掌握"JavaScrip ...