实现一个 DFA 正则表达式引擎 - 4. DFA 的最小化
(正則引擎已完成,Github)
最小化 DFA 是引擎中另外一個略繁瑣的點(第一個是構建語法樹)。
基本思路是,先對 DFA 進行重命名,然后引入一個拒絕態 0,定義所有狀態經過非接受字符轉到狀態 0,0 接受所有字符轉換為自身。也就是說我們先建立一個轉換表,然后把第一行填寫為:
| state | a | b | c | d | e | f | g | h | ... |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
再之后,我們講 DFA 的其余狀態從 1 開始重命名,填入狀態表。代碼實現如下:
// rename all statesfor (Set<NFAState> nfaState : oriDFATransitionMap.keySet()) {if (initStateAfterRenaming == -1 && nfaState.equals(initClosure)) {initStateAfterRenaming = renamingStateID; // record init state id}stateRenamingMap.put(nfaState, renamingStateID++);}renamedDFATransitionTable.put(0, newRejectState()); // the reject state 0finalFlags.put(0, false);// construct renamed dfa transition tablefor (Map.Entry<Set<NFAState>, Map<Character, Set<NFAState>>> entry : oriDFATransitionMap.entrySet()) {renamingStateID = stateRenamingMap.get(entry.getKey());int[] state = newRejectState();for (Map.Entry<Character, Set<NFAState>> row : entry.getValue().entrySet()) {state[row.getKey()] = stateRenamingMap.get(row.getValue());}renamedDFATransitionTable.put(renamingStateID, state);if (entry.getKey().contains(finalNFAState)) {finalFlags.put(renamingStateID, true);} else finalFlags.put(renamingStateID, false);}這里有一個 finalFlags 用來記錄哪些 DFA 的終態包含了 NFA 的終態 1。(這些狀態都作為 DFA 的終態)
接下來,把所有狀態分為終態作為一個 group,非終態作為另一個 group:
// split states to final states and non-final statesMap<Integer, Integer> groupFlags = new HashMap<>();for (int i = 0; i < finalFlags.size(); i++) {boolean b = finalFlags.get(i);if (b) groupFlags.put(i, 0);else groupFlags.put(i, 1);}我們定義任意 group 中的任意狀態的轉換規則為:接受某個字符轉移到某個 group(區別于轉換到某個狀態)。
不停地遍歷所有的 group,把同一個 group 中具有不同轉換規則的狀態分隔為不同的 group。
do { // splitting, group id is the final state idpreGroupTotal = groupTotal;for (int sensitiveGroup = 0; sensitiveGroup < preGroupTotal; sensitiveGroup++) {// <target group table, state id set>Map<Map<Integer, Integer>, Set<Integer>> invertMap = new HashMap<>();for (int sid = 0; sid < groupFlags.size(); sid++) { //use state id to iterateint group = groupFlags.get(sid);if (sensitiveGroup == group) {Map<Integer, Integer> targetGroupTable = new HashMap<>(CommonSets.ENCODING_LENGTH);for (char ch = 0; ch < CommonSets.ENCODING_LENGTH; ch++) {int targetState = renamedDFATransitionTable.get(sid)[ch];int targetGroup = groupFlags.get(targetState);targetGroupTable.put((int) ch, targetGroup);}Set<Integer> stateIDSet = invertMap.get(targetGroupTable);if (stateIDSet == null) {stateIDSet = new HashSet<>();invertMap.put(targetGroupTable, stateIDSet);}stateIDSet.add(sid); // gather all sids having the same target group table into this set}}boolean first = true;for (Set<Integer> stateIDSet : invertMap.values()) {if (first) {first = false;} else {for (int sid : stateIDSet) {groupFlags.put(sid, groupTotal);}groupTotal++;}}}} while (preGroupTotal != groupTotal);如此分到不可再分,再把每一個 group 中的所有狀態合并為同一個狀態,這樣我們就得到了 group 個狀態,就完成了 DFA 的最小化。
接著是再次確定整個 DFA 的初態、終態和拒絕態。我們只要把包含這些狀態的 group 作為最小化之后的相應狀態就可以了。
// determine initial group stateis = groupFlags.get(initStateAfterRenaming);// determine reject group staters = groupFlags.get(0);// determine final group statesSet<Integer> finalGroupFlags = new HashSet<>();for (int i = 0, groupFlagsSize = groupFlags.size(); i < groupFlagsSize; i++) {Integer groupFlag = groupFlags.get(i);if (finalFlags.get(i)) {finalGroupFlags.add(groupFlag);}}fs = new boolean[groupTotal];for (int i = 0; i < groupTotal; i++) {fs[i] = finalGroupFlags.contains(i);}最后一步是把 DFA 轉換為一個以數組形式存放的狀態表,就可以用來進行快速而準確的正則匹配了。
// construct the final transition tabletransitionTable = new int[groupTotal][];for (int groupID = 0; groupID < groupTotal; groupID++) {for (int sid = 0; sid < groupFlags.size(); sid++) {if (groupID == groupFlags.get(sid)) {int[] oriState = renamedDFATransitionTable.get(sid);int[] state = new int[CommonSets.ENCODING_LENGTH];for (char ch = 0; ch < CommonSets.ENCODING_LENGTH; ch++) {int next = oriState[ch];state[ch] = groupFlags.get(next);}transitionTable[groupID] = state;break;}}}至此,我們的整個 DFA 正則表達式引擎就構建完成了。
之后我用一個 apache log 的正則做了一下性能測試,這個引擎的匹配速度比 JDK 自帶的正則要快至少一倍,還是有一定的實用性的,所以我把它放到了?Github?上,歡迎下載源碼。
個人水平有限,代碼中一定有不少尚待優化的地方,如有建議請不吝賜教。
轉載于:https://www.cnblogs.com/zbdzzg/p/4509326.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的实现一个 DFA 正则表达式引擎 - 4. DFA 的最小化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java算法-符号~
- 下一篇: 【Android学习】自定义Androi