湖大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的圖文表示

  轉移函數δ定義自下列狀態轉移表:
δ 0 1
q0 {q0} {q0q1q2}
q1 {q2} {q3}
q2 {} {q3}
q3 {q3} {q3}
  表示狀態集合的子集合,采用二進制(特征)串的方式,一個子集中包含該狀態,對應的特征串就為1,否則為0,比如上面狀態集合的子集{q0q1q2},其特征串就是0111,而子集{q0},其特征串就是0001。將對應的特征串轉換為十進制的數字,得到轉移函數δ:
δ 0 1
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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM