词法分析☞NFA语言识别
Description
对于给出的NFA和输入的字符串,判断字符串是否是NFA识别的语言。
输入
输入有多组数据。每组数据的第一行是两个整数N(N<=50)和M(M<=27),表示NFA有N个状态,以及字母表有M-1个字符。
NFA的N个状态用整数0~N-1表示,状态0为起始状态。字母表包含小写英文字母的前M-1个字符。
接下来的N行,每行有M个整数集(用’{‘和’}’括起)。其中,第i行第1列的整数集表示在状态i-1时,对应于є(空串)的状态迁移;
第i行第j(j>1)列的整数集,表示NFA在状态i-1,当输入符号为第j-1个小写字母时,迁移到的状态集。
接下来的一行包含若干个整数,代表NFA的接受状态,这一行以-1结尾。
接下来的每一行是一个待识别的字符串,字符串的长度在1到50之间且只含有小写字母。字符串”#”代表本组数据结束。
N=M=0表示输入结束。
输出
对于每个待识别的字符串,如果能被给出的NFA识别,输出YES;否则输出NO
分析
NFA匹配和DFA的匹配思路相似。对于每个输入的字符串,找到其最后可能出现的状态,然后再判断最后的状态中是否存在接受状态
NFA和DFA的最主区别在于,对于DFA来说,在当前状态接受到某个字母后能到达的状态是唯一且确定的
但是对于NFA来说,对每个状态,当输入一个字母的时候,NFA能够达到的状态的个数时不确定的,即不确定性有限状态机的特性。
因此,在处理NFA的输入字符时,对于每个状态,读取一个字符后,需要确定出所有状态,因此,需要一个数组来记录所有能够到达的状态
值得注意的是,由于NFA允许存在空字符串,所以,在对开始状态进行读取一个字母的时候,需要先找到开始状态经过空字符串所能达到的所有状态作为其真正的开始状态。
而在每次读入一个字符,找到其NFA可能到达的状态后,还需要找到所有到达状态经过空字符串能够到达的状态作为真正的到达状态
以下图为例
![]()
起始的状态集合应该是{0,1,2,4,7},而对于状态“2”,当接收一个’a’时,它能够到达的状态应该是{6, 1, 7},其中,状态1和7是经过空串到达的。因此,需要特别注意,处理完读入字符后的直接到达状态后,还需要找到所有间接到达状态(直接到达状态经过一个或多个空串能够到达的状态)
上代码
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
void getInput(int N, int M);
void print(int N, int M);
int trans[50][27][51]; // 转换函数,第三维的第一位用来记录接受状态的个数
int main() {
int M, N, accept_num;
char alphabet[27]; // 字母表
int accept[51]; // 记录接受状态
int finall_state[50]; // 记录每一步的状态
int next_state[50]; // 记录下一步的可能状态.
int finall_state_num, next_state_num;
string input; // 输出的字符串
bool beAccepted; // 标记是否被接受
// 初始化字母表
alphabet[0] = 'A';
for (int i = 1; i < 27; i++) {
alphabet[i] = 97+i;
}
// 读入N和M
// 判断N和M的值是否为0 直到为0时结束
while ((cin >> N >> M) && N != 0 && M != 0) {
// 输入转换函数
memset(trans, 0, sizeof(trans));
getInput(N, M);
// 输入接受状态
for (accept_num = 0; accept_num < 51; accept_num++) {
cin >> accept[accept_num];
if (accept[accept_num] == -1) break;
}
// 开始匹配输入的字符串
while (1) {
int tmp_state;
cin >> input;
beAccepted = false;
// 判断是否输入结束
if (input == "#") break;
// 初始化第一个状态
memset(finall_state, 0, sizeof(finall_state));
finall_state_num = 1;next_state_num = 0;
// 找到该字符串的最后一个状态
for (int i = 0; i < input.size(); i++) {
for (int j = 0; j < finall_state_num; j++) {
// 加入走空串能到达的状态
for (int k = 0; k < trans[finall_state[j]][0][0]; k++) {
tmp_state = trans[finall_state[j]][0][k+1];
bool hasbeentrans = false; // 用于判断当前状态是否查找过空串可到达状态
for (int x = 0; x < finall_state_num; x++) {
if (finall_state[x] == tmp_state) hasbeentrans = true;
}
// 如果没有查找过,则加入队列
if (hasbeentrans == false) {
finall_state[finall_state_num++] = tmp_state;
}
}
// 查找读入当前字符能到达的状态
for (int k = 0; k < trans[finall_state[j]][input[i]-96][0]; k++) {
tmp_state = trans[finall_state[j]][input[i]-96][k+1];
bool hasbeenfind = false;
for (int x = 0; x < next_state_num; x++) {
if (tmp_state == next_state[x]) hasbeenfind = true;
}
if (hasbeenfind == false) {
next_state[next_state_num++] = tmp_state;
}
}
}
// 将当前状态赋值给finall_state
for (int x = 0; x < next_state_num; x++) {
finall_state[x] = next_state[x];
}
finall_state_num = next_state_num;
next_state_num = 0;
}
// 最后状态再走一次空串
for (int j = 0; j < finall_state_num; j++) {
int tmp_state;
for (int k = 0; k < trans[finall_state[j]][0][0]; k++) {
tmp_state = trans[finall_state[j]][0][k+1];
bool hasbeentrans = false; // 用于判断当前状态是否查找过空串可到达状态
for (int x = 0; x < finall_state_num; x++) {
if (finall_state[x] == tmp_state) hasbeentrans = true;
}
// 如果没有查找过,则加入队列
if (hasbeentrans == false) {
finall_state[finall_state_num++] = tmp_state;
}
}
}
// 判断该状态是否为接受状态
for (int i = 0; i < accept_num; i++) {
for (int j = 0; j < finall_state_num; j++) {
if (finall_state[j] == accept[i]) {
beAccepted = true;
break;
}
}
}
if (beAccepted == true) {
cout << "YES\n";
} else {
cout << "NO\n";
}
}
}
return 0;
}
void getInput(int N, int M) {
string tmp;
int num;
int index;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
cin >> tmp;
num = -1; // 用来读取接受状态
index = 1;
for (int k = 0; k < tmp.size(); k++) {
if (tmp[k] >= '0' && tmp[k] <= '9') {
if (num == -1) {
num = tmp[k] - '0';
} else {
num = num*10+tmp[k]-'0';
}
} else {
if (num != -1) {
trans[i][j][0] += 1;
trans[i][j][index++] = num;
num = -1;
}
}
}
trans[i][j][0] = index-1;
}
}
}
void print(int N, int M) {
int index;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
index = trans[i][j][0];
for (int k = 0; k <= index; k++) {
cout << trans[i][j][k] << ' ';
}
cout << endl;
}
cout << endl;
}
}