NFA识别语言
题目描述
对于给出的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。
输入样例
4 3
{} {0,1} {0}
{} {} {2}
{} {} {3}
{} {} {}
3 -1
aaabb
abbab
abbaaabb
abbb
#
0 0
输出样例
YES
NO
YES
NO
题目解析
由于是NFA,有两点情况需要尤其注意:
- 输入一个字符可能转移到多种状态,因此需要用一个数据结构维护在识别完第i个字符后可能会处于哪些状态
- 千万小心空串!因为在读入一个字符前可以先由空串转移好几个状态,然后再读入这个字符进行转移。
基于上述分析,在识别的时候我们采用如下步骤(以现在在识别第i个字符为例):
- 首先遍历识别完第i-1个字符可能处于的状态,对于每个状态都尝试进行识别空串的转移,并把能转移到的状态全部加入队列,直到没有新的可能状态加入队列为止,这是因为一个状态可以识别若干个空串转移到很远的一个状态,所以对于识别空串后转移的状态也需要再进行分析,看是否能转移到新的状态。将现在队列中所有的状态作为识别完第i-1个字符可能处于的状态。
- 现在开始识别第i个字符,将识别完第i个字符所有可能处于的状态保留下来,用于识别第i+1个字符。
在识别完所有的字符之后,我们还需要进行一次空串的转移判断,即识别完第n个字符后再去识别空串,看看还能转移到哪些状态。
最后,我们遍历最后保留下来的可能处于的这些状态,如果这其中有接收状态,我们就认为这个字符串可以被成功识别。
源代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
int n, m; //f[i][j][k]表示输入字母i时能从状态j迁移到状态k
bool f[28][51][51], end_state[51];
int main() {
while (1) {
scanf("%d%d", &n, &m);
if (n == 0 && m == 0) {
break;
}
memset(f, 0, sizeof(f));
memset(end_state, 0, sizeof(end_state));
//下面进行状态转移的预处理
string str;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> str; //注意!题目样例中的{}后面有空格的!
int len = str.length(), num = 0;
bool num_begin = false; //意味着是否开始识别数字
for (int k = 0; k < len; k++) {
if (str[k] >= '0' && str[k] <= '9') {
if (!num_begin) {
num_begin = true;
}
num = num * 10 + str[k] - '0';
}
else {
if (num_begin) {
num_begin = false;
f[j][i][num] = 1;
num = 0;
}
}
}
}
}
/*for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
for (int k = 0; k < n; k++) {
printf("f[%d][%d][%d] = %d\n", i, j, k, f[i][j][k]);
}
}
}*/
//状态转移输入结束
/*下面开始处理空串,即计算一个状态在遇到某一个字符时先经过若干个空串然后再
输入这个字符可以到达的状态集合*/
//下面开始预处理接收状态
int end;
while (1) {
scanf("%d", &end);
if (end == -1) {
break;
}
end_state[end] = true;
}
//预处理接收状态结束
//下面开始识别字符串,看是否可以识别
while (1) {
string input_str;
int possible_final_state[50];//记录有哪些状态是识别完这个当前字符可能处于的状态
int next_state[50];
int possible_final_state_num = 1, next_state_num = 0;;
int tmp_state;
cin >> input_str;
if (input_str[0] == '#') {
break;
}
memset(possible_final_state, 0, sizeof(possible_final_state));
int input_len = input_str.length();
for (int i = 0; i < input_len; i++) {
for (int j = 0; j < possible_final_state_num; j++) {
//下面开始进行空串的处理,首先找到当前状态可以通过空串走到哪些状态
for (int k = 0; k < n; k++) {
if (f[0][possible_final_state[j]][k] == 1) {
tmp_state = k;
bool hasbeentrans = false; // 用于判断当前状态是否查找过空串可到达状态
for (int l = 0; l < possible_final_state_num; l++) {
if (possible_final_state[l] == tmp_state) {
hasbeentrans = true;
}
}
// 如果没有查找过,则加入数组记录
if (hasbeentrans == false) {
possible_final_state[possible_final_state_num++] = tmp_state;
}
}
}
//空串处理结束
// 下面开始字符读入处理,查找读入当前字符能到达的状态
for (int k = 0; k < n; k++) {
//printf("f[%d][%d][%d] = %d\n", input_str[i] - 'a', possible_final_state[j], k, f[input_str[i] - 'a'][possible_final_state[j]][k]);
if (f[input_str[i] - 'a' + 1][possible_final_state[j]][k] == 1) {
tmp_state = k;
bool hasbeenfind = false;
for (int l = 0; l < next_state_num; l++) {
if (tmp_state == next_state[l]) {
hasbeenfind = true;
}
}
if (hasbeenfind == false) {
next_state[next_state_num++] = tmp_state;
}
}
}
}
//现在计算出的next_state就是读入当前字符后可能处于的所有状态
//我们把它赋值给possible_final_state,进行下一轮迭代
for (int k = 0; k < next_state_num; k++) {
possible_final_state[k] = next_state[k];
}
possible_final_state_num = next_state_num;
/*for (int l = 0; l < possible_final_state_num; l++) {
printf("%d ", possible_final_state[l]);
}
puts("");*/
//printf("possible_num = %d\n", possible_final_state_num);
next_state_num = 0;
}
//现在已经读入了所有的字符,我们再看看它再读入空串能到达哪些状态
for (int j = 0; j < possible_final_state_num; j++) {
int tmp_state;
for (int k = 0; k < n; k++) {
if (f[0][possible_final_state[j]][k] == 1) {
tmp_state = k;
bool hasbeentrans = false; // 用于判断当前状态是否查找过空串可到达状态
for (int l = 0; l < possible_final_state_num; l++) {
if (possible_final_state[l] == tmp_state) {
hasbeentrans = true;
}
}
// 如果没有查找过,则加入队列
if (hasbeentrans == false) {
possible_final_state[possible_final_state_num++] = tmp_state;
}
}
}
}
/*puts("-------------------");
for (int l = 0; l < possible_final_state_num; l++) {
printf("%d ", possible_final_state[l]);
}
puts("");*/
bool accepted = false;
for (int j = 0; j < possible_final_state_num; j++) {
//printf("!%d\n", possible_final_state[j]);
if (end_state[possible_final_state[j]] == true) {
accepted = true;
break;
}
}
if (accepted) {
puts("YES");
}
else {
puts("NO");
}
}
}
return 0;
}

浙公网安备 33010602011771号