1. 程式人生 > >利用有限自動機分析正則表示式【轉】

利用有限自動機分析正則表示式【轉】

【轉】

版權宣告:可以任意轉載,但轉載時必須標明原作者charlee、原始連結http://tech.idv2.com/2006/05/08/parse-regex-with-dfa/以及本宣告。

【/轉】

概念

記號
有字母表中的符號組成的有限長度的序列。記號s的 長度記為 |s|。 長度為0的記號稱為 空記號,記為 ε
有限自動機(Finite State Automaton)
為研究某種計算過程而抽象出的計算模型。 擁有有限個狀態,根據不同的輸入每個狀態可以遷移到其他的狀態。
非確定有限自動機(Nondeterministic Finite Automaton)
簡稱 NFA,由以下元素組成:
1. 有限狀態集合 S
2. 有限輸入符號的字母表 Σ
3. 狀態轉移函式 move
4. 開始狀態  sSUB{0}
5. 結束狀態集合 FF ∈ S
自動機初始狀態為 sSUB{0},逐一讀入輸入字串中的每一個字母,根據當前狀態、讀入的字母, 由狀態轉移函式 move控制進入下一個狀態。如果輸入字串讀入結束時自動機的狀態屬於結束狀態集合 F, 則說明該自動機接受該字串,否則為不接受。
確定有限自動機(Deterministic Finite Automaton)
簡稱 DFA,是NFA的一種特例, 有以下兩條限制:
1. 對於空輸入 ε,狀態不發生遷移;
2. 某個狀態對於每一種輸入最多隻有一種狀態轉移。

將正則表示式轉換為NFA(Thompson構造法)

演算法

演算法1 將正則表示式轉換為NFA(Thompson構造法)

輸入 字母表Σ上的正則表示式r

輸出 能夠接受L(r)的NFA N

方法 首先將構成r

的各個元素分解,對於每一個元素,按照下述規則1規則2生成NFA。 注意:如果r中記號a出現了多次,那麼對於a的每次出現都需要生成一個單獨的NFA。

之後依照正則表示式r的文法規則,將生成的NFA按照下述規則3組合在一起。

規則1 對於空記號ε,生成下面的NFA。

fig01.png

規則2 對於Σ的字母表中的元素a,生成下面的NFA。

fig02.png

規則3 令正則表示式st的NFA分別為N(s)N(t)

a) 對於s|t,按照以下的方式生成NFA N(s|t)

fig03.png

b) 對於st,按照以下的方式生成NFA N(st)

fig04.png

c) 對於s*,按照以下的方式生成NFA N(s*)

fig05.png

d) 對於(s),使用s本身的NFA N(s)

性質

演算法1生成的NFA能夠正確地識別正則表示式,並且具有如下的性質:

  1. N(r)的狀態數最多為r中出現的記號和運算子的個數的2倍。
  2. N(r)的開始狀態和結束狀態有且只有一個。
  3. N(r)的各個狀態對於Σ中的一個符號,或者擁有一個狀態遷移,或者擁有最多兩個ε遷移。

示例

利用演算法1,根據正則表示式 r=(a|b)*abb 可以生成以下的NFA。

fig06.png

將NFA轉化為DFA

演算法

使用以下的演算法可以將NFA轉換成等價的DFA。

演算法2 將NFA轉化為DFA

輸入 NFA N

輸出 能夠接受與N相同語言的DFA D

方法 本演算法生成D對應的狀態遷移表Dtran。DFA的各個狀態為NFA的狀態集合, 對於每一個輸入符號,D模擬N中可能的狀態遷移。

定義以下的操作。

操作 說明
ε-closure(s) 從NFA的狀態s出發,僅通過ε遷移能夠到達的NFA的狀態集合
ε-closure(T) T中包含的某個NFA的狀態s出發,僅通過ε遷移能夠到達的NFA的狀態集合
move(T, a) T中包含的某個NFA的狀態s出發,通過輸入符號a遷移能夠到達的NFA的狀態集合
令 Dstates 中僅包含ε-closure(s), 並設定狀態為未標記;
while Dstates中包含未標記的狀態T do
begin
  標記T;
  for 各輸入記號a do
  begin
    U := ε-closure(move(T, a));
    if U不在Dstates中 then
      將 U 追加到 Dstates 中,設定狀態為未標記;
    Dtrans[T, a] := U;
  end
end

ε-closure(T)的計算方法如下:

將T中的所有狀態入棧;
設定ε-closure(T)的初始值為T;
while 棧非空 do
begin
  從棧頂取出元素t;
  for 從t出發以ε為邊能夠到達的各個狀態u do
    if u不在ε-closure(T)中 then
    begin
      將u追加到ε-closure(T)中;
      將u入棧;
    end
end

示例

將上面生成的NFA轉化為DFA。

最初,Dstates內僅有ε-closure(0) = A = {0, 1, 2, 4, 7}。然後對於狀態A,對於輸入記號a,計算 ε-closure(move(A, a)) = ε-closure(move({0, 1, 2, 4, 7}, a)) = ε-closure({3, 8}) = {1, 2, 3, 4, 6, 7, 8}, 即 B={1, 2, 3, 4, 6, 7, 8}, Dtran[A, a]=B。 對於狀態A,由輸入記號b能夠到達的僅有4->5,因此 C = ε-closure({5}) = {1, 2, 4, 5, 6, 7}, 即 Dtran[A, b] = C

以此類推,可得到以下的狀態和Dtran

A = {0, 1, 2, 4, 7}          D = {1, 2, 4, 5, 6, 7, 9}
B = {1, 2, 3, 4, 6, 7, 8}    E = {1, 2, 4, 5, 6, 7, 10}
C = {1, 2, 4, 5, 6, 7}
狀態 輸入符號
a b
A B C
B B D
C B C
D B E
E B C

由此得出DFA如下圖所示。

fig07.png

NFA和DFA的效率

給定正則表示式r和輸入記號序列x,判斷r是否能夠接受x

使用NFA的情況下, 由正則表示式生成NFA的時間複雜度為O(|r|),另外由於NFA的狀態數最多為r的2倍,因此空間複雜度為O(|r|)。 由NFA判斷是否接受x時,時間複雜度為O(|r|×|x|)。因此,總體上處理時間與 r、x的長度之積成比例。 這種處理方法在x不是很長時十分有效。

如果使用DFA,由於利用DFA判斷是否接受x與狀態數無關,因此時間複雜度為O(|x|)。但是DFA的狀態數 與正則表示式的長度呈指數關係。例如,正則表示式 (a|b)*a(a|b)(a|b)...(a|b),尾部有 n-1 個 (a-b)的話, DFA最小狀態數也會超過 2SUP{n}。