后缀排序

ぐ巨炮叔叔 提交于 2020-02-19 16:31:25

Tim正在自学《数据结构》,他刚刚学会如何比较两个字符串大小。书上是这么说的(和Pascal语言中的比较规则相同,学习过Pascal语言的同学可以跳过这段): 

比较两个不同字符串s1=’p1p2p3…pN’和s2=’q1q2q3…qM’的大小,设N<=M。
若s1是s2的前缀,则s1qi,且i最小;若pis2。
Tim想通过练习熟练运用这个规则,于是打算出许多字符串,并将它们从小到大排序。可是Tim非常懒,随机写出 K个很长的字符串实在是太麻烦了。不过聪明的他想到了一个好办法,他写了一个很长的字符串,自言自语说,“我只要把这个字符串的所有后缀从小到大排序就可以了”。
Input
仅有一行,且是一个仅包含小写字母的字符串,长度K不超过10^5。
Output
有K行,每行一个数字,第i行的数字Pi表示所有后缀中,第i小的是由原字符串第Pi个字符引导的后缀。
Sample Input
mississippi
Sample Output
11
8
5
2
1
10
9
7
4
6
3

Sol:

sa[i]=j...排名第i的后缀是从原串中的第j个位置开始的

rank[i]=j...原串中第i个位置开始的后缀,排名为j

两者是个互逆的函数,整个程序就是两个数组倒来倒过

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#define maxn 210000
#define ll long long
#define fill(a,b) memset(a,b,sizeof(a))
using namespace std;
int Rank[maxn],sa[maxn],a[maxn],wr[maxn],rsort[maxn],y[maxn];
char c[110000];
inline bool cmp(int k1,int k2,int ln)
{
return wr[k1]==wr[k2]&&wr[k1+ln]==wr[k2+ln];
}
inline void get_sa(int n,int m)
{
    int k,p,ln;
    memcpy(Rank,a,sizeof(Rank));  //将a数组copy给Rank 
    memset(rsort,0,sizeof(rsort));
    for(int i=1;i<=n;i++) //对于每个字符统计它的出现次数 
	     rsort[Rank[i]]++;
    for(int i=1;i<=m;i++) //对于每个字符统计有多少个比它小 
	    rsort[i]+=rsort[i-1];
    for(int i=n;i>=1;i--) 
	     sa[rsort[Rank[i]]--]=i;
	     //sa[i]=j,排在第i小的后缀串是从原字符串第j个位置开始的 
    ln=1; p=0;
    while (p<n)
    {
        k=0;
        for(int i=n-ln+1;i<=n;i++) 
		     y[++k]=i;
        for(int i=1;i<=n;i++) 
		    if(sa[i]>ln) y[++k]=sa[i]-ln;
        for(int i=1;i<=n;i++) 
		     wr[i]=Rank[y[i]];
		//rank[i]=j ,从i开始的后缀是第 j 名的(和 sa 互逆,是排名(值))
        memset(rsort,0,sizeof(rsort));
        for(int i=1;i<=n;i++) 
		     rsort[wr[i]]++;
        for(int i=1;i<=m;i++) 
		    rsort[i]+=rsort[i-1];
        for(int i=n;i>=1;i--) 
		     sa[rsort[wr[i]]--]=y[i];
        for(int i=1;i<=n;i++) 
		    wr[i]=Rank[i];
        p=1; 
		Rank[sa[1]]=1;
        for(int i=2;i<=n;i++) 
        {
            if(!cmp(sa[i],sa[i-1],ln)) p++;
            Rank[sa[i]]=p;
        }
        m=p; 
		ln*=2;
    }
    for(int i=1;i<=n;i++)
        printf("%d\n",sa[i]);
}
int main(){
    scanf("%s",c+1);
    int n=strlen(c+1);
    for(int i=1;i<=n;i++) 
	     a[i]=c[i];
    get_sa(n,300);
    return 0;
}

  

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!