2020牛客多校第一場 B題Suffix Array(結論+后綴數組)
剛學后綴數組有點菜沒a出來,看題解后,是個結論沒發現:
• Let C_i = min_{j > i and s_j = s_i} {j - i}
• The B-Suffix Array is equivalent to the suffix array of C_1 C_2 ... C_n
這是題解的原話,我給大家翻譯一下,c[i]的值是索引i后面離它最近的與s[i]同字符的距離,然后字符串的后綴數組就與這個c數組的后綴數組,這個結論只適用於只有兩個字符的情況,所以題目字符也只有兩種a與b,怎么證明的話大家去看官方證明吧,本人過菜,我是看半天沒看明白,orz。
不過我可以給大家演示一下。(注意如果是最后的a,b的話它后面無a,b那么結果是無窮大,我這邊就讓他等於n了,最后一位讓它加一位為n+1)。
如abaab的c數組為231556,列出c的后綴為:
231556,31556,1556,556,56,6
所以,sa數組就是312456,反過來54213就是答案了。
我的寫法是把231556變成32400-1,這樣比較契合我的寫法,不用大改cmp。
代碼,之前看過其他大佬的,都寫得很多,好像不是這么做的,可能是自己想出來的神仙寫法吧orz。
還有我的后綴數組板子的話用的是書上的,比大家的短很多,主要我剛學,也不清楚他們那種是不是有什么優化,希望大佬指點迷津,以下是ac代碼。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int n,c[100007];
string s;
int ran[100007],tmp[100007],sa[100007],k;
void init(){
k=0;
memset(ran,0,sizeof(ran));
memset(tmp,0,sizeof(tmp));
memset(c,0,sizeof(c));
}
bool cmp(int i,int j){
if(ran[i]!=ran[j]) return ran[i]<ran[j];
else{
int ri,rj;
if(i+k<=n){
ri=ran[i+k];
}
else{
ri=-1;
}
if(j+k<=n){
rj=ran[j+k];
}
else{
rj=-1;
}
return ri<rj;
}
}
void get_sa(int sa[]){
for(int i=0;i<=n;i++){
sa[i]=i;
if(i<n){
ran[i]=c[i];
}
else{
ran[i]=-1;
}
}
for(k=1;k<=n;k*=2){
sort(sa,sa+n+1,cmp);
tmp[sa[0]]=0;
for(int i=1;i<=n;i++){
tmp[sa[i]]=tmp[sa[i-1]]+(cmp(sa[i-1],sa[i]) ? 1 : 0);
}
for(int i=0;i<=n;i++){
ran[i]=tmp[i];
}
}
}
int main(){
while(~scanf("%d",&n)){
cin>>s;
int a=n,b=n;
for(int i=n-1;i>=0;i--){
if(s[i]=='a'){
if(a==n)c[i]=(n-a)%n;
else c[i]=(n-a+i)%n;
a=i;
}
else{
if(b==n)c[i]=(n-b)%n;
else c[i]=(n-b+i)%n;
b=i;
}
}
get_sa(sa);
for(int i=1;i<=n;i++){
printf("%d ",sa[i]+1);
}puts("");
}
}