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

后缀数组 学习笔记

时间:2023-10-30 10:37:13浏览次数:34  
标签:后缀 Rank Height MAXN 笔记 数组 Sa

后缀数组 学习笔记

定义

我们定义后缀数组 \(Sa\) 中的元素 \(Sa_i\) 为,字典序排名为 \(i\) 的后缀所在的位置。我们定义排名数组 \(Rank\) 中的元素 \(Rank_i\) 为,在位置 \(i\) 的后缀的排名。

求解后缀数组

首先 \(O(n^2logn)\) 的解法很显然,直接排序。

那么有一个利用倍增的思想求解后缀数组的 \(O(nlog^2n)\)的解法 ,首先,我们优先对单个字符排序,发现出现重复的话,我们倍增扩大长度,再次进行比较,直到每个后缀的排名各不相同。

其实还有 \(O(nlogn)\) 和 \(O(n)\) 的解法,详见 后缀数组简介 - OI Wiki

Code

/*
 * @Author: Ehundategh
 * @Date: 2023-10-22 20:15:56
 * @FilePath: \Code\Model\String\SA.cpp
 * @Description: You Steal,I kill
 */
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 2000010
using namespace std;
int Suffix_Array[MAXN],Rank[MAXN],n,k,Height[MAXN];
int Temp[MAXN];
char In[MAXN];
bool cmpsa(int i,int j){
    if(Rank[i]!=Rank[j]){
        return Rank[i]<Rank[j];
    }
    else{
       int Secondi=i+k<=n?Rank[i+k]:-1;
       int Secondj=j+k<=n?Rank[j+k]:-1;
       return Secondi<Secondj;
    }
}
void Get_SA(){
    for(int i=1;i<=n;i++){
        Suffix_Array[i]=i; Rank[i]=In[i];
    }
    for(k=1;k<=n;k*=2){
        sort(Suffix_Array+1,Suffix_Array+n+1,cmpsa);
        Temp[Suffix_Array[1]]=0;
        for(int i=1;i<=n;i++){
            Temp[Suffix_Array[i+1]]=Temp[Suffix_Array[i]]+(cmpsa(Suffix_Array[i],Suffix_Array[i+1])?1:0);
        }
        for(int i=1;i<=n;i++) Rank[i]=Temp[i];
    }
    return;
}
int main(){
    scanf("%s",In+1);
    n=strlen(In+1);
    Get_SA();
    for(int i=1;i<=n;i++) printf("%d ",Suffix_Array[i]);
}

这样就可以求出后缀数组了。

例题:

[[JSOI 2007]字符加密]([P4051 JSOI2007] 字符加密 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn))

思路
首先我们先把字符串复制一遍,那么将全新的字符串的后缀排序,从后缀排名从小到大遍历,如果出现后缀位置在原串中,就输出当前位置前一个位置的字符

高度数组

高度数组,后缀数组的神。

分为以下三个部分讲解:

  • 定义
  • 求解
  • 例题

定义

首先我们先定义 \(LCP(S1,S2)\)(longest Common Prefix) 即最长公共前缀,为:一个最大的 \(x\) 满足 $ S1_i=S2_i (\forall1\le i\le x)$。而我们下面提到的 \(LCP(a,b)\),都是表示以 \((a,b)\) 两个位置为起点的后缀的最长公共前缀。

我们定义:\(Height_i=LCP(Sa_i,Sa_{i-1})\),特别地, \(Height_1=0\)。

举个例子,方便理解高度数组。

我们来看以下的字符串:\(\texttt{abcbc}\),对于这一个字符串,我们尝试求解其高度数组。

首先有 \(Height_1=0\)。

然后我们来求解 \(Height_2\),将 \(Sa_2\) 与 \(Sa_1\) 作比较求最长公共前缀,也就是求解 \(\texttt{abcbc}\) 和 \(\texttt{bc}\) 的最长公共前缀,也就是 \(0\)。

然后对于 \(Height_3\),将 \(Sa_3\) 与 \(Sa_2\) 作比较求最长公共前缀,那么比较 \(\texttt{bc}\) 和 \(\texttt{bcbc}\) 即可,答案为 \(2\)。

对于 \(Height_4\) 和 \(Height_5\) 同理可得答案为,\(0,1\)。

求解

求解有一个神奇的引理 \(Height_{Rank_i} \ge Height_{Rank_{i-1}}-1\)。

关于这个引理的证明。详见2009国家队论文。

那么我们就可以通过这个引理,来实现 \(O(n)\) 时间复杂度的高度数组处理。

每次尝试与排名上一个的后缀比较,拓展得到当前的 \(Height_i\) 即可,而拓展也不用从 \(0\) 开始,从上次的 \(Height_i-1\) 开始即可,这样高度数组最多变化 \(3n\) 次,所以时间复杂度是 \(O(n)\)。

代码

void Get_Height(){
    for(int i=1,k=0;i<=n;i++){
        if(Rank[i]==0)continue;
        if(k>0) --k;
        while(In[i+k]==In[Suffix_Array[Rank[i]-1]+k])++k;
        Height[Rank[i]]=k;
    }
}

例题

ADASTRNG - Ada and Substring

思路
高度数组模板题,对于每一个字符开头的后缀只需要每次统计总共的子串个数,然后再减去两两后缀的最长公共前缀去重即可。

UVA Editor

思路
对于每一组询问,求Height的max即可。(话说这道题有一个加强版,要求出现k次,但是其实是一个做法,只需要求相邻k-1个的Height的max即可)

标签:后缀,Rank,Height,MAXN,笔记,数组,Sa
From: https://www.cnblogs.com/Ehundategh/p/17797194.html

相关文章

  • 整型数组按照字典序排序
    整型数组按照字典序排序输入...0,1,2,3,5,7,8,1001,109...输出...0,1,10,1001,2,3,5,7,8Collections.sort(list,newComparator<Integer>(){@Overridepublicintcompare(Integero1,Integero2){StringA=o1+"";StringB=......
  • 黑马程序员2023新版JavaWeb开发教程学习笔记
    前言该笔记灵感来源于B站《黑马程序员2023新版JavaWeb开发教程,实现javaweb企业开发全流程(涵盖Spring+MyBatis+Springboot》源视频地址:黑马程序员2023新版JavaWeb开发教程个人声明:本文记录个人在进行该视频学习中的知识总结,帮助大家能更快地进行对该视频内容的学习;由于该视频对......
  • C#学习笔记之使用Access读取Excel表格
    一、读取Excel表的内容(使用DataSet)1.DataSet定义:表示数据在内存中的缓存。可以理解为,将从Excel表中读取出来的数据存入DataSet类中,之后对DataSet进行数据处理,能提高处理的速度。2.DataSet属性和方法:①属性CaseSensitive:获取或设置一个值,该值指示DataTable中的字符串是否区分......
  • .NET中的数组在内存中如何布局?
    总的来说,.NET的值类型和引用类型都映射一段连续的内存片段。不过对于值类型对象来说,这段内存只需要存储其字段成员,而对应引用类型对象,还需要存储额外的内容。就内存布局来说,引用类型有两个独特的存在,一个是字符串,另一个就是数组。我在《你知道.NET的字符串在内存中是如何存储的吗?......
  • C#入门到精通读书笔记
    一、C#编程基础//usingstaticSystem.Console以简化代码//Main方法中intnumberOfApples=12;decimalpricePerApple=0.35M//C#中声明变量为十进制10使用decimal,并且在数字后加字母MConsole.WriteLine( format:"{0}applescosts{1:C}",//使用编号的未知参数可以使得字符......
  • 《有效需求分析》阅读笔记
    《有效需求分析》是一本关于如何进行有效需求分析的书籍,作者通过实际案例和理论知识的结合,详细介绍了需求分析的重要性、方法和技巧。在阅读这本书的过程中,我深刻地认识到了需求分析在软件开发过程中的关键作用,以及如何运用一些实用的方法来提高需求分析的质量。以下是我在阅读过......
  • FreeSWITCH的moh使用笔记
    操作系统:CentOS7.6_x64FreeSWITCH版本:1.10.9之前写过FreeSWITCH安装的文章,今天整理下moh使用过程中遇到的问题及解决方案,并提供moh音频下载途径。FreeSWITCH安装的文章可参考如下链接:docker构建FreeSWITCH编译环境及打包使用docker构建可动态启动的FreeSWITCH实例CentOS7环......
  • Kafka基础学习笔记
    一、Kafka:1、简介:Kafka是由Apache开源,具有分布式、分区的、多副本的、多订阅者,基于Zookeeper协调的分布式处理平台,由Scala和Java语言编写。最大的特性就是可以实时并高速的处理大量数据来满足需求,同时对消息数据进行持久化存储。2、优点:Kafka与其他消息队列MQ(如ActiveMQ、Rabb......
  • PE文件文件笔记
    PEPE简介可执行文件(executablefile)指的是可以由操作系统进行加载执行的文件。大致有两种可执行文件的格式:PE文件格式(Windows平台);ELF文件格式(Linux平台)。其中常见的PE文件格式的可执行文件有:exe,sys,dll等。PE文件格式与win32汇编的关系由于EXE文件被执行、传播......
  • linux基本文件命令复习笔记
    1,放大缩小终端窗口字体  放大 ctrlshift+=   缩小  ctrl-2,6个常见终端命令 (1)ls  查看当前文件夹下的内容 (2)pwd 查看当前所在文件夹  (3)cd目录名 切换文件夹 (4)touch文件名 如果文件不存在,新建文件。和mkdir不同的是,mkdir创......