考完了noip,雖然d2腦子進水,然而還是目測水到了一等獎,避免了gg。也是時候開啟一些新的演算法了。思來想去,還是搞一下字尾陣列吧。
先簡單說明字尾陣列,是啥。字串字尾知道吧。陣列知道吧。字尾陣列就是在將乙個字串的所有字尾按照常見的字典序排法,排一下。顯然我們可以用stdsort來進行非常暴力的排序,然而複雜度為n^2logn,這並不優美。我們需要找到一種更高效的演算法,有兩種,一種是倍增,一種是dc3,後者為線性複雜度,然而常數較大,並且難以理解,**較長,所以先來研究一下第一種。
我們先來明確一下演算法流程,先開一下下圖
我們先根據原來的字串,排序出每一位是第幾大。
我們的演算法叫倍增,怎麼個倍增,我們是倍增每次比較字串的長度。
然後我們設比較的長度為k
我們每次把i和i+k合併到一起,最後將新得到的陣列進行排序,反覆多次,知道陣列內的每個排名都不相同就可以退出了。
然後我們如果在這用暴力的stl sort的話,顯然複雜度不夠優美,所以我們需要用到基數排序
並且基數排序還有乙個優美的性質,就是在排序關鍵字相等時,不更改在原序列中的相對位置前後。
for (int i = 0;i < m;i++) c[i] = 0;
for (int i = 0;i < n;i++) c[x[i] = r[i]]++;
for (int i = 1;i < m;i++) c[i] += c[i - 1];
for (int i = n - 1;i >= 0;i--) sa[--c[x[i]]] = i;
我們看一下上述**,首先我們用c記錄出每個元素的個數。
然後我們將c陣列做字首和處理。
然後我們這樣子來愉快的排好序,至於這麼做為什麼是對的,讀者可以自己模擬思考,在這解釋下,sa i 表示長度排第i的字尾是從原字串的哪一位開始。
這樣子我們只要在倍增的過程中,處理出第二關鍵字,並按照這個來進行排序,就可以優美的求出字尾陣列了。
上面講的肯定會有些不足,可以參考下下面的**,我會給予詳細的注釋,讀者可以自行思考。
void sa_init()
}
有一句話,字尾陣列求出來沒有用,關鍵是還要求出heigt陣列,height陣列表示什麼呢,是字典序排序相鄰的兩個字尾的最大公共長度。
我們為什麼要搞相鄰的兩位,因為他們的字典序最相近,可能的公共字首也最長。
我們此處定義一下,height[i]表示sa[i]和sa[i + 1]的最長公共字首長度。
我們先考慮暴力的求法,顯然是n^2的做法,我們暴力的掃瞄這個sa陣列,並且暴力的去判斷相鄰的字串,但我們想一下,我們現在已經暴力的判斷了最長的字尾和他的下乙個字尾,然後我們求出他們的最長公共字首長k,那麼我們考慮第二長的字尾和他在sa陣列的下乙個字尾的最長字首。然而我感覺我將不清楚,於是來一張自己畫得圖
最後給出詳細的**:
uoj有模板題~
#include #include #include #include using namespace std;
const int maxn = 200000;
int n,m = 30;
char s[maxn];
int c[maxn],t1[maxn],t2[maxn],sa[maxn],r[maxn],height[maxn],rank[maxn];
void sa_init()
}void rank_init()
int bl(int a, int b)
void getht()
}int main()
演算法,字尾陣列
字尾陣列就是將字串所有字尾排序後的陣列,設字串為s,令字尾suffix i 表示s i.len s 用兩個陣列記錄所有字尾的排序結果 然後就是怎麼快速求所有字尾的順序了,其中的關鍵是如何減少兩個字尾比較的複雜度 方法是倍增法,定義乙個字串的k 字首為該字串的前k個字元組成的串,關於在k 字尾上的定義...
演算法 字尾陣列
前言 這篇部落格真難寫,暫定待更 定義 字尾陣列 suffixarray 是乙個一維陣列,簡稱sa,它儲存1到n的某個排列 sa 1 sa 2 dots,sa n 並且保證 suffix sa i 1 leq i n 也就是將s的n個字尾按字典序從小到大進行排序之後把排好序的字尾的編號順次放入sa中...
字尾陣列 da演算法
sa陣列,他儲存1.n 的某個排列,sa 2 sa n 並且保證 suffix sa i suffix sa i 1 1 i 也就是將 s 的 n 個字尾從小到大進行排序之後把排好序的字尾的開頭位置順次放入 sa 中。rank陣列,他儲存的是每個位置的字尾子串的排名,與sa陣列是可以互逆的。heig...