任意正则表达式都存在一个与之对应的NFA,反之亦然.

正则表达式 ((A*B|AC)D)对应的NFA(有向图), 其中红线对应的为该状态的ε转换, 黑线表示匹配转换

我们定义的NFA具有以下特点:

  • 正则表达式中的每个字符在NFA中都有且只有一个对应状态,NFA的其实状态为0,并包含一个虚拟的接收状态
  • 正则表达式中的字母所对应的状态都有一条从它指出的黑色的边,并且一个状态只能有一条指出的黑色边
  • 正则表达式中的元字符所对应的状态至少含有一条指出的红色的边

ε转换

不需要扫描匹配文本中的任意字符,自动机就可以从一个状态转换到另一状态

使用 NFA模拟匹配过程:

  • 首先获取初始状态通过ε转换可以到达的所有状态集合,上图为0,1,2,3,4,6
  • 顺序扫描匹配文本中的字符,如果状态集合中找到匹配该字符的状态(可以使多个),自动机就可以扫过该字符并由黑色的边转到下一个状态,这种转换成为匹配转换,由下一状态及下一状态的的ε转换生成新的状态集合,继续扫描下一个字符
  • 扫描完所有字符后,如果最终到达的所有状态中包含接受状态,则匹配该字符串

源代码namespace NFA
{
public class IntList : List<int>
{
} public class Digraph
{
public int E { get; set; }
public int V { get; set; }
public IntList[] Adjs { get; set; } public Digraph(int v)
{
this.V = v;
this.E = 0;
Adjs = new IntList[v];
for (int i = 0; i < v; i++)
{
Adjs[i] = new IntList();
}
} public void AddEdge(int from, int to)
{
Adjs[from].Add(to);
E++;
} public IntList Adj(int index)
{
return Adjs[index];
}
} public class DirectedDFS
{
public bool[] Marked;
public DirectedDFS(Digraph g, int s)
{
Marked = new bool[g.V];
Dfs(g, 0);
} public DirectedDFS(Digraph g, List<int> source)
{
Marked = new bool[g.V];
source.ForEach(x =>
{
if (!Marked[x])
{
Dfs(g, x);
}
});
} public void Dfs(Digraph g, int v)
{
Marked[v] = true;
g.Adjs[v].ForEach(x =>
{
if (!Marked[x])
{
Dfs(g, x);
}
});
}
}
} namespace NFA
{
public class NFA
{
private string regex;
//NFA的ε转换有向图
private Digraph G; public NFA(string reg)
{
this.regex = reg;
Stack<int> ops = new Stack<int>();
int M = regex.Length;
G = new Digraph(M+1);
//循环状态
for (int i = 0; i < M; i++)
{
int lp = i;
if (regex[i] == '(' || regex[i] == '|')
{
ops.Push(i);
}
else if (regex[i] == ')')
{
int or = ops.Pop();
if (regex[or] == '|')
{
lp = ops.Pop();
G.AddEdge(lp, or + 1);
G.AddEdge(or, i);
}
else
{
lp = or;
}
}
if(i<M-1 && regex[i+1] == '*')
{
G.AddEdge(lp,i+1);
G.AddEdge(i + 1, lp);
}
if (regex[i] == '(' || regex[i] == '*' || regex[i] == ')')
{
G.AddEdge(i, i + 1);
}
}
} public bool Recognize(string txt)
{
List<int> pc = new List<int>();
DirectedDFS dfs = new DirectedDFS(G, 0); for (int i = 0; i < G.V; i++)
{
if (dfs.Marked[i])
{
pc.Add(i);
}
} for (int i = 0; i < txt.Length; i++)
{
List<int> match = new List<int>();
foreach (int v in pc)
{
if (v < regex.Length)
{
if (regex[v] == txt[i] || regex[v] == '.')
{
match.Add(v + 1);
}
}
}
pc = new List<int>();
dfs = new DirectedDFS(G, match); for (int v = 0; v < G.V; v++)
{
if (dfs.Marked[v])
{
pc.Add(v);
}
}
}
foreach (int v in pc)
{
if (v == regex.Length)
{
return true;
}
}
return false;
}
}
}

NFA的更多相关文章

  1. NFA转DFA - json数字识别

    json的主页上,提供了number类型的符号识别过程,如下: 图片引用:http://www.json.org/json-zh.html 实际上这张图片表示的是一个状态机,只是状态没有标出来.因为这 ...

  2. 求子串-KPM模式匹配-NFA/DFA

    求子串 数据结构中对串的5种最小操作子集:串赋值,串比较,求串长,串连接,求子串,其他操作均可在该子集上实现 数据结构中串的模式匹配 KPM模式匹配算法 基本的模式匹配算法 //求字串subStrin ...

  3. NFA引擎匹配原理

    1       为什么要了解引擎匹配原理 一个个音符杂乱无章的组合在一起,弹奏出的或许就是噪音,同样的音符经过作曲家的手,就可以谱出非常动听的乐曲,一个演奏者同样可以照着乐谱奏出动听的乐曲,但他/她或 ...

  4. 编译系统中的 NFA/DFA算法理解

    1.问题概述 NFA 和 DFA浅析---要深入了解正则表达式,必须首先理解有穷自动机. 有穷自动机(Finite Automate)是用来模拟实物系统的数学模型,它包括如下五个部分: 有穷状态集St ...

  5. C# 词法分析器(四)构造 NFA

    系列导航 (一)词法分析介绍 (二)输入缓冲和代码定位 (三)正则表达式 (四)构造 NFA (五)转换 DFA (六)构造词法分析器 (七)总结 有了上一节中得到的正则表达式,那么就可以用来构造 N ...

  6. nfa转dfa,正式完成

    为了加速转换的处理,我压缩了符号表.具体算法参考任何一本与编译或者自动机相关的书籍. 这里的核心问题是处理传递性闭包,transitive closure,这个我目前采取的是最简单的warshall算 ...

  7. 正则转nfa:完成

    太累了,感觉不会再爱了.问题已经解决,具体的懒得说了. #include "regular_preprocess.h" //这个版本终于要上nfa了,好兴奋啊 //由于连个节点之间 ...

  8. 正则转nfa:bug消除

    正则到nfabug的解决方法 前面提到了这个bug,为了解决这个bug,我们必须在每次引用到一个假名的时候,都构建一个拷贝.现在假设我们遇到了一个假名,并得到了他的开始节点和结束节点,当前的难题就是构 ...

  9. 正则转nfa:bug出现。

    本人写的一个正则到nfa的bug 刚写完前面的那篇,自己用脑子过了一下,发现了一个bug.具体情况如下. 这个bug的产生条件是多次调用假名的时候,每次调用都会修改假名的nfa图.直接这么说不好理解, ...

  10. 最初步的正则表达式引擎:nfa的转换规则。

    [在此处输入文章标题] 正则到nfa 前言 在写代码的过程中,本来还想根据龙书上的说明来实现re到nfa的转换.可是写代码的时候发现,根据课本来会生成很多的无用过渡节点和空转换边,需要许多的代码.为了 ...

随机推荐

  1. matlab学习checkbox使用

    添加radiobutton1和checkbox2控件以及两个edittextbox控件 %得到控件的值var=get(handles.kj2,'value');%将数值显示到文本框中,将得到的数值转换 ...

  2. grpc-web与react的集成

    很久没写总结了,在这里跟大家分享一下自己踩的坑,同时也方便自己多记忆下. 大致流程: 使用create-react-app脚手架生成react相关部分,脚手架内部会通过node自动起一个客户端,然后和 ...

  3. python-flask-1

    https://askubuntu.com/questions/244641/how-to-set-up-and-use-a-virtual-python-environment-in-ubuntu ...

  4. Centos 7.x 源码编译搭建Nginx

    环境: centos 7 防火墙关闭 Selinx关闭 Nginx Web安装 安装依赖库 yum install pcre-devel pcre gcc gcc-c++ zlib zlib-deve ...

  5. node源码详解(三)

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource3 本博客同步在https://cnodejs.o ...

  6. Linux 常用密令总结 ------随用随记吧

    ubuntu or uqilin 目录文件类ls 查看目录cd 转到目录ps -aux查看所有进程grep 查找| 管道符 用户账户类 su 切换用户sudo 以管理员权限运行命令重启机器 reboo ...

  7. 1009在WINDOWS上面如何备份数据库

    第一步 解决windows上面使用GZIP命令 参考http://www.xuebuyuan.com/1676976.html 后经研究,发现解决方法也很简单,只需下载gzip的windows版本,解 ...

  8. 消息队列Rabbit安装

    先安装elang 再安装Rabbit *Rabbit安装路径不能有空格 安装完成

  9. C#--in逆变-out协变

    MSDN上的解释 协变保留兼容性,逆变与之相反 in的使用 个人理解:就是表明泛型就是可以逆变的(逆变就是大变小) // Contravariant interface. interface ICon ...

  10. 完毕port模型

    完毕port模型过程例如以下: 1.调用CreateIoCompletionPort函数创建完毕port. HANDLE CompletionPort=CreateIoCompletionStatus ...