感觉后缀数组很难学的说= = 不过总算是啃下来了

首先 我们需要理解一下倍增法构造的原理

设原串的长度为n 对于每个子串 我们将它用'\0'补成长度为2^k的串(2^k-1<n<=2^k)

比如串aba的子串就有 aba'\0'    ba'\0''\0'  a'\0''\0''\0'

每次操作我们可以排出所有长度为 2^x的子串的大小

比如串aba的排序过程

第一遍 a                   a             b

第二遍 a'\0'             ab           ba

第三遍 a'\0''\0''\0'   aba'\0'    ba'\0''\0'

理解这些后 我们可以先写一个 nlog^2n的快排实现的方法

这种方法比较好写 如果n<=10^5就放心地去用吧

//SA nlog^2n
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define rep(i,n) for(int i=1;i<=n;++i)
#define imax (x>y?x:y)
#define imax (x<y?x:y)
using namespace std;
const int N=;
struct node
{
int x,y,ma;
}tr[N];
char ch[N];
int r[N<<],sa[N];
int n;
bool cmp(node aa,node bb)
{
return aa.x<bb.x||(aa.x==bb.x&&aa.y<bb.y);
}
void getsa()
{
for(int i=;<<(i-)<n;++i)
{
rep(j,n)
{
tr[j].x=r[j];
tr[j].y=r[j+(<<i-)];
tr[j].ma=j;
}
sort(tr+,tr++n,cmp);
int cnt=;
rep(j,n)
r[tr[j].ma]=tr[j].x==tr[j-].x&&tr[j].y==tr[j-].y?cnt:++cnt;
}
rep(j,n)
sa[r[j]]=j;
}
int main()
{
scanf("%s",ch+);
n=strlen(ch+);
rep(i,n)
r[i]=ch[i];
getsa();
printf("RANK: ");
rep(i,n)
printf("%d ",r[i]);
printf("\nSA: ");
rep(i,n)
printf("%d ",sa[i]);
return ;
}

然而 考虑到rank数组的特殊性(一定<=n) 我们还可以使用基数排序把复杂度降到nlogn

这样就可以解决n<=10^6的问题啦

然而这个的确比较容易写错 并且需要先掌握基数排序的原理

基数排序从直观上是需要链表去做的 然而只用一个数组也同样可以很方便的实现

具体可以参考下代码

//SA nlogn(n=1需要特判下 这里懒得写了)
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define rep(i,n) for(int i=1;i<=n;++i)
#define imax (x>y?x:y)
#define imax (x<y?x:y)
using namespace std;
const int N=,S=;//通常字符都是在0-127之间的
char ch[N];
int sum[N],r[][N<<],sa[][N];
int n,t;
void getsa(int i)
//这里面的SA值并非最后的SA值 但保证对应rank值相等的一定相邻 从而方便比较大小
{
memset(sum,,sizeof(sum));
rep(j,n)
++sum[r[t][j+i]];
rep(j,n)
sum[j]+=sum[j-];
rep(j,n)
sa[][sum[r[t][j+i]]--]=j;
memset(sum,,sizeof(sum));
rep(j,n)
++sum[r[t][j]];
rep(j,n)
sum[j]+=sum[j-];
for(int j=n;j;--j)
//基数排序从第二次排序开始是一定要倒序找的(如果不懂的话自行搜索下基数排序)
sa[][sum[r[t][sa[][j]]]--]=sa[][j];
}
int main()
{
scanf("%s",ch+);
n=strlen(ch+);
rep(i,n)
sum[ch[i]]=;
for(int i=;i<S;++i)
sum[i]+=sum[i-];
rep(i,n)
r[][i]=sum[ch[i]];//函数外的sum用于求出初始排名
for(int i=;i<n;i<<=)
{
getsa(i);
t^=;
rep(j,n)
r[t][sa[][j]]=r[t^][sa[][j]]==r[t^][sa[][j-]]&&
r[t^][sa[][j]+i]==r[t^][sa[][j-]+i]?
r[t][sa[][j-]]:r[t][sa[][j-]]+;
if(r[t][sa[][n]]==n)break;//已经排好序了便可以提前退出
}
printf("RANK: ");
rep(i,n)
printf("%d ",r[t][i]);
printf("\nSA: ");
rep(i,n)
printf("%d ",sa[][i]);
return ;
}

用倍增法构造后缀数组中的SA及RANK数组的更多相关文章

  1. extract_by_one 根据二维数组中某字段来提取数组信息,查看有无重复信息

    public function tt(){ $param = array( array ( 'hykno' => '2222222-CB', 'tcdk_fid' => '458B6D70 ...

  2. MongoDB 学习笔记之 从数组中删除元素和指定数组位置

    从数组中删除元素: 从数组中删除单个元素: db.ArrayTest.updateOne({ "name" : "Bill"},{$pop: {"ad ...

  3. Javascript 获得数组中相同或不同的数组元素   

    Javascript 获得数组中相同或不同的数组元素 在Javascript中,偶尔会用到获取数组中相同或不同的元素值的情况,以下提供了获得数组中相同或不同的 元素函数供参考学习使用. // 数字类型 ...

  4. K:找寻数组中第n大的数组元素的三个算法

    相关介绍:  给定一个数组,找出该数组中第n大的元素的值.其中,1<=n<=length.例如,给定一个数组A={2,3,6,5,7,9,8,1,4},当n=1时,返回9.解决该问题的算法 ...

  5. C# 在数组中判断是否存在某个数组值

    (1) 第一种方法: ,,}; ); // 这里的1就是你要查找的值 ) // 不存在 else // 存在 (2) 第二种方法: string[] strArr = {"a",& ...

  6. 百度:在O(1)空间复杂度范围内对一个数组中前后连段有序数组进行归并排序

    一.题目理解 题目:数组al[0,mid-1]和al[mid,num-1]是各自有序的,对数组al[0,num-1]的两个子有序段进行merge,得到al[0,num-1]整体有序.要求空间复杂度为O ...

  7. 3.键盘输入10个数,放到数组中,(1)去除该数组中大于10的数 (2)将该数组中的数字写入到本地文件number.txt中

    package cn.it.text; import java.io.FileWriter; import java.io.IOException; import java.util.Scanner; ...

  8. 剑指Offer 35. 数组中的逆序对 (数组)

    题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对1000000007取模的结果输出. 即输出P%1000 ...

  9. 剑指Offer 37. 数字在排序数组中出现的次数 (数组)

    题目描述 统计一个数字在排序数组中出现的次数. 题目地址 https://www.nowcoder.com/practice/70610bf967994b22bb1c26f9ae901fa2?tpId ...

随机推荐

  1. 2019牛客暑期多校训练营(第一场) - H - XOR - 线性基

    https://ac.nowcoder.com/acm/contest/881/H 题意: 给定n个整数,求其中异或和为 \(0\) 的子集的大小的和. 题解思路: 首先转化为每个可以通过异或表示 \ ...

  2. 没有找到mspdb80.dll,因此这个应用程序未能启动...问题解决

    这里主要针对使用link.exe进行SIG文件制作时,报错. 首先下载,mspdb80.dll:https://www.lanzous.com/i59dgfi 将dll文件移动到我的电脑(32位)C: ...

  3. ssh 操作 esxi 基本命令

    1.查看虚拟机: vim-cmd vmsvc/getallvms 会显示当前esxi上的虚拟机数量,没一个都有编号. 2.停用虚拟机:vim-cmd vmsvc/power.suspend + 之前命 ...

  4. 微信小程序(10)--开发者工具更新以后wxss编译错误

    更新最新版微信开发者工具后,出现下面报错: 解决办法: 1.在控制台输入openVendor() ,确定Enter: 2.清除里面的wcc.exe  wcsc.exe : 3.重启开发者工具

  5. 十、.NET使用本地Outlook邮箱指定邮箱用户名和密码发送邮件

    十..NET使用本地Outlook邮箱指定邮箱用户名和密码发送邮件 1.添加Microsoft.Office.Interop.Outlook引用 2.封装发送邮件方法 using System; us ...

  6. 8VC Venture Cup 2017 - Elimination Round - C

    题目链接:http://codeforces.com/contest/755/problem/C 题意:PolandBall 生活在一个森林模型的环境中,定义森林由若干树组成,定义树为K个点,K-1条 ...

  7. 牛客小白月赛16 F 小石的妹子 (线段树)

    链接:https://ac.nowcoder.com/acm/contest/949/F来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言52428 ...

  8. Jade To Pug过程中的一个小问题

    最近在使用jade+express+typescript搭建一个博客项目,在使用jade-bootstrap项目时出现了一个问题 在使用其中的carousel时,发现其中的变量并没有转义 +carou ...

  9. Vue-鼠标按键修饰符

    left .right .middle 这些修饰符会限制处理函数仅响应特定的鼠标按钮. 如下例子 <div id="app"> <input type=" ...

  10. elasticsearch 进行聚合+去重查询

    转载:https://www.jianshu.com/p/c03c3c8bf583 以客户customer为例 我想查询每日的客户数. 先按照日期分桶,然后在桶内按照 姓名来去重 来计算客户数(实际会 ...