后缀数组--SA--字符串
SA (Suffix Array) -- 后缀数组
简介
这里明白两个定义:
\(SA_i\) : 按字典序排列后大小为 \(i\) 的后缀的后缀头的下标。
\(Rank_i\) : 后缀头的下标为 \(i\) 按字典序排列后的排名。
一个显而易见却很重要的结论:
\]
如何进行后缀排序?
\(O(n^2 \log n)\)
jb 方式,直接处理出所有后缀,直接 sort , 字符串匹配的时间复杂度为 \(O(n)\) , 乘在一起是 \(O(n^2\log n)\) 德
\(O(n \log^2n)\)
需要神奇的倍增做法。
这时候我们贴一个图图:

我们先从长度为 \(1\) 开始。 考虑我们更新后的为 \(2\times i\) . 所以只需要将 \(Rank_j\) 和 \(Rank_{\frac{i}{2} + j}\) 作为第一关键字和第二关键字排序即可。
\(O(n\log n)\)
我们发现那个 gb 时间复杂度变为 \(\log^2\) 多乘了一个 \(\log\) 的原因为快速排序。所以考虑 \(O(n)\) 复杂度的排序。
好的通过基数排序就可以进行 \(O(n \log n)\) 了(乐)
但是把这个玩意交到 \(LOJ\) 就发现 \(\color{yellow}T\) .
主要是因为介个玩意常数忒大。
考虑其实我们填一个这个东西本来是第一关键字排好的,其第二关键字在末尾的有可能是 \(0\) , 所以把 \(0\) 的放在最前面,剩下的按原序排好,进行一遍排序即可。
code
CODE
#include <bits/stdc++.h>
#define int long long
using namespace std ;
const int N = 2e6 + 114514 ;
inline int read() {
int x = 0 , f = 1 ;
char c = getchar() ;
while ( c < '0' || c > '9' ) {
f = c == '-' ? -f : f ;
c = getchar() ;
}
while ( c >= '0' && c <= '9' ) {
x = x * 10 + c - '0' ;
c = getchar() ;
}
return x * f ;
}
char s[N] ;
int Rank[N] , Lstrk[N] , SA[N] , n , m = 127 ;
int cnt[N] , key1[N] , id[N] , p ;
signed main() {
#ifndef ONLINE_JUDGE
freopen( "1.in" , "r" , stdin ) ;
freopen( "1.out", "w" ,stdout ) ;
#endif
auto compare = [](int x , int y , int j) {
return Lstrk[x] == Lstrk[y] && Lstrk[x + j] == Lstrk[y + j] ;
} ;
cin >> s + 1 ;
n = strlen( s + 1 ) ;
for ( int i = 1 ; i <= n ; ++ i ) { Rank[i] = s[i] ; ++ cnt[Rank[i]] ; }
for ( int i = 1 ; i <= m ; ++ i ) { cnt[i] += cnt[i - 1] ; }
for ( int i = n ; i >= 1 ; -- i ) { SA[cnt[Rank[i]] --] = i ; }
for ( int j = 1 ; ; j <<= 1 , m = p ) {
p = 0 ;
for ( int i = n ; i > n - j ; -- i ) id[++ p] = i ;
for ( int i = 1 ; i <= n ; ++ i ) {
if ( SA[i] - j > 0 ) id[++ p] = SA[i] - j ;
}
memset( cnt , 0 , sizeof(cnt) ) ;
for ( int i = 1 ; i <= n ; ++ i ) { ++ cnt[key1[i] = Rank[id[i]]] ; }
for ( int i = 1 ; i <= m ; ++ i ) { cnt[i] += cnt[i - 1] ; }
for ( int i = n ; i >= 1 ; -- i ) { SA[cnt[key1[i]] --] = id[i] ; }
memcpy( Lstrk , Rank , sizeof(Rank) ) ;
p = 0 ;
for ( int i = 1 ; i <= n ; ++ i ) {
Rank[SA[i]] = compare( SA[i] , SA[i - 1] , j ) ? p : ++ p ;
}
if ( p == n ) break ;
}
for ( int i = 1 ; i <= n ; ++ i ) {
cout << SA[i] << ' ' ;
}
}
\(height\) 数组
你会后缀排序却不会 \(height\) 数组就像你会求 \(Next\) 数组却不会 \(KMP\) 匹配一样。 —— Wang54321
定义一个东西: \(height_i\) 表示 \(Rank\) 为 \(i\) 的和 \(Rank\) 为 \(i - 1\) 的的 \(LCP\)
即 \(LCP(i , i - 1)\)
证明引理
求的话需要证一个引理:
\[height_{Rank[i]} \ge height_{Rank[i - 1]} - 1
\]
如果 \(height_{Rank[i - 1]} \le 1\) 时,这不显然嘛。
else :
我们将具体的东西表示出来:
\(height_{Rank[i]} = LCP(SA[Rank[i]] , SA[Rank[i] - 1]) = LCP(i , SA[Rank[i] - 1])\)
\(height_{Rank[i - 1]} = LCP(SA[Rank[i - 1]] , SA[Rank[i - 1] - 1]) = LCP(i - 1 , SA[Rank[i - 1] - 1])\)
我们已知 \(height_{Rank[i - 1]}\) 是 \(\ge 1\) 的,设那个 \(1\) 为 \(a\) .
所以 \(i - 1\) 可以设为 \(aAC\) , \(SA[Rank[i - 1] - 1]\) 设为 \(aAD\) .
\(aA\) 为其最长公共前缀。
我们这个 \(C\) 是 \(>\) \(D\) 的。
我们知道 \(i\) 为 \(AC\) 。且 \(SA[Rank[i] - 1]\) 和 \(AC\) 中不含有任何后缀。
所以 \(AD \le SA[Rank[i] - 1] < AC\)
所以一定含有公共前缀 \(A\) .
\(\therefore height_{Rank[i]} \ge height_{Rank[i - 1]} - 1\)
证毕.
求 \(height\)
有了上面的引理就可以求了。代码:
CODE
for ( int i = 1 , k = 0 ; i <= n ; ++ i ) {
if ( Rank[i] == 0 ) continue ;
if ( k ) k -- ;
while ( s[i + k] == s[Rank[i - 1] + k] ) k ++ ;
height[Rank[i]] = k ;
}
\(height\) 的用法
如果你只知道这是个什么玩意却不知道怎么用和不知道显然没什么区别。
一个很明显的东西:
\[LCP(l , r) = \min_{l + 1 \le i \le r}\{height_{i}\}
\]
感性理解一下:
这两段之间,如果前缀有变更,就直接没了吧,其实变更处的 \(LCP\) 就是其 \(LCP\) .
然后就可以把两个字符串的公共长度问题变为了 \(RMQ\) 问题。即 \(ST\) 表维护区间最小值了。
例题(后缀数组配合单调栈): AHOI差异
一个无脑题, \(HASH\) 可过的那种: Sandy 的卡片
结尾撒花 \(\color{pink}✿✿ヽ(°▽°)ノ✿\)
后缀数组--SA--字符串的更多相关文章
- [笔记]后缀数组SA
参考资料这次是真抄的: 1.后缀数组详解 2.后缀数组-学习笔记 3.后缀数组--处理字符串的有力工具 定义 \(SA\)排名为\(i\)的后缀的位置 \(rk\)位置为\(i\)的后缀的排名 \(t ...
- 后缀数组(SA)总结
后缀数组(SA)总结 这个东西鸽了好久了,今天补一下 概念 后缀数组\(SA\)是什么东西? 它是记录一个字符串每个后缀的字典序的数组 \(sa[i]\):表示排名为\(i\)的后缀是哪一个. \(r ...
- 后缀数组SA学习笔记
什么是后缀数组 后缀数组\(sa[i]\)表示字符串中字典序排名为\(i\)的后缀位置 \(rk[i]\)表示字符串中第\(i\)个后缀的字典序排名 举个例子: ababa a b a b a rk: ...
- 后缀数组SA入门(史上最晦涩难懂的讲解)
参考资料:victorique的博客(有一点锅无伤大雅,记得看评论区),$wzz$ 课件(快去$ftp$%%%),$oi-wiki$以及某个人的帮助(万分感谢!) 首先还是要说一句:我不知道为什么我这 ...
- 【字符串】后缀数组SA
后缀数组 概念 实际上就是将一个字符串的所有后缀按照字典序排序 得到了两个数组 \(sa[i]\) 和 \(rk[i]\),其中 \(sa[i]\) 表示排名为 i 的后缀,\(rk[i]\) 表示后 ...
- poj 3518 Corporate Identity 后缀数组->多字符串最长相同连续子串
题目链接 题意:输入N(2 <= N <= 4000)个长度不超过200的字符串,输出字典序最小的最长公共连续子串; 思路:将所有的字符串中间加上分隔符,注:分隔符只需要和输入的字符不同, ...
- bzoj3796(后缀数组)(SA四连)
bzoj3796Mushroom追妹纸 题目描述 Mushroom最近看上了一个漂亮妹纸.他选择一种非常经典的手段来表达自己的心意——写情书.考虑到自己的表达能力,Mushroom决定不手写情书.他从 ...
- poj 3294 后缀数组 多字符串中不小于 k 个字符串中的最长子串
Life Forms Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 16223 Accepted: 4763 Descr ...
- Codeforces VK Cup 2015 A.And Yet Another Bracket Sequence(后缀数组+平衡树+字符串)
这题做得比较复杂..应该有更好的做法 题目大意: 有一个括号序列,可以对其进行两种操作: · 向里面加一个括号,可以在开头,在结尾,在两个括号之间加. · 对当前括号序列进 ...
- POJ2774 Long Long Message —— 后缀数组 两字符串的最长公共子串
题目链接:https://vjudge.net/problem/POJ-2774 Long Long Message Time Limit: 4000MS Memory Limit: 131072 ...
随机推荐
- USB OTG有关协议
想了解USB OTG的工作原理,需要知道三个协议: ADP:Attach Detection Protocol HNP:Host Negotiation Protocol SRP:Session Re ...
- sqlmap 环境搭建 sqli-labs平台搭建
sqlmap 环境搭建: windows 1.先去官网下载:https://sqlmap.org/ 2.在python的Scripts目录下创建一个sqlmap 把官网下载的东西解压到那里 3.添加环 ...
- unp.h的安装以及第一个程序的运行
unp.h的安装以及第一个程序的运行 源代码下载以及编译 点击此处下载源代码 解压到本地文件夹,如果访问不了GitHub的话就用我搬到gitee的仓库吧 git clone https://gitee ...
- P8451 题解
显然,题面明摆着让你写一个可持久化 AC 自动机. 但是从空间来说这是不可能的. 想起做 不强制在线 的可持久化数据结构的一种方法,建立"时光树",具体来说,假若版本 \(x\) ...
- Python 潮流周刊#59:Polars 1.0 发布了,PyCon US 2024 演讲视频也发布了(摘要)
本周刊由 Python猫 出品,精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章.教程.开源项目.软件工具.播客和视频.热门话题等内容.愿景:帮助所有读者精进 Python 技术,并增长职 ...
- input标签 手机端数字键盘
要一点击提起数字键盘,安卓只要设置input的类型是number或tel, ios 需要 pattern="number"可以直接打开搜狗输入法的数字键盘,可以输入.和数字如果只能 ...
- 循环冗余检验CRC
先约定一个除数,除数根据生成多项式来决定,余数的位数就是除数位数减一,在被除数后面添加余数个0,然后进行运算. 注意虽然叫做除数被除数,但是是按位进行异或操作,相同为1,不同为0,最后的余数就叫帧检验 ...
- SQL Server 截取字符串
select top 100 substring(qr_code,8,8) ,* from [LiuJun_PKh_lcfc_hf] --where right(ri,8) or substring( ...
- 洛谷P1029 [NOIP2001 普及组] 最大公约数和最小公倍数问题
[NOIP2001 普及组] 最大公约数和最小公倍数问题 题目描述 洛谷题目链接:https://www.luogu.com.cn/problem/P1029 输入两个正整数 x, y,求出满足下列条 ...
- 【HTML】hr标签 分割线 自定义样式
参考: https://blog.csdn.net/weixin_40716682/article/details/90064473 默认是有边框线的 删除边框线,设置高度和背景色即可 backgro ...