2倍倍增算法的主要思路是:用倍增的方法对每个字符开始的长度为2^k的字符串进行排序,求出排名,即rank值。
#include<iostream> using namespace std; const int maxlen = 10011; int tsa[maxlen], RANK[maxlen], sum[maxlen], sa[maxlen], TRANK[maxlen]; void radix_sort(int j, int len){ //对第二关键字计数排序,tsa代替sa为排名为i的后缀是tsa[i] memset(sum,0,sizeof(sum)); for (int i=1; i<=len; i++){ if(i+j>len) RANK[i+j]=0; sum[ RANK[i+j] ]++; } for (int i=1; i<=maxlen; i++) sum[i]+=sum[i-1]; for (int i=len; i>0; i--) tsa[ sum[ RANK[i+j] ]-- ]=i; //对第一关键字计数排序,构造互逆关系 memset(sum,0,sizeof(sum)); for (int i=1; i<=len; i++) sum[ RANK[i] ]++; for (int i=1; i<=maxlen; i++) sum[i]+=sum[i-1]; for (int i=len; i>0; i--) sa[ sum[ RANK[ tsa[i] ] ]-- ]= tsa[i]; } void calc_sa(int *s, int len){ int p; // TRANK存放字符串 // int len=s.size(); for (int i=0; i<len; i++) TRANK[i+1]=s[i]; // 对单个字符进行计数排序 for (int i=1; i<=len; i++) sum[ TRANK[i] ]++; for (int i=1; i<=maxlen; i++) sum[i]+=sum[i-1]; for (int i=len; i>0; i--) sa[ sum[ TRANK[i] ]-- ]=i; // 计算RANK RANK[ sa[1] ]=1; for (int i=2,p=1; i<=len; i++){ if (TRANK[ sa[i] ]!=TRANK[ sa[i-1] ]) p++; RANK[ sa[i] ]=p; }//第一次的sa与RANK构造完成 for (int j=1; j<=len; j*=2){ // 对长度为j的字符串进行排名 radix_sort(j,len); TRANK[ sa[1] ]=1; p=1; //用TRANK代替RANK // 计算RANK for (int i=2; i<=len; i++){ if(sa[i]+j > len) RANK[ sa[i]+j ]=0; if(sa[i-1]+j > len) RANK[ sa[i-1]+j ]=0; if ((RANK[ sa[i] ]!=RANK[ sa[i-1] ]) || (RANK[ sa[i]+j ]!=RANK[ sa[i-1]+j ])) p++; TRANK[ sa[i] ]=p;//空间要开大一点,至少2倍 } for (int i=1; i<=len; i++) RANK[i]=TRANK[i]; } } void calc_height(int *s, int len){ int k=0,j; for(int i=0; i<len; height[i++]=k){ if(RANK[i]==1)continue; for(k?k--:0; j=sa[ RANK[i]-1 ]; s[i+k]==s[j+k]; k++); } }
罗穗骞 《后缀数组——处理字符串的有力工具》