湖大OJ-实验C----NFA转换为DFA

实验C----NFA转换为DFA
Time Limit: 1000ms, Special Time Limit:2500ms, Memory Limit:65536KB
Total submit users: 74, Accepted users: 58
Problem 13120 : Special judge
Problem description
  有限状态自动机(FSM "finite state machine" 或者FSA "finite state automaton" )是为研究有限内存的计算过程和某些语言类而抽象出的一种计算模型。有限状态自动机拥有有限数量的状态,每个状态可以迁移到零个或多个状态,输入字串决定执行哪个状态的迁移。有限状态自动机可以表示为一个有向图。有限状态自动机是自动机理论的研究对象。 定义:有限状态自动机(FA—finite automaton)是一个五元组:
  ? M=(Q, Σ, δ, q0, F)
  · 其中,
  ? Q——状态的非空有穷集合。?q∈Q,q称为M的一个状态。
  ? Σ——输入字母表。
  ? δ——状态转移函数,有时又叫作状态转换函数或者移动函数,δ:Q×Σ→Q,δ(q,a)=p。
  ? q0——M的开始状态,也可叫作初始状态或启动状态。q0∈Q。
  ? F——M的终止状态集合。F被Q包含。任给q∈F,q称为M的终止状态。
  非确定有限状态自动机(NFA)与确定有限状态自动机(DFA)的唯一区别是它们的转移函数不同。确定有限状态自动机对每一个可能的输入只有一个状态的转移。非确定有限状态自动机对每一个可能的输入可以有多个状态转移,接受到输入时从这多个状态转移中非确定地选择一个。下图是一个非确定性有限状态自动机(NFA)的例子:

图一 一个NFA的图文表示

  转移函数δ定义自下列状态转移表:
δ01
q0 {q0} {q0q1q2}
q1 {q2} {q3}
q2 {} {q3}
q3 {q3} {q3}
  表示状态集合的子集合,采用二进制(特征)串的方式,一个子集中包含该状态,对应的特征串就为1,否则为0,比如上面状态集合的子集{q0q1q2},其特征串就是0111,而子集{q0},其特征串就是0001。将对应的特征串转换为十进制的数字,得到转移函数δ:
δ01
q0 1 7
q1 4 8
q2 0 8
q3 8 8

  你的任务,是要将一个给定的NFA转换为一个完全等价的DFA(有限状态自动机等价的意思是识别相同的语言)。这里我们约定自动机识别的字符集为{0,1},初始状态集合为Q0,状态集为{q0,q1,…,qn-1}。

Input

  输入第一行只有一个正整数t,表示有t个测试数据(意味着t个NFA)t≤10;
  对于每组测试数据(每个NFA),首先是3个正整数n,Q0,f,分别表示状态数、起始状态集合和接受状态集合的特征串对应的整数。n≤10;Q0,f < 2n;
  接下来两行是NFA的转移函数矩阵,第一行是每个状态在输入为0的状态转移情况,用特征串对应的整数表示;第二行是每个状态在输入为1的状态转移情况。

Output

  对于每个NFA,输出四行表示与之等价的DFA。输出格式如下:
  第一行3个空格隔开的整数a b c,分别表示DFA的状态数,接受状态数,起始状态的编号(从0开始对状态编号)。要求 a < 65536。
  b,c ≤ a 第二行b个空格分隔的整数,表示每个接收状态的编号,每个编号的值一定在[0,a)之间。
  第三行、第四行每行a个空格分隔的整数,表示DFA的转移函数矩阵,第三行第i个值ui表示状态转移函数的一项δ(qi,0)→ui,第四行第i个值vi表示状态转移函数的一项δ(qi,1)→vi,,每个ui,vi的值一定在[0,a)之间。 

Sample Input
  1
  4 1 8
  1 4 0 8
  7 8 8 8
Sample Output
  16 8 1
  8 9 10 11 12 13 14 15
  0 1 4 5 0 1 4 5 8 9 12 13 8 9 12 13
  0 7 8 15 8 15 8 15 8 15 8 15 8 15 8 15
Judge Tips
  样例中的NFA如图一所示
  与某个NFA等价的DFA不一定是唯一的,比如和图一等价的DFA可以是样例的解答,也可以是如下的DFA
  4 1 0
  3
  0 2 0 3
  1 3 3 3

  本题会使用special judge,只要是符合条件的解答都可以接受(Accept)。

1、算法设计思路

  状态集合的子集合,采用二进制(特征)串的方式,一个子集中包含该状态,对应的特征串就为1,否则为0,比如上面状态集合的子集{q0q1q2},其特征串就是0111,而子集{q0},其特征串就是0001。将对应的特征串转换为十进制的数字,得到转移函数δ。

2、实验总结

  在转化的过程中经NFA中状态矩阵中的每一个状态的集合映射到DFA中的一个状态。即NFA中的状态子集为一个DFA中的状态;只要NFA状态子集中有一个为接受态,相应的映射的DFA中的状态就为接受态

3、AC代码

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<cmath>
#define M 99999
using namespace std;
int ans[M];
int one[M];
int zero[M];
int lft[M];
int rgt[M];
int change[M];
bool vis[M];
bool ac[M];

int cnt, n, q, f;
int index(int p)
{
    int x = 1;
    if(p == 1)
        return 0;
    int i = 0;
    while(++i)
    {
        x <<= 1;
        if(p == x)
            return i;
    }
    return 0;
}
int mege(int a, int b)
{
    while(b)
    {
        int x = b&(-b);
        if(!(a&x))
            a ^= x;
        b ^= x;
    }
    return a;
}
void dfs(int p)
{
    ans[cnt] = p;
    int lsum = 0, rsum = 0;
    while(p)
    {
        int x = p&(-p);
        int y = index(x);
        lsum = mege(lsum, zero[y]);
        rsum = mege(rsum, one[y]);
        p ^= x;
    }
    lft[cnt] = lsum;
    rgt[cnt] = rsum;
    cnt++;
    if(!vis[lsum])
        vis[lsum] = 1, dfs(lsum);
    if(!vis[rsum])
        vis[rsum] = 1, dfs(rsum);
}

int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d%d", &n, &q, &f);
        for(int i = 0; i < n; i++)
            scanf("%d", &zero[i]);
        for(int i = 0; i < n; i++)
            scanf("%d", &one[i]);
        cnt = 0;
        memset(vis, 0, sizeof(vis));
        memset(ac, 0, sizeof(ac));
        vis[q] = 1;
        dfs(q);
        int sum = 0;
        for(int i = 0; i < cnt; i++)
            if(ans[i]&f)
                ac[i] = 1, sum++;
        for(int i = 0; i < cnt; i++)
            change[ans[i]] = i;
        printf("%d %d %d\n", cnt, sum, 0);
        for(int i = 0, j = 0; i < cnt; i++)
        {
            if(ac[i])
            {
                if(j)
                    printf(" ");
                printf("%d", i);
                j++;
            }
        }
        printf("\n");
        for(int i = 0; i < cnt; i++) {
            if(i)
                printf(" ");
            printf("%d", change[lft[i]]);
        }
        printf("\n");
        for(int i = 0; i < cnt; i++){
            if(i)
                printf(" ");
            printf("%d", change[rgt[i]]);
        }
        printf("\n");

    }
    return 0;
}
posted @ 2016-10-18 19:38  Just-Live  阅读(4196)  评论(0编辑  收藏  举报