这题就是出题人把自己的编译原理大作业拿出来做了题目吧
这题的代码量和逻辑量实在有点大,这要真是考试在2个小时很难A出来,但这题貌似是2013年左右的机试真题,那时候貌似是3小时4道题?
首先说一下这题的逻辑,我是按照编译原理手算模拟的,反正一共不超过10个式子,怎么暴力怎么来,
只要当前有一点更新,就再进行一次while循环,反正时间复杂度有的是
但是这题真的数据挺强的,大家没过的可以拿下面这样例试试
4
A BCDe
B CD
C D
D #
答案是
A e
B #
C #
D #
是的,对于一个非终结符是否真的含有#要十分慎重,不能因为右侧有一个非终结符就把它的first集合并入到左侧的first集合中
我就是在这里卡了好久好久
总结一下:此题无它,费时费力而已。
另外由于我这是大暴力,所以即便有左递归什么的也没关系,大家写的时候注意下你们有没有解决左递归问题(测试数据有没有我不知道)
#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<sstream>
#include<map>
using namespace std;
string a[10],b[10];
int main(){
int num;
std::ios::sync_with_stdio(false);
while(cin>>num){
bool vis[26][26];
bool chuxian[26];
memset(vis,0,sizeof(vis));
memset(chuxian,0,sizeof(chuxian));
for(int i=0;i<num;i++){
cin>>a[i]>>b[i];
chuxian[a[i][0]-'A']=1;
}
vector<char>v[26];
for(int i=0;i<26;i++)
v[i].clear();
//第一次预处理
for(int i=0;i<num;i++){
if((b[i][0]>='a'&&b[i][0]<='z')||b[i][0]=='#'){ //如果如A->a,上来就遇到终结符的,直接加入
v[a[i][0]-'A'].push_back(b[i][0]);
}
else{ //如A->BCD之类的,暂时只考虑A的first与B相同,CD在第二次处理再考虑
// vis[b[i][0]-'A'][a[i][0]-'A']=1;
vis[a[i][0]-'A'][b[i][0]-'A']=1;
}
}
// 第一次正式处理
while(true){
bool jieshu=true;
for(int i=0;i<26;i++){
for(int j=0;j<26;j++){
if(vis[i][j]==1&&i!=j){ //如果i的first集合包含j的first集合
for(int k=0;k<v[j].size();k++){ //暂时不考虑#,因为并不知道在B能退出#的情况下,是A->B还是A->BC(假定C不能推出#)
if(v[j][k]!='#'&&find(v[i].begin(),v[i].end(),v[j][k])==v[i].end()){
v[i].push_back(v[j][k]);
jieshu=false;
}
}
}
}
}
if(jieshu)
break;
}
// for(int i=0;i<26;i++){
// if(chuxian[i]){
// // cout<<v[i].size()<<endl;
// cout<<char('A'+i);
// cout<<v[i].size()<<endl;
// }
// else{
// continue;
// }
// }
// 第二次处理
while(true){
bool jixu=false;
for(int i=0;i<num;i++){
for(int j=0;j<b[i].size();j++){
if(b[i][0]=='#'){ //本身就能推出#的,直接加入
if(find(v[a[i][0]-'A'].begin(),v[a[i][0]-'A'].end(),'#')==v[a[i][0]-'A'].end()){
v[a[i][0]-'A'].push_back('#');
jixu=true;
}
break;
}
//遇到非终结符了,之后的不论什么都不在继续求了,求first到此为止
if((b[i][j]>='a'&&b[i][j]<='z')){
if(find(v[a[i][0]-'A'].begin(),v[a[i][0]-'A'].end(),b[i][j])==v[a[i][0]-'A'].end()){
v[a[i][0]-'A'].push_back(b[i][j]);
jixu=true;
}
break;
}
//当前是非终结符,且该非终结符不含有#
if(find(v[b[i][j]-'A'].begin(),v[b[i][j]-'A'].end(),'#')==v[b[i][j]-'A'].end()){
for(int k=0;k<v[b[i][j]-'A'].size();k++){ //B的first都是A的first的一部分
if(find(v[a[i][0]-'A'].begin(),v[a[i][0]-'A'].end(),v[b[i][j]-'A'][k])==v[a[i][0]-'A'].end()){
v[a[i][0]-'A'].push_back(v[b[i][j]-'A'][k]);
jixu=true;
}
}
break;
}
else{
//含有#,此时看当前是否是右侧表达式的最后一个
//例如当前是C,如果是A->BC,则加入#到A中
//如果是A->BCD,则不能加入#到A中,因为还要看D才能确定
if(j==b[i].size()-1){
if(find(v[a[i][0]-'A'].begin(),v[a[i][0]-'A'].end(),'#')==v[a[i][0]-'A'].end()){
v[a[i][0]-'A'].push_back('#');
jixu=true;
}
}
for(int k=0;k<v[b[i][j]-'A'].size();k++){
if(v[b[i][j]-'A'][k]!='#'&&find(v[a[i][0]-'A'].begin(),v[a[i][0]-'A'].end(),v[b[i][j]-'A'][k])==v[a[i][0]-'A'].end()){
v[a[i][0]-'A'].push_back(v[b[i][j]-'A'][k]);
jixu=true;
}
}
}
}
}
if(jixu==false)
break;
}
//如果非终结符在式子左侧曾经出现过,那么就打印出来
for(int i=0;i<26;i++){
if(chuxian[i]){
// cout<<v[i].size()<<endl;
cout<<char('A'+i);
// cout<<v[i].size()<<endl;
}
else{
continue;
}
sort(v[i].begin(),v[i].end());
if(v[i][0]=='#'){
for(int j=1;j<v[i].size();j++){
cout<<" "<<v[i][j];
}
cout<<" #"<<endl;
}
else{
for(int j=0;j<v[i].size();j++){
cout<<" "<<v[i][j];
}
cout<<endl;
}
}
}
}
// 4
// A BCDe
// B CD
// C D
// D #