后缀数组--summer-work之我连模板题都做不起
这章要比上章的AC自动机要难理解。
这里首先要理解基数排序:基数排序与桶排序,计数排序【详解】
下面通过这个积累信心:五分钟搞懂后缀数组!后缀数组解析以及应用(附详解代码)
下面认真研读下这篇: [转载]后缀数组算法总结 里面的图片真Nice
然后找到适合再加深认识的:后缀数组学习笔记 算法合集之《后缀数组——处理字符串的有力工具》
后缀数组:通过对字符串后缀的处理得到子串的信息。
这里我通过借鉴上面的blog和训练指南写下了一段学习程序,还有在线多模板匹配的二分查找未完成。
用代码和测试数据的结果对比第三篇文章里的图片,有助于理解后缀数组sa的构建过程。
- #include <bits/stdc++.h>
- using namespace std;
- const int maxn = ;
- char s[maxn]; //字符串s,每个字符值[0,m-1]
- int c[maxn]; //辅助数组
- int x[maxn]; //rank数组,下标表示关键字的位置,值表示关键字的名次
- // x代表每个后缀的前缀的种类
- int y[maxn]; //表示排序后的第二关键字数组,下标表示名次
- //值表示第二关键字的首字符位置
- int sa[maxn];
- /* 在构造完前表示关键字数组:下标表示名次,值表示关键字首字符位置,值相同时名次根据在原串中的相对位置决定
- * 构造完成后表示后缀数组,下标表示名次,值表示后缀的首字符位置
- */
- void show_sort_to_get_first_sa(int n){
- cout << "第一次计数排序结果:" << endl;
- for(int i=; i<n; i++){
- cout << "第" << i << "位" << endl;
- cout << "大小 = " << x[i] << " " << char(x[i]) << endl;
- cout << "该后缀的首字符的下标(或称后缀-): " << sa[i] << endl;
- // aaaaaabb
- //
- }
- }
- void show_second_key(int n){
- cout << "y的结果" << endl;
- for(int i=; i<n; i++){
- cout << y[i] << " ";
- }cout << endl;
- /*
- * xaaaaabb
- * 70234516
- */
- }
- void show_last(int n){
- cout << "\nsa的终值:\n";
- for(int i=; i<n; i++){
- cout << sa[i] << " ";
- }cout << endl;
- }
- bool cmp(int *f, int x, int y, int k){
- return f[x]==f[y] && f[x+k]==f[y+k];
- }
- void build_sa(int m, int n){
- //每个字符值为[0,m-1]
- //字符串长度为n
- for(int i=; i<=m; i++){
- c[i] = ;
- }
- for(int i=; i<n; i++){
- c[x[i] = s[i]]++;
- } //x是s中字符的键值//c存储各键值在输入数组中出现的次数
- for(int i=; i<m; i++){
- c[i] += c[i-];
- }
- /*
- * 计算前缀和,即可知小于小于每个数的个数
- * aabaaaab=11211112 --> c[1]=6, c[2]=2
- * 计算前缀和--> c[2]=8
- */
- for(int i=n-; i>=; i--){
- sa[--c[ x[i]]] = i;
- }//满足第一关键字的同时满足第二关键字
- /* 以上代码
- * 对后缀的第一位进行计数排序。
- */
- show_sort_to_get_first_sa(n);
- for(int k=; k<=n; k<<=){
- // k:当前子字符串长度
- int p = ;
- //当前的第二关键字可直接由上次的sa得到
- for(int i=n-k; i<n; i++){
- y[p++] = i;
- }// 长度越界,第二关键字为0
- for(int i=; i<n; i++){
- if(sa[i] >= k){
- y[p++] = sa[i] - k; //-k后移上次排序的结果
- //y存储对第二关键字排序的结果
- }
- }//使用有第k个字符的字符串,直接排序第二关键字
- //以上结合两个关键字进行总排序
- show_second_key(n);
- //基数排序第一关键字
- for(int i=; i<=m; i++){
- c[i] = ;
- }
- for(int i=; i<n; i++){
- c[x[y[i]]]++;
- }
- for(int i=; i<m; i++){
- c[i] += c[i-];
- }
- for(int i=n-; i>=; i--){
- sa[ --c[ x[ y[i]]]] = y[i];
- //y[i] = 0;
- } //更新sa,
- swap(x, y); //用y暂存上一轮的x(rank)
- /*
- * 集和两个关键字排总序
- * 完成了关键字的合并
- */
- p = ; x[sa[]] = ;
- //用已经完成的sa来更新与它互逆的rank(x)
- for(int i=; i<n; i++){
- x[sa[i]] = cmp(y,sa[i],sa[i-],k)?p-:p++;
- }
- if(p >= n) break; //即使倍增,sa也不会改变
- m = p;
- }
- show_last(n); //
- }
- int rank[maxn]; //rank[string's num] = rank
- int height[maxn]; //height[rank] = len of lcp
- void getheight(int n){
- for(int i=; i<n; i++){
- rank[sa[i]] = i;
- }//计算每个字符串字典序的排名
- /*
- * h[i]:第i个位置开始的后缀与排名在其前一名的后缀的最长公共前缀
- * k=0:从首字符开始看第i个字符串和第j个字符串前面有多少是相同的
- * k!=0, height[rank[i]] = height[rank[i-1]] - 1,LCP长度最少是k-1,
- * 于是从首字符后面k-1个字符开始检查
- */
- for(int i=, k=; i<n; i++){
- if(k) k--;
- /* 若k不为0, h[i]>=h[i-1]-1,最长公共前缀长度至少是k-1,从首字符后k-1开始检查
- * h[i-1]-1是一系列h值的最小值,包括后缀i和排在它前一个的后缀p的LCP长度
- */
- int j = sa[rank[i] - ];
- while(j+k<n && i+k<n && s[j+k]==s[i+k]) k++;
- //计算height[rank[i]],LCP(rank[i],rank[i]-1)
- height[rank[i]] = k;
- }
- }
- int main(){
- freopen("in.txt", "r", stdin);
- int n, m;
- cin >> n >> m;
- cin >> s;
- build_sa(m, n);
- for(int i=; i<n; i++){
- for(int j=sa[i]; j<n; j++){
- cout << s[j] << " ";
- }cout << endl;
- }
- getheight(n);
- return ;
- }
- /*
- in.txt
- 8 200
- aabaaaab
- */
还没开始做题,这个知识点怕暂时要凉了。
后缀数组--summer-work之我连模板题都做不起的更多相关文章
- AC自动机--summer-work之我连模板题都做不出
这章对现在的我来说有点难,要是不写点东西,三天后怕是就一无所有了. 但写这个没有营养的blog的目的真的不是做题或提升,只是学习学习代码和理解一些概念. 现在对AC自动机的理解还十分浅薄,这里先贴上目 ...
- SPOJ Distinct Substrings(后缀数组求不同子串个数,好题)
DISUBSTR - Distinct Substrings no tags Given a string, we need to find the total number of its dist ...
- UOJ35 后缀数组(模板)
#35. 后缀排序 这是一道模板题. 读入一个长度为 nn 的由小写英文字母组成的字符串,请把这个字符串的所有非空后缀按字典序从小到大排序,然后按顺序输出后缀的第一个字符在原串中的位置.位置编号为 1 ...
- 【BZOJ5304】[HAOI2018]字串覆盖(后缀数组,主席树,倍增)
[BZOJ5304][HAOI2018]字串覆盖(后缀数组,主席树,倍增) 题面 BZOJ 洛谷 题解 贪心的想法是从左往右,能选就选.这个显然是正确的. 题目的数据范围很好的说明了要对于询问分开进行 ...
- hdu 4622 Reincarnation(后缀数组)
hdu 4622 Reincarnation 题意:还是比较容易理解,给出一个字符串,最长2000,q个询问,每次询问[l,r]区间内有多少个不同的字串. (为了与论文解释统一,这里解题思路里sa数组 ...
- poj2774 Long Long Message 后缀数组求最长公共子串
题目链接:http://poj.org/problem?id=2774 这是一道很好的后缀数组的入门题目 题意:给你两个字符串,然后求这两个的字符串的最长连续的公共子串 一般用后缀数组解决的两个字符串 ...
- Codeforces VK Cup 2015 A.And Yet Another Bracket Sequence(后缀数组+平衡树+字符串)
这题做得比较复杂..应该有更好的做法 题目大意: 有一个括号序列,可以对其进行两种操作: · 向里面加一个括号,可以在开头,在结尾,在两个括号之间加. · 对当前括号序列进 ...
- LUOGU P2408 不同子串个数(后缀数组)
传送门 解题思路 后缀数组求本质不同串的裸题.\(ans=\dfrac{n(n+1)}{2} -\sum height[i]\). 代码 #include<iostream> #inclu ...
- hdu 1711 KMP算法模板题
题意:给你两个串,问你第二个串是从第一个串的什么位置開始全然匹配的? kmp裸题,复杂度O(n+m). 当一个字符串以0为起始下标时.next[i]能够描写叙述为"不为自身的最大首尾反复子串 ...
随机推荐
- 对List<Map>里的map的某个属性重复的值进行处理的方法
package test; import java.util.*;import java.util.stream.Collectors; public class Test5 { public sta ...
- 利用 canvas 实现压缩图片
/** * nase64Data --> 要压缩的图片base64数据 * width --> 宽度 * height --> 高度 * _callback --> 回调函数 ...
- 题解 【NOIP2006】作业调度方案
[NOIP2006]作业调度方案 Description 我们现在要利用 m 台机器加工 n 个工件,每个工件都有 m 道工序,每道工序都在不同的指定的机器上完成.每个工件的每道工序都有指定的加工时间 ...
- [转]vue-router各个属性的作用及用法
转自以下网址,当备忘使用:https://www.cnblogs.com/goloving/p/9211358.html vue-router是vue单页面开发的路由,就是决定页面跳转的! <r ...
- FPGA数据舍入方式
1,在Verilog代码中,常用的代码写法为直接截位: 2,在Vivado的IP核中常见的两种舍入方式为Truncation和Rounding, 3,在Matlab中常见的四种舍入函数为floor, ...
- Luogu P5022 旅行 搜索+贪心
好吧...一直咕..现在才过...被卡常卡到爆... 写的垃圾版本,$n^2$无脑删边..可以发现走出来的是棵树...更优秀的及数据加强版先咕着...一定写.qwq #include<cstdi ...
- Codevs 1105 过河 2005年NOIP全国联赛提高组
1105 过河 2005年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description 在河上有一座独木桥,一只青蛙 ...
- UOJ社区版安装多个Judger
目录 声明 在同一台机器上安装 在不同机子上安装 声明 本文档非官方文档,为我试坑的经验总结. 本文编写时间 2019.11.04 ,并不一定会随UOJ更新而更新. 由于UOJ需要用SVN传题,并不那 ...
- Docker+Rancher构建部署流水线
工作多年,在项目部署方面, 1:以前用ftp或者rz上传更新的,每次更新算上打包.目录切换.更新遗漏.备份.出错还原.启动等工作都得搞上一来小时甚至更长,要是多两台服务器那心都凉了: 2:后来有用sv ...
- Centos镜像下载地址
https://blog.csdn.net/weixin_42430824/article/details/81019039