而nfa確定化的本質,就是將原來多個狀態改用一個狀態來表示,新狀態其實是一個狀態集,比如nfa中狀態s1在輸入a下可以到達s2和s3,那么,在dfa中,就把s2和s3合起來認為是一個狀態。還有一個問題是nfa中的空轉換(?輸入),如果s1在?輸入下可以到達s2,就表示s1可以無條件地轉移到s2,那s1和s2自然可以合併起來作為dfa中的一個狀態,於是nfa轉dfa的算法也就好理解了。但首先得先定義下空閉包(?-closure):
?-closure: 狀態s的?-closure即s經過?轉換可以到達的狀態集,s的?-closure永遠都會包含s自身。一個狀態集的?-closure即該狀態集中各狀態的?-closure的集合。
nfa確定化算法(subset construction):
從開始狀態開始,計算它的?-closure,得到狀態集set1,然後考察set1在某個輸入a的作用下會遷移到哪些狀態,把這些狀態集中到一起,再求這個集合的?-closure,得到set2,這樣我們就可以畫兩個圈,一個標上set1,另一個標上set2,然後畫條從set1到set2的線把這兩圓連起來,線上上標上a,這樣dfa的一部分就畫好了,然後我們再考察set1在其它輸入下可以達到的狀態集的?-closure,同樣畫圈連線,以此類推,最後的時候,把包含了原nfa中終結狀態(final state或acceptin state)的dfa狀態(在轉換後的dfa中,每個狀態都是包含了一個或多個原nfa中的狀態)標記為終結狀態。
最小化dfa狀態數
由一個正則表達式,可以構建出一個等價的nfa,然後nfa又可以確定化成dfa,似乎到此事情搞完了,但事實證明(有時也可以顯然地發現),最終構成的這dfa似乎有些複雜,有些狀態好像很冗餘,呃,是的,dfa是可以最小化的。
最小化dfa狀態數算法的思想是,在開始時,假設是最完美的情況,整個dfa只有兩個狀態,一個終結狀態,一個開始(難道不能有隻有一種狀態的情況么?如果原dfa中存在非終結狀態,當然就不能,非終結態怎么可以和終結態合併!),當然,這是假設,不是真的,所以這個算法,就是在這個完美的假設下,對假設進一步考察,如果發現有些狀態不能合併,那就分出來吧,這樣重複考察,直到發現沒有狀態會不能合併時,就做完了,此時不也正是最優解么。
嗯,就是這個,所以一開始,我們把所有非終結狀態用一個袋子包起來,看成是一個狀態,把所有終結狀態也用另一袋子包起來,也看成是一個狀態,注意,別把原dfa中各狀態間的連線給扯斷了。然後,我們抽出其中一個袋子,考察其中的各個狀態,我們準備好所有的可能輸入,然後逐個拿出來測試,如果該袋子中的所有狀態在某個輸入a下達到的狀態正好都在這個袋子中,那就說明,這個袋子中的這些狀態“在目前看來”是可以合併的,也就是說,如果在所有的可能輸入的作用下,袋子中的狀態達到的新狀態正好也都是這個袋子中的狀態,那它們就可以合併。而如果,在某個輸入a下,袋子中的一部分狀態會轉移到同一袋子中的其它狀態,而有幾個叛徒,假設是s1和s2,竟然在輸入a下會遷移到其它袋子中的狀態,那就說明s1和s2是不可以和其它轉移到同一袋子中的狀態合併的,於是,我們就把s1和s2裝成一個新袋子,從原袋子中分出來,當然,現在還是假設s1和s2可以合併,所以才把它們裝一起,究竟真的可不可以合併呆會還要再考察。考察完輸入a,還要接著考察其它的可能輸入。如果在考察完一個袋子後,發現所有狀態在a輸入下都可以轉移到本袋子中的狀態,那么最後的dfa它們就被合併成一個狀態,並且在a輸入下,它有一個到自身的狀態遷移。