自动机初步之DFA

自动机是一种非常有力的工具,其完备的理论可以参考编译原理或者形式语言与自动机等相关教材。从某种定义角度而言,图灵机也是自动机的一种。

这里提到的自动机特指有限状态自动机,简称为FA,根据状态转移的性质又分为确定的自动机(DFA)和非确定的自动机(NFA)。FA的表达能力等价于正规表达式或者正规文法。

FA可以看做是一个有向带权图,图的顶点集合称为自动机的状态集合,图的权值集合为自动机的字母集合,图的边代表了自动机中状态变化的情况。此外,根据需要,自动机还需指定初始状态和终态。

FA最基本的作用就是形式化描述,而且有利于编程实现。以下提到自动机全部指的是DFA。

考虑仅由字母{a, b}组成的字符串,要求字符串中字母b必须成对出现,否则字符串非法。这个规则实现起来其实非常简单,不需要自动机也完全可以。但是我们考虑使用自动机来进行判断。顺便,该规则的正规表达式描述是:(a|bb)*。星号运算代表重复若干次,包括零次。

我们考虑做一个图来表示描述该规则的DFA。

令状态1为初始状态,显然在状态1上,我们还没有违反规则。因此,经过字母a以后我们还可以回到状态1。经过字母b就不能回到状态1了,此时需要一个新状态,令其为2。

状态2表示“待定的”状态,在这个状态时不能肯定字符串是非法的,但也不是合法的。在状态2上,如果经过字母b,就回到了合法的状态也就是状态1,;如果经过字母a,则该字符串肯定是非法的。建立一个新状态即状态3,用于表示非法状态。

状态3比较简单,已经到了非法状态,其后的任何字母都不会改变这个状态了。

因此,该DFA可以表示如下。

这里写图片描述

程序实现也非常简单,状态和字母都被编码成整数,使用一个矩阵表示状态转移,再写一个函数表示自动机的运行,对每一个字符串,从状态1开始运行,运行完毕进行状态判断即可。最后能停留在状态1的字符串才是符合规则的,其余都是非法的。

#include <cstdio>

int DFA[4][2] = {
    {0},
    {1,2},
    {3,1},
    {3,3}
};

int START_STATE = 1;

int run(char const word[]){
    int state = START_STATE;
    for(char const*p=word;*p;++p){
        state = DFA[state][*p-'a'];
        if ( 3 == state ) return state;     
    }
    return state;
}

int main(){
    char a[] = "abbaaa";
    char b[] = "aabbba";
    char c[] = "aaab";

    printf("%d\n",run(a));
    printf("%d\n",run(b));
    printf("%d\n",run(c));  

    return 0;
}

DFA的使用,在一定程度上可以简化程序设计,将程序设计的关键步骤变为了自动机的设计。

考虑hdu2206,判断给定的字符串是否符合IPv4的规则。

为了方便起见,首先设计一个简单一点的DFA,可以判断出该字符串是否由4节组成,每节1到3位数字组成,节与节由小数点分开。

这里写图片描述

其中d表示0-9的十个数字。如果是小数点或者数字以外的字母全部去到错误状态,错误状态记作0,在图中未画出。

当格式符合该DFA标准后,再将数值超过255的剔除即可。使用DFA也可以直接判断诸如256、3xx等串不符合IPv4的格式标准,但是那样的话DFA就会更加复杂,不如直接使用数值判断方便。

#include <cstdio>
#include <algorithm>
using namespace std;

int const T[17][3] = {
//     d  .  other
/*0*/  0, 0, 0,//Error state
/*1*/  2, 0, 0,
       3, 5, 0,
       4, 5, 0,
       0, 5, 0,
/*5*/  6, 0, 0,
       7, 9, 0,
       8, 9, 0,
       0, 9, 0,
/*9*/  10,0, 0,
       11,13,0,
       12,13,0,
       0, 13,0,
/*13*/ 14,0, 0,
       15,0, 0,//final state 
       16,0, 0,//final state 
       0, 0, 0 //final state                
};

inline int tr(char ch){
    if ( '0' <= ch && ch <= '9' ) return 0;
    if ( '.' == ch ) return 1;
    return 2;
}

int run(char const word[]){
    int state = 1;
    for(char const*p=word;*p;++p){
        state = T[state][tr(*p)];
        if ( 0 == state ) return state;
    }
    return state;
}

inline bool isFinal(int state){
    return 14 == state || 15 == state || 16 == state;       
}

char S[110];
int main(){
    while ( gets(S) ){       
        if ( !isFinal(run(S)) ){
            printf("NO\n"); 
            continue;   
        }

        int a,b,c,d;
        sscanf(S,"%d.%d.%d.%d",&a,&b,&c,&d);
        if ( a > 255 || b > 255 || c > 255 || d > 255 )
            printf("NO\n"); 
        else
            printf("YES\n"); 
    }
    return 0;
}

再考虑POJ3332,判断是否为合法的数字。根据题目描述的规则,可以设计如下的DFA。其中前导空格事先排除,后续空格放入了DFA进行判断。

这里写图片描述

#include <cctype>
#include <cstdio>

inline int tr(char ch){
    if ( isdigit(ch) ) return 0;
    if ( '.' == ch ) return 1;
    if ( 'E' == ch || 'e' == ch ) return 2;
    if ( '+' == ch || '-' == ch ) return 3;
    if ( ' ' == ch ) return 4;
    return 5;   
}

int DFA[10][6] = {
    {0},
    3,0,0,2,0,0,
    3,0,0,0,0,0,
    3,4,6,0,9,0,//final
    5,0,0,0,0,0,
    5,0,6,0,9,0,//final
    8,0,0,7,0,0,
    8,0,0,0,0,0,
    8,0,0,0,9,0,//final
    0,0,0,0,9,0 //final
};

int run(char const word[]){
    int state = 1;
    for(char const*p=word;*p;++p){
        state = DFA[state][tr(*p)];
        if ( 0 == state ) return state;
    }
    return state;
}

inline bool isFinal(int state){
    return 3 == state || 5 == state || 8 == state || 9 == state;
}

char S[1005];
int main(){
    int kase;
    scanf("%d",&kase);
    gets(S);
    while(kase--){
        gets(S);
        char *p = S;
        while( ' ' == *p ) ++p;

        printf(isFinal(run(p))?"LEGAL\n":"ILLEGAL\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值