「字串」字尾陣列
阿新 • • 發佈:2021-10-25
字尾陣列
int x[N],y[N],c[N],sa[N],rk[N],h[N]; for(int i=1;i<=n;++i) ++c[x[i]=s[i]]; //c[i]是桶,x[i]是第i個元素的第一關鍵字排名 for(int i=2;i<=m;++i) c[i]+=c[i-1]; //求字首和,確定每個關鍵字最多在第幾名 for(int i=n;i>=1;--i) sa[c[x[i]]--]=i; //確定排名為sa[i]的數在哪個位置,這裡正序和倒序迴圈都可以,為了和下面保持一致採用倒序 for(int k=1;k<=n;k<<=1) { int num=0;//計數器 for(int i=n-k+1;i<=n;++i) y[++num]=i; //y[i]存的是第二關鍵字排名為y[i]的字尾,第一關鍵字的位置 //n-k+1到n沒有第二關鍵字,排名最靠前 for(int i=1;i<=n;++i) if(sa[i]>k) y[++num]=sa[i]-k; //如果滿足排名為sa[i]的字尾在第k位之後,那麼它可以做別人的第二關鍵字,它的第一關鍵字在k位之前 //i列舉的是第二關鍵字的排名,第二關鍵字靠前先入隊 for(int i=1;i<=m;++i) c[i]=0;//清空 for(int i=1;i<=n;++i) ++c[x[i]]; for(int i=2;i<=m;++i) c[i]+=c[i-1]; for(int i=n;i>=1;--i) sa[c[x[y[i]]]--]=y[i],y[i]=0; //y的順序是按第二關鍵字排序的,x[y[i]]是第二關鍵字排名為y[i]的元素的第一關鍵字排名 //倒序滿足y[i]排名越靠後,在sa中排名越靠後 swap(x,y); //將舊的x存在y中 x[sa[1]]=1,num=1; //更新x[i] for(int i=2;i<=n;++i) x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num; //如果二者第一二關鍵字都相同則排名相同 if(num==n) break;//已經排出了n名則字尾排序完成 m=num; for(int i=1;i<=n;++i) rk[sa[i]]=i; int k=0; for(int i=1;i<=n;++i) { if(rk[i]==1) continue;//第一位的height為0 if(k) --k; int j=sa[rk[i]-1]; while(j+k<=n&&i+k<=n&&s[i+k]==s[j+k]) ++k; h[rk[i]]=k; } }
P4051 [JSOI2007]字元加密
加倍成環,然後做字尾陣列,只取\(sa[i]\leq n\)的部分
將\(s[sa[i]-1]\)拼起來
P2870 [USACO07DEC]Best Cow Line G
列舉左右端點,哪個小放哪個
如果出現相同的情況就要判斷後綴和字首的字典序了
假設\(S=AABCAA\)
在\(S\)後新增一個間隔符,再翻轉加倍
\(S=AABCAA\#AACBAA\)
跑字尾陣列,字元相同時比較字尾排名大小
P2852 [USACO06DEC]Milk Patterns G
二分和並查集都可以,這裡選並查集
將\(h[i]\)陣列從大到小將\(i,i-1\)集合合併,直到出現集合大於等於\(k\)