算法模板——后缀数组

来源:互联网 发布:淘宝客佣金比例怎么算 编辑:程序博客网 时间:2024/06/17 05:28

后缀数组是什么?可以吃吗?
不可以吃,它是给一个字符串的所有后缀进行排序的算法。
如:一个字符串aabaaaab,它的后缀排好序后起始位置分别是:4 5 6 1 7 2 8 3
显然如果暴力去排序时间复杂度为O(n2),可以用倍增算法或DC3算法来解决,本文主要介绍倍增算法(我才不会告诉你其实是我不会DC3算法呢)。
接下来定义如下几个数组:

数组名 意义 sai 原串S拍好序后第i个后缀的开头位置(就是排第几的是谁) ranki 开头为i的后缀的名次(就是谁排第几)

引用大佬图解:
大佬图解1
倍增法的主要内容?
每次对长度为2k的串进行排序时都可以利用两个连续的长为2k1的串排序的结果来进行排序,当2k>=n时,每个串都是以这个串的起点为起点的后缀。
再次引用大佬图解:
大佬图解2
这张图是什么意思呢?
就是:x和y分别作为第一关键字和第二关键字,进行排序,以这一次排序的结果作为下一次排序的参考。
怎么排序?
需要排序的范围不超过n,当然是选择计数排序(基数排序,桶排序)啊。
时间复杂度?
O(nlogn),如果不使用计数排序就是O(nlog2n)
代码
温馨提示:复制到记事本上并将tab键调成8格效果更佳

#include <cstdio>#include <cstring>const int maxn=100000;char s[(maxn<<1)+10];int sa[(maxn<<1)+10],rank[(maxn<<1)+10],tmp[(maxn<<1)+10];int sum[(maxn<<1)+10],a[(maxn<<1)+10],n;inline int swap(int *&x,int *&y)//指针swap{  int *t=x;  x=y;  y=t;  return 0;}inline int suffix_sort(){  int *x=rank,*y=tmp,m=26;  //先排序  for(register int i=1; i<=n; ++i)    {      x[i]=a[i];      y[i]=i;      ++sum[x[i]];    }  for(register int i=1; i<=m; ++i)    {      sum[i]+=sum[i-1];    }  for(register int i=n; i; --i)    {      sa[sum[x[i]]]=i;      --sum[x[i]];    }  int len=1,p=1;  while(p<n)    {      m=p;      p=0;      //每次循环设一下初值,然后排序      for(register int i=n-len+1; i<=n; ++i)    {      ++p;      y[p]=i;    }      for(register int i=1; i<=n; ++i)    {      if(sa[i]-len>0)        {          ++p;          y[p]=sa[i]-len;        }    }    //前面是按第二关键字排序,接下来按第一关键字排序      for(register int i=0; i<=m; ++i)    {      sum[i]=0;    }      for(register int i=1; i<=n; ++i)    {      ++sum[x[y[i]]];    }      for(register int i=1; i<=m; ++i)    {      sum[i]+=sum[i-1];    }      for(register int i=n; i; --i)    {      sa[sum[x[y[i]]]]=y[i];      --sum[x[y[i]]];    }      swap(x,y);      //现在y数组已经没有用了      p=1;      x[sa[1]]=1;      //下一个步骤是去重      for(register int i=2; i<=n; ++i)    {      if(!((y[sa[i-1]]==y[sa[i]])&&(y[sa[i-1]+len]==y[sa[i]+len])))        {          ++p;        }      x[sa[i]]=p;    }      len<<=1;    }  //如果所有的都已经排好序,那么就没有必要排下去了,退出  p=0;  //在上面计算的步骤中x数组记录的值可能不是真正的rank数组,需要重新计算  for(register int i=1; i<=n; ++i)    {      ++p;      rank[sa[i]]=p;    }  return 0;}int main(){  scanf("%s",s+1);  n=strlen(s+1);  for(register int i=1; i<=n; ++i)    {      a[i]=s[i]-'a'+1;    }  suffix_sort();  for(register int i=1; i<=n; ++i)    {      printf("%d\n",sa[i]);    }  return 0;}