首页 > 其他分享 >后缀数组(SA)

后缀数组(SA)

时间:2023-12-26 21:56:37浏览次数:27  
标签:后缀 MAX id int 数组 SA sa rk

终于刷完网络流后准备继续做sa,发现自己忘完了,于是来写个博客。

应用

用\(O(nlogn)\)将字符串后缀排序,以找到优美的性质

概念

两个数组:\(sa\)和\(rk\)
\(sa_i\)表示将字符串后缀排序后,排名为\(i\)的后缀的开头字母在原串的位置
\(rk_i\)表示后缀\(i\)的排名
满足性质: \(sa[rk[i]]=rk[sa[i]]=i\)

一定一定要清楚区分两个数组,因为经常会嵌套使用,如\(sa[cnt[rk[id[i]]]--]=id[i]\)。意义不够清楚的话,就很难理解代码

oi-wiki的字符串太长了,不方便手模,所以手绘了一个

原理

主要是倍增的思想

对于两个串\(S\)和\(T\),其中\(S=s1+s2,T=t1+t2. s1,s2,t1,t2\)长度相等。已知\(s1\)和\(t1\),\(s2\)和\(t2\)的大小关系,可知\(S\)和\(T\)的大小关系,以前串为第一关键字,后串为第二关键字比较即可。

当排名各不相同时跳出。得到\(rk\)数组,由\(sa[rk[i]]=i\)可得\(sa\)。

代码

前方大量注释来袭

int p,rk[MAX],sa[MAX],cnt[MAX],id[MAX],ork[MAX],s[MAX];
inline bool cmp(int x,int y,int w){
    return ork[x]==ork[y]&&ork[x+w]==ork[y+w];
};
inline void SA(int n){
	m=128;//字符串内容的最大值
	for(int i=1;i<=n;++i)  cnt[rk[i]=s[i]]++;
    for(int i=1;i<=m;++i)  cnt[i]+=cnt[i-1];
    for(int i=n;i;--i)  sa[cnt[rk[i]]--]=i;
//O(n)基数排序
//与上图的串为例,rk 97 97 98 97 98,sa 1 2 4 3 5
    for(int j=1;p!=n;j<<=1,m=p){
//p为排名数组中的最大值,当p==n时排序完成
//j为上次排序长度
        p=0;
//当前串已排好长度为j的序,即第一关键字是有序的,那么只需排序第二关键字。实质是将超出字符串范围的后缀放到同第一关键字的最前,即一起放在基数排序的前面
        for(int i=n;i>n-j;--i)  id[++p]=i;
//没超出的依次放入
        for(int i=1;i<=n;++i)
            if(sa[i]>j)  id[++p]=sa[i]-j;
//第二次排序中,id 5 1 3 2 4
        memset(cnt,0,(m+1)*sizeof(int));
        for(int i=1;i<=n;++i)  cnt[rk[id[i]]]++;
        for(int i=1;i<=m;++i)  cnt[i]+=cnt[i-1];
        for(int i=n;i;--i)  sa[cnt[rk[id[i]]]--]=id[i];
//id中5在3前,所以基数排序中从后往前扫,从后往前放,于是sa中5在3前。sa 1 2 4 5 3
//此时只排了第二关键字为0的后缀,sa不完全是此次排序后的sa
        memcpy(ork,rk,sizeof(rk));p=0;
        for(int i=1;i<=n;++i)
            rk[sa[i]]=cmp(sa[i],sa[i-1],j)?p:++p;
//离散化,第一或第二关键字不一样就和上一个区分开,更新rk和p,如上图第二次排序rk 1 2 4 2 3,p=4
    }for(int i=1;i<=n;++i)  sa[rk[i]]=i;
//根据rk更新最终sa
}

代码不好背也不好理解,可以多手模或者干脆所学几遍

标签:后缀,MAX,id,int,数组,SA,sa,rk
From: https://www.cnblogs.com/yswn/p/17929440.html

相关文章

  • Day40 数组的使用
    数组的使用1.普通的for循环packagecom.baixiaofan.array;publicclassArrayDemo03{publicstaticvoidmain(String[]args){int[]arrays={1,2,3,4,5};//打印全部数组元素for(inti=0;i<arrays.length;i++){Sys......
  • Day39 数组基本特点及下标越界,小结
    数组基本特点及下标越界,小结数组的4个基本特点:1.其长度是确定的。数组一旦被创建,它的大小就是不可以改变的。2.其元素必须是相同类型,不允许出现混合类型。3.数组中的元素可以是任何数据类型,包括基本类型和引用类型。4.数组变量属引用类型,数组也可以看成是对象,数组中的每个元......
  • 杨辉三角的问题,借助二维数组的方法来解决。
    1publicclasscode1{2publicstaticvoidmain(String[]args){3int[][]x=newint[6][6];4for(inti=0;i<x.length;i++){5x[i][0]=1;6x[i][i]=1;78}9for(inti......
  • 苹果推信群发,苹果推信群发软件,iMessage群发系统
    在当今数字化的时代,智能手机的普及率已达到了前所未有的高度,其中,苹果公司的iPhone无疑是市场上最受欢迎的智能手机之一,然而,与手机的广泛应用相伴的是,众多企业对于如何有效地向这些手机用户推送信息,以推广产品或服务的需求也日益增强,为此,苹果公司推出了推信服务,允许开发者通过特定......
  • 用ColossalAI完成一次完整的预训练
    太难了,累懵了,全是坑...   最近没更新,其实有机会(怎么个机会不细说了)可以玩玩两台新出炉的H100,而且是8卡400GIB的,这两台估计已经超过了库里南的价格了,极其的豪华...   因为我正好没看《乡村爱情15》,我买了个youku会员,可以边看《乡村爱情15》边拿H100跑一跑训练,看看具......
  • P2865 [USACO06NOV] Roadblocks G
    原题链接题解1.在处理最短路的时候,我们采用优先队列的方法,即第一个出现的点一定是最小的,之后出现的点都是在其他点的基础上叠加的值,肯定不小于第一个。那么依然是这个思路,第二个出现的点一定是次短的。代码#include<bits/stdc++.h>usingnamespacestd;structunit{in......
  • Spring事务@Transaction失效原因
    目录1、数据库引擎不支持事务2、事务管理器配置问题3、没有被SpringIOC管理4、非public方法5、内部方法调用(常见)6、异常被捕获(常见)7、非受检异常(常见)1、数据库引擎不支持事务某些数据库引擎不支持事务,如果你使用这些引擎,则不能正确地使用@Transactional注解。2、事务管理器配......
  • NDK-以十六进制字符串的形式打印char[]数组到logcat
    NDK-以十六进制字符串的形式打印char[]数组到logcat1.在Java中打印publicstaticStringconvertByteArr2String(byte[]bArr){StringBuilderbuilder=newStringBuilder();for(inti=0;i<bArr.length;i++){builder.append(String.format(Locale......
  • C#深度理解:数组、集合、哈希、字典、堆、栈 优缺点
    一、数组1、Array固定数组优点:1).快速访问:数组通过索引来访问元素,访问速度非常快,因为可以通过索引进行直接定位。2).内存连续存储:数组在内存中以连续的方式存储元素,这样有助于提高数据的读取和写入效率。3).多维支持:C#中的数组支持多维(二维、三维等)数据结构,可以用于表示......
  • 非动态数组版本下的筛选
    问题:一对多查找(筛选)的结果需要横向排列,但是表格暂时不支持动态数组。右拉下拉公式解决:{=IFERROR(INDEX(FILTER($E:$E,$D:$D=$G2),COLUMN(A1)),"")}公式中的Filter部分筛选出满总D列中等产于G2对应E列的内容,其结果是多个单元格组成的数组。使用Index提取数组中的内容,第二参数用Colum......