湖大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)设计一个一个多项式时间的验证算法。
  实验方法:
  编写一个算法/程序,对于给定的输入<g,w>,可以在多项式时间内判定ACFG
  实验结果:
  交一个程序验证。

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;
}
posted @ 2016-01-12 11:15  Just-Live  阅读(823)  评论(0编辑  收藏  举报