标签:技术 csdn cpp 代码 怎么 存在 而且 简单 并且
题目很简单,就是给出一个字符串,把这个字符串的所有非空后缀从小到大排序后,按顺序输出后缀的第一个字符在原串中的位置。
输入样例:
ababa
输出样例:
5 3 1 4 2
解释:
排好序后为:
a
aba
ababa
ba
baba
暴力肯定不行啦!
这里我们使用一个叫后缀数组的东西
PS:搞懂模版还要感谢大佬zhangjianweivv的博客!
后缀数组为rank[i]
,为Suffix array的简写,表示排名为i的字符串的 编号 (PS:这里,我们简称排序后后缀的第一个字符所在的位置为 编号 )
还有一个玩意儿叫名次数组,为rank[i]
,表示编号为i的字符串的 排名
可以发现,rank[i]
是sa[i]
的逆运算,而且我们所需要的答案就是sa[i]
这才是真正的步入主题啊。。ps:前面讲辣么多废话
这里我们讲倍增算法,因为好理解我菜
另当前的字符串为aabaaaab
下面这个图很清楚:
图片来源:《后缀数组--处理字符串有力工具》
我们先把字符串的每个字母排序,存在rank
数组里面,我们认为这是第一关键字
将排序后的结果作为第一关键字,然后继续循环第二,三次操作
计数排序和基数排序很像。
先讲基数排序:
图片来源:jinkun113大佬的博客
类似于桶排序,
先按个位的数字一个个放入0~9的桶里,再按顺序从0~9取出来,(当然如果你是从大到小就是9~0啦!)
以此类推,一直循环到最后一位
计数排序,就更高级了。这里优化了一下,
就是我们在基数排序所用的桶,是记录他出现的次数,然后累加(写入代码就是cnt[i]+=cnt[i-1]
),这样我们得到的cnt[i]
数组里就是不大于i的个数,就是他的排名了!
我先解释一下每个变量的含义
rank[i]表示编号为i的排名
sa[i]表示排名为i的编号
cnt[i]计数排序的桶
pos[i]表示当前第二关键字已经排好序时第i名第二关键字所对应的第一关键字位置
tmp[i]
排序时:表示当前排序中编号为i的排名
排序后:表示调整rank前的排名
#include<cstdio>
#include<cstring>
using namespace std;
/*
rank[i]表示编号为i的排名
sa[i]表示排名为i的编号
cnt[i]计数排序的桶
pos[i]表示当前第二关键字已经排好序时第i名第二关键字所对应的第一关键字位置
tmp[i]
排序时:表示当前排序中编号为i的排名
排序后:表示调整rank前的排名
*/
char s[1200000];
int cnt[1200000],pos[1200000],sa[1200000],tmp[1200000],rank[1200000];
bool pd(int x,int y,int k){return tmp[x]==tmp[y]&&tmp[x+k]==tmp[y+k];}
void suffix(int n,int m)
{
int i,k;
for(i=1;i<=n;i++)rank[i]=s[i],cnt[rank[i]]++;
for(i=1;i<=m;i++)cnt[i]+=cnt[i-1];
for(i=n;i>=1;i--)sa[cnt[rank[i]]--]=i;//计数排序,设置好rank和sa数组
for(k=1;k<n;k<<=1)//k表示长度,k<<1表示k*2
{
int len=0;for(i=n-k+1;i<=n;i++)pos[++len]=i;//如果第二关键字为0的话,肯定最小,我们先加入pos
for(i=1;i<=n;i++)if(sa[i]>k)pos[++len]=sa[i]-k;//准备好pos数组
memset(cnt,0,sizeof(cnt));
for(i=1;i<=n;i++)tmp[i]=rank[pos[i]],cnt[tmp[i]]++;
for(i=1;i<=m;i++)cnt[i]+=cnt[i-1];
for(i=n;i>=1;i--)sa[cnt[tmp[i]]--]=pos[i];//更新sa数组
for(i=1;i<=n;i++)tmp[i]=rank[i];//记录之前的排名
len=1;rank[sa[1]]=1;//初始化
for(i=2;i<=n;i++){if(pd(sa[i],sa[i-1],k)==false)len++;rank[sa[i]]=len;}//通过sa来更新rank数组,并且去重
if(len==n)break;m=len;
}
for(i=1;i<n;i++)printf("%d ",sa[i]);
printf("%d\n",sa[n]);
}
int main()
{
scanf("%s",s+1);int len=strlen(s+1);
suffix(len,130);//'z'是122 ,为了保险设置为130
return 0;
}
标签:技术 csdn cpp 代码 怎么 存在 而且 简单 并且
原文地址:https://www.cnblogs.com/candy067/p/11401946.html