字尾陣列-DC3學習
阿新 • • 發佈:2018-12-15
字尾陣列一直是字串中重點的演算法
字尾陣列的一般求法是倍增,時間複雜度是
但是有的時候,這樣的演算法複雜度及常數不能滿足題目要求
那我們就來學習一種線性的構造方法:DC3
演算法的過程是這樣的,首先我們把字尾分成兩類,一類是下標是三的倍數的,另一類則不是。
我們先處理下標不是
的這一類,比如
這個字串(下標從
開始),令
表示以
下標開頭的字尾,則兩類分別是:
第一類
第二類
我們將
先各自在串後面補比整個串小的字元使長度為3的倍數,然後連起來,形成一個新串,如
然後我們發現每一個原來要排序的串等價於這個新串的
這樣的話我們就先把這個串變成一個三元組串,即
、
、
、
、
我們可以先基數排序,然後又轉化成了一個sa問題
接下來我們處理第二類
我們發現每個字尾可以轉化為一個字元和一個已知的字尾,如
,
(
表示
的第
個字元)
我們再基數排序就得到第二類的排名
最後我們歸併
考慮分類討論第一類字尾:
如果一個
的字尾比較一個第二類字尾,那麼兩類字尾均可以轉化為
,可以比較
如果一個
的字尾比較一個第二類字尾,那麼兩類字尾均可以轉化為
,也可以比較
所以就做完了!
時間複雜度的話,可以通過等比數列求和等方法推出
最後附上莫名其妙寫了
行的uoj模板
#include<iostream>
#include<cstring>
#include<cassert>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<time.h>
#include<algorithm>
using namespace std;
#define REP(i,x,y) for(int i=x;i<=y;i++)
#define rep(i,n) REP(i,1,n)
#define rep0(i,n) REP(i,0,n-1)
#define repG(i,x) for(int i=pos[x];~i;i=e[i].next)
#define ll long long
#define db double
const int N=1e6+7;
const int INF=1e9+7;
char ss[N];
int d[N];
int n;
struct SA{
int ra[N*4],sa[N*4],a[N*4],p[N],rk[N],sk[N],ct[N],ls[N],nw[N],dr[N],ds[N],gs[N],gr[N],u[N];
bool cmp1(int x1,int y1,int x2,int y2){
if(x1!=x2)return x1<x2;
return y1<y2;
}
bool cmp2(int x1,int y1,int z1,int x2,int y2,int z2){
if(x1!=x2)return x1<x2;
if(y1!=y2)return y1<y2;
return z1<z2;
}
void sor(int L,int d,int o){//基數排序
rep0(i,o+2)ct[i]=ls[i]=nw[i]=0;
rep(i,L){
nw[a[p[sk[i]]+d]+1]++;
if(ct[a[p[sk[i]]+d]+1]!=rk[sk[i]]){
ct[a[p[sk[i]]+d]+1]=rk[sk[i]];
ls[a[p[sk[i]]+d]+1]=nw[a[p[sk[i]]+d]+1];
}
ds[sk[i]]=nw[a[p[sk[i]]+d]+1];
dr[sk[i]]=ls[a[p[sk[i]]+d]+1];
}
rep(i,o+1)nw[i]+=nw[i-1];
rep(i,L){
sk[nw[a[p[i]+d]]+ds[i]]=i;
rk[i]=nw[a[p[i]+d]]+dr[i];
}
}
void build(int l,int r){
int len=r-l+1,cnt=0,c1=0,h1=1,h2=1,h3=l;
if(len==1){//邊界
sa[l]=l;
ra[l]=1;
return;
}
REP(i,l,r){
if((i-l)%3==0)continue;
p[++cnt]=i;
rk[cnt]=1;
sk[cnt]=cnt;
}
for(int i=2;~i;i--)sor(cnt,i,len);
bool fl=0;
rep(i,cnt-1)if(rk[sk[i]]==rk[sk[i+1]]){fl=1; break;}
if(fl==1){//沒排好再遞迴
rep(i,cnt){
if(i&1)a[r+4+i/2]=rk[i]+1;
else a[r+4+(cnt+1)/2+i/2]=rk[i]+1;
}
a[r+4+(cnt+1)/2]=1;//中間記得新增分割
build(r+4,r+cnt+4);
rep(i,cnt){
if(i&1)rk[i]=ra[r+4+i/2]-1;
else rk[i]=ra[r+4+(cnt+1)/2+i/2]-1;
}
rep(i,cnt)sk[rk[i]]=i;
cnt=0;
REP(i