实验内容

  将非确定性有限状态自动机通过子集法构造确定性有限状态自动机。

实验步骤

  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 + ;
const int prime = ;
const ll mod = 1e9 + ;
int getHash(const set<int> &s)
{
ll res = ;
for (auto x : s)
res = res * prime + x;
return res % mod;
}
struct FA
{
int debug = ;
char ep = '*';
set<char> chs;
int cnt = ; //最大状态数
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 = ; i < m; i++)
{
int s, t;
char ch;
in >> s >> t >> ch;
addState(s, t, ch);
}
in >> m; //终止状态数目
for (int i = ; 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 = ; 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();
set<int> cur = bfs(st, ep); //初态计算,同时之后的st也代表了计算出来的状态
mp[] = cur; //初态hash值为0不用计算
queue<int> q;
st.clear();
q.emplace();
mp2[] = ;
int num = ; //状态数目
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 = ; 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 = ; x < num; x++)
{
set<int> tmp = mp[x];
int f = ;
for (auto y : end_state)
if (tmp.count(y))
{
f = ;
break;
}
if (f)
res.addEndState(x);
}
return res;
}
int isok(string to)
{
int len = to.size();
int st = ;
for (int i = ; i < len; i++)
{
char ch = to[i];
int sz = move[st].size();
int f = ;
for (int j = ; j < sz && !f; j++)
{
P tmp = move[st][j];
if (tmp.second == ch)
{
f = ;
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 ;
}

编译原理实验 NFA子集法构造DFA,DFA的识别 c++11实现的更多相关文章

  1. 《编译原理》LR 分析法与构造 LR(1) 分析表的步骤 - 例题解析

    <编译原理>LR 分析法与构造 LR(1) 分析表的步骤 - 例题解析 笔记 直接做题是有一些特定步骤,有技巧.但也必须先了解一些基本概念,本篇会通过例题形式解释概念,会容易理解和记忆,以 ...

  2. 编译原理实验之SLR1文法分析

    ---内容开始--- 这是一份编译原理实验报告,分析表是手动造的,可以作为借鉴. 基于  SLR(1) 分析法的语法制导翻译及中间代码生成程序设计原理与实现1 .理论传授语法制导的基本概念,目标代码结 ...

  3. [编译原理代码][NFA转DFA并最小化DFA并使用DFA进行词法分析]

    #include <iostream> #include <vector> #include <cstring> #include "stack" ...

  4. 编译原理-递归下降分析法 c程序部分的分析

    实验三 语法分析程序实验 专业 商软2班   姓名 黄仲浩  学号 201506110166 一. 实验目的      编制一个部分文法分析程序. 二. 实验内容和要求 输入:源程序字符串 输出:正确 ...

  5. 编译原理-确定有穷自动机(deterministic finite automata ,DFA)

    是一个五元组 M=(S,∑,f,S0,F) 其中 S:有穷状态集 ∑:输入字母表(有穷) f:状态转换函数.f(S,a)=S' 是单值部分映射,每个状态面临一个输入符号时,转入的后继状态是确定的. S ...

  6. 编译原理--05 用C++手撕PL/0

    前言 目录 01 文法和语言.词法分析复习 02 自顶向下.自底向上的LR分析复习 03 语法制导翻译和中间代码生成复习 04 符号表.运行时存储组织和代码优化复习 05 用C++手撕PL/0 在之前 ...

  7. 编译原理-NFA构造DFA

    本题摘自北邮的编译原理与技术. 首先,根据此图构造状态转换表 表中第一列第一行表示从第一个符号B通过任意个空转换能到达的节点,Ia表示由此行的状态数组({B,5,1}可以看作0状态)经过一个a可以到达 ...

  8. 《编译原理》构造与正规式 (0|1)*01 等价的 DFA - 例题解析

    <编译原理>构造与正规式 (0|1)*01 等价的 DFA - 例题解析 解题步骤: NFA 状态转换图 子集法 DFA 的状态转换矩阵 DFA 的状态转图 解: 已给正规式:(0|1)* ...

  9. 编译原理之非确定的自动机NFA确定化为DFA

    1.设有 NFA M=( {0,1,2,3}, {a,b},f,0,{3} ),其中 f(0,a)={0,1}  f(0,b)={0}  f(1,b)={2}  f(2,b)={3} 画出状态转换矩阵 ...

随机推荐

  1. 树上数据结构——LCT

    目录 树上数据结构--LCT 概述 基本概念 核心操作 其他操作 完整模板 树上数据结构--LCT 概述 LCT是一种强力的树上数据结构,支持以下操作: 链上求和 链上求最值 链上修改 子树修改 子树 ...

  2. phaser学习总结之Text对象详解

    前言 在phaser学习总结之phaser入门教程中,我们已经入门了phaser,对phaser也有所了解但是我们并没有对phaser中的每个对象的属性和方法进行详解,本章将对phaser中的Text ...

  3. B/S 工业互联网 地铁行业

    前言 近几年,互联网与交通运输的融合,改变了交易模式,影响着运输组织和经营方式,改变了运输主体的市场结构.模糊了运营与非营运的界限,也更好的实现了交通资源的集约共享,同时使得更多依靠外力和企业推动交通 ...

  4. asp.net core过滤器记录响应对象

    百度到的基本上就是读取response.body的流.然后记录完了之后,把流的index重新复位,这样也太麻烦了. 其实asp.net core团队肯定已经考虑到了这种需求,比如记录请求响应日志.给响 ...

  5. GAN算法笔记

    本篇文章为Goodfellow提出的GAN算法的开山之作"Generative Adversarial Nets"的学习笔记,若有错误,欢迎留言或私信指正. 1. Introduc ...

  6. Git学习记录-基本命令篇

    目录 网页在线练习地址 https://learngitbranching.js.org/ 1.git commit Git 仓库中的提交记录保存的是你的目录下所有文件的快照,就像是把整个目录复制,然 ...

  7. 爬虫 xpath

    xpath简介 1.xpath使用路径表达式在xml和html中进行导航 2.xpath包含标准函数库 3.xpath是一个w3c的标准 xpath节点关系 1.父节点 2.字节点 3.同胞节点 4. ...

  8. MySQL从库生成大量小的relay log案例模拟

    最近看到"八怪"写的<MySQL:产生大量小relay log的故障一例>,因之前也遇到类似的情况,一直没搞懂原理及复现,看完此文章后,本着实践是检验真理的唯一标准的原 ...

  9. Bran的内核开发教程(bkerndev)-06 全局描述符表(GDT)

    全局描述符表(GDT)   在386平台各种保护措施中最重要的就是全局描述符表(GDT).GDT为内存的某些部分定义了基本的访问权限.我们可以使用GDT中的一个索引来生成段冲突异常, 让内核终止执行异 ...

  10. Msfvenom命令总结大全

    1.    –p (- -payload-options) 添加载荷payload. 载荷这个东西比较多,这个软件就是根据对应的载荷payload生成对应平台下的后门,所以只有选对payload,再填 ...