确定性有穷状态自动机 (DFA) 可以理解为由若干个状态构成的,且能够通过一定的规则自动在状态间转换的结构。其中一种状态遇到某一种标志只可能转换为一种状态,即确定性。
下图是一个DFA的示意图。
初始状态为d0,此后逐个读入字符。d0遇到a
后转换为d1;d1遇到b
后转换为状态d2;d2遇到b
后仍为d2;d2遇到c
后转化为d3;d1遇到c
后转换为状态d3,... ,依此类推。
其中带双圆圈的代表终态。当一个字符串读取结束时刚好到达终态,则其能通过这个DFA的检测。
上图的自动机可以用正则表达式a(b|c)*
表示。
现在给出一个DFA,和一个字符串,判断其能否通过该DFA的检测。
输入格式
第一行给出整数 M 和 N (0<M<=100,0<N<=2000),分别代表DFA的状态数和转换规则数。其中状态用数字 0, 1, 2, … ,M−1 表示,初始状态为0。
第二行给出整数 K 和 K 个整数 t1, t2, …, tK。表示有 K 个终态,t1 - tK表示终态的编号。
之后 N 行,每行给出一个规则,其格式为:
状态1 状态2 字符
其中字符为大小写字母 (a-z和A-Z)。 比如0 1 a
,表示状态0遇到字符a
转换为状态1。
接下来给出整数 Q,表示有 Q 个字符串待检测。
最后 Q 行每行给出一个字符串。
输出格式
对于每个待检测字符串,输出 Yes
或 No
表示能否通过检测。
解题思路及解决方法
这道题的解题思路相对清晰。
一、明确状态之间的转换特点:
1.有方向
2.有条件
比如图中,d2->d3和d3->d2的转换条件是不同的。因此,难点在于如何将两个状态(或一个状态与自身)之间不同方向的转换条件储存下来。
二维矩阵:实现一一对应关系
dist[i][[j]=k:状态di遇到k时变为状态dj
二、如何判断结束时状态是否合法(即结束状态是否为终态)
由于输入中已明确合法的结束状态有哪些,所以在读取这些状态时就可以直接进行标记,便于程序结束时作出判断。
标记数组:判断结束状态的合法性
t[T]=1:状态dT是合法的结束状态
三、成功通过检测的条件:
条件1.被检测的字符串中的每一个字符,都能使状态顺利进行转换
条件2.在满足1的情况下,结束状态必须合法
注意,以上两个条件必须同时满足!
其中,条件2的实现比较简单,只需要将结束状态记录下来,并查看标记数组对应位置处的值是否等于1即可。
较难之处在于,条件1中如何将第一次转换的末状态变为第二次转换的初状态,并继续对下一次转换作出判断。
重点:理解状态转换的过程!
在二维矩阵中,状态的转换实际上就是在初状态所在行一一查找,直到找到某一列处的值等于被检测字符时,说明转换成功,且该列对应的数字就是下一次转换的初状态。
flag:标记转换是否顺利
顺利转换时,flag=1;未能在查找行中找到被检测字符时,flag=0
样例分析
字符串abc的检测过程如下:
初状态 | 被检测字符 | 转换是否成功 | 末状态 | flag | 结束状态 | 合法性 |
d0 | a | dist[0][1]==a | d1 | 1 | ||
d1 | b | dist[1][2]==b | d2 | 1 | ||
d2 | c | dist[2][3]==c | d3 | 1 | d3 | t[3]=1 |
字符串abdc的检测过程如下:
初状态 | 被检测字符 | 转换是否成功 | 末状态 | flag | 结束状态 | 合法性 |
d0 | a | dist[0][1]==a | d1 | 1 | ||
d1 | b | dist[1][2]==b | d2 | 1 | ||
d2 | d | 未找到 | d2 | 0 | d2 | t[2]=1 |
由flag可知不满足条件1。
注意,题目所给的输入样例中不包含特殊情况。若字符串可以顺利进行状态转换,但结束状态并不合法,则检测失败。
测试代码
测试点通过情况如下
代码如下:
#include<stdio.h>
#include<string.h>
int main(){
int M,N;
int K;
int Q;
int i,j=0,k,x,y;
int T;
int t[101]={0};
scanf("%d %d\n",&M,&N);
scanf("%d",&K);
char dist[M][M];//状态转换关系矩阵,行为起点,列为终点
char z;
for(i=1;i<=K;i++){
scanf("%d",&T);
t[T]=1;
}
for(i=0;i<N;i++){
scanf("%d %d %c",&x,&y,&z);
dist[x][y]=z;
}
char s[N];
int flag=0;//标记转换是否顺利
scanf("%d",&Q);
for(i=0;i<Q;i++){
scanf("%s",s);
int h=0;
j=0;
while(s[h]!='\0'){
for(k=0;k<M;k++){
if(dist[j][k]==s[h]){
j=k;
flag=1;
break;
}
else flag=0;
}
if(!flag){
break;
}//若未找到则直接结束检测,检测失败
h++;
}
if(flag==1&&t[j]==1){
printf("Yes\n");
}//必须同时满足两个条件
else printf("No\n");
}
return 0;
}
个人总结
本题没有涉及到太复杂的数据结构,难点在于对题目的理解和对二维矩阵的熟悉度。希望读者们认真阅读解题思路和代码后能有所收获,在理解题意的基础上自己写出代码。第一次发文请多指教!文章若有错误请大家及时指正,谢谢阅读。