学习词法分析,要理解并牢记下面的转换图
子集构造法
#include<stdio.h>
#include<string.h>
struct NFA{
int d1;
char b;
int d2;
};
struct NFA nfa[25];
char T[100]; //边的类型
int temp[100]; //记录到达结点
int n = 0;//nfa总的边数
struct DFA{
int list[100];
int length;
};
struct DFA dfa[25];
int main()
{
int i = 0,k = 0,dfacount = 0;//n为nfa的边数
printf("请输入边上的值,空边除外\n");
scanf("%s",&T);
//printf("%s",T);
printf("请输入每条边和节点\n例如:1a8表示0从1边到8,*代表空边以0 / 0结束输出\n");
while(1)
{
scanf("%d",&nfa[i].d1);
scanf("%c",&nfa[i].b);
scanf("%d",&nfa[i].d2);
if(nfa[i].d1 == 0 &&nfa[i].b== '/' &&nfa[i].d2== 0 )
break;
i++;
n++;
}
temp[k] = nfa[0].d1;
k++;
// for(int j = 0;j<i;j++)
// {
// printf("%d %c %d\n",nfa[j].d1,nfa[j].b,nfa[j].d2);
// }
for(int i=0;i<n;i++)//遍历nfa的所有边
{
if(nfa[i].d1==nfa[0].d1)//如果为初始状态
{
if(nfa[i].b=='*')//并且为空边时
{
temp[k]=nfa[i].d2;//最终得到初始状态集 (没有求闭包之前)
k++;
}
}
}
for(int i = 0;i<k;i++)//求闭包
{
for(int j = 0;j<n;j++)
{
if(temp[i] == nfa[j].d1)
if(nfa[j].b == '*')//遍历集合里的所有元素,看看每个元素经过空边都达到了哪些结点
{
int b = 0;
for(int ii=0;ii<k;ii++)//看到达的结点是否在集合中出现过
{
if(temp[ii]==nfa[j].d2)
b=1;
}
if(b==0)//如果没有出现过将其加入到状态集中
{
temp[k] = nfa[j].d2;
k++;
}
}
}
}
// for(int i=0;i<k;i++)
// {
// printf("%d ",temp[i]);
// }
for(int i = 0;i<k;i++)
{
dfa[dfacount].list[i] = temp[i];
}
dfa[dfacount].length = k;
k = 0;
dfacount++;
//printf("%d",dfacount);
for(int i=0;i<dfacount;i++) //开始循环
{
for(int j=0;j<strlen(T);j++)
{
k = 0;//相当于temp数组为空
for(int x=0;x<dfa[i].length;x++)//a_n为状态集的长度 得到temp数组
for(int y=0;y<n;y++)/*number of NFA edage*/
{
if(dfa[i].list[x] == nfa[y].d1)
if(nfa[y].b == T[j])//遍历集合里的所有元素,看看每个元素经过特定边都达到了哪些结点
{
int b=0;
for(int ii=0;ii<k;ii++)//看到达的结点是否在集合中出现过
{
if(temp[ii]==nfa[y].d2)
b=1;
}
if(b==0)//如果没有出现过将其加入到状态集中
{
temp[k]=nfa[y].d2;
k++;
}
}
}
// for(int m = 0;m<k;m++)
// printf("%d ",temp[m]);
if(k!=0)//考虑是否要求temp的闭包
{
//看看当前的状态集是否与原来的相同
int pp=0;
//先求 closure再
//遍历所有的状态集
//如果没有重复,将状态加入状态中并状态集的总数加一
//并打印输出DFA
for(int bi = 0;bi<k;bi++)//求闭包
{
for(int bj = 0;bj<n;bj++)
{
if(temp[bi] == nfa[bj].d1)
if(nfa[bj].b == '*')//遍历集合里的所有元素,看看每个元素经过空边都达到了哪些结点
{
int b = 0;
for(int ii=0;ii<k;ii++)//看到达的结点是否在集合中出现过
{
if(temp[ii]==nfa[bj].d2)
b=1;
}
if(b==0)//如果没有出现过将其加入到状态集中
{
temp[k] = nfa[bj].d2;
k++;
}
}
}
}
printf("{");
for(int q = 0;q<dfa[i].length;q++)
{
if(q == 0)
printf("%d",dfa[i].list[q]);
else
printf(",%d",dfa[i].list[q]);
}
printf("}");
printf("——%c ——》",T[j]);
printf("{");
for(int q = 0;q<k;q++)
{
if(q == 0)
printf("%d",temp[q]);
else
printf(",%d",temp[q]);
}
printf("}");
printf("\n");
for(int r=0;r<dfacount;r++)//判断是否要加入最左一列
{
if(dfa[r].length == k)
{
int e = 0,ecount = 0;
for(int q = 0;q<k;q++)
{
for(int qq = 0;qq<k;qq++)//找完一遍
{
if(dfa[r].list[q] == temp[qq])
ecount++;
}
}
if(ecount == k)
{
pp =1;
break;
}
}
}
if(pp == 0)
{
for(int i = 0;i<k;i++)
{
dfa[dfacount].list[i] = temp[i];
}
dfa[dfacount].length = k;
dfacount++;
}
}
}
}
return 0;
}
使用该代码的注意事项:
结点须为数字,边为单个字符
该程序的缺点是:输入每条边和节点的第一行数据的第一个结点必须为nfa的开始结点
例如:
样例1
abc
1a2 //1为nfa的开始结点
1*4
1a3
2a2
2b4
3c3
3c4
0/0
下面是样例:
样例1:
NFA:
NFA转DFA的过程:
DFA:
程序实现:
样例2:
NFA:
NFA转DFA的过程:
DFA:
程序实现: