題意
一個DFA可以用一個5元組 $((Q, \sum , \delta , q_0, F))$ 表示,其中 $Q$ 為狀態集,$\sum$ 為字母表,$\delta$ 為轉移函數,$q_0$ 為起始狀態,$F$ 為終態集。給出兩個 DFA(有限狀態自動機),判斷他們是否等價。
分析
一個簡單的做法:把 “a和b” 等價轉化為 "a" 的補和 "b" 不相交,且 "b" 的補和 'a" 不相交。
如何求 DFA 的補?也就是把接受的串變成不接受的串,不接受的串變成接受的串。由此可想到,只需把終態和非終態互換即可。
如何判斷兩個 DFA 不相交?可試着找一個可同時被兩個 DFA 接受的串,如果找不到,則說明兩個 DFA 不相交。如何找到這個串?構造一個新的 DFA,它的每個狀態都可以寫成($q_1, q_2$),其中 $q_1$ 和 $q_2$ 分別是兩個 DFA 中的狀態,當且僅當 $q_1$ 和 $q_2$ 分別是兩個 DFA 的終態時,($q_1, q_2$)是新DFA的終態。這樣,問題轉化為:找一個能被新DFA接受的串。只需要用經典的圖的遍歷(DFS 或 BFS)即可。
還有一個問題,對於“該轉移不存在” 的處理。雖然可以直接處理,但更經典的做法是加一個“所有轉移都指向自己“的”孤島狀態”,把所有不存在的轉移都改成轉移到孤島。在下面的代碼實現中,是將每個狀態編號加1,留出狀態0作為孤島狀態。
#include<bits/stdc++.h> using namespace std; const int maxn = 2000 + 10; const int max_siz = 26 + 5; int siz, sta1, sta2, a1[maxn][max_siz], a2[maxn][max_siz]; bool vis[maxn][maxn]; bool judge(int x1, int x2) //終態判定 { return a1[x1][0] ^ a2[x2][0]; } bool dfs(int x1, int x2) //存在公共串返回true { vis[x1][x2] = true; if(judge(x1, x2)) return true; for(int i = 1;i <= siz;i++) { int next1 = a1[x1][i], next2 = a2[x2][i]; if(!vis[next1][next2]) { if(dfs(next1, next2)) return true; } } return false; } int main() { int kase = 0; while(scanf("%d", &siz) == 1 && siz) { scanf("%d", &sta1); for(int i = 1;i <= sta1;i++) for(int j = 0;j <= siz;j++) { int tmp; scanf("%d", &tmp); a1[i][j] = tmp+(j!=0); } scanf("%d", &sta2); for(int i = 1;i <= sta2;i++) for(int j = 0;j <= siz;j++) { int tmp; scanf("%d", &tmp); a2[i][j] = tmp+(j!=0); } memset(vis, 0, sizeof(vis)); printf("Case #%d: ", ++kase); if(dfs(1, 1)) printf("No\n"); else printf("Yes\n"); } }
參考鏈接:
1. https://www.luogu.org/problemnew/solution/UVA1671
2. https://blog.csdn.net/programmerya/article/details/81350287
