UVa 1671 语言的历史——判断两个DFA是否等价
题意
一个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
个性签名:时间会解决一切