字尾陣列
阿新 • • 發佈:2020-07-23
用倍增思想求sa_k,rk_k -> sa_2k,rk_2k
用基數排序,排序二元組(rk[i],rk[i+k])
#include<bits/stdc++.h> const int N=1000010; char s[N]; int n,m,num; int sa[N],rk[N],bac[N],y[N],tmp[N];//sa[i]陣列表示字尾排名為i的位置,rk[i]表示字尾[i..n]的排名 void getsa(){ //rk[i]表示位置i的第一關鍵字(排名) //二元組(rk[i],rk[i+k]) //初始化基數排序(單字元,其實是單元) for(int i=1;i<=n;i++)bac[rk[i]=s[i]]++;//bac[i]表示第一關鍵字小於i的個數 for(int i=2;i<=m;i++)bac[i]+=bac[i-1];//故就先逐個統計,再求字首和,相當於用桶計數 for(int i=n;i>=1;i--)sa[bac[rk[i]]--]=i;//在第二關鍵字有序的情況下,對位置按照第一關鍵字的bac陣列,逐個附排名 for(int k=1;k<=n;k<<=1){ num=0;//排名的數量,初始化為單個字元的ASCLL碼上限,m=ascll('z')=122 //y[i]表示第二關鍵字排名為i的二元組的第一關鍵字位置(i) for(int i=n-k+1;i<=n;i++)y[++num]=i;//沒有第二關鍵字的排名最靠前 for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;//利用已有的sa_(k/2)陣列,rk靠前的且有第一關鍵字的,將第一關鍵字位置sa_(k/2)[i]-k放入y //在上一輪中,rk_k陣列已經求好了,倍增求解, //接下來利用基數排序,求出sa_k for(int i=1;i<=m;i++)bac[i]=0; for(int i=1;i<=n;i++)bac[rk[i]]++;//bac[i]表示第一關鍵字小於i的個數 for(int i=2;i<=m;i++)bac[i]+=bac[i-1];//故就先逐個統計,再求字首和,相當於用桶計數 for(int i=n;i>=1;i--)sa[bac[rk[y[i]]]--]=y[i];//在第二關鍵字有序的情況下,對位置按照第一關鍵字的bac陣列,逐個附排名 memcpy(tmp,rk,sizeof(tmp));//用tmp存一下rk_(k/2),在求rk_k時仍會用到 //其實就是利用sa_k和rk_(k/2)陣列求rk_k rk[sa[1]]=1;num=1; for(int i=2;i<=n;i++){ if(tmp[sa[i]]==tmp[sa[i-1]]&&tmp[sa[i]+k]==tmp[sa[i-1]+k])rk[sa[i]]=num; else rk[sa[i]]=++num; } if(num==n)break;//已經有了n種排名 m=num;//m表示排名的數量(在桶排中為值域) } } int main(){ scanf("%s",s+1);n=strlen(s+1);m=122; getsa();for(int i=1;i<=n;i++)printf("%d ",sa[i]); return 0; }
無註釋版:
#include<bits/stdc++.h> const int N=1000010; char s[N]; int n,m,num; int sa[N],rk[N],bac[N],y[N],tmp[N]; void getsa(){ for(int i=1;i<=n;i++)bac[rk[i]=s[i]]++; for(int i=2;i<=m;i++)bac[i]+=bac[i-1]; for(int i=n;i>=1;i--)sa[bac[rk[i]]--]=i; for(int k=1;k<=n;k<<=1){ num=0; for(int i=n-k+1;i<=n;i++)y[++num]=i; for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k; for(int i=1;i<=m;i++)bac[i]=0; for(int i=1;i<=n;i++)bac[rk[i]]++; for(int i=2;i<=m;i++)bac[i]+=bac[i-1]; for(int i=n;i>=1;i--)sa[bac[rk[y[i]]]--]=y[i]; memcpy(tmp,rk,sizeof(tmp)); rk[sa[1]]=1;num=1; for(int i=2;i<=n;i++){ if(tmp[sa[i]]==tmp[sa[i-1]]&&tmp[sa[i]+k]==tmp[sa[i-1]+k])rk[sa[i]]=num; else rk[sa[i]]=++num; } if(num==n)break; m=num; } } int main(){ scanf("%s",s+1);n=strlen(s+1);m=122; getsa();for(int i=1;i<=n;i++)printf("%d ",sa[i]); return 0; }