实验内容

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

实验步骤

  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. 概率与期望详解!一次精通oi中的概率期望

    目录 基础概念 最大值不超过Y的期望 概率为P时期望成功次数 基础问题 拿球 随机游走 经典问题 期望线性性练习题 例题选讲 noip2016换教室 区间交 0-1边树求直径期望 球染色 区间翻转 二 ...

  2. Java基础学习笔记(二) - 面向对象基础

    面向对象 一.面向对象概述 面向对象思想就是在计算机程序设计过程中,参照现实事物,将事物的属性特征.行为特征抽象出来,描述成计算机时间的设计思想.面向对象思想区别于面向过程思想,强调的是通过调用对象的 ...

  3. 把功能强大的Spring EL表达式应用在.net平台

    Spring EL 表达式是什么? Spring3中引入了Spring表达式语言—SpringEL,SpEL是一种强大,简洁的装配Bean的方式,他可以通过运行期间执行的表达式将值装配到我们的属性或构 ...

  4. js匿名函数自执行的好处

    我们知道,在创建一个函数时如果要用到变量来存取信息的话,要尽量使用局部变量. 因为一方面局部变量会随着函数的执行结束被销毁:另一方面在不执行函数的时候也不会创建这个局部变量,对节省空间资源有很大的好处 ...

  5. (转) websocket 和 socket 剖析

    Socket 与 WebSocket 本站文章除注明转载外,均为本站原创或者翻译. 本站文章欢迎各种形式的转载,但请18岁以上的转载者注明文章出处,尊重我的劳动,也尊重你的智商: 本站部分原创和翻译文 ...

  6. Java 从入门到进阶之路(八)

    在之前的文章我们介绍了一下 Java 中的重载,接下来我们看一下 Java 中的构造方法. 我们之前说过,我们在定义一个变量的时候,java 会为我们提供一个默认的值,字符串为 null,数字为 0. ...

  7. Golang 实现设计模式 —— 装饰模式

    概念 "用于代替继承的技术,无需通过继承增加子类就能扩展对象的新功能" "动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活" 何时 ...

  8. 【TencentOS tiny】深度源码分析(8)——软件定时器

    软件定时器的基本概念 TencentOS tiny 的软件定时器是由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受硬件定时器资源限制的定时器服务,本质上软件定时器的使用相当 ...

  9. go 学习笔记之解读什么是defer延迟函数

    Go 语言中有个 defer 关键字,常用于实现延迟函数来保证关键代码的最终执行,常言道: "未雨绸缪方可有备无患". 延迟函数就是这么一种机制,无论程序是正常返回还是异常报错,只 ...

  10. python接口自动化2-第一次发送get请求

    前言 Requests: 让 HTTP 服务人类,唯一的一个非转基因的 Python HTTP 库,人类可以安全享用: Requests继承了urllib2的所有特性,能满足当前网络的需求,支持Pyt ...