题目
将通过以下有穷自动机DFA的图,识别输入的字符串,并判断输入的字符串是否符合该DFA图。
图
分析
输入aab如何判断aab是否符合DFA图?
首先DFA图肯定是从起始位置(始态-双箭头指向的状态)-1位置开始执行,如果输入aab,首先从字符串头开始遍历,例如a符号在1位置转换为2,a(第二个a)又在2位置状态下4,b又在4状态下转换为2状态,然后字符串遍历结束。这个时候我们可以发现最终DFA的状态停在了2状态也就是终态(双圆圈的状态),所以DFA也停止,因次aab可以被DFA识别。
由上发现只要最终DFA的状态为终态,则该字符串遍能被该DFA识别,例如输入ab,不能识别最终状态为1不是终态。
特殊情况
输入的字符串中含有DFA中不存在的符号,例如输入acb,DFA中无对应的c符号,肯定不能识别。
思路
每个状态写一个子程序,相互调用
上代码
package BianYi;
/*
* DFA
* 本方法只适应该DFA图
* I Ia Ib
* 1 2 3 0
* 2 4 1 1
* 3 1 5 0
* 4 4 2 0
* 5 2 3 1
* */
import java.util.Scanner;
public class Demo_02 {
public static int count=0;//全局计数器 用于遍历输入的字符串
public static boolean flag=false;//记录输入的字符串是否能被DFA识别,默认为false 全局变量
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
System.out.print("请输入一串字符串:");
String str=input.next();//接收该字符串
//先排除简单的情况 即输入的字符串中存在某个字符在DFA符号中无对应 例如输入aac c无对应(符号为a b)
for(int i=0;i<str.length();i++) {//遍历输入的字符串 一一对比
if(str.charAt(i)!='a'&&str.charAt(i)!='b') {//遍历的字符在DFA中无对应
System.out.println("字符串:"+str+"不能被当前的DFA识别!");
System.exit(0);//终止程序
}
}
//遍历输入的字符串
fun_01(str.charAt(count),str);
if(flag) {
System.out.print("字符串:"+str+"能被当前的DFA识别!");
}else {
System.out.println("字符串:"+str+"不能被当前的DFA识别!");
}
}
public static boolean funCtion(String str,int t) {
if((t==5||t==2)&&count==str.length()) {//如果此时count的值等于字符串的长度-即已经遍历到了最后一个字符了
flag=true;//因为此处会调用fun_02或fun_05,而且fun_02或fun_05为终态,并且已经是遍历的最后一个字符 所以该字符串能被DFA识别
return true;//让程序不再往下执行,不然会造成str.charAt(count)下标越界
}
if((t==1||t==3||t==4)&&count==str.length()){
flag=false;//因为此处会调用fun_03||04||01,而且都不为终态,并且已经是遍历的最后一个字符 所以该字符串不能被DFA识别
return true;//让程序不再往下执行,不然会造成str.charAt(count)下标越界
}
return false;
}
public static void fun_01(char ch,String str) {//DFA中的1状态
if(ch=='a') {
count++;
if(funCtion(str,2)) {
return;
}
fun_02(str.charAt(count),str);
}else {//ch=='b'
count++;
if(funCtion(str,3)) {
return;
}
fun_03(str.charAt(count),str);
}
}
public static void fun_02(char ch,String str) {//DFA中的2状态-终态
if(ch=='a') {
count++;
if(funCtion(str,4)) {
return;
}
fun_04(str.charAt(count),str);
}else {//ch=='b'
count++;
if(funCtion(str,1)) {
return;
}
fun_01(str.charAt(count),str);
}
}
public static void fun_03(char ch,String str) {//DFA中的3状态
if(ch=='a') {
count++;
if(funCtion(str,1)) {
return;
}
fun_01(str.charAt(count),str);
}else {//ch=='b'
count++;
if(funCtion(str,5)) {
return;
}
fun_05(str.charAt(count),str);
}
}
public static void fun_04(char ch,String str) {//DFA中的4状态
if(ch=='a') {
count++;
if(funCtion(str,4)) {
return;
}
fun_04(str.charAt(count),str);
}else {//ch=='b'
count++;
if(funCtion(str,2)) {
return;
}
fun_02(str.charAt(count),str);
}
}
public static void fun_05(char ch,String str) {//DFA中的5状态-终态
if(ch=='a') {
count++;
if(funCtion(str,2)) {
return;
}
fun_02(str.charAt(count),str);
}else {//ch=='b'
count++;
if(funCtion(str,3)) {
return;
}
fun_03(str.charAt(count),str);
}
}
}
矩阵法-二维数组
开辟一个二维数组存在每个状态经过输入的符号转后后的状态
其中行为状态的个数,列为DFA中的符号个数+1,最后一列存储当前的状态是否为终态-01标记 1-为终态。
为了能使程序通用其他DFA图,将DFA状态之间的相关关系写入了文本中
文本存储规则:状态标识用阿拉伯数字表示,符号用字母表示
文本存储代码
package BianYi;
import java.io.BufferedReader;
import java.io.File;//导入包
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
//读取文本中的信息
public class FileText {
public static int hang;//二维数组的行数
public static int lie;//二维数组的列数
private static int i=1;//目的是标记文本中的第一行,因为第一行只有2个数字,其他行有三个数字,且均以空格分离
private static String path="C:\\Users\\wsg\\Desktop\\编译原理.txt";//定义文本的名称,
private static File file;//定义一个File(File读取文件的流)的变量名
static {//static 为静态代码块 程序运行只执行一次
file = new File(path);
if(!file.exists()){
try {//捕获创建、读取文件时可能发生的异常
file.createNewFile();//文件如果不存在会创建如果存在不会创建
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static int[][] fun(int[][] array) {
try {
array=readDataFromFile4(file,array);
} catch (IOException e) {
e.printStackTrace();
}
//返回权限
return array;
}
private static int[][] readDataFromFile4(File file,int[][] array) throws IOException {//声明为staic可用类名访问 throws表示该方法可能抛出IOException异常
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));//缓冲流-读取
String str = "";//局部变量需要先赋值后引用
int count=0;//数组行数的统计
while((str = reader.readLine())!=null){//如果没有读取到最后一行
String[] stuInfo= str.split("\\s+");//按照空格正则分割,全部读取
if(i==3) {
Demo_01.chArry=stuInfo;
}
if(i==1) {//第一行存取的是行数和列数
hang=Integer.valueOf(stuInfo[1]).intValue();//将读取的行数转换为整数类型
lie=Integer.valueOf(stuInfo[3]).intValue();
//堆区开辟数组空间
array=new int[FileText.hang][FileText.lie+1];//声明一个二维数组,行数和列数从文本中读取 其中行为状态的个数,列为输入的符号个数和初始态0 1
}
if(i>3){//当读取的行数大于三时,此时读取的便是状态之间的转换关系,将其存储在二维数组中
array[count][0]=Integer.valueOf(stuInfo[0]).intValue();//存储某个状态经过输入某个字符转换后的状态
array[count][1]=Integer.valueOf(stuInfo[1]).intValue();//存储某个状态经过输入另一个字符转后的状态
array[count][2]=Integer.valueOf(stuInfo[2]).intValue();//存储此时状态是否为初始态 0 1
count++;
}
i++;
}
return array;//将新开辟的数组空间返回 便于调用 不返回会造成访问数组为null-即只能在该方法中访问该数组
}
}
矩阵代码
package BianYi;
import java.util.Scanner;
/*
* 文本中的存储规则:
* 如果DFA的输入字符在图中为数字 则用字母a b c...代替
* 如果DFA的状态在图中为字母,则用数字 1 2 3....代替
* */
//有穷自动机DFA程序
public class Demo_01 {
public static String[] chArry;//将有DFA的输入符号存储在chArry中 例如 a b
public static void main(String[] args) {
Scanner input=new Scanner(System.in);//输入语句
int[][] array = null;//首先定义一个二维数组,赋值为空 ,在读取的文件类FileText中赋值
array=FileText.fun(array);//传递数组名称,分配空间,并且读取文本中的内容(各状态转换之间的关系)
System.out.print("I ");
for(String st:chArry) {//将DFA中的输入字符打印出来 例如 a b
System.out.print("I"+st+" ");//空格分割
}
System.out.println();//换行
for(int i=0;i<FileText.hang;i++) {//将二维表中的状态关系全部打印出来 最后一列存储的是该状态是否终态0-1
System.out.print((i+1)+" ");
for(int j=0;j<FileText.lie+1;j++) {
System.out.print(array[i][j]+" ");//以空格隔开
}
System.out.println();//换行
}
System.out.print("请输入一串字符串:");
String str=input.next();//输入用户需要识别的字符串
if(funCtion(str,array)) {//调用funCtion函数,传递用户输入的字符串以及存储各状态转换关系的二维数组,识别成功返回ture
System.out.print("字符串:"+str+"可以被该有穷自动机识别");
}else {
System.out.print("字符串:"+str+"不可以被该有穷自动机识别");
}
}
public static boolean funCtion(String str,int[][] array) {//如果识别成功 返回true 否则返回false
char[] strArray=str.toCharArray();//将要用户输入的字符串转换为char[]类型,便于一个一个比较与转换
boolean flag=false;//记录用户输入的字符串与DFA中的字符号是否一模一样,默认不一样
//第一种情况 输入的字符在DFA中找不到该字符 例如 DFA中的符号是a b 用户输入的是abc c不存在
String s;//char[]类型和String[]类型无法比较,需要转换,s存储strArray[i]
for(int i=0;i<strArray.length;i++) {//遍历用户输入的字符串
flag=false;//用户输入的字符串中每一个字符都默认为在DFA符号中无对应
s=String.valueOf(strArray[i]);//将字符char类型转换为string类型便于比较
for(int j=0;j<chArry.length;j++) {//遍历DFA中的字符号
if(s.equals(chArry[j])) {//如果用户输入的字符串中的每一个字符在DFA中有相应的字符号对应,改变flag的状态
flag=true;//如果用户输入的字符串的每个字符在DFA中都有符号对应,则flag一直为true
break;//只要当前的该单个字符在DFA中有对应的字符号就不需要再遍历当前查找了,退出内层循环
}
}
if(!flag) {//表明用户输入的字符串的字符存在与DFA中的字符号不一致;则该字符串肯定不能被该机器识别 则不需要再查找,直接返回false
return false;
}
}
int num=1;//如果输入的是aab a在某一列 则num存储某一列的DFA转换状态
for(int i=0;i<strArray.length;i++) {//遍历用户输入的字符串
int n=-1;//记录当前遍历的字符在二维表中对应的列数(根据DFA中的符号判断)
s=String.valueOf(strArray[i]);//将字符char类型转换为string类型便于比较
for(int j=0;j<chArry.length;j++) {//遍历DFA中的符号
if(s.equals(chArry[j])) {//如果当前遍历的字符与当前遍历的DFA中的符号相等
n=j;//将s在二维表中的列数赋予n
num=array[num-1][j];//将要这列中的
break;
}
}
if(i==strArray.length-1&&array[num-1][2]==1) {//已经扫描完最后一个字符,并且当前该字符所在的状态为1 则说名该字符符合该有穷自动机
return true;
}
//System.out.println(n);
}
return false;
}
}
结果
注:此程序对于一些DFA图可能识别不了,例如某个状态转换为其他状态只有一种输入符号,那么DFA中的其他符号在二维数组中的存储值应该为空(可置为0,增加判断即可)。
如下图,如果输入abca字符串则结果为识别不成功,因为第输入a转换为2状态后,2状态没有与b字符有关的操作。