牛骨文教育服务平台(让学习变的简单)

1. NFA的确定化

1.1. 明确NFA的定义

一个非确定的有穷自动机(NFA)M是一个五元式:

  • M=(S,∑,δ,S0,F)

  • S是一个有限集,它额每个元素称为一个状态。

  • ∑是一个有穷字母表,它的每个元素称为一个输入字符
  • δ是一个从S×∑∗至S子集额单值映射。即:δ:S×∑∗→2⋅S
  • S0⊆S,是一个非空的初态集
  • F⊂ S , 是一个终态集(可空)

1.2. 定义运算

定义对状态集合I的几个有关运算:

  • 状态集合I的ε-闭包,表示为ε-closure(I),定义为一状态集,是状态集I中的任何状态s经任意条ε弧而能到达的状态的集合。状态集合I的任何状态s都属于ε-closure(I)。
  • 状态集合I的a弧转换,表示为move(I,a)定义为状态集合J,其中J是所有那些可从I的某一状态经过一条a弧而到达的状态的全体。
    定义Ia = ε-closure(move(I,a))

1.3. 算法描述

  • 每次从队头取出一个集合,(开始队列内只有初态集合I的ε-闭包(I) ),然后得到它对于任意一个字符a的Ia=ε−closure(move(I,a))
  • 然后如果当前状态之前没有出现过,那么当前状态作为一个新的状态I,放入队列。
  • 一直做如上操作,直到队列为空

1.4. 代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <fstream>
#include <queue>
#include <vector>
#include <string>
#include <cstring>
#include <set>
#define MAX 500
#define M 1000007

using namespace std;

struct Set
{
    set<int> elements;
    int state;
}I[MAX];

vector<int> e[MAX];
vector<int> edge[MAX];
vector<int> val1[MAX];
vector<int> val2[MAX];
vector<int> hash[M];
vector<int> types;
int vis[MAX][MAX];
int cntp,cnts,start,finish,used[MAX];

void _clear( );
void clear( );
void _add( int u , int v , int x );
void add( int u , int v , int x );
void dfs ( set<int>& elements , int u , int d , int flag );
void ensure();
void minimize();
int get_hash( set<int>& item );

//#define DEBUG

int main ( )
{
    int m;
    puts("给出NFA的边的条数:");
    while ( ~scanf ( "%d" , &m ) )
    {
        _clear();
        clear();
        puts("给出NFA的始态和终态:");
        scanf ( "%d%d" , &start , &finish );
        puts("给出所有的边,每行一条:");
        for ( int i = 0 ; i < m ; i++ )
        {
            int u,v,x;
            scanf ( "%d%d%d" , &u , &v , &x );
            _add ( u , v , x ); 
        }
#ifdef DEBUG
        set<int> temp;
        int num[]={2,3,6,7,8};
        //for ( int i = 2 ; i < 6 ; i++ )
        //    dfs ( temp , i , 0 , 2 );
        for ( int i = 0 ; i < 5 ; i++ )
            dfs ( temp , num[i] , 0 , 1 );
        set<int>::iterator it = temp.begin();
        for ( ; it != temp.end() ; it++ )
            printf ( "%d " , *it);
        puts("");
#else 
        ensure();
        puts("计算结果如下:");
        printf ( "%d
" , cnts );
        for ( int i = 0 ; i < cnts ; i++ )
            for ( int j = 0 ; j < edge[i].size() ; j++ )
                printf ( "edges : %d %d %d
" , i , edge[i][j] , val2[i][j] );
        puts("
给出DFA的边的条数:");
#endif
    }
}

void _clear()
{
    for ( int i = 0 ; i < MAX ; i++ )
    {
        e[i].clear();
        val1[i].clear();
    }
    types.clear();
    memset ( used , 0 , sizeof ( used ) );
}

void clear()
{
    for ( int i = 0 ; i < MAX ; i++ )
    {
        edge[i].clear();
        val2[i].clear();
    }
}

void _add ( int u , int v , int x )
{
    e[u].push_back ( v );
    val1[u].push_back ( x );
    if ( !used[x] )
    {
        types.push_back ( x );
        used[x] = types.size();
    }
}

int get_hash ( set<int>& item )
{
    int sum = 0;
    set<int>::iterator it = item.begin(),it1,it2;
    for ( ; it!= item.end() ; it++ )
    {
        sum += (*it)*(*it)%M;
        sum %= M;
    }
    for ( int i = 0 ; i < hash[sum].size() ; i++ )
    {
        int x = hash[sum][i];
        set<int>& temp = I[x].elements;
        it = temp.begin();
        bool flag = true;
        if ( temp.size() != item.size() ) continue;
        for ( it1 = temp.begin() , it2 = item.begin(); it2!= item.end() ; it1++,it2++ )
            if ( *it1 != *it2 ) 
            {
                flag = false;
                break;
            }
        if ( flag ) return x;
    }
    return -1;
}

int  make_hash ( set<int>& item )
{
    int sum = 0;
    set<int>::iterator it = item.begin();
    for ( ; it!= item.end() ; it++ )
    {
        sum += (*it)*(*it)%M;
        sum %= M;
    }
    hash[sum].push_back ( cnts );
    return cnts++;
}

void add ( int u , int v , int x )
{
    edge[u].push_back ( v );
    val2[u].push_back ( x );
}

void dfs ( set<int>& elements , int u , int d , int flag )
{
    if ( vis[u][d] ) return;
    vis[u][d] = 1;
    if ( d == 1 ) elements.insert ( u );
    if ( d == 2 ) return;
    int len = e[u].size();
    for ( int i = 0 ; i < len ; i++ )
    {
        int v = e[u][i],dd;
        int x = val1[u][i];
        if ( x == flag ) dd = d+1;
        else if ( x == 0 ) dd = d;
        else continue;
        dfs ( elements , v , dd , flag );
    }
}

void ensure ( )
{
    I[0].elements.insert(start);
    cnts = 1;
    for ( int i = 0 ; i < cnts ; i++ )
    {
        set<int>& cur = I[i].elements;
        for ( int j = 0 ; j < types.size() ; j++ )
        {
            if ( types[j] == 0 ) continue;
            memset ( vis , 0 , sizeof ( vis ) );
            set<int>& temp = I[cnts].elements;
            temp.clear();
            set<int>::iterator it = cur.begin();
            for ( ; it != cur.end() ; it++ )
                dfs ( temp , *it , 0 , types[j] );
            if ( temp.empty() ) continue;
            int x = get_hash ( temp );
            if ( x == -1 ) 
                x = make_hash ( temp );
            add ( i , x , types[j] );
        }
        set<int>::iterator it = cur.begin();
        printf ( "I%d :" , i );
        for ( ; it!= cur.end() ; it++ )
            printf ( "%d " , *it );
        puts ("");       
    }
}

2. DFA的最小化

2.1. 明确DFA的定义

一个确定的有穷自动机(DFA)M是一个五元式:

  • M=(S, ∑, δ, s0, F)其中

  • S是一个有限集,它的每个元素称为一个状态。

  • ∑是一个有穷字母表,它的每个元素称为一个输入字符
    -δ是一个从S×∑至S的单值映射。δ(s,a)=s’意味着:当现行状态-为s、输入字符为a时,将转换到下一个状态s’。我们称s’为s的一个后继状态。
  • s0∈S,是唯一的初态。
  • F ⊂ S,是一个终态集(可空)

2.2 算法描述

  • DFA M =(K,∑,f, k0,, kt),最小状态DFA M’

  • 1.构造状态的初始划分∏0:终态kt 和非终态K- kt两组

  • 2.对∏施用传播性原则 构造新划分∏new
  • 3.如∏new=∏,则令∏new=∏并继续步骤4,否则∏:=∏new重复2
  • 4.为∏final中的每一组选一代表,这些代表构成M’的状态。若k是一代表且f(k,a)=t,令r是t组的代表,则M’中有一转换f’(k,a)=r M’ 的开始状态是含有K0的那组的代表 M’的终态是含有Kt的那组的代表
  • 5.去掉M’中的死状态.

2.3 代码实现

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <vector>
#include <map>
#include <queue>
#include <fstream>
#define MAX 507

using namespace std;

struct Node
{
    int x;
    int type;
    Node(){}
    Node( int a , int b )
        :x(a),type(b){}
    bool operator < ( const Node& a ) const
    {
        return type < a.type;
    }
};

int mp[MAX][MAX];
vector<int> _edge[MAX];
vector<int> _val[MAX];
vector<int> edge[MAX];
vector<int> val[MAX];
vector<int> point;
vector<pair<int,int> > ans1;
vector<int> ans2;
vector<Node> col;
int fa[MAX];
int type[MAX];
int state[MAX];
int used[MAX];
int kinds;
int groups_num;
int n;
int m;

void init ();
int _find( int x );
void _union ( int x , int y );
void _add ( int u , int v , int x );
void add ( int u , int v , int x );
void minimize ( );
void make_csv();

int main ( )
{
    init ( );
    scanf ( "%d%d" , &n , &m );
    for ( int i = 1 ; i <= n ; i++ )
        scanf ( "%d" , &type[i] );
    for ( int i = 0 ; i < m ; i++ )
    {
        int u,v,x;
        scanf ( "%d%d%d" , &u , &v , &x );
        _add ( u ,v , x );
    }
    minimize();
    //最小化后的点的保留结果
    puts ("points : " );
    printf ( "%d
" , (int)point.size() );
    for ( int i = 0 ; i < point.size(); i++ )
        printf ( "%d " , point[i] );
    puts("");
    //最小化后的边的保留结果
    puts ( "edge: ");
    m = ans1.size();
    for ( int i = 0 ; i < ans1.size() ; i++ )
        printf ( "%d %d %d
" , ans1[i].first , ans1[i].second , ans2[i] );
    puts("");
    make_csv();
}

void init ( )
{
    for ( int i = 0 ; i < MAX ; i++ )
    {
        edge[i].clear();
        _edge[i].clear();
    }
    point.clear();
    memset ( type , 255 , sizeof ( type ) );
    memset ( used , 0 , sizeof ( used ) );
    kinds = 0;
    groups_num = 2;
    col.clear();
    for ( int i = 0 ; i < MAX ; i++ )
        fa[i] = i;
}

void _add ( int u , int v , int x )
{
    _edge[u].push_back ( v );
    _val[u].push_back ( x );
    if ( used[x] ) return ;
    used[x] = x;
    kinds++;
}

void add ( int u , int v , int x )
{
    edge[u].push_back ( v );
    val[u].push_back ( x );
}

int _find ( int x )
{
    return x == fa[x]? x: fa[x] = _find ( fa[x] );
}

void _union ( int x , int y )
{
    x = _find ( x );
    y = _find ( y );
    fa[x] = y;
}

void minimize ( )
{
    queue<vector<Node> > q[2];
    col.clear();
    for ( int i = 1 ; i <= n ; i++ )
    {
        if ( type[i] == 0 ) 
            col.push_back ( Node( i , 0 ) );
    }
    q[1].push ( col );
    col.clear();
    for ( int i = 1 ; i <= n ;i++ )
    {
        if ( type[i] == 1 )
            col.push_back ( Node ( i , 1 ) );
    }
    q[1].push( col );
    col.clear();
    for ( int i = 1 ; i <= kinds ; i++ )
    {
        int cur = i%2;
        int next = (i+1)%2;
        while ( !q[next].empty() ) q[next].pop();
        while ( !q[cur].empty() )
        {
            vector<Node> front = q[cur].front();
            q[cur].pop();
            for ( int j = 0 ; j < front.size() ; j++ )
            {
                Node& temp = front[j];
                int u = temp.x;
                for ( int k = 0 ; k < _edge[u].size() ; k++ )
                {
                    int v = _edge[u][k];
                    int x = _val[u][k];
                    if ( x != i ) continue;
                    temp.type = type[v];
                }
            }
            sort ( front.begin() , front.end() );
            if ( front[0].type == front[front.size()-1].type )
                q[next].push ( front );
            else 
            {
                col.clear();
                col.push_back ( front[0] );
                for ( int j = 1 ; j < front.size() ; j++ )
                {
                    if ( front[j].type != front[j-1].type )
                    {
                        q[cur].push ( col );
                        col.clear();
                        groups_num++;
                    }
                    type[front[j].x] = groups_num;
                    col.push_back ( front[j] );
                }
                q[cur].push( col );
            }
        }
    }
//#define DEBUG
#ifdef DEBUG
    int id = (kinds+1)%2;
    int num = 1;
    while ( !q[id].empty() )
    {
        vector<Node> temp = q[id].front();
        q[id].pop();
        printf ( "%d : ",  num++ );
        for ( int i = 0 ; i < temp.size() ; i++ )
            printf ( "%d " , temp[i].x );
        puts("");
    }

#else
    int id = (kinds+1)%2;
    while ( !q[id].empty() )
    {
        vector<Node> temp = q[id].front();
        q[id].pop();
        for ( int i = 1 ; i < temp.size() ; i++ )
            _union ( temp[i].x , temp[i-1].x);
    }
    memset ( used , 0 , sizeof ( used ) );
    memset ( mp , 0 , sizeof ( mp ) );
    for ( int i = 1 ; i <= n ;i++ )
    {
        int x = _find(i);
        if ( used[x] ) continue;
        used[x] = 1;
        point.push_back ( x );
    }
    ans1.clear();
    for ( int i = 1 ; i <= n ; i++ )
        for ( int j = 0 ; j < _edge[i].size() ; j++ )
        {
            int v = _edge[i][j];
            int x = _val[i][j];
            int a = _find(i);
            int b = _find(v);
            if ( mp[a][b] ) continue;
            mp[a][b] = 1;
            add ( a , b , x );
            ans1.push_back ( make_pair ( a , b ) );
            ans2.push_back ( x );
        }
#endif
}

void make_csv ( )
{
    freopen ( "g1.csv" , "w" , stdout );
    printf ( "%d,%d, 
" , n , m  ); 
    for ( int i = 0 ; i < point.size() ; i++ )
        if ( i == 0 ) printf ( "%d" , point[i] );
        else printf ( ",%d" , point[i] );
    puts("");
    for ( int i = 0 ; i < m ; i++ )
        printf ( "%d,%d,%d
" , ans1[i].first , ans1[i].second , ans2[i] );
    fclose(stdout);
}