刚学了后缀数组,似懂非懂,看了很久的代码,也理解了很久,总结出了一份自己的代码
包含满满的注释:
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<cmath>
using namespace std;
const int maxn=100+5;
int n;
char s[maxn];
int rank[maxn],sa[maxn],height[maxn];
int wd[maxn],wa[maxn],wb[maxn];
//wd辅助基数排序 每次技术使用
//sa为排位第k的后缀开头在哪里
//rank为开头位置在k的后缀的排名
void da(int *r,int n,int m)//m是最大ASCII
{
int p,*x=wa,*y=wb,*t;//x≈rank, y≈第二关键字
//x的实际意义就是当前所对应的ASCII值
for(int i=0;i<m;i++)wd[i]=0;
for(int i=0;i<n;i++)wd[x[i]=r[i]]++;//x[i]初始排名
for(int i=1;i<m;i++)wd[i]+=wd[i-1];
for(int i=n-1;i>=0;i--)sa[--wd[x[i]]]=i;
//计数排序
//这一段wd是一个辅助工具,初始化为0
//先统计ASCII为r[i]的个数,累加计数,再排名
//排位i的字符是x[i] wd[x[i]]是位置贡献
//sa[]=i表示排名xx的位置在i
for(int k=1;k<=n;k<<=1)//每次的合并长度为k
{
p=0;
//对第二关键字进行排序
//第一步- 后面有一段没有,填0
for(int i=n-k;i<n;i++)y[++p]=i;
//☆由于这边是空串,就第2关键字而言,空串一定会排到前面(填0)
for(int i=0;i<n;i++)if(sa[i]>=k)y[++p]=sa[i]-k;//某个位置左移k位存在,
//那么它可以作为第2关键字,
//由于之前从小到大排所以它也是按一定顺序插入的
//之后对第一关键字排序 类似于第一次计数排序
for(int i=0;i<m;i++)wd[i]=0;
for(int i=0;i<n;i++)wd[x[y[i]]]++;//排名第i位(y[i])位置的x[]值的个数统计
for(int i=1;i<m;i++)wd[i]+=wd[i-1];
for(int i=n-1;i>=0;i--)sa[--wd[x[y[i]]]]=y[i]; //排名为i的位置y[i]对应ASCII值的对应位置贡献
//sa[这个贡献]=y[i]表示排名为这个贡献的后缀第一个位置
swap(x,y);//明白了,是这个意思:因为下次我们还要使用x,x是要更新的
//而更新时需要用到某些之前信息,就让y暂时储存x的信息
//我们还要做的工作是检查是否排序完成
//并且优化最大ASCII值
p=1,x[sa[0]]=0;
for(int i=1;i<n;i++)
{
//比较每次合并前后两项
x[sa[i]]= y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p-1:p++;
}
if(p>=n)break;//优化,全部不同证明排好了
m=p;//优化最大ASCII值
}
}
//在已经排好的sa[]数组可以得到rank[]
//rank代表第i开头的后缀的排名
//height代表的意思是相连两个排名的公共前缀
//有以下性质height[i]>=height[i-1]-1
void getheight()
{
int i,j,k=0;
for(i=1;i<=n;i++)rank[sa[i]]=i;
for(i=0;i<n;i++)
{
if(k)k--;//利用性质
j=sa[rank[i]-1];
//位置i和它排名的前一个所对应的位置
while(s[i+k]==s[j+k])k++;
height[rank[i]]=k;
}
}
int main()
{
return 0;
}