题目链接:传送门

描述

后缀数组 (SA) 是一种重要的数据结构,通常使用倍增或者DC3算法实现,这超出了我们的讨论范围。

在本题中,我们希望使用快排、Hash与二分实现一个简单的 $O(n \log^2 ⁡n )$ 的后缀数组求法。

详细地说,给定一个长度为 n 的字符串S(下标 0~n-1),我们可以用整数 k(0≤k<n) 表示字符串S的后缀 S(k~n-1)。

把字符串S的所有后缀按照字典序排列,排名为 i 的后缀记为 SA[i]。额外地,我们考虑排名为 i 的后缀与排名为 i-1 的后缀,把二者的最长公共前缀的长度记为 Height[i]。

我们的任务就是求出SA与Height这两个数组。

输入格式

一个字符串,长度不超过30万。

输出格式

第一行为数组SA,相邻两个整数用1个空格隔开。

第二行为数组Height,相邻两个整数用1个空格隔开,特别地,假设Height[1]=0。

样例输入

ponoiiipoi

样例输出

9 4 5 6 2 8 3 1 7 0
0 1 2 1 0 0 2 1 0 2

样例解释

排名第一(最小)的后缀是9(S[9~9],即字符串 i),第二的是后缀4(S[4~9],即字符串iiipoi),第三的是后缀5(S[5~9],即字符串iipoi)以此类推。Height[2]表示排名第2与第1的后缀的最长公共前缀,长度为1,Height[3]表示排名第3与第2的后缀的最长公共前缀,长度为2,以此类推。

题解:

假设字符串长度为 $N$,那么如果用暴力的方式来比较两个后缀子串的字典序大小(以及求最长公共前缀的长度),显然一次比较是 $O\left( {N} \right)$ 的时间复杂度,

如果用快排对 $N$ 个后缀子串进行排序,那么就要 $O(N^2 \log N)$ 的时间复杂度,

我们尝试考虑优化的地方:比较两个字符串的时间,从 $O(N)$ 降到 $O(\log N)$,

对于两个后缀子串,或者更一般的,对于两个字符串,怎么更快速的比较字典序大小,或者,怎么更快速求的最长公共前缀?

容易想到,可以二分最长公共前缀的长度,用字符串哈希 $O(1)$ 判断是否两个前缀子串是否一样,

然后判断两个字符串中哪个字典序更大就很简单了,去掉最长公共前缀,比较一下剩下的第一个字符即可。

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull; const int P=;
const int maxn=+; char s[maxn];
int len;
int sa[maxn],h[maxn]; ull pre[maxn],Ppow[maxn];
void pretreat()
{
pre[]=;
Ppow[]=;
for(int i=;i<=len;i++)
{
pre[i]=pre[i-]*P+(s[i]-'a'+);
Ppow[i]=Ppow[i-]*P;
}
} inline bool isSame(int l1,int r1,int l2,int r2)
{
return pre[r1]-pre[l1-]*Ppow[r1-(l1-)] == pre[r2]-pre[l2-]*Ppow[r2-(l2-)];
}
inline int maxpre(int a,int b)
{
int l=,r=min(len-a+,len-b+),mid;
while(l<r)
{
mid=(l+r)/+;
if(isSame(a,a+mid-,b,b+mid-)) l=mid;
else r=mid-;
}
return l;
}
bool cmp(int a,int b)
{
int mp=maxpre(a,b);
return s[a+mp]<=s[b+mp];
} int main()
{
scanf("%s",s+);
len=strlen(s+);
pretreat();
for(int i=;i<=len;i++) sa[i]=i;
sort(sa+,sa+len+,cmp);
for(int i=;i<=len;i++)
{
if(i==) h[i]=;
else h[i]=maxpre(sa[i-],sa[i]);
printf("%d%c",sa[i]-,i<len?' ':'\n');
}
for(int i=;i<=len;i++) printf("%d%c",h[i],i<len?' ':'\n');
}

1A开心

时间复杂度:

排序 $O(N \log^2 N)$,计算Height数组 $O(N \log N)$,总时间复杂度 $O(N \log^2 N)$。

CH 1402 - 后缀数组 - [字符串hash]的更多相关文章

  1. 1402 后缀数组 (hash+二分)

    描述 后缀数组 (SA) 是一种重要的数据结构,通常使用倍增或者DC3算法实现,这超出了我们的讨论范围.在本题中,我们希望使用快排.Hash与二分实现一个简单的 O(n log^2⁡n ) 的后缀数组 ...

  2. CH1402 后缀数组【Hash】【字符串】【二分】

    1402 后缀数组 0x10「基本数据结构」例题 描述 后缀数组 (SA) 是一种重要的数据结构,通常使用倍增或者DC3算法实现,这超出了我们的讨论范围.在本题中,我们希望使用快排.Hash与二分实现 ...

  3. [poj 1743] Musical Theme 后缀数组 or hash

    Musical Theme 题意 给出n个1-88组成的音符,让找出一个最长的连续子序列,满足以下条件: 长度大于5 不重叠的出现两次(这里的出现可以经过变调,即这个序列的每个数字全都加上一个整数x) ...

  4. bnuoj 34990(后缀数组 或 hash+二分)

    后缀数组倍增算法超时,听说用3DC可以勉强过,不愿写了,直接用hash+二分求出log(n)的时间查询两个字符串之间的任意两个位置的最长前缀. 我自己在想hash的时候一直在考虑hash成数值时MOD ...

  5. BZOJ 4278: [ONTAK2015]Tasowanie (后缀数组 / 二分+hash)

    直接归并,然后如果哪边的后缀字典序比较小就去哪边,然后就可以后缀数组 博客传送门- 但是本蒟蒻不会后缀数组 Upd:Upd:Upd:现在会了233.一道差不多的题:BZOJ 1692: [Usaco2 ...

  6. BZOJ 1692: [Usaco2007 Dec]队列变换 (后缀数组/二分+Hash)

    跟BZOJ 4278: [ONTAK2015]Tasowanie一模一样 SA的做法就是把原串倒过来接在原串后面,O(nlogn)O(nlogn)O(nlogn)做后缀数组,就能O(1)O(1)O(1 ...

  7. Poj 3294 Life Forms (后缀数组 + 二分 + Hash)

    题目链接: Poj 3294 Life Forms 题目描述: 有n个文本串,问在一半以上的文本串出现过的最长连续子串? 解题思路: 可以把文本串用没有出现过的不同字符连起来,然后求新文本串的heig ...

  8. 140. 后缀数组(hash + 二分 / 后缀数组)

    题目链接 : https://www.acwing.com/problem/content/description/142/ Hash + 二分 #include <bits/stdc++.h& ...

  9. Suffix Array 后缀数组

    后缀数组 顾名思义.SuffixArray(下面有时简称SA) 和字符串的后缀有关. 后缀:字符串中某个位置一直到结尾的子串.(SA中讨论包含了原串和空串).所以共同拥有len+1个后缀. 后缀数组: ...

随机推荐

  1. Linux内核编译与安装

    2013-04-16    Linux内核介绍  Linux内核是一个用C语言写成的,符合POSIX标准的类Unix操作系统.内核是操作系统中最基本的一部分,提供了众多应用程序访问计算机硬件的机制.L ...

  2. Asp.Net WebAPI及相关技术介绍(含PPT下载)

    此PPT讲述了Asp.Net WebAPI及相关Web服务技术发展历史. 共80多页,Asp.Net WebAPI在讲到第36页的时候才会出现,因为这个技术不是凭空产生的,它有着自己的演变进化的历史. ...

  3. 改变R和Matlab的默认工作目录

    在快捷方式上右键->属性->起始位置处填上你需要的默认工作目录即可

  4. Socket网络编程--聊天程序(1)

    很早的一段时间,看了APUE和UNPv1了解了网络编程,但是但是只是看而已,没有具体的实践,趁现在没有什么事做,就来实践了解一下网络编程.写博客保存下来,方便以后用到的时候可以查到. 此次的聊天程序是 ...

  5. 【MongoDB】MongoDb的“not master and slaveok=false”错误及解决方法 mongo连接从库出现问题

    链接mongodb报错如下 2016-03-14T16:26:00.912+0800 E QUERY [thread1] Error: listDatabases failed:{ "ok& ...

  6. 学习Mysql过程中拓展的其他技术栈:Docker入门介绍

    一.Docker的介绍和安装 1. Docker是什么 百度百科的介绍: Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linu ...

  7. tronado学习

    请求处理程序和请求参数: 原创首发:http://www.cnblogs.com/zxlovenet/p/4128644.html 程序将URL映射到tornado.web.RequestHandle ...

  8. python中,如何将字符串转换为数字(将数字转换为整型),字符串的10转换为整型的10,10.5转换为10

    说明: 在实际的应用过程中,有的时候可能会遇到字符串的10,需要将字符串的10转换为数字的10 在此记录下,通过int函数转换的过程. 操作过程: 1.将字符串转换为整型的10 >>> ...

  9. rtmp简要流程

  10. NUC972 linux 烧录

    节介绍如何刻录uboot.kernel和文件系统到NAND Flash, 并且设定NUC970系列芯片从NAND Flash中开机.本节操作需要windows环境下进行.(初次连接电脑需要安装驱动) ...