实验B----CFG是P成员

来源:互联网 发布:淘宝电商代运营靠谱吗 编辑:程序博客网 时间:2024/05/20 01:44

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">Problem description</span>
上下文无关文法CFG G是否派生某个串W。采用动态规划(Dynamic Programming)设计一个一个多项式时间的验证算法。
  实验方法:
  编写一个算法/程序,对于给定的输入,可以在多项式时间内判定ACFG
  实验结果:
  交一个程序验证。

Input输入第一行为一个正整数n,接下来n行为一个满足乔姆斯基范式的文法描述。然后一个正整数m,接下来m行为m个小写字母组成的字符串(长度小于100) 表示m个待测试的串。Output对于每一个测试串返回"yes"或者"no",表示该串是否能被文法派生出来。Sample Input
4S->ABA->AB|aB->BC|bC->CA|CC|c3abacbc
Sample Output
yesnono

解题思路:利用动态规划填表(n*n的对角线,n为待测串儿的长度)。在规则中有两种规则分别是A->XY和A->a,于是遍历规则,分成两个规则数组rule1和rule2,目的是为了方便查找。

在填表时,一个格子里可能会有多个变量,比如A->a,B->a,那么A和B都要填在一个格子里;同时,也可能会重复出现,如:A->AB|CB,而现在的表正好是:

A,CA,A B  所以A会在一个格子里出现多次。

为了解决“判重”以及多个变量需要开三维char型数组(占很大的内存)的问题,可以采用特征串。只需要一个一维的int数组,大小为26,对应26个字母是有(1)还是无(0)。所以对于表格,只需要一个二维整型数组int T[n][n](n是待测串的长度)。

填表的时候通过位或操作:T[j][j]|=(1<<(rule1[p][0]-'A'));

查找特征串表示的字母时用按位与操作:(T[p][k]&(1<<(zimu-'A')))==(1<<(zimu-'A')) 判断zimu是否在T[p][k]表示的特征串中。

代码如下:

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<fstream.h>int main(){freopen("testfile.txt","r",stdin);//输入的处理int n,m;char rules[30][100];char rule1[30][100];char rule2[30][30];char test[300][300];//待测的串int T[300][300];//动态规划所填的表char right[1000];//用来保存格子里的大写字母(会不止一个,所以用数组)char left[1000];int sizer,sizel;//记录表的一个格子里有几个大写字母int i,j,p,q,k,l,w,z,r,index;//length表示一条规则的长度。//len表示待测串的长度//size1表示一条规则里可能会有多少个终结符//size2表示一条规则里可能会有多少对两个大写字母int length,size1,size2,len;scanf("%d",&n);//n条规则for(i=0;i<n;i++)scanf("%s",&rules[i]);scanf("%d",&m);//m个待测的串for(i=0;i<m;i++){scanf("%s",&test[i]);}for(i=0;i<n;i++)//第i行规则{length=strlen(rules[i]);//printf("第一条规则的长度是:%d\n",length);size1=1;size2=1;for(j=3;j<length;j++){//查找A->a这种派生,并放入rule1中if(rules[i][j]>='a'&&rules[i][j]<='z')//如果在第i行规则中第j个位置找到了终结符{rule1[i][0]=rules[i][0];rule1[i][size1++]=rules[i][j];//printf("!!!%c\n",rule1[i][size1-1]);}rule1[i][size1]='\0';//查找S->AB这种派生,并放入rule2中if(rules[i][j]>='A'&&rules[i][j]<='Z'&&rules[i][j+1]>='A'&&rules[i][j+1]<='Z')//如果有连续两个大写字母{rule2[i][0]=rules[i][0];rule2[i][size2++]=rules[i][j];rule2[i][size2++]=rules[i][j+1];}rule2[i][size2]='\0';}//printf("%d\n",size1);//printf("%d\n",size2);/*//测试rule1的值for(j=0;j<size1;j++){printf("%c",rule1[i][j]);}printf("\n");//测试rule2的值printf("%c",rule2[i][0]);for(j=1;j<size2;j+=2){printf("%c",rule2[i][j]);printf("%c",rule2[i][j+1]);}printf("\n");*/}//上述代码已经把规则分别保存到了rule1和rule2中//下面开始用位或来填表int S=1<<(rules[0][0]-'A');//用数字代替字符,统一转化标准for(i=0;i<m;i++)//先处理第一个串儿{//初始化表Tfor(k=0;k<300;k++)for(p=0;p<300;p++)T[k][p]=0;//先填对角线len=strlen(test[i]);//第一个待测串的长度为lenfor(j=0;j<len;j++)//从第一个待测串的第一个小写字母开始{for(p=0;p<n;p++)//第p行规则{size1=strlen(rule1[p]);//printf("size1: %d",size1);for(q=1;q<size1;q++){//printf("......%c\n",rule1[0][1]);if(test[i][j]==rule1[p][q])//在第P行规则的q位置找到终结符{T[j][j]|=(1<<(rule1[p][0]-'A'));//按位或避免 判重 !如果在多行规则里都能找到一样的终结符//printf("p is:%d !!%d\n",p,T[j][j]);//printf("...%c\n",rule1[p][q]);break;}}}/*for(k=0;k<len;k++)printf("%d\n",T[k][k]);*/}//以上填好了斜对角线//下面填其他的对角线了!for(l=1;l<len;l++)//剩余多少条对角线{//k=0;//for(p=0,q=l+1;p<len-l-1,q<=len-l;p++,q++)//要填的格子是T[p][q]for(p=0;p<=len-l-1;p++){q=p+l;//k=0;//用于分裂位置//找存在的字母char zimu;//while(k>=p&&(k+1)<=q){for(k=p;k>=p&&(k+1)<=q;k++){sizel=0;sizer=0;for(zimu='A';zimu<='Z';zimu++)//把T[p][k]与T[k+1][q]中的所有字母都放在了left和right中{//printf("zimu 开始是:%c\n",zimu);if((T[p][k]&(1<<(zimu-'A')))==(1<<(zimu-'A')))//在T[p][k]中找到了大写字母zimu{//printf("T[%d][%d]:%d\n",p,k,T[p][k]);//printf("current zimu is:%c\n",zimu);left[sizel++]=zimu;//printf("left zimu is:%c\n",zimu);}if((T[k+1][q]&(1<<(zimu-'A')))==(1<<(zimu-'A')))//在T[k+1][q]中找到了大写字母zimu{//printf("T[%d][%d]:%d\n",k+1,q,T[k+1][q]);//printf("current zimu is:%c\n",zimu);right[sizer++]=zimu;//printf("right zimu is:%c\n",zimu);}}for(w=0;w<sizer;w++)for(z=0;z<sizel;z++){for(r=0;r<n;r++)//第r行规则{size2 = strlen(rule2[r]);for(index=1;index<size2;index+=2)if((left[z]==rule2[r][index])&&right[w]==rule2[r][index+1]){T[p][q]|=(1<<(rule2[r][0]-'A'));}}}}//printf("T[%d][%d]:%d\n",p,q,T[p][q]);}}if((T[0][len-1]&S)==S)//如果找到Sprintf("yes\n");elseprintf("no\n");}return 0;}


通过这次实验,不仅接触了位操作的便利,也体会到了文件读操作的简便。

#include<fstream.h>

freopen("testfile.txt","r",stdin);

实现直接从testfile.txt中读所有的输入,代码里正常的scanf就行,只是不从dos界面获取输入,转而从文件中获取所需的输入!




2 0