列表

详情


NC50988. 后缀数组

描述

后缀数组 (SA) 是一种重要的数据结构,通常使用倍增或者DC3算法实现,这超出了我们的讨论范围。在本题中,我们希望使用快排、Hash与二分实现一个简单的 O(n log^2⁡n ) 的后缀数组求法。详细地说,给定一个长度为 n 的字符串S(下标 ),我们可以用整数 表示字符串S的后缀 。把字符串S的所有后缀按照字典序排列,排名为 i 的后缀记为 SA[i]。额外地,我们考虑排名为 i 的后缀与排名为 i-1 的后缀,把二者的最长公共前缀的长度记为 Height[i]。我们的任务就是求出SA与Height这两个数组。

输入描述

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

输出描述

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

示例1

输入:

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,以此类推。

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++14(g++5.4) 解法, 执行用时: 1238ms, 内存消耗: 10724K, 提交时间: 2020-06-02 23:14:01

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
const int N=3e5+10,base=131;
char str[N];
ll h[N],p[N]; 
int sa[N],heigh[N],n;
ll get(int l,int r)
{
	return h[r]-h[l-1]*p[r-l+1];
}
int get_max(int a,int b)
{
	int l=0,r=min(n-a+1,n-b+1);
	while(l<r)
	{
		int mid=l+r+1>>1;
		if(get(a,a+mid-1)!=get(b,b+mid-1))r=mid-1;
		else l=mid;
	}
	return l;
}
bool cmp(int a,int b){
	int l=get_max(a,b);
	int av=a+l>n?INT_MIN:str[a+l];
	int bv=b+l>n?INT_MIN:str[b+l];
	return av<bv; 
}
int main()
{
	scanf("%s",str+1);
	n=strlen(str+1);
	p[0]=1;
	for(int i=1;i<=n;i++)
	{
		h[i]=h[i-1]*base+str[i]-'a'+1;
		p[i]=p[i-1]*base;
		sa[i]=i;
	 } 
	 sort(sa+1,sa+1+n,cmp);
	 for(int i=1;i<=n;i++)
	 printf("%d ",sa[i]-1);
	 puts("");
	 for(int i=1;i<=n;i++)
	 {
	 	if(i==1)printf("0 ");
	 	else printf("%d ",get_max(sa[i-1],sa[i]));
	 }
	return 0;
}

C++(g++ 7.5.0) 解法, 执行用时: 759ms, 内存消耗: 11632K, 提交时间: 2023-07-27 09:38:40

#include<bits/stdc++.h>
using namespace std;
unsigned long long h[1000001],p[1000001],s[1000001];
long long len;
char str[1000001];
inline long long get(long long l,long long r){return h[r]-h[l-1]*p[r-l+1];}
inline long long check(long long a,long long b){
	long long l=0,r=len-max(a,b)+1,mid;
	while(l<r){
		mid=l+r>>1;
		if(get(a,a+mid)==get(b,b+mid))l=mid+1;
		else r=mid;
	}
	return l;
}
inline long long cmp(long long a,long long b){
	long long l=check(a,b),av=a+l>len?INT_MIN:str[a+l],bv=b+l>len?INT_MIN:str[b+l];
	return av<bv;
}
int main(){
	scanf("%s",str+1);len=strlen(str+1);p[0]=1;
	for(register int i=1;i<=len;++i)h[i]=h[i-1]*13331+str[i]-'a'+1,p[i]=p[i-1]*13331,s[i]=i;
	sort(s+1,s+len+1,cmp);
	for(register int i=1;i<=len;++i)cout<<s[i]-1<<' ';
	printf("\n0");
	for(register int i=2;i<=len;++i)cout<<' '<<check(s[i],s[i-1]);
}

C++ 解法, 执行用时: 896ms, 内存消耗: 10360K, 提交时间: 2021-12-18 15:36:26

#include<bits/stdc++.h>
using namespace std;
char str[300001];
long long h[300001],p[300001]={1};
int sa[300001],n;
long long gethash(int l, int r){return h[r]-h[l-1]*p[r-l+1];}
int sumsub(int a,int b){
    int l=0,r=min(n-a+1,n-b+1);
    while(l<r){
        int mid=(l+r+1)/2;
        if(gethash(a,a+mid-1)!=gethash(b,b+mid-1))r=mid-1;
        else l=mid;
    }
    return r;
}
bool cmp(int a,int b){
    int l=sumsub(a,b),x=a+l>n?-1e9:str[a+l],y=b+l>n?-1e9:str[b+l];
    return x<y;
}
int main() {
    scanf("%s",str+1);
    n=strlen(str+1);
    for(int i=1;i<=n;i++)h[i]=h[i-1]*131+str[i]-'a'+1,p[i]=p[i-1]*131,sa[i]=i;
    sort(sa+1,sa+n+1,cmp);
    for(int i=1;i<=n;i++)cout<<sa[i]-1<<" ";
    cout<<"\n0 ";
    for(int i=2;i<=n;i++)cout<<sumsub(sa[i],sa[i-1])<<" ";
    return 0;
}

上一题