SA字尾陣列詳解與運用

2021-09-20 12:48:40 字數 2797 閱讀 3970

主要用於解決最長公共字首(lcp)問題,大多數時候此類問題都可以用sam(字尾自動機)來解決。不過應為sa演算法相對更加優秀的時空複雜度,在大資料集上可以防止tmle。

首先宣告幾個變數。

1、str:需要處理的字串(長度為len)

2、suffix[i]:str下標為i ~ len的連續子串(即字尾)

3、rank[i]: suffix[i]在所有字尾中的排名

4、sa[i]: rank的逆運算,就是排名第i大的字串是啥。

構造方式大概有兩種:dc3和倍增

dc3 時間o(n),空間o(3*n) 常數大 (看臉)

倍增時間o(nlogn),常數小

這裡介紹倍增 其實是我只會倍增

考慮暴力

對於乙個字尾,想要直接構造出它的rank可以用快排(nlogn排序+每次o(n)比較)

這種是n^2logn的時間複雜度,顯然不能接受。

使用倍增的思想 設substr(i, len)為從第i個字元開始,長度為len的字串我們可以把第k輪substr(i, 2^k)看成是乙個由substr(i, 2k−1)和substr(i + 2k−1, 2k−1)拼起來的東西。

這兩個長度而2^k−1的字串是上一輪計算過的,當然上一輪的rank也知道。

那麼把每個這一輪的字串都轉化為這種形式,並且大家都知道字串的比較是從左往右,左邊和右邊的大小我們可以用上一輪的rank表示。

這就可以視為一些第一關鍵字和第二關鍵字比較大小再把這些兩位數重新排名就是這一輪的rank。

tips:如果使用傳統排序方法(快排)那麼時間複雜度就是nlog^2n,在這裡我們可以使用基數排序,將時間複雜度降為nlogn。

#include#include#include#includeusing namespace std;

char s[1000011];

int len,n;

int sa[1000011];

int x[1000011],y[1000011],t[1000011];

int p[1000011];

inline void get_sa()

putchar('\n');*/

int num=0;

for(int k=1;k<=n&&num<=n;k<<=1) //可以用p[i]=x[y[i]]來卡常

for(int i=1;i<=m;++i) t[i]+=t[i-1];

for(int i=n;i;--i) sa[t[x[y[i]]]--]=y[i];

//桶排計算sa

//按第二關鍵字的順序訪問第一關鍵字,要倒序(越大的桶排順序越後)

//x已經計算好了

/*for(int i=1;i<=n;i++)

putchar('\n');*/

swap(x,y);

x[sa[1]]=1;num=1;

for(int i=2;i<=n;++i) //按照sa的順序計算下一輪的x,模擬即可,注意邊界

m=num; }}

void prt(int x)

int main()

return 0;

}

同樣先是定義一些變數

heigth[i] : 表示suffix[sa[i]]和suffix[sa[i - 1]]的最長公共字首,也就是排名相鄰的兩個字尾的最長公共字首 。

h[i] : 等於height[rank[i]],也就是字尾suffix[i]和它前一名的字尾的最長公共字首 。

而兩個排名不相鄰的最長公共字首定義為排名在它們之間的height的最小值。

如何高效的計算height陣列

可以知道h[i]>=h[i-1]-1;

可以思考一下。

然後按照hi的順序來算heighti,暴力算

簡單應用

(1. 乙個串中兩個串的最大公共字首是多少?

(2. 乙個串中可重疊的重複最長子串是多長?

(3. 乙個串中不可重疊的重複最長子串是多長? (poj1743)

1,這就是height啊,用rmq處理即可。

2,on掃過去,看height的最大值即可。

3,二分答案,把所有的height按照k分組,保證每組間的height大於k,然後檢視每組最大的sa值和最小的sa值相差是不是超過了k,有一組超過答案就合法

高階應用

乙個字串不相等的子串的個數是多少?

每個子串一定是某個字尾的字首,那麼原問題等價於求所有字尾之間的不相同的字首的個數。

可以發現每乙個字尾suffix[sa[i]]的貢獻是len - sa[i] + 1,但是有子串算重複。

重複的就是heigh[i]個與前面相同的字首,那麼減去就可以了。最後,乙個字尾suffix[sa[i]]的貢獻就是len - sa[i] + 1 - height[i]。

命運石之傳送門

題意:可重疊的k次最長重複子串

容易想到二分長度(重疊》k次的也一定重疊了k次,滿足單調性),然後分組檢視每組中有沒有足夠多數量的字串

題解就先咕了,下週可能會在機房打

5、後記

字尾陣列感覺。。其實理解了也沒那麼難

好像下節課講sam和回文陣列。。祝我好運

SA 字尾陣列

首先一定要確定sa 是個什麼東西 sa i 表示的是排名為 i 的字尾是哪乙個 至於字尾 i的排名是多少,那個是ra nk i 當然啦 最最最難懂的就是基數排序 要是不用基數排序,每次對於乙個二元組直接so rt一下 這樣的複雜度是o nlog 2 對於二元組的基數排序應該是這樣做的 首先把所有元素...

字尾陣列SA

給定乙個字串s,按字典序排序s的所有子串 鬼知道什麼思想,好像沒有什麼思想。哦,想起來了,是倍增。考慮最簡單的字尾間o n o n 比較和快排o nlog n o n logn 總複雜度o n2lo gn o n 2log n 考慮優化字串間的比較,用倍增的思想,假設k 2 k 2 長度的已經比完了...

字尾陣列SA

原理 其本質就是把字串的所有字尾進行排序。用普通排序需要o nlogn 但是字串比較和數字比較不同,所以實際需要o n nlogn 為了讓這個過程快一點,所以有了倍增演算法,o nlogn 和dc3演算法,o n 倍增演算法比較簡單,也比較好寫,具體可以參考這個大佬的部落格。dc3演算法複雜一點,但...