正规表达式转NFA(C++)

Posted shiyicode

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了正规表达式转NFA(C++)相关的知识,希望对你有一定的参考价值。

分析

表达式里包含五种符号:左括号,右括号,连接符,选择符,闭包符。
连接符因为优先级最高,可以将其过滤掉,不予考虑。

闭包符*

首先来说闭包,无非两种情况:
X* 或者 (…..)*
两者都可以看做是从一个起始状态,经过诸多输入到达一个终止状态。如 1– X –>2 和 1– (……) –> 2。
那么闭包符可以看做是以下操作:
1 – ε –> 2
2 – ε –> 1
即将首尾以空连接即可。

选择符|

把整体式也看做在一个大括号内的话,我们可以认为选择符一定是在括号内出现的。那么对一个括号内的表达式,我们给定它一个起始状态和终止状态,那么选择符处理方法如下:
(.A.|.B.|.C.)
st – .A. –> ed
st – .B. –> ed
st – .C. –> ed
即将|分隔的每部分表达式都与首尾状态连接。

括号()

因为括号的嵌套问题,让这个转化变得稍微棘手了些,但利用递归思想,每个括号里面的表达式看做一个子问题,对于括号内的式子给定该式一起始状态和终止状态,那么(…)和X也就没有什么区别了。

思路

首先我们定义三个栈:
s_str,保存输入信息
s_st,保存起始状态序号
s_ed,保存终止状态序号
我们用一个例子来解释栈的进出规则。

ab*(a*|(ab)*)

上式基本涵盖了所有表达式存在的情况,仔细看完它的转化过程,转化为代码是很容易的。
首先,将整个式子的起始状态id设为0和终止状态id为1,将其分别入栈s_st和s_ed
接下来对整个表达式进行扫描
[a] 添加状态id=3,入栈s_st,a入栈s_str.
[b] 添加状态id=4,入栈s_st,b入栈s_str.
[(] 因为上一个符号b已经添加的状态4可以作为括号内部分的起始状态,那么只添加终止状态=5,入栈s_ed,(入栈s_str.
[a] 添加状态id=6,入栈s_st,a入栈s_str.
[*] 获取s_st栈顶的两个状态,4和6, 添加4–ε–>6 和 6–ε–>4
[|] 依次对s_str进行出栈直到栈顶为(,同时对s_st进行出栈,
并将其两两用s_str进行连接. 如a出栈(s_str),6出栈(s_st),添加4–a–>6.
最重要的地方是,将最先出栈的状态id与s_ed进行连接,即添加 6–ε–>5.
[(] 添加终止状态=7,入栈s_ed,(入栈s_str.
[a] 添加状态id=8,入栈s_st,a入栈s_str.
[b] 添加状态id=9,入栈s_st,b入栈s_str.
[)] 依次对s_str进行出栈直到栈顶为(,同时对s_st进行出栈,
并将其两两用s_str进行连接(和|有些类似),然后将该括号部分的终止id出栈=7,并将其入栈s_st,
同时入栈’-‘至s_str,’-‘作为括号里面部分式子的输入符号(其实是起占位符的作用,
这样的话,括号整体部分就与单个输入没有区别,既便于闭包符的处理,又解决了多个括号并列的情况).
[*] 获取s_st栈顶的两个状态,6和7, 添加7–ε–>6 和 6–ε–>7
[)] 与上面)的处理是完全一样的。

代码

#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
using namespace std;

struct Node

    Node(int id, char input, int nextId)
    
        this->id = id;
        this->input = input;
        this->nextId = nextId;
    
    int id;
    char input;
    int nextId;
;

class MyClass

public:
    MyClass() 
        strExpress = "";
        statusId = 0;
    

    MyClass(string str)
    
        strExpress = str;
    

    void printNFA();
    void strToNFA();

private:
    string strExpress;
    int statusId;
    vector<Node*> map;
;

void MyClass::strToNFA()

    stack<int> s_st;
    stack<int> s_ed;
    stack<char> s_str;

    s_str.push('$');
    s_st.push(statusId++);
    s_ed.push(statusId++);

    for(auto i=strExpress.begin(); i!=strExpress.end(); ++i)
    
        char ch = *i;
        cout<<ch;
        switch (ch)
        
            case '(':
            
                s_ed.push(statusId++);
                s_str.push('(');
                break;
            
            case ')':
            
                int ed = s_ed.top();
                int st = s_st.top();
                map.push_back(new Node(st, '-', ed));
                ch = s_str.top();
                while(ch != '(')
                
                    int nxt = s_st.top();
                    s_st.pop();
                    int pre = s_st.top();
                    if(ch != '#')
                        map.push_back(new Node(pre, ch, nxt));
                    s_str.pop();
                    ch = s_str.top();
                
                s_str.pop();
                s_str.push('#');
                int top_num = s_ed.top();
                s_st.push(top_num);
                s_ed.pop();
                break;
            
            case '|':
            
                int ed = s_ed.top();
                int st = s_st.top();
                map.push_back(new Node(st, '-', ed));
                ch = s_str.top();
                while(ch != '(' && ch != '$')
                
                    int nxt = s_st.top();
                    s_st.pop();
                    int pre = s_st.top();
                    if(ch != '#')
                        map.push_back(new Node(pre, ch, nxt));
                    s_str.pop();
                    ch = s_str.top();
                
                break;
            
            case '*':
            
                int nxt = s_st.top();
                s_st.pop();
                int pre = s_st.top();
                map.push_back(new Node(pre, '-', nxt));
                map.push_back(new Node(nxt, '-', pre));
                s_st.push(nxt);
                break;
            
            default:
            
                s_str.push(ch);
                s_st.push(statusId++);
                break;
            
        
    
    char ch = s_str.top();
    while(ch != '$')
    
        int nxt = s_st.top();
        s_st.pop();
        int pre = s_st.top();
        if(ch != '#')
            map.push_back(new Node(pre, ch, nxt));
        s_str.pop();
        ch = s_str.top();
    


void MyClass::printNFA()

    cout<<"NFA:"<<endl;
    for(auto node : map)
    
        cout<<node->id<<"--["<<node->input<<"]-->"<<node->nextId<<endl;
    


int main()

    string str;
    stack<int> s;
//    cin>>str;
    str = "ab*(a*|(ab)*|b)*b";

    MyClass myclass(str);
    myclass.strToNFA();
    myclass.printNFA();

    return 0;

output

ab*(a*|(ab)*|b)*bNFA:
2--[-]-->3
3--[-]-->2
3--[-]-->5
5--[-]-->3
5--[-]-->4
3--[a]-->5
8--[-]-->6
7--[b]-->8
3--[a]-->7
3--[-]-->6
6--[-]-->3
6--[-]-->4
9--[-]-->4
3--[b]-->9
3--[-]-->4
4--[-]-->3
4--[b]-->10
2--[b]-->3
0--[a]-->2

以上是关于正规表达式转NFA(C++)的主要内容,如果未能解决你的问题,请参考以下文章

请给正规式a(ab)*abb转变成DFA。(提示:先转变成NFA,再用子集法确定化并化简)?

正规式与正规集,DFA与NFA

形式系统的Regular Expression NFA

正则表达式与回溯

DFA确定化和最小化

软件设计师