regex -> 逆波兰表达式 -> NFA -> DFA

  1 class Solution {
  2 public:
  3 
  4     /*
  5     * 利用DFA解决leetcode 10.正则表达式 问题
  6     * 
  7     * 步骤:
  8     * 1. 将正则字符串视为后序遍历表达式,利用栈和运算符优先级转换为
  9     *    中序遍历表达式,即逆波兰表达式。
 10     * 2. 使用中序串通过Thompson算法构造NFA。
 11     * 3. 将NFA使用子集构造算法转换为DFA。
 12     * 4. 将给定串s输入DFA模拟所有状态,若出现s读取完毕且处于结束节点则返回true。
 13     * 
 14     * 注意:
 15     * 为了简单起见,所有的new都没有进行delete。
 16     * 
 17     * 参考:
 18     * 第1步可以参考台湾清华大学 韩永楷 《数据结构》课程 第7.2 - 7.3课
 19     * 第2-4步可以参考中科大 华保健 《编译原理》课程 第2.3.1 - 3.2.4课
 20     */
 21 
 22     // NFA使用的节点
 23     struct Node;
 24 
 25     // NFA使用的路径
 26     struct Path
 27     {
 28         char c;
 29         Node * dest;
 30         Path(char in_c, Node* in_dest) :c(in_c), dest(in_dest) {}
 31     };
 32 
 33     struct Node
 34     {
 35         int index;
 36         vector<Path> pathes;
 37         Node()
 38         {
 39             static int i = 0;
 40             index = i++;
 41         }
 42         Node(vector<Path> in_pathes) :pathes(in_pathes) {}
 43         void AddPath(char c, Node* dest)
 44         {
 45             pathes.emplace_back(c, dest);
 46         }
 47     };
 48 
 49     // 将后序遍历正则串转换为中序遍历
 50     // 使用建立ExpressionTree的方法
 51     // 字符连接则添加一个&作为运算符
 52     queue<char> ToInOrder(string s)
 53     {
 54         int len = s.length();
 55         queue<char> ans;
 56         stack<char> symbols; // 符号栈
 57         bool prevIsChar = false;
 58 
 59         // 将低优先级的运算符全部弹出至ans
 60         auto PopLowPriority = [&]()
 61         {
 62             while (!symbols.empty())
 63             {
 64                 ans.push(symbols.top());
 65                 symbols.pop();
 66             }
 67 
 68         };
 69 
 70         for (int i = 0; i < len; ++i)
 71         {
 72             char c = s[i];
 73             if (c == '*')
 74             {
 75                 if (!symbols.empty() && symbols.top() == '&')
 76                 {
 77                     symbols.push(c);
 78                 }
 79                 else
 80                 {
 81                     PopLowPriority();
 82                     symbols.push(c);
 83                 }
 84                 prevIsChar = true;
 85                 continue;
 86             }
 87 
 88             if (prevIsChar)
 89             {
 90                 PopLowPriority();
 91                 symbols.push('&');
 92             }
 93 
 94             ans.push(c);
 95 
 96             //
 97             if (i + 1 < len && s[i + 1] != '*')
 98                 prevIsChar = true;
 99             else
100                 prevIsChar = false;
101         }
102 
103         PopLowPriority();
104 
105         return ans;
106     }
107 
108     struct Tree
109     {
110         Node* start,*end;
111     };
112 
113     // 使用Thompson算法,将中序的串构造成NFA
114     Tree CalcInOrder(queue<char> inorder)
115     {
116         stack<Tree> temp;
117 
118         while (!inorder.empty())
119         {
120             char c = inorder.front();
121             inorder.pop();
122 
123             if (c == '*')
124             {
125                 auto &tree = temp.top();
126 
127                 Node* oldStart = tree.start;
128                 Node* oldEnd = tree.end;
129 
130                 Node* newStart = new Node;
131                 Node* newEnd = new Node;
132                 newStart->AddPath(0, oldStart);
133                 newStart->AddPath(0, newEnd);
134                 oldEnd->AddPath(0, oldStart);
135                 oldEnd->AddPath(0, newEnd);
136 
137                 tree.start = newStart;
138                 tree.end = newEnd;
139                 continue;
140             }
141 
142             if (c == '&')
143             {
144                 auto e2 = temp.top();temp.pop();
145                 auto e1 = temp.top(); temp.pop();
146 
147                 Tree tree;
148                 tree.start = e1.start;
149                 tree.end = e2.end;
150                 e1.end->AddPath(0, e2.start);
151 
152                 temp.push(tree);
153                 continue;
154             }
155 
156             Node* node1 = new Node;
157             Node* node2 = new Node;
158             node1->AddPath(c, node2);
159             temp.push({ node1,node2 });
160         }
161         return temp.top();
162     }
163 
164     // 闭包函数
165     // 返回:一个节点的闭包,即
166     //       一个节点通过ε路径能够达到的所有节点的集合(包括自身)
167     set<Node*> e_closure(Node* start)
168     {
169         set<Node*> ans;
170         queue<Node*> q;
171         q.push(start);
172         while (!q.empty())
173         {
174             auto node = q.front();
175             q.pop();
176 
177             ans.insert(node);
178 
179             for (auto& path : node->pathes)
180                 if (path.c == 0 && ans.find(path.dest) == ans.end())
181                     q.push(path.dest);
182         }
183         return ans;
184     }
185 
186     // DFA使用的节点结构
187     struct FinalNode;
188     struct FinalPath
189     {
190         char c;
191         FinalNode* dest;
192         FinalPath(char c, FinalNode* dest) :c(c), dest(dest) {}
193     };
194 
195     struct FinalNode
196     {
197         int index;
198         bool isEnd;
199         vector<FinalPath> pathes;
200         FinalNode(bool isEnd) :isEnd(isEnd)
201         {
202             static int i = 0;
203             index = i++;
204         }
205         void AddPath(char c, FinalNode *dest)
206         {
207             pathes.emplace_back(c,dest);
208         }
209     };
210 
211     using FinalTree = FinalNode*;
212 
213     // 子集构造算法 将NFA转换为DFA
214     FinalTree BuildSubSet(const Tree tree)
215     {
216         // 返回一个节点是否为end节点
217         auto IsEnd = [&](Node* node)->bool
218         {
219             return node == tree.end;
220         };
221 
222         // 返回一个集合中是否有end节点
223         auto SetIsEnd = [&](const set<Node*>& nodeSet)->bool
224         {
225             return nodeSet.find(tree.end) != nodeSet.end();
226         };
227 
228         set<Node*> q0;
229         q0 = e_closure(tree.start);
230 
231         FinalNode* n0 = new FinalNode(SetIsEnd(q0));
232         map<set<Node*>, FinalNode*> newTreeDict;
233         newTreeDict[q0] = n0;
234 
235         FinalTree newTree;
236         newTree = n0;
237 
238         set<set<Node*>> Q;
239         Q.insert(q0);
240 
241         queue<set<Node*>> worklist;
242         worklist.push(q0);
243         while (!worklist.empty())
244         {
245             auto q = worklist.front();
246             worklist.pop();
247 
248             // 
249             map<char, set<Node*>> dict;
250             for (auto node : q)
251             {
252                 for (auto& path : node->pathes)
253                 {
254                     if (path.c != 0)
255                     {
256                         dict[path.c].insert(path.dest);
257                     }
258                 }
259             }
260 
261             for (auto& pr : dict)
262             {
263                 for (auto node : pr.second)
264                 {
265                     auto t = e_closure(node);
266                     FinalNode* node_t = nullptr;
267                     if (newTreeDict.find(t) != newTreeDict.end())
268                         node_t = newTreeDict[t];
269                     else
270                     {
271                         node_t=new FinalNode(SetIsEnd(t));
272                         newTreeDict[t] = node_t;
273                     }
274 
275                     //
276                     FinalNode* node_q = newTreeDict[q];
277                     node_q->AddPath(pr.first, node_t);
278 
279                     if (Q.find(t) == Q.end())
280                     {
281                         Q.insert(t);
282                         worklist.push(t);
283                     }
284                 }
285             }
286         }
287         return newTree;
288     }
289 
290     // 遍历DFA,确定字符串是否匹配
291     bool IsMatch(string s, FinalTree dfa)
292     {
293         struct State
294         {
295             int spos; // 当前的字符串序号
296             FinalNode* node; // 当前所在的节点
297         };
298 
299         int len = s.length();
300         State state0 = { -1,dfa };
301         queue<State> q;
302         q.push(state0);
303 
304         while (!q.empty())
305         {
306             auto state = q.front();
307             q.pop();
308 
309             // 若字符串已读取到头,且当前节点为末尾,则返回true
310             if (state.spos == len-1)
311             {
312                 if (state.node->isEnd)
313                     return true;
314 
315                 // 只是字符串读到头,但节点不是尾节点
316                 continue;
317             }
318 
319             for (auto& path : state.node->pathes)
320             {
321                 // 若下一个字符 和路径匹配,或者该路径匹配一个'.'
322                 if (path.c == s[state.spos + 1] || path.c=='.')
323                 {
324                     // 加入队列
325                     q.push({ state.spos + 1,path.dest });
326                 }
327             }
328         }
329         return false;
330     }
331 
332     bool isMatch(string s, string p)
333     {
334         if (p.empty()) return s.empty();
335         auto q = ToInOrder(p);
336         auto tree = CalcInOrder(q);
337         auto DFA = BuildSubSet(tree);
338 
339         return IsMatch(s, DFA);
340     }
341 };

 

posted @ 2022-04-19 09:01  yinhuachen  阅读(97)  评论(0编辑  收藏  举报