【C++实现】编译原理 免考小队 NFA转换为等价的DFA
背景
期末考試免考,沖!
實驗名稱
對任意給定的NFA M進行確定化操作
實驗時間
2020年5月21日 到 2020年5月24日
院系
信息科學(xué)與工程學(xué)院
組員姓名
Chocolate、kry2025、鐘先生、leo、小光
實驗環(huán)境介紹
- windows 10 操作系統(tǒng)
- Eclipse 進行 java 編程
- CodeBlocks 進行 C++ 編程
實驗?zāi)康呐c要求
目的
- 深刻理解 NFA 確定化操作
- 掌握子集構(gòu)造算法過程
- 加強團隊合作能力
- 提高自身的編程能力和解決問題的能力
要求
NFA 轉(zhuǎn)換為等價的 DFA
正則 到 NFA的轉(zhuǎn)換
有窮自動機
作用:將輸入的序列轉(zhuǎn)換成一個狀態(tài)圖,方便之后的處理。通常被用在詞法分析器中。
1)有窮自動機是一個識別器,對每個可能的的輸入串簡單的回答“是”或“否”。
2)有窮自動機分為兩類:
a)不確定的有窮自動機(NFA)對其邊上的標(biāo)號沒有任何限制。一個符號標(biāo)記離開同一狀態(tài)的多條變,并且空串ε也可以作為標(biāo)號。
b)確定的有窮自動機(DFA)有且只有一條離開該狀態(tài),以該符號位標(biāo)號的邊。
不確定的有窮自動機
正則式(RE)轉(zhuǎn)不確定型有窮自動機(NFA)
找出所有可以被匹配的字符即符號集合∑作為每一列的字段名,然后從起始態(tài)開始
1)狀態(tài)0可以匹配a,匹配后可以到狀態(tài)0或狀態(tài)1,記為?。匹配b只能得到狀態(tài)0,記為{0}。
2)狀態(tài)1可以匹配a,沒有匹配到,記為?。匹配b得到狀態(tài)2,記為{2}。
3)狀態(tài)0可以匹配a,沒有匹配到,記為?。匹配b得到狀態(tài)3,記為{3}。
4)狀態(tài)0可以匹配a,沒有匹配到,記為?。匹配b沒有匹配到,記為?。
至此,狀態(tài)表建立完成。正則式(RE)轉(zhuǎn)不確定型有窮自動機(NFA)完成。
NFA 到 DFA
NFA M 確定化
1)根據(jù)NFA構(gòu)造DFA狀態(tài)轉(zhuǎn)換矩陣:
- ①確定DFA的字母表,初態(tài)(NFA的所有初態(tài)集)
- ②從初態(tài)出發(fā),經(jīng)字母表到達的狀態(tài)集看成一個新狀態(tài)
- ③將新狀態(tài)添加到DFA狀態(tài)集
- ④重復(fù)②③步驟,直到?jīng)]有新的DFA狀態(tài)
2)畫出DFA
3)看NFA和DFA識別的符號串是否一致
NFA N 確定化
涉及操作:
算法:
更多詳細內(nèi)容可參考:NFA到DFA的轉(zhuǎn)換及DFA的簡化
實驗過程
對NFA M 確定化
采用二進制方法來對NFA M 進行確定化,先來說說其中用到的關(guān)鍵知識:
將狀態(tài)集合用二進制表示,例如,如果一個集合(從0開始)包含了 0,1,2,總共有4個狀態(tài)(即 0,1,2,3 ),那么,當(dāng)前集合用 0111 表示。同理,如果包含了0,3 ,總共有4個狀態(tài)(即 0,1,2,3 ),那么,當(dāng)前集合用 1001 表示。
x & (-x) 表示拿到 x 的最右邊第一個
a & x 可以判斷 a 狀態(tài)集合 是否包含 x 狀態(tài)集合
a ^= x 可以用來將 x 狀態(tài)集合 加入到 a 狀態(tài)集合
下面依次來講述代碼實現(xiàn):
int ans[maxn],one[maxn],zero[maxn],lft[maxn],rgt[maxn]; char change[maxn]; bool vis[maxn],ac[maxn];用到的數(shù)據(jù)結(jié)構(gòu):
- ans 數(shù)組表示所求的狀態(tài)集合
- one 數(shù)組表示狀態(tài)經(jīng) 1 所到達的狀態(tài)
- zero 數(shù)組表示狀態(tài)經(jīng) 0 所到達的狀態(tài)
- lft 數(shù)組表示經(jīng)過 0 狀態(tài)到達的狀態(tài)集合
- rgt 數(shù)組表示經(jīng)過 1 狀態(tài)到達的狀態(tài)集合
- change 數(shù)組用來轉(zhuǎn)換輸出結(jié)果,即將狀態(tài)集合用字母 ‘A’-‘Z’ 來表示
- vis 數(shù)組用來去重操作,判斷當(dāng)前狀態(tài)集合是否存在過
- ac 數(shù)組用來判斷集合狀態(tài)中是否包含終態(tài),若包含,置為1
下面函數(shù)是用來找到對應(yīng)狀態(tài)下標(biāo)
//找到對應(yīng)的狀態(tài)下標(biāo) int index(int p){int x = 1;if(p == 1) //p為1表示當(dāng)前為初始狀態(tài)return 0;int i = 0;while(++i){ //循環(huán)找出當(dāng)前對應(yīng)的狀態(tài)下標(biāo)x <<= 1;if(p == x)return i; //找到即返回對應(yīng)下標(biāo)}return 0; }move操作
int moveT(int a, int b){while(b){int x = b&(-b); //去當(dāng)前集合中的最后一個節(jié)點if(!(a&x)) //如果不存在該節(jié)點,加入集合當(dāng)中a ^= x;b ^= x; //已經(jīng)存在該節(jié)點,就進行舍去操作}return a; }核心代碼,將狀態(tài)集合逐個拿出來,進行 move 操作,然后進行去重操作,最后進行更新新的狀態(tài)集合
void dfs(int p){ans[cnt] = p;int lsum = 0, rsum = 0;while(p){int x = p&(-p); //取出當(dāng)前集合中的最后一個節(jié)點int y = index(x); //找到對應(yīng)的狀態(tài)下標(biāo)lsum = moveT(lsum, zero[y]); //進行move操作rsum = moveT(rsum, one[y]); //進行move操作p ^= x; //將當(dāng)前拿出來的節(jié)點從原集合中去掉}lft[cnt] = lsum; //更新當(dāng)前的狀態(tài)集合rgt[cnt] = rsum; //更新當(dāng)前的狀態(tài)集合cnt++; //更新狀態(tài)行數(shù)if(!vis[lsum])vis[lsum] = 1, dfs(lsum); //進行去重操作if(!vis[rsum])vis[rsum] = 1, dfs(rsum); //進行去重操作 }輸入處理
while(cin>>preNode){if(preNode=='$') break;cin>>tchar>>nexNode;if(tchar-'a'==0) zero[preNode-'0']|=(1<<(nexNode-'0'));else one[preNode-'0']|=(1<<(nexNode-'0')); }輸出處理
for(int i=0;i<cnt;i++)change[ans[i]]=i+'A'; //輸出處理,用字母'A'-'Z'來表示集合對NFA N 確定化
用到的數(shù)據(jù)結(jié)構(gòu):
struct edge{char preNode; //前驅(qū)節(jié)點char tchar; //弧char nexNode; //后繼節(jié)點 }e[maxn]; //獲得的狀態(tài)集合 struct newJ{string setJ; }; //集合與集合之間的轉(zhuǎn)換關(guān)系 struct relation{newJ* preJ;char jchar;newJ* nexJ; };- edge 結(jié)構(gòu)體用來表示邊的信息
- newJ 表示狀態(tài)集合J
- relation 結(jié)構(gòu)體表示最后輸出的轉(zhuǎn)換關(guān)系表
求某個狀態(tài)的閉包
//得到閉包 void getEClosure(const edge* e,int cntEdge,newJ* st){for(int i=0;i<st->setJ.length();i++){for(int j=0;j<cntEdge;j++){ //遍歷所有的邊if(st->setJ[i] == e[j].preNode && e[j].tchar=='#')st->setJ+=e[j].nexNode;}} }move操作
//move操作 void moveT(char ttchar,const edge* e,int cntEdge,newJ* source,newJ* dest){//e為所有邊的集合,然后就能從一個轉(zhuǎn)換字符得到全部的,比如2得到bd,而不會第一個2得到b,第二個2得到dfor(int i=0;i<source->setJ.length();i++){for(int j=0;j<cntEdge;j++){ //遍歷所有的邊if(source->setJ[i] == e[j].preNode && e[j].tchar == ttchar)dest->setJ+=e[j].nexNode;}} }去重操作,判斷是否加入 allSet(總狀態(tài)集合) 中
//通過狀態(tài)集合中的setJ來決定是否添加 bool isInsert(vector<newJ*> allSet,newJ* newSet){for(int i=0;i<allSet.size();i++){if(allSet.at(i)->setJ == newSet->setJ)return false;}return true; }去重操作,判斷當(dāng)前狀態(tài)轉(zhuǎn)換是否加入轉(zhuǎn)換關(guān)系表中
//判斷relation結(jié)構(gòu)體去重 bool isInsertForRel(vector<relation*> relVec,newJ* preJ,char jchar,newJ* nexJ){for(int i=0;i<relVec.size();i++){if(relVec.at(i)->preJ->setJ == preJ->setJ && relVec.at(i)->jchar == jchar && relVec.at(i)->nexJ->setJ == nexJ->setJ)return false;}return true; }重命名轉(zhuǎn)換函數(shù)
//重命名轉(zhuǎn)換函數(shù) void changeName(vector<newJ*> allSet,newJ* newSet,string& newStr){newJ* tmpJ = new newJ();for(int i=0;i<allSet.size();i++){if(allSet.at(i)->setJ == newSet->setJ)tmpJ->setJ = 'A'+i;}newStr = tmpJ->setJ; }后續(xù)省略…(詳情請見后文源代碼說明)
實驗結(jié)果(附源代碼)
對 NFA M 確定化
#include<bits/stdc++.h> #define endl '\n' using namespace std; const int maxn=999999; int ans[maxn],one[maxn],zero[maxn],lft[maxn],rgt[maxn]; char change[maxn]; bool vis[maxn],ac[maxn]; int cnt,n,q,f; //找到對應(yīng)的狀態(tài)下標(biāo) int index(int p){int x = 1;if(p == 1) //p為1表示當(dāng)前為初始狀態(tài)return 0;int i = 0;while(++i){ //循環(huán)找出當(dāng)前對應(yīng)的狀態(tài)下標(biāo)x <<= 1;if(p == x)return i; //找到即返回對應(yīng)下標(biāo)}return 0; } int moveT(int a, int b){while(b){int x = b&(-b); //去當(dāng)前集合中的最后一個節(jié)點if(!(a&x)) //如果不存在該節(jié)點,加入集合當(dāng)中a ^= x;b ^= x; //已經(jīng)存在該節(jié)點,就進行舍去操作}return a; } void dfs(int p){ans[cnt] = p;int lsum = 0, rsum = 0;while(p){int x = p&(-p); //取出當(dāng)前集合中的最后一個節(jié)點int y = index(x); //找到對應(yīng)的狀態(tài)下標(biāo)lsum = moveT(lsum, zero[y]); //進行move操作rsum = moveT(rsum, one[y]); //進行move操作p ^= x; //將當(dāng)前拿出來的節(jié)點從原集合中去掉}lft[cnt] = lsum; //更新當(dāng)前的狀態(tài)集合rgt[cnt] = rsum; //更新當(dāng)前的狀態(tài)集合cnt++; //更新狀態(tài)行數(shù)if(!vis[lsum])vis[lsum] = 1, dfs(lsum); //進行重復(fù)操作if(!vis[rsum])vis[rsum] = 1, dfs(rsum); //進行重復(fù)操作 } int main(){int t;cout<<"多組輸入,請先輸入對應(yīng)的組數(shù):"<<endl;cin>>t; //多組輸入while(t--){cout << "輸入各邊的信息,并且以 '前點(char '0'-'1000') 轉(zhuǎn)換字符(a 或 b) 后點(int '0'-'1000')'格式,結(jié)束以'$'開頭" << endl;char preNode,tchar,nexNode;while(cin>>preNode){if(preNode=='$') break;cin>>tchar>>nexNode;if(tchar-'a'==0) zero[preNode-'0']|=(1<<(nexNode-'0'));else one[preNode-'0']|=(1<<(nexNode-'0'));}q=1;cout<<"輸入終止?fàn)顟B(tài)集合,結(jié)束以'$'開頭"<<endl;char endNode;while(cin>>endNode){if(endNode=='$') break;f|=(1<<(endNode-'0'));}cnt=0;memset(vis,0,sizeof(vis)); //初始化memset(ac,0,sizeof(ac)); //初始化vis[q]=1;dfs(q); //轉(zhuǎn)換開始int sum=0;for(int i=0;i<cnt;i++)if(ans[i]&f) //判斷所求集合中是否包含終態(tài)ac[i]=1,sum++; //標(biāo)記終態(tài)集合并統(tǒng)計個數(shù)for(int i=0;i<cnt;i++)change[ans[i]]=i+'A'; //輸出處理,用字母'A'-'Z'來表示集合cout<<"轉(zhuǎn)換結(jié)果:"<<endl;cout<<"DFA的狀態(tài)數(shù):"<<cnt<<" "<<"終止?fàn)顟B(tài)數(shù):"<<sum<<endl<<endl;cout<<"終態(tài):"<<endl; //輸出終態(tài)集合for(int i=0,j=0;i<cnt;i++){if(ac[i]){if(j)cout<<" ";cout<<(char)(i+'A');j++;}}cout<<endl<<endl; //輸出DFA狀態(tài)轉(zhuǎn)換矩陣cout<<"由NFA得到的DFA狀態(tài)轉(zhuǎn)換矩陣:"<<endl;cout<<"----------------------------"<<endl;cout<<" "<<"a"<<" "<<"b"<<endl;cout<<"----------------------------"<<endl;for(int i=0;i<cnt;i++) //輸出打印新的轉(zhuǎn)換結(jié)果cout<<(char)('A'+i)<<" "<<change[lft[i]]<<" "<<change[rgt[i]]<<endl;cout<<"----------------------------"<<endl;cout<<endl;}return 0; }輸出結(jié)果
輸出結(jié)果文字版
多組輸入,請先輸入對應(yīng)的組數(shù): 100 輸入各邊的信息,并且以 '前點(char '0'-'1000') 轉(zhuǎn)換字符(a 或 b) 后點(int '0'-'1000')'格式,結(jié)束以'$'開頭 0 b 2 4 a 0 0 a 1 1 a 1 2 b 3 3 b 2 3 a 3 5 a 5 4 b 5 5 b 4 1 b 4 2 a 1 $ 輸入終止?fàn)顟B(tài)集合,結(jié)束以'$'開頭 0 $ 轉(zhuǎn)換結(jié)果: DFA的狀態(tài)數(shù):6 終止?fàn)顟B(tài)數(shù):1終態(tài): A由NFA得到的DFA狀態(tài)轉(zhuǎn)換矩陣: ----------------------------a b ---------------------------- A B E B B C C A D D D C E B F F F E ----------------------------對NFA N 確定化
#include<bits/stdc++.h> using namespace std; const int maxn=1000; struct edge{char preNode; //前驅(qū)節(jié)點char tchar; //弧char nexNode; //后繼節(jié)點 }e[maxn]; //獲得的狀態(tài)集合 struct newJ{string setJ; }; //集合與集合之間的轉(zhuǎn)換關(guān)系 struct relation{newJ* preJ;char jchar;newJ* nexJ; }; //得到閉包 void getEClosure(const edge* e,int cntEdge,newJ* st){for(int i=0;i<st->setJ.length();i++){for(int j=0;j<cntEdge;j++){ //遍歷所有的邊if(st->setJ[i] == e[j].preNode && e[j].tchar=='#')st->setJ+=e[j].nexNode;}} } //move操作 void moveT(char ttchar,const edge* e,int cntEdge,newJ* source,newJ* dest){//e為所有邊的集合,然后就能從一個轉(zhuǎn)換字符得到全部的,比如2得到bd,而不會第一個2得到b,第二個2得到dfor(int i=0;i<source->setJ.length();i++){for(int j=0;j<cntEdge;j++){ //遍歷所有的邊if(source->setJ[i] == e[j].preNode && e[j].tchar == ttchar)dest->setJ+=e[j].nexNode;}} } //通過狀態(tài)集合中的setJ來決定是否添加 bool isInsert(vector<newJ*> allSet,newJ* newSet){for(int i=0;i<allSet.size();i++){if(allSet.at(i)->setJ == newSet->setJ)return false;}return true; } //判斷relation結(jié)構(gòu)體去重 bool isInsertForRel(vector<relation*> relVec,newJ* preJ,char jchar,newJ* nexJ){for(int i=0;i<relVec.size();i++){if(relVec.at(i)->preJ->setJ == preJ->setJ && relVec.at(i)->jchar == jchar && relVec.at(i)->nexJ->setJ == nexJ->setJ)return false;}return true; } //重命名轉(zhuǎn)換函數(shù) void changeName(vector<newJ*> allSet,newJ* newSet,string& newStr){newJ* tmpJ = new newJ();for(int i=0;i<allSet.size();i++){if(allSet.at(i)->setJ == newSet->setJ)tmpJ->setJ = 'A'+i;}newStr = tmpJ->setJ; } int main(){int cntEdge=0; //統(tǒng)計邊的數(shù)量char staNode; //初始狀態(tài)點string endNode,node; //終止?fàn)顟B(tài)節(jié)點和總的節(jié)點集合int cntRealEdge[maxn]={0}; //用于判斷是否包含空字符的邊 為1代表含空字符的邊 初始話為0,不包含cout << "輸入各邊的信息,并且以 '前點(char '0'-'1000') 轉(zhuǎn)換字符(char 'a'-'z') 后點(char '0'-'1000')'格式,結(jié)束以'$'開頭" << endl;cout << "如果轉(zhuǎn)換字符為空,則用'#'表示" << endl;char ttchar[2];for(int i=0;i<maxn;i++){cin>>e[i].preNode; //輸入前點if(e[i].preNode == '$') break; //遇到'$' 結(jié)束輸入cin>>ttchar;cin>>e[i].nexNode; //輸入轉(zhuǎn)換字符及后點e[i].tchar=ttchar[0];if(ttchar[0] == '#') cntRealEdge[cntEdge]=1; //標(biāo)記含有空字符的邊++cntEdge; //統(tǒng)計邊的數(shù)量}//將輸入的邊節(jié)點進行整合for(int i=0;i<cntEdge;i++){char preTmp = e[i].preNode;char nexTmp = e[i].nexNode;if(node.find(preTmp)>node.length()) node+=preTmp;if(node.find(nexTmp)>node.length()) node+=nexTmp;}cout<<"輸入初始點字符:"<<endl;cin>>staNode;while(node.find(staNode)==string::npos){cout<<"初始狀態(tài)輸入錯誤,請重新輸入:"<<endl;cin>>staNode; //錯誤即重新輸入一次}cout<<"輸入終止點字符(若有多個終結(jié)狀態(tài),直接寫成字符串形式)"<<endl;cin>>endNode;bool inputStatus=true; //用于結(jié)束終止點字符的輸入while(inputStatus){for(int i=0;i<endNode.length();i++){if(node.find(endNode[i])==string::npos){cout << "終結(jié)狀態(tài)輸入錯誤,請重新輸入:" << endl;cin >> endNode;}}inputStatus=false;}newJ* newSet = new newJ();newSet->setJ = staNode; //設(shè)置初始點為狀態(tài)集合IgetEClosure(e, cntEdge, newSet); //得到閉包vector<newJ*>allSet(1, newSet); //設(shè)置所有狀態(tài)集合的向量/*用來存儲每一次的閉包操作前的第一列的狀態(tài)集合*比如第一次strVec存儲的是初始狀態(tài),求閉包時多了2個狀態(tài)集合。在第二次時存儲的是新的2個狀態(tài),原先的初始狀態(tài)被去除。*總的狀態(tài)集合存儲在allSet中*/vector<newJ*>strVec(1, newSet);int sizeOfStrVec = 1; //初始大小就是初始點所構(gòu)成的狀態(tài)集合vector<relation*>relVec; //轉(zhuǎn)換關(guān)系表的向量while(sizeOfStrVec){ //如果不符合則說明新增的集合都是原有的集合int oldAllSize=allSet.size(); //求出目前存儲的狀態(tài)集合大小for(int j=0;j<sizeOfStrVec;j++){for(int i=0;i<cntEdge;i++){newJ* dest=new newJ();if(!cntRealEdge[i]){ //不是空字符邊的,對它進行move操作moveT(e[i].tchar, e, cntEdge, strVec.at(j), dest);//如果有一個字符在多條邊上,所以要按字符相同的歸類集合。否則就會使得狀態(tài)集合分開而造成錯誤!!getEClosure(e, cntEdge, dest); //此時dest為 Ia,Ib之類的if(isInsert(allSet,dest) && dest->setJ!="") //沒找到并且dest->setJ且不為空則添加allSet.push_back(dest);//在添加relVec時,只要是不為空就要添加,這里會使relDest的元素可能重復(fù)(當(dāng)一個字符出現(xiàn)在多條邊中)if (dest->setJ != ""){relation* relDest = new relation();relDest->preJ = strVec.at(j);relDest->jchar = e[i].tchar;relDest->nexJ = dest;bool isIN = isInsertForRel(relVec,relDest->preJ,relDest->jchar,relDest->nexJ); //去重if(isIN) relVec.push_back(relDest);}}}}strVec.clear(); //去除原狀態(tài)集合for(int i=oldAllSize;i<allSet.size();i++){//將allSet中新增的后面元素添加進strVec中newJ* dest = new newJ();dest = allSet.at(i);strVec.push_back(dest);}sizeOfStrVec = strVec.size(); //求出目前存儲的狀態(tài)集合大小}cout << "轉(zhuǎn)換結(jié)果:" << endl;vector<relation*>::iterator relIt;for(relIt=relVec.begin();relIt!=relVec.end();relIt++) //遍歷輸出關(guān)系轉(zhuǎn)換表的集合cout<<(*relIt)->preJ->setJ<<" "<<(*relIt)->jchar<<" "<<(*relIt)->nexJ->setJ<<endl;char upperChars[26];memset(upperChars,0,sizeof(upperChars));cout<<"重命名如下:"<<endl;for(int i=0;i<allSet.size();i++){upperChars[i] = 'A'+i; //通過下標(biāo)將每一個集合依次從'A'開始 命名cout<<upperChars[i]<<":"<<allSet.at(i)->setJ<<endl;}vector<relation*> newRelVec; //重命名后的relVec;for(int i=0;i<relVec.size();i++){relation* newRel = new relation(); //依次更換新的名稱string preNew,nexNew;changeName(allSet,relVec.at(i)->preJ,preNew);changeName(allSet,relVec.at(i)->nexJ,nexNew);newJ* tpreJ = new newJ();newJ* tnexJ = new newJ();newRel->preJ = tpreJ;newRel->nexJ = tnexJ;newRel->preJ->setJ = preNew;newRel->nexJ->setJ = nexNew;newRel->jchar = relVec.at(i)->jchar;newRelVec.push_back(newRel);}//輸出驗證重命名的集合關(guān)系cout << "最終轉(zhuǎn)換:" << endl;vector<relation*>::iterator newRelIt;for(newRelIt = newRelVec.begin();newRelIt!=newRelVec.end();newRelIt++) //遍歷輸出新的關(guān)系轉(zhuǎn)換表的集合cout<<(*newRelIt)->preJ->setJ<<" "<<(*newRelIt)->jchar<<" "<<(*newRelIt)->nexJ->setJ<<endl;//輸出終止?fàn)顟B(tài)cout<<"終態(tài):"<<endl;set<char> st; //通過set來進行去重操作set<char>::iterator it;for(int k=0;k<allSet.size();k++){for(int i=0;i<endNode.size();i++){if(allSet.at(k)->setJ.find(endNode[i]) != string::npos) //確保終態(tài)在集合中存在st.insert('A'+k); //放入set集合中,達到去重效果}}for(it=st.begin();it!=st.end();it++) cout<<(*it)<<" "; //遍歷輸出終態(tài)集合cout<<endl;return 0; }輸出結(jié)果
輸出結(jié)果文字版
輸入各邊的信息,并且以 '前點(char '0'-'1000') 轉(zhuǎn)換字符(char 'a'-'z') 后點(char '0'-'1000')'格式,結(jié)束以'$'開頭 如果轉(zhuǎn)換字符為空,則用'#'表示 a 1 b b 1 b b 2 b b # e a # c c 1 c c 2 c c 1 d $ 輸入初始點字符: a 輸入終止點字符(若有多個終結(jié)狀態(tài),直接寫成字符串形式) ed 轉(zhuǎn)換結(jié)果: ac 1 bcde ac 2 c bcde 1 bcde bcde 2 bce c 1 cd c 2 c bce 1 bcde bce 2 bce cd 1 cd cd 2 c 重命名如下: A:ac B:bcde C:c D:bce E:cd 最終轉(zhuǎn)換: A 1 B A 2 C B 1 B B 2 D C 1 E C 2 C D 1 B D 2 D E 1 E E 2 C 終態(tài): B D E參考文獻
感謝以下博主的文章,本文參考了部分代碼和知識。
Hungryof:NFA到DFA的轉(zhuǎn)換
zhen12321:編譯原理-(NFA->DFA)
發(fā)芽ing的小啊嗚:【20200319】編譯原理課程課業(yè)打卡九之NFA的確定化
Just-Live:湖大OJ-實驗C----NFA轉(zhuǎn)換為DFA
愛玩游戲的小隱:正則到NFA的轉(zhuǎn)換
愛玩游戲的小隱:NFA到DFA的轉(zhuǎn)換及DFA的簡化
學(xué)如逆水行舟,不進則退總結(jié)
以上是生活随笔為你收集整理的【C++实现】编译原理 免考小队 NFA转换为等价的DFA的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ubuntu系统 USB设备端口绑定
- 下一篇: C++ QT Bejeweled宝石迷阵