首页 > 其他分享 >SA:从入门到入土

SA:从入门到入土

时间:2024-10-30 12:34:41浏览次数:1  
标签:入门 int 后缀 入土 基数排序 -- sa SA

基本应用

读入一个长度为 $ n $ 的由大小写英文字母或数字组成的字符串,请把这个字符串的所有非空后缀按字典序(用 ASCII 数值比较)从小到大排序。

解法

1.将每个后缀取出来,直接排序 \(O(n^2 \log n)\)
2.用hash二分LCP比较下一位,\(O(n \log^2 n)\)
3.倍增求后缀数组,\(O(n \log n)\)
4.高级方法求后缀数组,\(O(n)\)

倍增

先比较每个后缀的第一位,再比较前两位,前四位...
问题在于如何快速比较前两位,前四位。
一个有趣的性质是在比较\(2^k\)位时,我们知道\(2^{k-1}\)位的大小,所以\(2^k\)位的大小只与前一半\(2^{k-1}\)和后一半\(2^{k-1}\)有关,所以可以用基数排序由上一层推到这一层。
image

基数排序

正常基数排序,是按数位从高到低依次比较大小,比如说三位数,就先比较百位的数字,将百位为 \(0\) 的放在一起,将百位为 \(1\) 的放在一起...。然后,对十位进行比较,在百位为 \(0\) 的里面把十位为 \(0\) 的放在一起,十位为 \(1\) 的放在一起...,最后所有数都有序。
SA的基数排序,就是相当于只有两位数来排序。

代码实现

代码比较抽象要多理解,多思考

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,m,sa[N],rk[N],x[N],y[N],cnt,num;
char s[N];
void SA()
{
	for(int i=1;i<=n;i++)rk[x[i]=s[i]]++;//rk辅助数组,x是上一层的排名
	for(int i=1;i<=m;i++)rk[i]+=rk[i-1];
	for(int i=n;i>=1;i--)sa[rk[x[i]]--]=i;//正序倒序都可以,sa是排名为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;//如果可以做后一半,就做
		//正序枚举,因为y的顺序是后一半从小到大的顺序
		for(int i=1;i<=m;i++)rk[i]=0;//清零
		for(int i=1;i<=n;i++)rk[x[i]]++;//根据前一半
		for(int i=1;i<=m;i++)rk[i]+=rk[i-1];
		for(int i=n;i>=1;i--)sa[rk[x[y[i]]]--]=y[i],y[i]=0;//后一半更大的在前一半相同时排后面
		swap(x,y);//y临时存一下上一层x的值。
		x[sa[1]]=1,num=1;
		for(int i=2;i<=n;i++)
		{
			x[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;
	} 
    for(int i=1;i<=n;i++)cout<<sa[i]<<' ';
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
    cin>>s+1;
    n=strlen(s+1),m=150;
    SA();
	return 0;
} 

进阶应用

标签:入门,int,后缀,入土,基数排序,--,sa,SA
From: https://www.cnblogs.com/storms11/p/18515610

相关文章

  • 轻松上手CANoe Scenario Editor———智能网联工程师入门篇
    (小编先带大家扫盲一下)V2X(Vehicle-to-Everything,车与万物通信)是一种先进的通信技术,使车辆能够与周围环境进行信息交换。这不仅包括与其他车辆(V2V)的互动,还涵盖与基础设施(V2I)和行人(V2P)的通信。通过V2X,车辆能够实时获取周围信息,从而提升行驶安全性和交通效率,真正实现智能交通的愿景。......
  • 科普文:软件架构网络系列之【信创:SAN 交换机“卡脖子”,RoCE V2 成破局关键】
    概叙目前,不少企业数据中心使用FC交换机和集中式SAN存储(以下简称“FC-SAN架构”),支持核心业务系统、数据库、AI/ML等高性能业务场景。科普文:软件架构Linux系列之【非信创方案VMAX250F:城商行核心存储系统升级改造和统一存储监控实现实践分享】李军华-CSDN博客而在开展IT......
  • 电赛入门之软件keil+cubemx
    hal库可以帮我们一键生成许多基本配置,就不需要自己写了,用多了hal库就会发现原来用基本库的时候都过的什么苦日子(笑下面我们以f103c8t6,也就是经典的最小核心板来演示一、配置工程首先来新建一个工程这里我们配置rcc和sys,sys这个选择高时钟然后我们点上面栏第二个,可以......
  • pwn入门体验
    pwn真的最初让人敬而远之,但现在发现一些刚入门的题目稍微学一下还是能做的,起码了解pwn的答题模式。。。本篇讲一讲我入门遇到的一些困难和我的解决方案,以供借鉴。首先是配环境,配环境一定一定要跟着视频教程走,而且是pwn方向的最新的教程。我最初自己装ubuntu,调这个调那个装了两......
  • python 入门九大排序:1冒泡排序2插入排序3选择排序4快速排序5归并排序6堆排序7计数排序
    1冒泡排序:冒泡排序是一种简单的排序算法,它重复地遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。代码如下:importnumpyasnpdefbubbling(arr):n=len(arr)foriinrange(n-1):forjinrange(n-i-1):ifarr[j......
  • P9131 [USACO23FEB] Problem Setting P 题解
    P9131[USACO23FEB]ProblemSettingP题解注意到最终形成的困难序列是一个不断包含的子集的关系,包含是非严格单调的,考虑转化为单调的形式易于计数dp。具体地,对于一些相同的困难值\(i\),算出其内部排列数\(g(i)\),于是转化成了单调的dp形式。于是实际上计算\(dp_{i}\)表示......
  • ctfshow-web入门-爆破(24)
    1.根据题目提示:参考PHP随机数的伪随机数mt_srand(seed);函数播种MersenneTwister随机数生成器。seed,可选。从PHP4.2.0开始,随机数生成器自动播种,因此没有必要使用该函数因此不需要播种,并且如果设置了seed参数生成的随机数就是伪随机数,意思就是每次生成的随机数是一......
  • 2024/10/29 HTML --》关于HTML的快速入门与标签
    以下为快速入门部分点击查看代码--HTML--什么是HTML?--·HTML是一门语言,所有的网页都是用HTML这门语言编写出来的.--·HTML(HyperTextMarkupLanguage):超文本标记语言---->超文本:超越了文本的限制,比普通文本更强大。除了文字信息,还可以定义图片、音频、视频等内......
  • SS241007D. 航行(sail)
    SS241007D.航行(sail)题意在区间\([1,n]\)上,每个位置有参数\(p_i\),每个时刻,你在\(i\)航道,有\(p_i\)的概率速度\(-1\),有\(1-p_i\)的概率速度\(+1\),然后你会来到\(i+v\)的位置。如果你走到了\(1\)左边或者\(n\)右边,行驶结束。问对于每个位置\(i\in[1,n]\),\(0......
  • [python]多线程快速入门
    前言线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。由于CPython的GIL限制,多线程实际为单线程,大多只用来处理IO密集型任务。Python一般用标准库threading来进行多线程编程。基本使用方式1,创建threading.Thread类的示例importthreadi......