2715. 后缀数组

题目链接

2715. 后缀数组

给定一个长度为 n 的字符串,只包含大小写英文字母和数字。

将字符串中的 n 个字符的位置编号按顺序设为 1n

并将该字符串的 n 个非空后缀用其起始字符在字符串中的位置编号表示。

现在要对这 n 个非空后缀进行字典序排序,并给定两个数组 SAHeight

排序完成后,用 SA[i] 来记录排名为 i 的非空后缀的编号,用 Height[i] 来记录排名为 i 的非空后缀与排名为 i1 的非空后缀的最长公共前缀的长度(1in)。

特别的,规定 Height[1]=0

请你求出这两个数组。

输入格式

共一行,包含一个长度为 n 的仅包含大小写英文字母或数字的字符串。

输出格式

第一行包含 n 个整数,表示 SA 数组。

第二行包含 n 个整数,表示 Height 数组。

数据范围

1n106

输入样例:

abababab

输出样例:

7 5 3 1 8 6 4 2 0 2 4 6 0 1 3 5

解题思路

后缀数组,倍增

后缀数组有两个关键的数组:
在后缀形成的数组 x[i] 中,

  • sa[i] 数组表示数组 x 中 排名为 i 的数组下标

  • height[i] 表示 sa[i]sa[i1] 的最长公共前缀

sa
基于基数排序的思想,设置一个桶 c[i] 表示值为 i 的数的数量,先将所有后缀子串按第一个字符排序,基数排序是稳定的,当第一个字符相等时不会改变原后缀子串的顺序,每次排序按长度排序,长度不足后面用字典序最小的字符补齐,例如前一次倍增排好了长度为 k 的子串,此时排序长度为 2×k 的子串,由于基数排序的稳定性,整体上可以将长度为 2×k 的子串先将后 k 个字符排序,再将前 k 个字符排序,这样可以保证对于长度为 2×k 的子串来说,前 k 个子串相等,后 k 个子串也会是排好序的,这样长度为 2×k 的子串就排好了序,同时可求解 sa 数组

  • 时间复杂度:O(nlogn)

height

先定义函数 lcp(i,j):表示排名为 ij 的最长公共前缀,则有 lcp(i,j)=min(lcp(i,k),lcp(k,j)),其中 i<j
证明:先证明 min(lcp(i,k),lcp(k,j))lcp(i,j),假设 lcp(i,j) 为某一段,如果 lcp(i,k) 要小于该段,显然会导致 k 不应该处在 ij 之间,矛盾,故 lcp(i,k)lcp(i,j),同理 lcp(j,k)lcp(i,j),即 min(lcp(i,k),lcp(k,j))lcp(i,j);再证明 min(lcp(i,k),lcp(k,j))lcp(i,j),当 lcp(i,k)>lcp(i,j) 时,如果 lcp(k,j)>lcp(i,j),此时会导致 lcp(i,j) 不止原来这么小,所以 lcp(k,j)lcp(i,j),故 min(lcp(i,k),lcp(k,j))lcp(i,j),当 lcp(i,k)lcp(i,j) 时,显然也有 min(lcp(i,k),lcp(k,j))lcp(i,j),故 lcp(i,j)=min(lcp(i,k),lcp(k,j)),很容易引申出 lcp(i,j)=min(lcp(i,i+1),lcp(i+1,i+2),,lcp(j1,j))

再定义数组 h[i] 表示下标为 i 的后缀和排名为 sa[i]1 的后缀的最长公共前缀,则有 h[i]h[i1]1
证明:假设第 i1 个后缀的排名比第 i 个后缀靠前,靠后的话也是同样考虑,如果 h[i1]=0 即第 i1 个后缀与前一个排名的后缀的第一个字符不相等,显然有 h[i]01,否则第 i1 个后缀除去第一个字符正好对应第 i 个后缀,考虑第 i1 个后缀的前一个排名的后缀除去第一个字符后的后缀 k,除去第一个后缀后,两者顺序不变,第 i1 个后缀变为第 i 个后缀,所以此时 ki 的前面,由上面的定理,lcp(k,i)=min(lcp(k,k+1),lcp(k+1,k+2),,lcp(i1,i)),即有 lcp(k,i)lcp(i1,i),即 h[i1]1h[i]
定义数组 rk[i] 表示下标为 i 的排名,则 rk[sa[i]]=i
而又有 height[rk[i]]=h[i],求出 h 等价于求出 height 数组

  • 时间复杂度:O(n)

代码

// Problem: 后缀数组 // Contest: AcWing // URL: https://www.acwing.com/problem/content/2717/ // Memory Limit: 64 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=1e6+5; int n,m,cnt,c[N],x[N],y[N],sa[N],rk[N],height[N]; char s[N]; void get_sa() { for(int i=1;i<=n;i++)c[x[i]=s[i]]++; for(int i=2;i<=m;i++)c[i]+=c[i-1]; for(int i=n;i>=1;i--)sa[c[x[i]]--]=i; for(int k=1;k<=n;k<<=1) { cnt=0; for(int i=n-k+1;i<=n;i++)y[++cnt]=i; for(int i=1;i<=n;i++) if(sa[i]>k)y[++cnt]=sa[i]-k; for(int i=1;i<=m;i++)c[i]=0; for(int i=1;i<=n;i++)c[x[i]]++; for(int i=2;i<=m;i++)c[i]+=c[i-1]; for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0; //y[i]表示排名为i的下标,x[y[i]]表示排名为i的权值 swap(x,y); x[sa[1]]=1,cnt=1; for(int i=2;i<=n;i++) x[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k])?cnt:++cnt; if(cnt==n)break; m=cnt; } } void get_height() { for(int i=1;i<=n;i++)rk[sa[i]]=i; for(int i=1,k=0;i<=n;i++) { if(rk[i]==1)continue; if(k)k--; int j=sa[rk[i]-1]; while(i+k<=n&&s[i+k]==s[j+k])k++; height[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 ",height[i]); return 0; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/16201087.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示