NFA

任意正则表达式都存在一个与之对应的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;
        }
    }
}
时间: 2025-01-16 01:15:38

NFA的相关文章

NFA的实现

此次发表的是一个不确定的自动机(NFA),它可以根据输入的正规式输出由函数映像表示的结果. 此版本可以输入括号'(',')',但是,实现的过程理解起来有点吃力,所以,在时间允许的情况下,我还将写新文章,使用单纯递归方法实现该程序. #include"stdio.h" #include"stdlib.h" #define MAX 200 struct Stack { int Stack[MAX]; int top; }St; struct Queue { int fr

利用子集构造法实现NFA到DFA的转换

概述 NFA非有穷自动机,即当前状态识别某个转换条件后到达的后继状态不唯一,这种自动机不便机械实现,而DFA是确定有限状态的自动机,它的状态转换的条件是确定的,且状态数目往往少于NFA,所以DFA能够比较方便的机械实现且识别能力方面也和NFA相当.本次实验采用子集构造法来实现不带空弧的由NFA到DFA的转换. 子集构造法的算法如下: 设NFA为M=(K,Σ,f,S0,Z),则构造相应的DFA  M′=(Q,Σ,f′,I0,F)①取I0=S0:②对于状态集Q中任一尚未标记的状态qi={Si1,Si

基于ε-NFA的正则表达式引擎

正则表达式几乎每个程序员都会用到,对于这么常见的一个语言,有没有想过怎么去实现一个呢?乍想一下,也许觉得困难,实际上实现一个正则表达式的引擎并没有想像中的复杂,<编译原理>一书中有一章专门讲解了怎么基于状态机来构建基本的正则表达式引擎,讲这个初衷是为词法分析服务,不过书里的东西相对偏理论了些,实现起来还是要费些功夫的,只是它到底指明了一条路,当然,书里只针对基本的语法进行了分析讲解,对于在实际中有些非常有用的很多扩展语法,它就基本没有涉及了,这些扩展的语法中有些是比较好实现的,有些则很难. 基

NFA的确定化

NFA的确定化:这里指的 NFA 到 DFA的转换(不包括 ε 自动机),构造一个和 NFA 等价的 DFA.书中有介绍两种确定化的方法(子集法和造表法),这里只介绍造表法,造表法是比子集法简单而有效的一种确定化方法. 1,为什么不用子集法?? 在子集法中,如果 NFA 的状态个数 n 比较大,那么,确定化后的 DFA 的状态个数 2^n-1 将更大,其中不少状态是不可达状态. 2,造表法算法的基本思想 把 DFA 中的每一个状态对应 NFA中的一组状态.即由于 NFA 中的 t 是一个多值映射

java实现正则表达式到NFA的转换

我用java实现了一个正则表达式到NFA的转换程序,以下是我的代码 package com.siwanghu.regextoNFA; public class Node {     private int id;     private static int ID=0;          public Node(){      this.id=ID++;     } public int getId() { return id; } public static void reset(){ ID=

编译原理:正规式转变成NFA算法

import java.util.ArrayList; import java.util.Map; import java.util.TreeMap; class Edge{ private int u, v; private char key; public Edge(int u, int v, char key) { super(); this.u = u; this.v = v; this.key = key; } @Override public String toString() {

NFA到DFA的转换

#include<iostream> #include<string> #include<cstring> #include<vector> #include<algorithm> #include<set> #define MAX 100 using namespace std; struct edge { char preNode; //节点表示只能用单个字符 char nextNode; char tchar; //转换字符 }

构造Half(L)的NFA

 构造Half(L)的NFA 搬运自我的百度空间 原创文章,转贴请贴出处 偶尔来点纯理论问题.这是一道高级算法作业题,L是正则语言,语言B是L中所有字符串对半开的前一半,证明B也是正则的. 目的明确,构造B的一台NFA,非确定型有限状态机. 要用到"平行NFA"的概念.比如举例另外一个问题:如何判断一个字符串既是正则语言A又是正则语言B?可以构造这样一台NFA,状态集合为Qa*Qb(Qa与Qb为A与B的状态集).接受集合为Fa*Fb,这样机器的一部分(状态元组的前半部分)匹配语言A

js 正则学习小记之NFA引擎

之前一直认为自己正则还不错,在看 次碳酸钴,Barret Lee 等大神都把正则玩的出神入化后发现我只是个战五渣.  求抱大腿,求大神调教. 之前大致有个印象,正则有很多种引擎,但我根本不知道有哪些引擎. 今天在读<精通正则表达式>才发现有Traditional NFA,POSIX NFA 和 DFA (具体自己百度下吧).可用了这么久的正则,还不知道 js 属于哪一种呢.在<精通正则表达式>里有个简单是方法检测属于哪一种. 用 /nfa|nfa not/ 去匹配 "nf