有意思的题目,给出两个自动机,判断这个两个自动机是否是等价的?
两个自动机是等价的,那么他们可接受的字符串集合一定是完全一样的。
于是我们可以从(0,0)开始bfs,每次看看在两个自动机上走到的两个点儿子指针以及终态信息是否完全一致,是的话就把所有儿子指针都拉到队列中进行后面的判断。
由于我们对于每一个二元组,最多只需要判断一次,显然时间复杂度就不会超过n^2了。
不过我个人觉得题目好像有些问题,在题目给出的数据中,自动机可能存在一些“不合法”的状态,什么意思呢?就是说有的状态,在它的所有后继状态中没有任何一个可以到达终态,但是这种状态在构建自动机的时候应该不会被构造出来吧?题目此处让我很疑惑。
因此,我们需要从每一个终态开始沿着指针的反方向往回遍历一遍,看看那些状态是有用状态,然后bfs的时候只需要考虑有效的状态就好了。
召唤代码君:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <vector> #define maxn 2020 #define pr pair<int,int> #define mp(x,y) make_pair(x,y) using namespace std; int next[2][maxn][26],tag[2][maxn]; int n[2],m; bool vis[2][maxn],walk[2][maxn],f[maxn][maxn]; vector<int> from[2][maxn]; void _init(int x) { for (int i=0; i<n[x]; i++){ vis[x][i]=false; walk[x][i]=false; from[x][i].clear(); } if (x==1){ for (int i=0; i<n[0]; i++) for (int j=0; j<n[1]; j++) f[i][j]=false; } } void visit(int x,int cur) { if (vis[x][cur]) return ; vis[x][cur]=true; for (unsigned i=0; i<from[x][cur].size(); i++) visit(x,from[x][cur][i]); } bool match(int u,int v) { if (tag[0][u]!=tag[1][v]) return false; for (int i=0; i<m; i++) if ((next[0][u][i]>=0 && vis[0][next[0][u][i]])^(next[1][v][i]>=0 && vis[1][next[1][v][i]])) return false; return true; } bool check() { queue<pr> Q; Q.push(mp(0,0)); while (!Q.empty()){ int u=Q.front().first,v=Q.front().second; Q.pop(); f[u][v]=true; if (!match(u,v)) return false; for (int i=0; i<m; i++){ if (next[0][u][i]>0 && vis[0][next[0][u][i]]){ if (f[next[0][u][i]][next[1][v][i]]) continue; Q.push(mp(next[0][u][i],next[1][v][i])); } } } return true; } int main() { int cas=0; while (scanf("%d",&m) && m){ for (int i=0; i<2; i++){ scanf("%d",&n[i]); _init(i); for (int j=0; j<n[i]; j++){ scanf("%d",&tag[i][j]); for (int k=0; k<m; k++){ scanf("%d",&next[i][j][k]); if (next[i][j][k]>=0) from[i][next[i][j][k]].push_back(j); } } for (int j=0; j<n[i]; j++) if (tag[i][j] && !vis[i][j]) visit(i,j); } printf("Case #%d: ",++cas); if (check()) puts("Yes"); else puts("No"); } return 0; }