P3809 【模板】后缀排序

题目背景

这是一道模板题。

题目描述

读入一个长度为 nn 的由大小写英文字母或数字组成的字符串,请把这个字符串的所有非空后缀按字典序从小到大排序,然后按顺序输出后缀的第一个字符在原串中的位置。位置编号为 11 到 nn。

输入输出格式

输入格式:

 

一行一个长度为 nn 的仅包含大小写英文字母或数字的字符串。

 

输出格式:

 

一行,共n个整数,表示答案。

 

输入输出样例

输入样例#1: 复制

ababa

输出样例#1: 复制

5 3 1 4 2

说明

n <= 10^6n<=106

借鉴:https://www.cnblogs.com/victorique/p/8480093.html

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 1000050
char str[MAXN];
int tp[MAXN],rk[MAXN],tax[MAXN],sa[MAXN],wt[30],height[200];
int len,M;
void getsa()
{
     int i;
     for(i=1;i<=M;i++)   tax[i]=0;
     for(i=1;i<=len;i++) tax[rk[i]]++;
     for(i=2;i<=M;i++)   tax[i]+=tax[i-1];
     for(i=len;i>=1;i--)   sa[tax[rk[tp[i]]]--]=tp[i];
	    //第二关键字排名靠后的,在同一个第一关键字桶中这第一关键字排名越靠后,这是第二关键字运用的关键
}
void suffix()
{
	int i,k;
	for(i=1;i<=len;i++) ++tax[rk[i]=str[i]];
    for(i=2;i<=M;i++)   tax[i]+=tax[i-1]; //用tax做前缀和,可知关键字最多排在几名。
	//同时用前缀和还能实现基数排序, 按字典序
	//i是字符的ascii码,第一次排序的时候。
	for(i=len;i>=1;i--) sa[tax[rk[i]]--]=i;
	for(k=1;k<=len;k<<=1)
	{
		int num=0;
		for(i=len-k+1;i<=len;i++) tp[++num]=i;
		//tp[i]表示第二关键字的排名存第一关键字的位置
	   //第n-k+1到第n位是没有第二关键字的 所以排名在最前面,越小肯定越前面的。
	    for(i=1;i<=len;i++) if(sa[i]>k) tp[++num]=sa[i]-k;
	    //将当前sa[i]看做第二关键字,求出其对应第一关键字的位置,单独存在的第二关键字提前排名
        getsa();
	    swap(rk,tp);//更新排名,用tp记录旧排名
		rk[sa[1]]=1;num=1;
		for(i=2;i<=len;i++)
		rk[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+k]==tp[sa[i-1]+k])?num:++num; //这里的tp是旧排名
	    //()里面的是比较第一和第二关键字是否相同
        //()里面的如果成立,就直接等于num
        //如果不成立就num++
        if(num==len)  break;
        M=num;
    }
    for(i=1;i<=len;i++) printf("%d ",sa[i]);//sa更新完毕时,rk同时也更新完毕
}
void get_height()
{
    int i,j,k=0;
    for(i=1;i<=len;i++)
    {
        if(k) k--;  //利用h[i]>=h[i-1]-1,大大地减少了枚举次数
        j = sa[rk[i]-1];
        while(str[i+k]==str[j+k]) k++;
        height[rk[i]]=k;
    }
}
int main()
{
	scanf("%s",str+1);
	len=strlen(str+1);
	M=122; //'z'的ascii码
	suffix();
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值