题目地址:

https://www.acwing.com/problem/content/2717/

给定一个长度为nnn的字符串,只包含大小写英文字母和数字。将字符串中的nnn个字符的位置编号按顺序设为1∼n1∼n1∼n。并将该字符串的nnn个非空后缀用其起始字符在字符串中的位置编号表示。现在要对这nnn个非空后缀进行字典序排序,并给定两个数组sasasa和heightheightheight。排序完成后,用sa[i]sa[i]sa[i]来记录排名为iii的非空后缀的编号,用height[i]height[i]height[i]来记录排名为iii的非空后缀与排名为i−1i−1i−1的非空后缀的最长公共前缀的长度(1≤i≤n1≤i≤n1≤i≤n)。特别的,规定height[1]=0height[1]=0height[1]=0。请你求出这两个数组。

输入格式:
共一行,包含一个长度为nnn的仅包含大小写英文字母或数字的字符串。

输出格式:
第一行包含nnn个整数,表示sasasa数组。
第二行包含nnn个整数,表示heightheightheight数组。

数据范围:
1≤n≤1061≤n≤10^61≤n≤106

后缀数组的构造一般用倍增法来做。假设字符串下标从111开始计数,设s[i][2k]=s[i:i+2k−1]s[i][2^k]=s[i:i+2^k-1]s[i][2k]=s[i:i+2k−1](如果越界了则用空字符补齐,规定空字符的字典序最小),那么对于s[i][20]s[i][2^0]s[i][20]的排序可以直接按单个字母的字典序顺序来排;假设∀i,s[i][2k]\forall i, s[i][2^k]∀i,s[i][2k]已经排好,考虑对∀i,s[i][2k+1]\forall i, s[i][2^{k+1}]∀i,s[i][2k+1]来排序,而其可以视为二元组(s[i][2k],s[i+2k][2k])(s[i][2^k],s[i+2^k][2^k])(s[i][2k],s[i+2k][2k]),从而对其的排序可以视为是对二元组的排序,而对二元组的排序可以用s[i][2k]s[i][2^k]s[i][2k]的排序的结论来排。当2k≥n2^k\ge n2k≥n的时候,也就排好了序。每一轮排序的时候,可以用基数排序做到O(n)O(n)O(n),而一共倍增log⁡n\log nlogn次,所以总时间复杂度就是O(nlog⁡n)O(n\log n)O(nlogn)。用基数排序的时候,需要对长2k2^k2k的子串做离散化,可以直接用排名来离散化。

接下来考虑高度数组怎么求。设lcp(i,j)lcp(i, j)lcp(i,j)表示排名第iii的后缀和排名第jjj的后缀的最长公共前缀的长度,那么显然lcp(i,j)=lcp(j,i)lcp(i,j)=lcp(j,i)lcp(i,j)=lcp(j,i)。设i<k<ji<k<ji<k<j,我们有:lcp(i,j)=min⁡{lcp(i,k),lcp(k,j)}lcp(i, j)=\min\{lcp(i, k),lcp(k, j)\}lcp(i,j)=min{lcp(i,k),lcp(k,j)}考虑高度数组heightheightheight,设h[i]=height[rk[i]]h[i]=height[rk[i]]h[i]=height[rk[i]],即h[i]h[i]h[i]表示第iii个后缀和比其字典序小111的后缀的最长公共前缀长度,则有h[i]≥h[i−1]−1h[i]\ge h[i-1]-1h[i]≥h[i−1]−1可以这样想,如果h[i−1]=0h[i-1]=0h[i−1]=0的话显然成立;否则设排名第rk[i−1]−1rk[i-1]-1rk[i−1]−1的后缀是s[k:]s[k:]s[k:],那么s[i:]s[i:]s[i:]和s[k+1:]s[k+1:]s[k+1:]的最长公共前缀就是s[i−1:]s[i-1:]s[i−1:]和s[k:]s[k:]s[k:]的最长公共前缀减去111,即h[i−1]−1h[i-1]-1h[i−1]−1,而由lcplcplcp的性质,排名范围跨度大的最长公共前缀长度不大于排名范围跨度小的最长公共前缀长度,所以有h[i]≥h[i−1]−1h[i]\ge h[i-1]-1h[i]≥h[i−1]−1。

有了关于height[rk[i]]height[rk[i]]height[rk[i]]的表达式之后,就可以快速求出高度数组了,可以在O(n)O(n)O(n)时间内求出(因为在求完k=height[rk[i]]k=height[rk[i]]k=height[rk[i]]之后,可以直接从k−1k-1k−1开始枚举长度,求出height[rk[i+1]]height[rk[i+1]]height[rk[i+1]])。

代码如下:

#include <iostream>
#include <cstring>
using namespace std;const int N = 1e6 + 10;
// m是离散化的最大值
int n, m;
char s[N];
// sa[i]排名第i位的是第几个后缀
// rk[i]第i个后缀排名是多少,在排序的过程中,rk是每个后缀用第一关键字的排名来离散化之后的值
// he[i]是sa[i]与sa[i - 1]的最长公共前缀的长度
// y[i]是按第二关键字排的情况下第i个后缀的排名
int sa[N], rk[N], y[N], c[N], he[N];void get_sa() {// 将所有后缀按第一个字母排序,排序结果存入sa// 先将每个后缀离散化为其第一个字母的ASCII码值for (int i = 1; i <= n; i++) c[rk[i] = s[i]]++;for (int i = 2; i <= m; i++) c[i] += c[i - 1];for (int i = n; i; i--) sa[c[rk[i]]--] = i;// 每一轮按长2k的前缀来对所有后缀排序for (int k = 1;; k <<= 1) {// 先按第二关键字排序int num = 0;// 长度不够k的那些后缀第二关键字为空,最小,先排它们for (int i = n - k + 1; i <= n; i++) y[++num] = i;// 按以长k前缀的字典序从小到大遍历所有后缀,只看第k + 1个及其以后的后缀,只有它们可以作为第二关键字for (int i = 1; i <= n; i++)if (sa[i] > k)// 第sa[i]个后缀是第sa[i] - k个后缀的第二关键字y[++num] = sa[i] - k;// 到此,y存的是所有后缀按第二关键字排好序的结果,即y[i]是排名第i的后缀是第几个后缀// 接下来要将所有后缀按第一关键字排序for (int i = 1; i <= m; i++) c[i] = 0;for (int i = 1; i <= n; i++) c[rk[i]]++;for (int i = 2; i <= m; i++) c[i] += c[i - 1];// rk[y[i]]是排名第i的后缀的第一关键字离散化之后的值for (int i = n; i; i--) sa[c[rk[y[i]]]--] = y[i];// 到此sa已经是两个关键字都排好序的结果// 接下来y没用了,将rk覆盖掉y,并且重新做离散化,此时是将每个后缀按照长2k的前缀来离散化swap(rk, y);rk[sa[1]] = num = 1;for (int i = 2; i <= n; i++)rk[sa[i]] = y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k] ? num : ++num;// 如果离散化后两两不同,说明本轮排好序后,已经没有排名一样的后缀了,说明已经整体排好序,直接退出if (num == n) break;// 更新离散化的最大值m = num;}
}void get_height() {// 按排名枚举后缀for (int i = 1, k = 0; i <= n; i++) {// 如果排名第1,那高度为0,略过if (rk[i] == 1) continue;// 找到排名少1位的后缀是第几个int j = sa[rk[i] - 1];if (k) k--;// 直接暴力枚举while (i + k <= n && j + k <= n && s[i + k] == s[j + k]) k++;he[rk[i]] = k;}
}int main() {scanf("%s", s + 1);n = strlen(s + 1), m = 'z';get_sa();get_height();for (int i = 1; i <= n; i++) printf("%d ", sa[i]);puts("");for (int i = 1; i <= n; i++) printf("%d ", he[i]);puts("");return 0;
}

时间复杂度O(nlog⁡n)O(n\log n)O(nlogn),空间O(n)O(n)O(n)。

【ACWing】2715. 后缀数组相关推荐

  1. AcWing 140 后缀数组

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

  2. 寻找一个字符串的重复子串 后缀数组

    什么是后缀数组 令字符串 S=S[1]S[2]...S[n]S=S[1]S[2]...S[n]{\displaystyle S=S[1]S[2]...S[n]} , S[i,j]S[i,j]{\dis ...

  3. 【2012百度之星/资格赛】H:用户请求中的品牌 [后缀数组]

    时间限制: 1000ms 内存限制: 65536kB 描述 馅饼同学是一个在百度工作,做用户请求(query)分析的同学,他在用户请求中经常会遇到一些很奇葩的词汇.在比方说"johnsonj ...

  4. Boring counting HDU - 3518 (后缀数组)

    Boring counting \[ Time Limit: 1000 ms \quad Memory Limit: 32768 kB \] 题意 给出一个字符串,求出其中出现两次及以上的子串个数,要 ...

  5. HDU4080 Stammering Aliens(二分 + 后缀数组)

    题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=4080 Description Dr. Ellie Arroway has establish ...

  6. 后缀数组 + Hash + 二分 or Hash + 二分 + 双指针 求 LCP ---- 2017icpc 青岛 J Suffix (假题!!)

    题目链接 题目大意: 就是给你n个串每个串取一个后缀,要求把串拼起来要求字典序最小!! sum_length_of_n≤5e5sum\_length\_of\_n\leq 5e5sum_length_ ...

  7. 后缀数组 ---- 2018~2019icpc焦作H题[后缀数组+st表+二分+单调栈]

    题目链接 题目大意: 给出nnn个数,定义f[l,r]f[l,r]f[l,r]表示 区间[l,r][l,r][l,r]的最大值,求所有 子区间的最大值的和,要求相同的子区间只能算一次 比如数列 5 6 ...

  8. [Ahoi2013]差异[后缀数组+单调栈]

    链接 解题思路:很明显前面∑1<=i<j<=nlen(Ti)+len(Tj)\sum_{1<=i<j<=n}len(T_i)+len(T_j)∑1<=i< ...

  9. poj2217详解 ( 后缀数组 + 高度数组 )

    题目大概意思就是 给两个字符串,求最长公共字符串子串长度 我们可以考虑用后缀数组和高度数组 一个字符串 中 最长的两个相同字符串长度, 不就是 后缀数组中相邻两个后缀的最长公共前缀, 不就是 高度数组 ...

最新文章

  1. 图像识别-opencv
  2. 学习全基因组测序数据分析1:测序技术
  3. 礼让行人监控系统+政策助力,共建城市文明交通
  4. 【20171111】Codevs 1064 虫食算80分
  5. 分享一些面试中的经验和心得
  6. 综合布线工作组2009年工作简报
  7. Redis操作ZSet相关API
  8. 十一届蓝桥杯国赛 扩散-多源bfs
  9. rcp异步多参数实例
  10. 计算机音乐至少还有你,至少还有你-林忆莲
  11. Struts2中的国际化
  12. mysql 主从 索引_Mysql繁忙主从库在线修改表结构与添加索引问题
  13. 音乐网站Spotify将融资4亿美元,估值84亿美元
  14. ADI收发器新品-ADRV9002特性与对比(AD9361/71/ADRV9009)
  15. 道路照明智能监控用5G智慧灯杆网关
  16. c# 服务器打印word文档,C#中5步完成word文档打印的方法
  17. python怎么读取dat文件_小白也能学会系列:用python文件读写代码实例!(简单案例)...
  18. 中金易云:为出版社找到下一本《解忧杂货店》
  19. 数据分析案例 |【01】电影数据分析
  20. 【短信发送】实现腾讯云发送短信功能--工具类和SpringBoot配置两种方法实现

热门文章

  1. 二维码学习笔记(二) | 数据分析与数据编码
  2. html字体颜色渐变
  3. 银联云闪付开发联调步骤向导
  4. 【模拟器】华为模拟器eNSP安装注意事项及常见报错处理
  5. git合并分支Pulling is not possible because you have unmerged files.
  6. 球重力异常matlab程序,球体重力异常正演程序介绍.docx
  7. rpm mysql nokey_rpm包时遇到Header V3 DSA signature: NOKEY时解决办法
  8. Kinect for Windows SDK v2.0 开发笔记 (十三) 高清面部帧(4) 面部模型构建器
  9. 即有分期 提前还款手续费就是不在办理的时候告诉你
  10. 新出炉的 100+ 篇技术热文,在微信热传,别错过哦