具体讲解:点击此处查看
模板:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=20000+1000;
struct SuffixArray
{
//保存原始字符串+‘\0’后形成的字符串
//即原始字符串在s中表示[0,n-2]范围,
//然后s[n-1]其实是人为添加的'\0'字符
char s[maxn];
//排名(后缀)数组,sa[i]==j表示字典序为i的后缀是后缀j
//其中i从0到n-1,j从0到n-1范围
int sa[maxn];
//名次数组,rank[i]==j表示后缀i的字典序排名为j
int rank[maxn];
int height[maxn];
//辅助数组用于x和y数组
int t1[maxn],t2[maxn];
//c[i]==j表示关键字<=i的关键字有j个
int c[maxn];
//s原始字符串+‘\0'字符后的长度
//由于添加了尾0,所以n一般都>=2的
int n;//n>=2,不能等于1,否则build_height()函数可能会出BUG
//m大于s[]数组出现的任意字符的int值
void build_sa(int m)
{
int i,*x=t1,*y=t2;
//预处理每个后缀的长度为1的前缀,求出x数组和sa数组
//此时x[i]==j表示第i个字符的绝对值(可以看成是名次数组)
//但有可能x[1]=2,且x[3]=2,说明1字符和3字符完全一样。
//此时算出的sa[i]==j表示当前长度为1的字符串的排名数组,
//排名数组值不会一样
//就算x[1]==x[3]==2,但是sa[1]=1,而sa[2]=3。
//即就算1号字符和3号字符是完全一样的,
//但是排名时第1名是1号字符,第2名才是3号字符
for(i=0;i<m;i++) c[i]=0;
for(i=0;i<n;i++) c[x[i]=s[i]]++;
//此时c[i]表示关键字<=i的关键字一共有c[i]个
for(i=1;i<m;i++) c[i]+=c[i-1];
//计算当前长度(1)的排名数组
for(i=n-1;i>=0;i--) sa[--c[x[i]]] = i;
//每轮循环开始前我们通过之前的计算得到了x[]和sa[]:
//每个后缀的长为k的前缀(即每个后缀的前[0,k-1]字符)的名次数组x[],
//我们还知道每个后缀的长为k的前缀的排名数组sa[],
//然后通过sa[]数组我们可以求得每个后缀的第[k,2*k-1]字符的排名数组y[],
//然后通过k字符的x[]与k字符的y[],
//我们可以求得每个后缀的长为2k的前缀字符串的sa[]排名数组
//然后通过该sa[]排名数组,和k字符的x数组,我们可以求得2k字符的x[]数组
//以上每轮的x[]名次数组都是可能有重复值大小的,但是sa[]值不会重复
//比如表示k个字符的x[1]=2,x[4]=2时,
//那么表示[1,k+1]字符串与[4,k+4]字符完全相同,且排名为2(最高排名为0)
//当哪轮求出的x[]数组正好由n个值(即所有值都不重复时)
//说明所有后缀已经排序完毕
for(int k=1;k<=n;k<<=1)
{
//先计算每个后缀的前缀的[k,2*k-1]字符的排名数组y
//即y是每个后缀的长为2k前缀的第二关键字
int p=0;
//y[p]==i表第二关键字为第p名的是后缀i
//由于当前处理的是每个后缀的前缀的[k,2*k-1]字符
//而后缀n-k到后缀n-1不存在第k个字符(想想是不是)
//所以他们的第二关键字的名字自然优先
for(i=n-k;i<n;i++) y[p++]=i;
//除了上面那些后缀不存在第二关键字
//x+k后缀的第1关键字排名-k 等于 x后缀的第2关键字排名
for(i=0;i<n;i++)if(sa[i]>=k) y[p++]=sa[i]-k;
//上面已经计算出了y[],(x[]数组上一轮已经算出)
//下面通过第1关键字x[]名次数组和第2关键字y[]排名数组
//计算综合后每个后缀的长2k前缀的sa[]数组
for(i=0;i<m;i++) c[i]=0;
for(i=0;i<n;i++) c[x[y[i]]]++;
for(i=1;i<m;i++) c[i]+=c[i-1];
for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]] = y[i];
//交换x和y,令y表示名次数组
//计算综合后每个后缀的长2k前缀的x[]数组
swap(x,y);
//此时p用来记录x[]数组中不同值的个数
p=1;x[sa[0]]=0;
for(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++;
//上面y[sa[i]+k]的sa[i]+k<=n-1,因为只要然后两个不同的后缀必然要分出大小
//所以在他们y[sa[i]]==y[sa[i-1]],即这两个后缀的长k的第一关键字相同的情况下
//他们必定还存在第二关键需要比较
if(p>=n) break;
m=p;
}
}
//此函数详解见刘汝佳<<训练指南>>P222
//height[i]表示sa[i-1]后缀与sa[i]后缀的最大公共前缀长度
//即表示排名i-1和排名i的后缀的最大公共前缀LCP长度
//所以height数组只有[1,n-1]是有效下标
void build_height()//n不能等于1,否则出BUG
{
int i,j,k=0;
for(i=0;i<n;i++)rank[sa[i]]=i;
for(i=0;i<n;i++)
{
if(k)k--;
j=sa[rank[i]-1];
while(s[i+k]==s[j+k]) k++;
height[rank[i]]=k;
}
}
}sa;
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1000000+100;
struct SuffixArray
{
char s[maxn];
int sa[maxn],rank[maxn],height[maxn];
int t1[maxn],t2[maxn],c[maxn],n;
int dmin[maxn][20];
void build_sa(int m)
{
int i,*x=t1,*y=t2;
for(i=0;i<m;i++) c[i]=0;
for(i=0;i<n;i++) c[x[i]=s[i]]++;
for(i=1;i<m;i++) c[i]+=c[i-1];
for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
for(int k=1;k<=n;k<<=1)
{
int p=0;
for(i=n-k;i<n;i++) y[p++]=i;
for(i=0;i<n;i++)if(sa[i]>=k) y[p++]=sa[i]-k;
for(i=0;i<m;i++) c[i]=0;
for(i=0;i<n;i++) c[x[y[i]]]++;
for(i=1;i<m;i++) c[i]+=c[i-1];
for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]] = y[i];
swap(x,y);
p=1,x[sa[0]]=0;
for(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;
}
}
void build_height()//n不能等于1,否则出BUG
{
int i,j,k=0;
for(i=0;i<n;i++)rank[sa[i]]=i;
for(i=0;i<n;i++)
{
if(k)k--;
j=sa[rank[i]-1];
while(s[i+k]==s[j+k])k++;
height[rank[i]]=k;
}
}
void initMin()
{
for(int i=1;i<=n;i++) dmin[i][0]=height[i];
for(int j=1;(1<<j)<=n;j++)
for(int i=1;i+(1<<j)-1<=n;i++)
dmin[i][j]=min(dmin[i][j-1] , dmin[i+(1<<(j-1))][j-1]);
}
int RMQ(int L,int R)//取得范围最小值
{
int k=0;
while((1<<(k+1))<=R-L+1)k++;
return min(dmin[L][k] , dmin[R-(1<<k)+1][k]);
}
int LCP(int i,int j)//求后缀i和j的LCP最长公共前缀
{
int L=rank[i],R=rank[j];
if(L>R) swap(L,R);
L++;//注意这里
return RMQ(L,R);
}
}sa;