湖大OJ-实验B----CFG是P成员
实验B----CFG是P成员 |
Time Limit: 1000ms, Special Time Limit:2500ms, Memory Limit:32768KB |
Total submit users: 279, Accepted users: 264 |
Problem 12596 : No special judgement |
Problem description |
上下文无关文法CFG G是否派生某个串W。采用动态规划(Dynamic Programming)设计一个一个多项式时间的验证算法。 |
Input |
输入第一行为一个正整数n,接下来n行为一个满足乔姆斯基范式的文法描述。然后一个正整数m,接下来m行为m个小写字母组成的字符串(长度小于100) 表示m个待测试的串。 |
Output |
对于每一个测试串返回"yes"或者"no",表示该串是否能被文法派生出来。 |
Sample Input |
4 S->AB A->AB|a B->BC|b C->CA|CC|c 3 ab ac bc |
Sample Output |
yes no no |
1、算法设计思路
A: 找出所有A->b型规则
B: 找出所有A->BC型规则
C: 考察每一个长为1的子串
D: 考察L长度的子串
E: 检查起始变元是否在table[0][n]中
2、实验总结
(1)刚开始用栈和转移矩阵来进行状态的切换,想要先把CFG转换为一个PDA,在根据PDA接受串的情况来判断(因为题目要求线性时间复杂度),后来发现转换成PDA的时候,每一个转移矩阵里应该是一个状态的集合。
(2)原本定义一个set<int> S来表示转移矩阵,但是觉得这样还不如直接用原来的CFG按照某种规则来进行替换,让他的复杂度为线性的。具体线性替换的顺序在2中详细介绍了
3、AC代码
#include<cstdlib> #include<iostream> #include<fstream> using namespace std; int main() { int n,m; //n行满足乔姆斯基范式的文法描述, m行待测字符串 while(cin>>n) { string *cfg; //CFG文法描述 cfg=new string[n]; for(int i=0;i<n;i++) cin>>cfg[i]; //输入CFG文法 cin>>m; //输入待测字符串数量 string *str; //待测字符串 str=new string[m]; for(int i=0;i<m;i++) cin>>str[i]; //输入待测字符串 int *lstr; //待测字符串的长度 lstr=new int[m]; for(int i=0;i<m;i++) lstr[i]=str[i].length(); string ***table; //定义表格 table=new string**[m]; for(int i=0;i<m;i++) table[i]=new string*[lstr[i]]; for(int i=0;i<m;i++) for(int j=0;j<lstr[i];j++) table[i][j]=new string[lstr[i]]; //找出所有A->b型规则 string Ab[100]; int num=0; for(int i=0;i<n;i++) { for(int k=3;k<cfg[i].length();k++) { if(cfg[i][k]>96 && cfg[i][k]<123) { Ab[num]+=cfg[i][0]; Ab[num]+=cfg[i][k]; num++; } } } //找出所有A->BC型规则 string ABC[100]; int num2=0; for(int i=0;i<n;i++) { for(int k=3;k<cfg[i].length();k++) { if(cfg[i][k]<91) { ABC[num2]+=cfg[i][0]; ABC[num2]+=cfg[i][k]; ABC[num2]+=cfg[i][k+1]; k++; num2++; } } } //考察每一个长为1的子串 for(int i=0;i<m;i++) { for(int j=0;j<lstr[i];j++) //考察每一个长为1的子串 { for(int k=0;k<num;k++) { if(str[i][j]==Ab[k][1]) //考察CFG文法的每一个字符 table[i][j][j]=Ab[k][0]; } } } //考察l长度的子串 for(int i=0;i<m;i++) { for(int l=2;l<=lstr[i];l++) //l是子串的长度 { for(int p=0;p<lstr[i]-l+1;p++) //p是子串的起始位置 { int j=p+l-1; //j是子串的结束位置 for(int k=p;k<j;k++) //k是分裂的位置 k<=j-1 ->k<j { for(int q=0;q<num2;q++) { if(table[i][p][k].find(ABC[q][1],0)!=string::npos && table[i][k+1][j].find(ABC[q][2],0)!=string::npos) { table[i][p][j]+=ABC[q][0]; } } } } } } //检查起始变元是否在table[0][n]中 for(int i=0;i<m;i++) { int flag=0; if(table[i][0][lstr[i]-1].find(cfg[0][0],0)!=string::npos) flag=1; if(flag==1) { cout<<"yes"<<endl; } else { cout<<"no"<<endl; } } delete []cfg; delete []str; for(int i=0;i<m;i++) for(int j=0;j<lstr[i];j++) delete []table[i][j]; for(int i=0;i<m;i++) delete []table[i]; delete []table; } return 0; }