PTA 7-4 基于DFA的字符串检测 (25 分)

确定性有穷状态自动机 (DFA) 可以理解为由若干个状态构成的,且能够通过一定的规则自动在状态间转换的结构。其中一种状态遇到某一种标志只可能转换为一种状态,即确定性。

下图是一个DFA的示意图。

29.png

初始状态为d​0​​,此后逐个读入字符。d​0​​遇到a后转换为d​1​​;d​1​​遇到b后转换为状态d​2​​;d​2​​遇到b后仍为d​2​​;d​2​​遇到c后转化为d​3​​;d​1​​遇到c后转换为状态d​3​​,... ,依此类推。

其中带双圆圈的代表终态。当一个字符串读取结束时刚好到达终态,则其能通过这个DFA的检测。

上图的自动机可以用正则表达式a(b|c)*表示。

现在给出一个DFA,和一个字符串,判断其能否通过该DFA的检测。

输入格式

第一行给出整数 M 和 N (0<M<=100,0<N<=2000),分别代表DFA的状态数和转换规则数。其中状态用数字 0, 1, 2, … ,M−1 表示,初始状态为0

第二行给出整数 K 和 K 个整数 t​1​​, t​2​​, …, t​K​​。表示有 K 个终态,t​1​​ - t​K​​表示终态的编号。

之后 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结束状态合法性
d0adist[0][1]==ad11
d1bdist[1][2]==b

d2

1
d2cdist[2][3]==cd31d3t[3]=1

字符串abdc的检测过程如下:

初状态被检测字符转换是否成功末状态flag结束状态合法性
d0adist[0][1]==ad11
d1bdist[1][2]==b

d2

1
d2d未找到d20d2t[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;
}

个人总结

       本题没有涉及到太复杂的数据结构,难点在于对题目的理解和对二维矩阵的熟悉度。希望读者们认真阅读解题思路和代码后能有所收获,在理解题意的基础上自己写出代码。第一次发文请多指教!文章若有错误请大家及时指正,谢谢阅读。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值