北邮OJ first集

这题就是出题人把自己的编译原理大作业拿出来做了题目吧

这题的代码量和逻辑量实在有点大,这要真是考试在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 #

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值