写得很草率的一篇东西。

后缀排序

#include<bits/stdc++.h>
#define il inline
using namespace std;
il int read()
{
int xr=0,F=1;char cr=getchar();
while(cr<'0'||cr>'9') {if(cr=='-') F=-1;cr=getchar();}
while(cr>='0'&&cr<='9')
xr=(xr<<3)+(xr<<1)+(cr^48),cr=getchar();
return xr*F;
}
const int N=2e6+5;
int n,m;
char s[N];
int sum[N],sa[N],tp[N],rk[N];
//编号:后缀 (i~n) 的编号为 i
//sa[i] (上一轮)排名为 i 的后缀的编号
//rk[i] (上一轮)编号为 i 的串的排名
//tp[i] 第二关键字排名为 i 的串的编号
void qsort()
{
//sum[i] 桶,存 rk[x]=i 的 x 数量
for(int i=0;i<=m;i++) sum[i]=0;//清空桶
for(int i=1;i<=n;i++) sum[rk[i]]++;
for(int i=1;i<=m;i++) sum[i]+=sum[i-1];//前缀和,此时 sum[i] 表示 rk[x]<=i 的 x 数量
//所以 sum[i] 就是 rk[x]=i 的这个 x 排序后的排名
for(int i=n;i;i--) sa[sum[rk[tp[i]]]--]=tp[i];
//这句话到底在干啥。
//因为 sum[i] 是所有 rk[x]=i 中最后一个位置,要倒序往里填
//倒序枚举 i,实际求的是 tp[i] 的排名,这样枚举是保证第二关键字降序
//rk[tp[i]] 为 (第二关键字排名为 i 的串)的 第一关键字排名
//sum[rk[tp[i]]],现在要填的位置,填完后 sum--,代表下次往前一个位置填
//sa[sum[rk[tp[i]]]] 现在位置是 sum[rk[tp[i]]] 的串,它的编号是 tp[i]
}
int main()
{
scanf("%s",s+1);
n=strlen(s+1),m=200;
for(int i=1;i<=n;i++) rk[i]=s[i],tp[i]=i;
//初始排名,第一关键字是起始字符,第二关键字是位置
qsort();
for(int w=1,tot=0;tot<n;m=tot,w<<=1)
{
//tot 当前有多少种不同的排名,如果已经有 n 个代表所有串都排出来了,退出
//w 当前倍增的长度,即 rk 和 tp **分别** 已经排完的长度
//实际上是在求长度为 2w 时的答案
tot=0;
for(int i=n-w+1;i<=n;i++) tp[++tot]=i;
//[n-w+1,n] 的长度不足 w,第二关键字为 0,按起始位置排名
for(int i=1;i<=n;i++)
if(sa[i]>w) tp[++tot]=sa[i]-w;
//按第二关键字的排名从小到大枚举串,sa[i] 即编号为 sa[i]-w 的串的第二关键字
qsort();swap(tp,rk);
//现在的 sa 已经更新成长度为 2w 的答案了,但 rk 没更新
//把长度为 w 时的答案备份进 tp 里,因为原来的 tp 没用了
//现在的 tp[i] 表示 上一轮排序中 编号为 i 的串的排名
tot=rk[sa[1]]=1;
//tot 表示当前的排名
for(int i=2;i<=n;i++)
//枚举新排名为 i 的串,如果它前一半和后一半都和上一个串的旧排名一样,它们新排名还是一样
//否则排名是上一个串+1
rk[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+w]==tp[sa[i-1]+w])?tot:++tot;
//这里面 rk[i] 是可以重复的,但 sa[i] 不可以,一定要绕明白(
}
for(int i=1;i<=n;i++) printf("%d ",sa[i]);
return 0;
}
struct 封装版(方便复制)
struct suffix_array
{
int n,m,h[N],sa[N],rk[N],tp[N],sum[N];
char s[N];
il void qsort()
{
for(int i=1;i<=m;i++) sum[i]=0;
for(int i=1;i<=n;i++) sum[rk[i]]++;
for(int i=1;i<=m;i++) sum[i]+=sum[i-1];
for(int i=n;i;i--) sa[sum[rk[tp[i]]]--]=tp[i];
}
il void SA()
{
for(int i=1;i<=n;i++) rk[i]=s[i],tp[i]=i;
m=200,qsort();
for(int w=1,tot=0;tot<n;m=tot,w<<=1)
{
tot=0;
for(int i=n-w+1;i<=n;i++) tp[++tot]=i;
for(int i=1;i<=n;i++) if(sa[i]>w) tp[++tot]=sa[i]-w;
qsort(),swap(rk,tp); rk[sa[1]]=tot=1;
for(int i=2;i<=n;i++)
rk[sa[i]]=(tp[sa[i]]!=tp[sa[i-1]]||tp[sa[i]+w]!=tp[sa[i-1]+w])?++tot:tot;
}
for(int i=1,now=0;i<=n;i++)
{
if(now) now--;
while(s[i+now]==s[sa[rk[i]-1]+now]) now++;
h[rk[i]]=now;
}
}
};

例题

P4051 [JSOI2007] 字符加密

SA 板子,把 s 复制两遍断环为链,再后缀排序。

height 数组

定义

\(height[i]=lcp(sa[i],sa[i-1])\),其中 \(height[1]=0\)。

性质

\(height[rk[i]]\ge height[rk[i-1]]-1\)。

求法

根据上面的性质即可 \(O(n)\) 求出。

void get_hgt()
{
for(int i=1,now=0;i<=n;i++)
{
if(now) now--;
while(s[i+now]==s[sa[rk[i]-1]+now]) now++;
hgt[rk[i]]=now;
}
}

例题

P2852 [USACO06DEC]Milk Patterns G

题意简述:给一个字符串,求至少出现 \(k\) 次的串的最长长度。

Solution:

一个串至少出现 \(k\) 次,则至少出现在 \(k\) 个后缀的 LCP 中。

所以答案是 所有连续 \(k-1\) 个 height 的最小值中的最大值。

单调队列即可做到 \(O(n)\)。

P2743 [USACO5.1]乐曲主题Musical Themes

题意简述:找最长的 \(s\),满足 \(s\) 在字符串中至少不重叠地出现了两次。

Solution:

二分答案 \(mid\),将 height 数组划分成连续的几段,其中每一段除第一个外 height 都>=mid。

求出每一段 \(sa[i]\) 的最大最小值,若 \(mx-mn>=mid\),则 \(mid\) 合法,两个串起始位置分别为 mx 和 mn。

在本题中,由于求的是差分数组,mx-mn 不取等号。

POJ 1226/ybt 例2

题意简述:给定 \(n\) 个字符串,求一个最长的字符串,要求其在 \(n\) 个串或者它们翻转后的串中出现过。

Solution:

把这 \(n\) 个串和它们反转后的串连在一起,中间用不同且没出现过的字符隔开。

二分答案将 \(height[i]\) 分段,若有一段包含了这 \(n\) 个串,答案合法。用 set 维护。

后缀数组 (SA) 学习笔记的更多相关文章

  1. 后缀数组SA学习笔记

    什么是后缀数组 后缀数组\(sa[i]\)表示字符串中字典序排名为\(i\)的后缀位置 \(rk[i]\)表示字符串中第\(i\)个后缀的字典序排名 举个例子: ababa a b a b a rk: ...

  2. 洛谷.3809.[模板]后缀排序(后缀数组 倍增) & 学习笔记

    题目链接 //输出ht见UOJ.35 #include<cstdio> #include<cstring> #include<algorithm> const in ...

  3. 后缀数组SA入门(史上最晦涩难懂的讲解)

    参考资料:victorique的博客(有一点锅无伤大雅,记得看评论区),$wzz$ 课件(快去$ftp$%%%),$oi-wiki$以及某个人的帮助(万分感谢!) 首先还是要说一句:我不知道为什么我这 ...

  4. 后缀数组(SA)总结

    后缀数组(SA)总结 这个东西鸽了好久了,今天补一下 概念 后缀数组\(SA\)是什么东西? 它是记录一个字符串每个后缀的字典序的数组 \(sa[i]\):表示排名为\(i\)的后缀是哪一个. \(r ...

  5. [笔记]后缀数组SA

    参考资料这次是真抄的: 1.后缀数组详解 2.后缀数组-学习笔记 3.后缀数组--处理字符串的有力工具 定义 \(SA\)排名为\(i\)的后缀的位置 \(rk\)位置为\(i\)的后缀的排名 \(t ...

  6. bzoj3796(后缀数组)(SA四连)

    bzoj3796Mushroom追妹纸 题目描述 Mushroom最近看上了一个漂亮妹纸.他选择一种非常经典的手段来表达自己的心意——写情书.考虑到自己的表达能力,Mushroom决定不手写情书.他从 ...

  7. SA 学习笔记

    后缀数组是解决字符串问题的有力工具--罗穗骞 后缀数组是对字符串的后缀排序的一个工具, sa将排名为i的字符串的开头位置记录下来, rnk将开头位置为i的字符串的排名记录下来. https://www ...

  8. 【字符串】后缀数组SA

    后缀数组 概念 实际上就是将一个字符串的所有后缀按照字典序排序 得到了两个数组 \(sa[i]\) 和 \(rk[i]\),其中 \(sa[i]\) 表示排名为 i 的后缀,\(rk[i]\) 表示后 ...

  9. 浅谈后缀数组SA

    这篇博客不打算讲多么详细,网上关于后缀数组的blog比我讲的好多了,这一篇博客我是为自己加深印象写的. 给你们分享了那么多,容我自私一回吧~ 参考资料:这位dalao的blog 一.关于求Suffix ...

  10. 后缀自动机SAM学习笔记

    前言(2019.1.6) 已经是二周目了呢... 之前还是有一些东西没有理解到位 重新写一下吧 后缀自动机的一些基本概念 参考资料和例子 from hihocoder DZYO神仙翻译的神仙论文 简而 ...

随机推荐

  1. 【SpringBoot】整合Redis

    1.前言 最近公司在做项目,用到了redis,,发现自己一点都不会,然后就乘闲暇时间,自己学习一些redis相关的知识,在这里分享给像我一样的初学者. 2.我的项目结构: 2.1 pom.xml &l ...

  2. Flutter 屏幕采集如何实现(提供示例demo)

    在视频会议.线上课堂.游戏直播等场景,屏幕共享是一个最常见的功能.屏幕共享就是对屏幕画面的实时共享,端到端主要有几个步骤:录屏采集.视频编码及封装.实时传输.视频解封装及解码.视频渲染.一般来说,实时 ...

  3. 《逆向工程核心原理》之DLL注入

    DLL注入 DLL注入指的是向运行中的其他进程强制插入特定的DLL文件.从技术细节来说,DLL注入命令其他进程自行调用LoadLibrary() API,加载(Loading)用户指定的DLL文件.D ...

  4. std::ofstream 写本地音频

    最近线上 PK 偶然出现双方主播互相听不见声音的情况,在日志不能明确体现问题时,就需要抓下主播本地的音频和远端的音频来确定数据是在哪消失的 所以我们用到一个比较简单的流写出的标准库类:std::ofs ...

  5. C#.NET 国密SM4对称加解密 与JAVA互通 ver:20230731

    C#.NET 国密SM4对称加解密 与JAVA互通 ver:20230731 .NET 环境:.NET6 控制台程序(.net core). JAVA 环境:JAVA8,带maven 的JAVA控制台 ...

  6. C++欧几里得算法求最大公约数和最小公倍数

    定义 最大公约数即为 Greatest Common Divisor,常缩写为 gcd. 一组整数的公约数,是指同时是这组数中每一个数的约数的数. 一组整数的最大公约数,是指所有公约数里面最大的一个. ...

  7. PXE操作过程 kickstart 无人值守安装

    PXE操作过程 分配给同一局域网内新加机器的地址(配置文件) dhcp 分配地址 指明tftp 服务器的地址 tftp服务端开启 udp 配置 默认关闭 安装syslinux 取得 pxelinux. ...

  8. 数仓备份经验分享丨详解roach备份原理及问题处理套路

    本文分享自华为云社区<GaussDB(DWS) 备份问题定位思路>,作者: yd_216390446. 前言 在数据库系统中,故障分为事务内部故障.系统故障.介质(磁盘)故障.对于事务内部 ...

  9. Vue【原创】可拖动列表 darg-list

    可拖动排序的列表 drag-list,这个比较简单易懂,拿例子直接运行看效果就好了. 组件代码: 1 <template> 2 <ul class="list" ...

  10. PyAV 使用浅谈

    背景: PyAV是一个用于音频和视频处理的Python库,它提供了一个简单而强大的接口,用于解码.编码.处理和分析各种音频和视频格式.PyAV基于FFmpeg多媒体框架,它本质上是FFmpeg 的Py ...