再解字串有關的題目時, 通常suffix array 是一個很有用的方法.

 

suffix array 就是將一個字串的所有後綴經過"排序"存到陣列裡.

例如 : 字串abagabal

它的suffix 就有

abagabal

bagabal

agabal

gabal

abal

bal

al

l

排序後變成

abagabal

abal

agabal

al

bagabal

bal

gabal

l

 

這樣擺放的好處是"搜尋變快", 例如給另一個字串bal , 要問bal 是不是存在上面的abagabal 裡頭,

其實就是對suffix array 做binary search

過程 : 因為後綴陣列大小為8, 先從(0+7)/2 = 3開始發現al < bal , 所以變成(4+7)/2 = 5,bal == bal.

 

那接下來要討論的是

怎麼樣才可以快算的建出後綴陣列 ?

天真的作法 (naive method )

 1. 先得到所有的後綴

2. 對所有的後綴排序

3. 結束

 

1. 如果是新手的話, 很容易就會先利用C++ 函式substr來做出後綴,但千萬別這麼做,

   如果你那樣做的話, 時間複雜度在 (1) 就會是O(n^2) 了. 那還不如不要建後綴陣列.

   這裡利用pointer的技巧就可以了.

例如 char str[1000];          char *suffix[1000];

            for(i = 0 ;i < 1000 ; i++) suffix[i] = str+i;

2. 做sort , 就呼叫 C++ sort 或C qsort.

 

天真的作法在於(2) sort 會花到O(n^2logn), 假設sort 花O(nlogn), 但string 的長度是n的話.

 

天真的作法不是不好, 有時在解題時, 它反而比較沒有限制, 要實際解題目就知道了!

 

接下來要進入經典演算法 , 我知道在處理suffix array 的演算法是

 A. prefix doubling algorithm   O(nlogn)

 B. DC3 algorithm                      O(n)

 我目前只code 過 (A) 所以下面是以(A) 為主.

 

 

在步驟(2) 時, sort 花太多時間.

prefix doubling algorithm  同樣也要sort , 但它是sort 字串的每個字元.

什麼意思?

例如 : abagabal

對每個字元sort 排名

a 1

b 2

a 1

g 3

a 1

b 2

a 1

l 4

同樣的字完有相同的排名( 排名表示以某字元為首的後綴長度為1 )

 

接下來得到長度為2, 過程會先得到

a 12

b 21

a 13

g 31

a 12

b 21

a 14

l 40

例如第一個a 是12 因為a 後面的字是b , b 排名2, 而a 本身是1

所以整理之後得到

a 1

b 4

a 2

g 5

a 1

b 4

a 3

l 6

這是後綴長度為2 的排名

 

繼續 得到長度為4如下

 

a 12

b 45

a 21

g 54

a 13

b 46

a 30

l 60

( 後面沒字母就補0)

 同樣的從長度為2 要得到長度為4 , 是把2個相鄰長度為2 的合併, 例如 a 12 是因為a 本身排名為1, 而a 後兩個字母是a 但此a 排名

為2 所以得到12.

 

整理之後

a 1

b 5

a 3

g 7

a 2

b 6

a 4

l 8

 

繼續得到長度為8

 

a 12

b 56

a 34

g 78

a 20

b 60

a 40

l 80

 

整理後

a 1

b 5

a 3

g 7

a 2

b 6

a 4

l 8

這就是長度為8 的後綴排名.

 

比較

abagabal

bagabal

agabal

gabal

abal

bal

al

l

==> sort 後得到

abagabal

abal

agabal

al

bagabal

bal

gabal

l

a 1

b 5 ==> bagabal 在sort 後的第五位

a 3

g 7

a 2

b 6

a 4

l 8

 

 

 

prefix doubling algorithm 的精神是: 1個的結果=> 2個的結果 => 4個的結果 => 8個的結果. ( 單位: 後綴長度)

 

通常suffix array 會搭配longest common prefix array (LCPA).

LCPA 紀錄後綴字串A, 與前者之間的共同最長前綴.

 

例如

                      LCPA

abagabal     0

abal              3

agabal          1

al                    1

bagabal        0

bal                 2

gabal            0

l                     0

 

接下來又出現新的問題了!

如果直接計算LCPA 的值會花到O(n^2). 假設字串長度為n, 字串有n 個

計算LCPA array 只需要O(n).

先注意一個特性

如果是排名第一的後綴那就直接是0

abagabal => 0

再來考慮bagabal, 它的排名前一名是al , 比較兩者得0

所以bagabal => 0

再來考慮agabal, 排名前一個是abal

得到agabal => 1

再來gabal, 排名前一個是bal , ( 注意 :  (agabal,abal) = 1+(agabal,bal) )

得到gabal => 0

再來abal , 排名前一個是abagabal

得到abal => 3

再來bal, bagabal ( 從上面注意其實我們只要從index 2 (index 從0開始) 開始比較就可以了, 因為

(abal,abagabal) = a + (bal,bagabal) , 既然, abal 得到3, bal 至少會是2, 所以只要從字母l 開始比較 )

所以bal中的l 與bagabal中的g 開始比較, 因為b!=g不同

因此bal => 2

再來al, 排名前一個 agabal

因為bal是2所以從index 1開始

al 中的l != agabal 的g

所以al=> 1

最後l與gabal得到

l => 0


 

arrow
arrow
    全站熱搜

    lettice0913 發表在 痞客邦 留言(0) 人氣()