编译原理实验 NFA子集法构造DFA,DFA的识别 c++11实现
实验内容
将非确定性有限状态自动机通过子集法构造确定性有限状态自动机。
实验步骤
1,读入NFA状态。注意最后需要设置终止状态。
2,初始态取空,构造DFA的l0状态,将l0加入未标记状态队列que
3,当que不为空,取出一个状态依次做转移和取空操作,并构造出当前转移状态tmp。
4,如tmp是一个新状态,加入到队列中。
5,将构造出的DFA用作模式识别。
具体实现
1,文件读入NFA状态转换图,采用vector存储。
2,判断状态tmp是否是一个新的状态使用自定义hash方法。
3,取空操作由于可以转移多步空字符,采用BFS实现。
4,源代码编译环境MinGW GCC 8.2.0,c++11,低于c++11的版本不兼容。
#include <bits/stdc++.h> using namespace std; using P = pair<int, char>; using ll = long long; const int maxn = 1e3 + 10; const int prime = 31; const ll mod = 1e9 + 7; int getHash(const set<int> &s) { ll res = 0; for (auto x : s) res = res * prime + x; return res % mod; } struct FA { int debug = 0; char ep = '*'; set<char> chs; int cnt = 0; //最大状态数 vector<P> move[maxn]; set<int> end_state; void setDebug(int de) { debug = de; } void addState(int s, int t, char ch) { move[s].emplace_back(t, ch); if (ch != ep) chs.emplace(ch); cnt = max(cnt, max(s, t)); } void addEndState(int s) { end_state.emplace(s); } void init(string file) { ifstream in(file); int m; in >> m; //边数 for (int i = 0; i < m; i++) { int s, t; char ch; in >> s >> t >> ch; addState(s, t, ch); } in >> m; //终止状态数目 for (int i = 0; i < m; i++) { int st; in >> st; end_state.emplace(st); } if (debug) cout << "done.\n"; } set<int> bfs(set<int> s, char ch) { set<int> res; res.clear(); queue<int> q; while (!q.empty()) q.pop(); for (auto it : s) q.emplace(it); while (!q.empty()) { int now = q.front(); q.pop(); if (res.count(now)) continue; res.emplace(now); int sz = move[now].size(); for (int i = 0; i < sz; i++) { P tmp = move[now][i]; if (tmp.second == ch && !res.count(tmp.first)) q.emplace(tmp.first); } } return res; } FA getDFA() { FA res; set<int> st; map<int, set<int>> mp; unordered_map<int, int> mp2; mp2.clear(); mp.clear(); st.clear(); st.emplace(0); set<int> cur = bfs(st, ep); //初态计算,同时之后的st也代表了计算出来的状态 mp[0] = cur; //初态hash值为0不用计算 queue<int> q; st.clear(); q.emplace(0); mp2[0] = 0; int num = 1; //状态数目 while (!q.empty()) { int cur = q.front(); q.pop(); if (st.count(cur)) continue; st.emplace(cur); set<int> now = mp[mp2[cur]]; for (auto ch : chs) { set<int> to; to.clear(); for (auto it : now) //转移 { int sz = move[it].size(); for (int j = 0; j < sz; j++) { P tmp = move[it][j]; if (tmp.second == ch) to.emplace(tmp.first); } } to = bfs(to, ep); //取空 int ha = getHash(to); if (!st.count(ha)) { q.emplace(ha); mp2[ha] = num; mp[num++] = to; } if (debug) { cout << mp2[cur] << "->" << mp2[ha] << " " << ch << "\n"; for (auto x : mp[mp2[cur]]) cout << x << " "; cout << "\n"; for (auto x : mp[mp2[ha]]) cout << x << " "; cout << "\n"; } res.addState(mp2[cur], mp2[ha], ch); } } for (int x = 0; x < num; x++) { set<int> tmp = mp[x]; int f = 0; for (auto y : end_state) if (tmp.count(y)) { f = 1; break; } if (f) res.addEndState(x); } return res; } int isok(string to) { int len = to.size(); int st = 0; for (int i = 0; i < len; i++) { char ch = to[i]; int sz = move[st].size(); int f = 0; for (int j = 0; j < sz && !f; j++) { P tmp = move[st][j]; if (tmp.second == ch) { f = 1; st = tmp.first; } } if (!f) break; } return end_state.count(st); } void diplayEnd() { for (auto x : end_state) cout << x << " "; cout << "\n"; } } NFA, DFA; int main() { NFA.init("prj2_5.txt"); DFA = NFA.getDFA(); cout << "Please enter matching sequence:\n"; string to; while (cin >> to && to != "#") { cout << (DFA.isok(to) ? "OK" : "NO") << "\n"; } return 0; }