五一之后就開始實習了,接觸的第一件事就是解析正則,於是開始學習正則轉DFA的知識。看了很多帖子,始終在狀態move中的解析一帶而過,最終在網易雲課堂的一門課中找到答案。http://study.163.com/course/courseMain.htm?courseId=1002830012。我從中摘抄部分內容如下,如果覺得有用請去雲課堂里繼續學習。非常感謝這位老師。
摘抄:
大家好,歡迎大家來到coding迪斯尼,上一節我們研究了如何使用NFA識別輸入字符串,同時提出了來個概念,一個是ε閉包操作,一個是move得到轉移集合。這兩個操作在我們今天的主題,將NFA轉換為DFA的算法中將占據主導地位。我們任然以上一節用到的NFA狀態機為例子,看看它是怎么轉換為DFA的
NFA轉DFA算法:
我們先獲取NFA的起始節點,然后計算它的ε閉包:
ε-closure({17}) = { 17, 3 , 1, 4, 5, 9}
我們知道,處於ε閉包中的任何一個狀態節點時,我們可以不用輸入任何字符就可以直達其他節點,因此,閉包中的所有節點其實可以等價於一個節點,這個節點就可以作為NFA對應的DFA中的一個節點。因此我們把集合{ 17, 3 , 1, 4, 5, 9}
對應於一個節點,記為S0:
(S0, { 17, 3 , 1, 4, 5, 9})
於此同時把上面的節點標記加入一個隊列中,最為隊列的開頭:
[(S0, { 17, 3 , 1, 4, 5, 9})]
NFA狀態機可接受的字符是數字字符和字符 ’.’ ,接下來我們計算S0對數字字符和字符’.’ 的轉移集合:
Move(S0, .) = Move({ 17, 3 , 1, 4, 5, 9}, . ) = {6}
接着計算{6}的ε閉包:
ε-closure({6}) = {6,7}, 然后看看{6,7}在上面的隊列中是否存在,由於當前隊列只有一個元素:[(S0, { 17, 3 , 1, 4, 5, 9})], 所以{6,7}在隊列中不存在,於是我們把{6,7}當做DFA的第二個狀態節點記做(S1, {6,7}), 把它加入到隊列中:
[(S0, { 17, 3 , 1, 4, 5, 9})]->[(S1, {6,7})]
這樣我們就有了兩個節點的對應關系 S0-(.)->S1.
我們再計算S0對應數字字符時所得的轉移集合:
Move(S0, D) = Move({ 17, 3 , 1, 4, 5, 9}, D) = {2, 10}
然后對{2,10}做閉包操作:
ε-closure({2,10}) = {2,10,4,5,1,11}
看看隊列中是否有{2,10,4,5,1,11}對應的節點,由於沒有對應節點,所以該集合可作為DFA的一個節點,記做(S2, {2,10,4,5,1,11}). 然后把它加入隊列:
[(S0, { 17, 3 , 1, 4, 5, 9})]->[(S1, {6,7})]->[(S2, {2,10,4,5,1,11})]
於是我們又有了一個節點對應關系:
S0-(D)->S2
最后我們得到DFA的三節點關系圖:

大家要注意,從圖上看S0 到 S2 只有一條邊,但是D代表的是數字字符的集合[0-9],所以實際上S0到S2有10條邊,也就是S0有10條出去的邊,邊對應的字符分別是0,1,2…9, 這十條邊都指向S2,上圖為了簡明,所以把這十條邊抽象為1條邊,在后續我們代碼中,構造的DFA將會有10條邊指向S2,這個差別大家要留心。
接下來我們計算S1對應數字字符和字符’.’所得到的轉移集合:
Move(S1, .) = Move({6,7}, .) = NULL
由於轉移集合是空,因此我們不做考慮,再看S1對應D的轉移集合
Move(S1, D) = Move({6,7}, D) = {8}
ε-closure({8}) = {8,18}.
在隊列中看看有沒有{8.18}對應的節點,由於沒有所以{8,18}可作為DFA新的節點,記為(S3, {8,18}),並加入隊列:
[(S0, { 17, 3 , 1, 4, 5, 9})]->[(S1, {6,7})]->[(S2, {2,10,4,5,1,11})]->[(S3, {8,18})]
同時意味着節點S1與節點S3有對應關系: S1-(D)->S3, 特別需要注意的是,S3的NFA節點集合中包含了NFA的終結節點18,所以S3是一個具有接受狀態的節點,於是我們得到DFA如下:

接下來我們計算S2的對應數字字符和字符’.’的轉移集合:
Move(S2, .) = Move({2,10,4,5,1,11}, .) = {6,12}
ε-closure({6,12}) = {6,12,7,15,13,16,18}
查看隊列看看有沒有上面閉包集合對應的點,由於沒有,所以上面閉包可以對應一個新的DFA節點,記為 (S4, {6,12,7,15,13,16,18})由於閉包集合含有NFA接受節點18, 所以S4是一個接受節點,把S4加入隊列:
[(S0, { 17, 3 , 1, 4, 5, 9})]->[(S1, {6,7})]->[(S2, {2,10,4,5,1,11})]->[(S3, {8,18})]->
[(S4, {6,12,7,15,13,16,18})]
同時我們得到對應關系 S2-(.)->S4
我們計算S2對應D的轉移集合:
Move(S2, D) = Move({2,10,4,5,1,11}, D) = {2}
ε-closure({2}) = {2, 1, 4, 5}
檢測一下閉包集合是否在隊列中,由於不在,所以它可以對應於DFA一個新節點記為 (S5, {2,1,4,5}), 把它加入隊列:
[(S0, { 17, 3 , 1, 4, 5, 9})]->[(S1, {6,7})]->[(S2, {2,10,4,5,1,11})]->[(S3, {8,18})]->
[(S4, {6,12,7,15,13,16,18})]->[(S5, {2,1,4,5})]
我們又得到一個對應關系S2-(D)->S5,這樣DFA狀態圖又更新為:

