给定一个仅含字母 a a a 和 b b b 的字符串 t t t ,并给出 B B B 数组的构造方法:
现在要求对 t t t 的每一个后缀求一遍 B B B 数组,然后按字典序升序输出后缀的编号
随便想几个例子构造一下 B B B 数组,大致的样式都是 0 1 k 0.... 01_{k}0.... 01k0.... 即前部分是两个 0 0 0 之间有若干个 1 1 1 ,然后后部分皆大于 0 0 0;与普通后缀不同的是,前部分两个 0 0 0 的值在往前的时候是可能会改变的,而后部分的值往前不会再改变;
抓住这一点,可以先比较 0 1 k 0 01_{k}0 01k0 ,相等的话再比较后部分,而后部分显然求一遍后缀数组是最好的选择。前面的部分,越长字典序越大,所以只需要求两个 0 0 0 的位置,而这也不难求;
0 1 k 0 01_{k}0 01k0 对应的形式即 a a k b aa_kb aakb 或者 b b k a bb_ka bbka,所以对于当前位置,只需要找到后面第一个不相等字母的位置即可;
所以思路是基于字符串 t t t 求出完整的 B B B 数组求一遍后缀数组,然后找到每个后缀的前部分的长度;排序就是先比较前部分大小,相等再利用 r k rk rk 数组比较后部分的大小;
会有特例比如:
对于情况 1 1 1 ,前部分相等的话, r k rk rk 数组的比较会延申到 r k [ n + 1 ] rk[n+1] rk[n+1];对于情况 2 2 2 ,我们假设第二个 0 0 0 的位置在 n + 1 n+1 n+1 ,前部分相等的话, r k rk rk 数组的比较会延申到 r k [ n + 2 ] rk[n+2] rk[n+2] ,所以相应的需要设 r k [ n + 1 ] = − 1 , r k [ n + 2 ] = − 2 rk[n+1]=-1,rk[n+2]=-2 rk[n+1]=−1,rk[n+2]=−2 ;
#include
#include
#include
using namespace std;
#define st first
#define nd second
#define P pair
#define ll long long
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define frep(i,a,b) for(int i=a;i>=b;i--)
const int N = 1E6+10;
int n,sa[N],rk[N],oldrk[N<<1];
int ht[N],px[N],id[N],cnt[N];
bool cmp(int x,int y,int w){
return oldrk[x]==oldrk[y] && oldrk[x+w]==oldrk[y+w];
}
void da(int s[],int n,int m){
int i,p=0,w,k;
rep(i,0,n) cnt[i]=0;
for(i=1;i<=n;i++) ++cnt[rk[i] = s[i]];
for(i=1;i<=m;i++) cnt[i] += cnt[i-1];
for(i=n;i>=1;i--) sa[cnt[rk[i]]--] = i;
for(w=1;w<n;w<<=1,m=p){
for(p=0,i=n;i>n-w;i--) id[++p]=i;
for(i=1;i<=n;i++)
if(sa[i]>w) id[++p]=sa[i]-w;
rep(i,0,n) cnt[i]=0;
for(i=1;i<=n;i++) ++cnt[px[i] = rk[id[i]]];
for(i=1;i<=m;i++) cnt[i] += cnt[i-1];
for(i=n;i>=1;i--) sa[cnt[px[i]]--] = id[i];
rep(i,0,n) oldrk[i]=rk[i];
for(p=0,i=1;i<=n;i++)
rk[sa[i]]=cmp(sa[i],sa[i-1],w)?p:++p;
}
for(i=1,k=0;i<=n;i++){
if(k) --k;
while(s[i+k]==s[sa[rk[i]-1]+k]) ++k;
ht[rk[i]]=k;
}
}
char str[N];
int s[N];
P v[N];
bool cmp2(P a,P b)
{
if(a.nd-a.st!=b.nd-b.st) return a.nd-a.st<b.nd-b.st;
return rk[a.nd+1]<rk[b.nd+1];
}
int main()
{
while(~scanf("%d",&n))
{
scanf("%s",str+1);
int a=-1,b=-1;
frep(i,n,1)
{
s[i]=0;
int c=str[i]-'a';
if(c==0){
if(a!=-1) s[a]=a-i;
a=i;
}
else{
if(b!=-1) s[b]=b-i;
b=i;
}
} // s 即完整的B数组
da(s,n,n); //求一遍后缀数组
rk[n+1]=-1;
rk[n+2]=-2;
a=b=n+1;
frep(i,n,1) //求出前部分的长度
{
int c=str[i]-'a';
if(c==0){
v[i]=P(i,b);
a=i;
}
else{
v[i]=P(i,a);
b=i;
}
}
sort(v+1,v+n+1,cmp2);
rep(i,1,n) printf("%d%c",v[i].st,i==n?'\n':' ');
}
}