首页 > 其他分享 >后缀数组 SA 学习笔记

后缀数组 SA 学习笔记

时间:2024-01-14 23:11:30浏览次数:26  
标签:suf 后缀 笔记 数组 SA 排序 rk

后缀数组 SA 学习笔记

后缀数组处理字符串后缀排名,公共子串类问题十分优秀,可以在部分情况下替代后缀自动机(SAM),本文主要讲解后缀数组的实现过程和部分例题。

正文

定义

后缀:从 \(i\) 开始到字符串结束的一个特殊子串,本文用 \(suf(i)\) 表示从 \(i\) 开始的后缀。

后缀数组 SA:SA 是一维数组,\(SA_i\) 表示所有后缀按字典序排序之后,第 \(i\) 名的后缀的开始位子,即 \(suf(SA_i)\) 在所有后缀中字典序排序是第 \(i\) 名。

名次数组 rk:rk 是一维数组,\(rk_i\) 表示后缀 \(suf(i)\) 和所有后缀按字典序排序后的排名。

倍增算法

前置知识:基数排序。

使用倍增方法,对字符开始的 \(2^k\) 长度的子字符串进行排序,求出其 rk 值。(这里 rk 允许相同)

当 \(2^k\) 大于 \(n\) 以后我们的后缀数组 SA 已经求出。

在求 \(2^k\) 长度的排序时,\(2^{k-1}\) 的排序已经求出,一个长度为 \(2^k\) 的段可以由两个长度为 \(2^{k-1}\) 的段合并得到。

那么把从 \(i\) 开始的前 \(2^{k-1}\) 位之前的排序结果的 rk,看做第一关键字,把后 \(2^{k-1}\) 的排序结果看做第二关键字,对关键字排序从而求出整个排序结果。

附一张 2009 年集训队论文的图:

这里的 \(x\) 为第一关键字,\(y\) 为第二关键字。

在排序时使用基数排序,可以利用上次的排序结果,直接排序好第二关键字。

#include<bits/stdc++.h>
using namespace std;

const int maxn=2e6+5;

int n,m=128;
int sa[maxn],rk[maxn],b[maxn],tmp[maxn];

char s[maxn];

int main()
{
    scanf("%s",s+1);
    n=strlen(s+1);
    for(int i=1;i<=n;i++) ++b[rk[i]=s[i]];
    for(int i=1;i<=m;i++) b[i]+=b[i-1];
    for(int i=n;i;i--) sa[b[rk[i]]--]=i;
    for(int i=1;i<=n;i++) tmp[i]=rk[i];
    int t=0;
    for(int i=1;i<=n;i++)
    {
        if(tmp[sa[i]]==tmp[sa[i-1]]) rk[sa[i]]=t;
        else rk[sa[i]]=++t;
    }
    m=t;

    for(int l=1;l<n;l=l<<1)
    {
        int t=0;
        for(int i=n-l+1;i<=n;i++) tmp[++t]=i;
        for(int i=1;i<=n;i++) if(sa[i]>l) tmp[++t]=sa[i]-l;
        for(int i=1;i<=m;i++) b[i]=0;
        for(int i=1;i<=n;i++) b[rk[tmp[i]]]++;
        for(int i=1;i<=m;i++) b[i]+=b[i-1];
        for(int i=n;i;i--) sa[b[rk[tmp[i]]]--]=tmp[i];//基数排序

        for(int i=1;i<=n;i++) tmp[i]=rk[i];//读取上次排名,修改本次rk
        t=0;
        for(int i=1;i<=n;i++)
        {
            if(tmp[sa[i]]==tmp[sa[i-1]]&&tmp[sa[i]+l]==tmp[sa[i-1]+l]) rk[sa[i]]=t;
            else rk[sa[i]]=++t;//过程中允许排名相同
        }
        m=t;
    }
    for(int i=1;i<=n;i++) printf("%d ",sa[i]);
}

SA-IS

先留个坑

关于后缀数组的应用

定义

height 数组:\(height_i=LCP(suf(SA_i),suf(SA_{i-1}))\)。

求 height 数组

如果直接去求 height 数组是 \(O(n^2)\) 的,并没有利用 SA 的优秀性质。

但这里有一个妙不可言的证明,可以把两者联系起来。

排序后,越接近的两个后缀,他们的 \(LCP\) 肯定越大。数学语言就是 \(|rk_i-rk_j|<|rk_i-rk_k|\),有 \(LCP(suf(i),suf(j))\ge LCP(suf(i),suf(k))\)。

设 \(h_i=height_{rk_i}\),我们有:

\[h_i\ge h_{i-1}-1 \]

画一张图:

其中 \(s_{i-1}\) 到 \(s_j\) 是的长度 \(j-(i-1)+1\) 等于 \(h_{i-1}=height_{rk_{i-1}}\)。

也就是说,\(s_{i-1}\) 到 \(s_j\) 是 \(suf(i-1)\) 和 \(suf(i-2)\) 的最长公共后缀(\(LCP\))。

那么 \(s_i\) 到 \(s_j\) 这一段区间肯定能够找到和其一样的区间,所以 \(h_i=height_{rk_i}\) 至少为 \(j-i+1\),即 \(h_{i-1}-1\)。

得证。

height 数组的实际运用

height 数组的实际运用有很多,这里先提出一个运用,后面例题再分析:

求 \(LCP(suf(i),suf(j))\ (i\neq j)\)。

不妨设 \(rk_i < rk_j\)。

理解一下,有

\[LCP(suf(i),suf(j))=\min_{k=rk_i+1}^{rk_j} height_k \]

上图:

不难证明上述结论,留作读者自己思考。

例题

下次补。

标签:suf,后缀,笔记,数组,SA,排序,rk
From: https://www.cnblogs.com/binbinbjl/p/17964417

相关文章

  • 2024/1/14 算法笔记
    1.图论的反向建边一般问题:有向图的多个起点到一个终点的最短距离是最短路的变式。我们只需要把图的箭头反向(正向变逆向,逆向变正向)矩阵:mp[u,v]=cost---->mp[v,u]=cost邻接表也是类似的方法[P2853USACO06DEC]CowPicnicS-洛谷|计算机科学教育新生态(luo......
  • MYISAM和INNODB的区别
    INNODB支持事务,而MYISAM不支持事务。INNODB支持外键,而MYISAM不支持外键。MYISAM中B+Tree的数据结构存储的内容是实际数据的地址值,它的索引和实际数据是分开的,只不过使用索引指向了实际数据。这种索引的模式被称为非聚集索引。InnoDB中B+树的数据结构中存储的都是实......
  • 超级简单的后缀数组(SA)笔记!!
    超级简单的后缀数组(SA)!!(未完)前言这里选择当一手标题党。由于刚学完这个字符串算法,本人字符串算法又比较薄弱,好不容易这一次在晚修看各种资料看得七七八八,决定趁脑子清醒的时候记录下来。免得自己不久后忘了后又要痛苦地再看各种资料。希望这篇博客能帮到你。前置知识:RMQ问题......
  • The 2nd Universal Cup Stage 18: Dolgoprudny H
    题意大概是说求有所有有标号有根树及其黑白染色方案使得定义\(S_{x}\)为\(x\)和其儿子节点构成的集合,则\(S_{x}\)中的黑色节点个数要求不少于白色节点个数,且定义\(x\)的白色节点个数为\(cnt_{x}\),则其方案的贡献为\(\sum_{i=1}^{n}cnt_{i}!\)(原题意这里似乎说的非常抽......
  • PostgreSQL 数据库日志收集功能开启一什么时候写-参数 log_min_messages 等其他参数设
    log_min_messages(enum)控制将哪些消息级别写入服务器日志。可以取值为:DEBUG5、DEBUG4、DEBUG3、DEBUG2、DEBUG1、INFO、NOTICE、WARNING、ERROR、LOG、FATAL、PANIC。每个关卡都包含了它之后的所有关卡。级别越高,发送到日志的消息就越少。默认值是WARNING。注意,这里的LOG......
  • 学习Java笔记 - Day2
    Java特性优势简单性:基于C,纯净版的C++面向对象:一切皆对象可移植性:Writeonce,runanywhere-跨平台高性能:及时编译,效率分布式:为网络分布式环境设计,可处理TCP/IP协议,通过URL,访问网络资源,相当于本地资源,简单。支持远程的方法调用。动态性:反射机制,有了动态性。多线程:看视频,......
  • 【动手学深度学习_李沐】笔记:(七)循环神经⽹络
    【七、循环神经⽹络】1.序列模型序列模型估计方法有自回归模型和隐变量自回归模型。在统计学中,前者(超出已知观测值的预测)称为外推(extrapolation),后者(在现有观测值之间进⾏估计)称为内插(interpolation)。内插和外推在难度上有很⼤差别,因此,在训练时要尊重数据的时间顺序,不要对未来......
  • 【动手学深度学习_李沐】笔记:(六)现代卷积神经⽹络
    【六、现代卷积神经⽹络】1.深度卷积神经⽹络(AlexNet)在2012年以前,神经⽹络往往被其他机器学习⽅法超越,如支持向量机(supportvectormachines)。而AlexNet在2012年ImageNet挑战赛中取得了轰动⼀时的成绩,在⽹络的最底层,模型学习到了⼀些类似于传统滤波器的特征抽取器。论......
  • 【动手学深度学习_李沐】笔记:(五)卷积神经⽹络(convolutional neural network,CNN)
    【五、卷积神经网络】笔记1.从全连接层到卷积特点(沃尔多检测器):①平移不变性:不管出现在图像中的哪个位置,神经⽹络的底层应对相同图像区域做出类似的响应,因此能够以相同的⽅式处理局部图像②局部性:神经⽹络的底层只探索输⼊图像的局部区域,这些局部特征可以融会贯通,在整个......
  • 搜索学习笔记+杂题 (基础二 dfs/bfs的拓展)
    搜索杂题:博客中讲述的题的题单:戳我二、dfs/bfs的各种变式1、深搜深搜以指数级的时间复杂度闻名,稍不注意时间就会爆炸,所以一般会用到剪枝的技巧(这个技巧基本上是因题而异,需要平时的刷题与积累)。深搜同样也是一种可变性极高的算法(其实都可以不叫做一种算法,深搜已经是一种做题的......