[IR] String Matching

爲了提升檢索效率,大概有兩種思路:html

  • 對文本作預處理,好比:BWT
  • 對字符串作預處理,好比:KMP、Boyer-Moore

 

BWT

[IR] BWT+MTF+AC 中已經介紹了BWT (Burrows–Wheeler_transform)數據轉換算法,算法

這種變換方式不只方便壓縮,同時對pattern search也帶來了意想不到的好處。數組

 

事實上,BWT形式的數據,能夠僅還原局部數據,而非必須還原完整的文件。分佈式

這個完整的搜索過程叫作:FM-index,包括三部分,①BWT(T),②checkpoint data,③一個簡化了的SA[]數組。post

 

Left字體

Symbol #Less Than
A 0
B 3
N 4
[ 6
] 7

 

Rightui

Position Symbol #Matching(idx)
0 B   3+0->3:[  0
1 N 0+4->4:A 0
2 N 4+1->5:A 1
3 [ E 0
4 A 0+0->0:B 0
5 A 0+1->1:N 1
6 ] S->7:A 0
7 A 0+2->2:N 2

 

圖示化以上搜索過程(其中一步Postion:5):編碼

匹配的過程,實際就是搜索範圍逐漸縮小的過程(backward search),以下:url

若能持續搜索到Pattern最後一個字符,則說明該字符串(pattern)在文本中。spa

時間複雜度就是O(len(pattern))

 

若使用Run-length FM-Index壓縮後,搜索的過程當中要伴隨着解碼。

Continue: [IR] BWT+MTF+AC

 

index  Last   B   S   B'  Front   C 
1 e 1 e 1 $ $
2 e 0 d 1 _ _
3 d 1 _ 1 _ _
4 _ 1 n 1 a a
5 n 1 r 1 d d
6 r 1 h 1 e e
7 r 0 t 0 e e
8 h 1 $ 1 e h
9 h 0 a 0 e n
10 t 1 e 1 h r
11 $ 1 _ 0 h t
12 a 1   1 n  
13 e 1   1 r  
14 e 0   0 r  
15 _ 1   1 t  

 

若不使用RLFM,本來的搜索方式以下(只考慮Last,Front列)

問題:index:13:e 的下一個是誰?(Notice:這裏Last列與Front列左右位置與一般相反)

過程:

(1)‘右邊’找‘左邊’對應的index

     (i) index:13:e 先找‘左邊’e block的首,也就是index:6。

     (ii)本身就是‘左邊’e block的3rd e,故,‘左邊’就是index:(6+3-1)=8,

(2)‘左邊「相應的index對應的‘右邊’是誰

     (i) 那麼,index:8對應的‘右邊’就是:h

 

若使用RLFM,搜索方式中伴隨着解碼(需還原出Last,Front列)

【紅色字體部分如今須要解碼才能得出】

(1)‘右邊’找‘左邊’對應的index

     (i) index:13:e 先找‘左邊’e block的首,也就是index:6。(block定位)

         a: 如何由index:13解碼出e?

            index:13經過B列數‘1’(包括本身)得出本身屬於10th block。(index->block)

            10th block在S列中就是e。

         b: 如何解碼找到‘左邊’e block的首?   <--重大區別:e block這裏變爲"block1+block2"

            10th block在S列中就是e,且是第二個e(經過數一下前面的,包括本身)。

            先 e block定位,故,e在C列 --> 6th block

            6th block經過B'列數‘1’得出對應的‘左邊’e1:index:6。  (block->index)

     (ii)本身就是‘左邊’e block的3rd e,故,‘左邊’就是index:8,(block內部定位)

         a:爲什麼是block中的3rd e?

            小block間位移:

            第二個e表明:e的block2在C列 --> 7th block

            7th block經過B'列數‘1’得出對應的‘左邊’e2:index:8。  (block->index)

            求block1->block2的位移:8-6=2

            小block間內部位移:

           ‘右邊’的index=13或者14,在數'1'時(包括本身),前面都是有10個'1'。

            index:13所屬block中的1st elem就是:上列中(包括本身)距離本身最近的‘1’的index,即:13

            顯然index:13:e距離目前所屬block中的1st e的距離是0(位移),故,本身就是‘e的block2’中的1st。

            最終,在‘左邊’e block中的位移:

            答:2+1st=3rd e。

         b:爲什麼是index:8(在‘左邊’)?

            ‘左邊’e Block內部定位:e1:index:6 + 3rd - 1 = 8

 

(2)‘左邊「相應的index對應的‘右邊’是誰

     (i) 那麼,index:8對應的右邊就是:h

             與(1)(i)(a)同理,

             index:8經過B列數‘1’(包括本身)得出本身屬於6th block。(index->block)

             6th block在S列中就是h.

 

(確實比較繞,呵呵呵呵)

 


 

Knuth-Morris-Pratt (KMP)

由於brute Force太蠢,因此有了該算法。

• Brute force pattern matching runs in time O(mn) in the worst case.
• But most searches of ordinary text take O(m+n), which is very quick.

 

 

那麼,剩下的惟一問題就是,如何構造《部分匹配表》(Partial Match Table)

 

P[j]: The largest prefix of P[0 .. j-1] that is a suffix of P[1 .. j-1].

"部分匹配值"就是"前綴"和"後綴"的最長的共有元素的長度。以"ABACAB"爲例,

[0] ABACAB- P[0 .. -1]的前綴和P[1 .. -1]的後綴爲「非法」,共有元素的長度爲-1;

[1] ABACAB- P[0 .. 0]的前綴和P[1 .. 0]的後綴爲空,共有元素的長度爲0;

[2] ABACAB- P[0 .. 1]的前綴爲{A},P[1 .. 1]的後綴爲空,共有元素的長度爲0;

[3] ABACAB- P[0 .. 2]的前綴爲{A, AB},P[1 .. 2]的後綴爲{A},共有元素的長度爲1;

[4] ABACAB- P[0 .. 3]的前綴爲{A, AB, ABA},P[1 .. 3]的後綴爲{AC, C},共有元素的長度爲0;

[5] ABACAB- P[0 .. 4]的前綴爲{A, AB, ABA, ABAC},P[1 .. 4]的後綴爲{ACA, CA, A},共有元素的長度爲1;

 

但,也有缺陷:

KMP doesn’t work so well as the size of the alphabet increases
– more chance of a mismatch (more possible mismatches)
– mismatches tend to occur early in the pattern, but KMP is faster when the mismatches occur later

 


 

Boyer-Moore Algorithm

算是一種改進形式,跟重視後綴;頭部對齊,從尾部比較。 

Most text processors use BM for 「find」 (&「replace」) due to its good performance for general text documents.

Ref: 字符串匹配的Boyer-Moore算法

Link: http://www.cs.utexas.edu/users/moore/publications/fstrpos.pdf

特色:《好字符規則》和《壞字符規則》,以最大移動值爲準。

 

一個簡單的示例:

Step 1

首先,"字符串"與"搜索詞"頭部對齊,從尾部開始比較。

這是一個很聰明的想法,由於若是尾部字符不匹配,那麼只要一次比較,就能夠知道前7個字符(總體上)確定不是要找的結果

咱們看到,"S"與"E"不匹配。這時,"S"就被稱爲"壞字符"(bad character),即不匹配的字符。

咱們還發現,"S"不包含在搜索詞"EXAMPLE"之中,這意味着能夠把搜索詞直接移到"S"的後一位。以下:

 

Step 2

依然從尾部開始比較,發現"P"與"E"不匹配,因此"P"是"壞字符"。

可是"P"包含在搜索詞"EXAMPLE"之中因此,將搜索詞後移兩位,兩個"P"對齊。(利用了pattern內部的信息)

這個兩位是怎麼來的呢?

Ans:《壞字符規則》

後移位數 = 壞字符的位置 - 搜索詞中的上一次出現位置

 

OK,根據這個規則,再從新審視Step1 and Step2。

Step 1: 後移位數=6-(-1)=7  // -1:在pattern中未發現壞字符

Step 2: 後移位數=6-4=2     //  4:在pattern中idx=4發現壞字符

 

However,這樣是不夠的,在某種狀況下還不能達到更優的移動策略。

 

 

繼續咱們的示例:

Step 1

依然從尾部開始比較,"E"與"E"匹配;接下來,匹配了更多。

比較前面一位,"MPLE"與"MPLE"匹配。咱們把這種狀況稱爲"好後綴"(good suffix),即全部尾部匹配的字符串。

注意,"MPLE"、"PLE"、"LE"、"E"都是好後綴。

但接下來,繼續比較前一位,發現"I"與"A"不匹配。因此,"I"是"壞字符"。

根據"壞字符規則",此時搜索詞應該後移 2 - (-1)= 3 位。以下:

但,看上去這個move不是很聰明的樣子,顯然能夠一次性移動更多步。

初步看上去,並無利用到Pattern中兩次出現的E

 

如何利用?

Ans:《好後綴規則》

後移位數 = 好後綴的位置 - Pattern中的上一次出現位置

 

OK,根據這個規則,再從新審視Step1。

 

Step 1: 後移位數=6-0=6  // 0:"好後綴"(MPLE、PLE、LE、E)之中[Ref:KMP"部分匹配表"],只有"E"在"EXAMPLE"出如今頭部,idx=0

  1. "好後綴"的位置以最後一個字符爲準。假定"ABCDEF"的"EF"是好後綴,則它的位置以"F"爲準,即5(從0開始計算)。
  2. 若是"好後綴"在搜索詞中只出現一次,則它的上一次出現位置爲 -1。也就是pattern靠前的位置沒有再出現了呢。
  3. 若是"好後綴"有多個
    1. 最長的那個"好後綴",位置靈活;靠前位置出現的話,優先選!不然,查看其餘「好後綴」。
    2. 其餘"好後綴",上一次出現位置必須在頭部

 

好比,假定"BABCDAB"的"好後綴"是"DAB"、"AB"、"B",這時"好後綴"的上一次出現位置是什麼?

BABCDAB

BABCDAB

BABCDAB  <----

回答是,此時採用的好後綴是"B",它的上一次出現位置是頭部,即第0位。

這個規則也能夠這樣表達:若是最長的那個"好後綴"只出現一次,則能夠把搜索詞改寫成以下形式進行位置計算"(DA)BABCDAB",即虛擬加入最前面的"DA"。

 

更巧妙的是,這兩個規則的移動位數,只與搜索詞有關,與原字符串無關。所以,能夠預先計算生成《壞字符規則表》和《好後綴規則表》。使用時,只要查表比較一下就能夠了。

那麼,如何事前製表?

Ref: http://www.cs.utexas.edu/users/moore/publications/fstrpos.pdf

 


 

 

補充:簽名文件索引(Signature File Index)

做爲一種經常使用的索引組織方式,它在不少領域獲得了應用。下面從存儲和查詢兩個階段對它進行介紹。

1.存儲階段

對於每一個關鍵字,分配一個固定大小的向量(k-bit),這個向量叫作簽名(Signature);

對於一個網頁文件,通過詞典切分後,造成由對應關鍵字序列構成的向量,即P=<key1,key2,…,keym>,對這些關鍵字的簽名作OR運算,就造成了網頁文件的簽名。這個過程也被稱爲重疊編碼(Superimposed Coding)。

簽名(Signature) = k-bit OR k-bit OR k-bit OR ...

而後把網頁文件的簽名結果依次存入一個個獨立的文件中,造成對應的簽名文件,這樣造成的簽名文件比原文件小不少。(大小不就取決於k了麼?)

 

例如:有一頁網頁分詞後有這樣一些關鍵字「文本」、「英語」、「單詞」、「信件」,假設將這些關鍵字經某哈希表散列成固定位的數字向量(以6位爲例),分別爲:

hash(文本)= 000110,

hash(英語)= 110001,

hash(單詞)= 001101,

hash(信件)= 000111,

這些數字向量即爲關鍵字的簽名,而後將這些簽名作OR運算,獲得網頁文件的簽名。

 

2.查詢階段

接受用戶查詢語Q,首先把用戶查詢串字符串切分紅關鍵字序列,造成查詢向量,即A=<key1,key2,…,keyn>。

而後把關鍵字映射成相應的向量簽名,再與網頁簽名文件進行按位運算,獲得最後的匹配結果。

 

3.優缺點

簽名文件索引方式是一種比較有效的索引機制,文件組織簡單,基本和原文件順序一致;

維護容易,生成、插入、刪除都很方便;

所需空間小,特別是採用重疊編碼以後;

實現比較簡單,更新比較容易;

適合並行處理和分佈式存儲。

可是簽名向量的大小選擇是一個須要研究的問題,並且對於大的文本文件,必須進行分塊處理,檢索速度慢,須要順序掃描。

相關文章
相關標籤/搜索