【刘汝佳书】习题5-9 UVA1596(用stack解决嵌套)

【2019.4.10】

思路:
输入数据一共有两种语句:
1、声明语句:形式如:数组名[数组大小]
其中数组名为一个char型字母,数组大小为一个int型数字

2、赋值语句:形式如:数组名[数组下标] = 赋值数字
其中【数组下标】和【赋值数字】都是int型数字,但其输入形式有可能为:
12
a[1]
a[b[0]]
b[a[a[0]]]
……
诸如此类的多层嵌套,需要从最内层一直计算到最外层,才能得到最终的数字

过程:
因此,当程序每输入一行字符串时,判断这行字符串是否含有等号=
1、如果无等号,证明为声明语句,用map<char, int>可以记录下这个数组的大小
2、如果有等号,证明为赋值语句,此时需要将等号左边的【数组下标】和等号右边的【赋值数字】从字符串形式变成int型数字

提取数字过程:
假设我们需要将字符串c[b[a[0]]]转变为数字,那我们的做法应该是:
① 计算最内层:取出a[0]的值x1 = a[0]
② 计算倒数第二层:取出b[x1]的值x2 = b[x1]
③ 计算最外层:取出c[x2]的值x3 = c[x2],最终x3就是我们要求的数字

这里我想用栈解决,因为如果从头遍历字符串c[b[a[0]]],可以看到:
读入字符的顺序为c、b、a,但计算的顺序为a、b、c,这种先入后出的结构很适合用栈存储
那么过程如下:
1、 从头扫描字符串,如果遇到字符,就压入字符栈stack<char>
2、 如果遇到数字,就压入数字栈stack<int>
一旦遇到数字,后面必然都只剩']'字符了,此时停止遍历,开始计算
3、 计算过程
① 每次从字符栈stack<char>中取出一个字母(数组名),从数字栈stack<int>中取出一个数字(下标)
② 检查下标合法性:下标是否越界、数组名[下标]是否还未赋值
③ 若下标合法,则根据数组名和下标取出相应的值,压入数字栈stack<int>
④ 若下标不合法,标记flag = true,直接返回
⑤ 上述过程若一直合法,则持续到字符栈为空为止,此时数字栈中只有一个数字,就是原始字符串转化成的数字

代码:

#include <iostream>
#include <map>
#include <stack>
#include <string>
#include <cctype>

using namespace std;

string s;
int line;
bool flag;

map<char, map<int, int>> arr; 	//<数组名, <下标,值>>
map<char, int> len;				//<数组名, 数组长度>
stack<char> name;	//字符栈(存数组名)
stack<int> index;	//数字栈(存数组下标)

//函数作用:
//假设赋值语句字符串的结构为:数组名[Index]=Num
//如果l和r分别为字符串中【Index】部分的上下界,那么函数会将Index转化为int
//如果l和r分别为字符串中【Num】部分的上下界,那么函数会将Num转化为int
//转化得到的int数值存在index栈中
//如果遇到异常情况,则置flag=true
void getIndexOrNum(int l, int r)
{
    //从上界遍历到下界
    for(int i = l; i < r; i++) {
        //遇到字母,证明是数组名,压入name栈
        if(isalpha(s[i])) {
            name.push(s[i]);
        }
        //遇到数字,证明是嵌套最内层
        else if(isdigit(s[i])) {
            //将数字转化为int类型
            int num = 0;
            while(isdigit(s[i])) {
                num *= 10;
                num += s[i]-'0';
                i++;
            }

            //将数字压入index栈中
            index.push(num);
            break;
        }
    }

    //从嵌套最内层逐渐得到最外层的值
    char c;
    int ind;
    while(!name.empty()) {
        //c为当前层的数组名
        c = name.top();
        name.pop();
        //ind为数组c的下标
        ind = index.top();
        index.pop();

        //检查下标是否越界
        if(ind<0 || ind>=len[c]) {
            flag = true;
            return;
        }
        //检查c[ind]元素是否还未赋值
        if(!arr[c].count(ind)) {
            flag = true;
            return;
        }
        //将c[ind]元素的值压入index栈中
            index.push(arr[c][ind]);
        }
}

void init()
{
    line = 1;
    flag = false;
    arr.clear();
    len.clear();
    while(!name.empty())  name.pop();
    while(!index.empty()) index.pop();
}

int main()
{
    while(getline(cin, s) && s!=".") {
        init();		//初始化

        do {
            //可以用eq判断字符串s中是否有等号
            int eq = s.find('=');

            //declaration,声明语句无等号
            if(eq < 0) {
                //获取【数组大小】
                int length = 0;
                for(int i = 2; s[i] != ']'; i++) {
                    length *= 10;
                    length += s[i]-'0';
                }
                //声明数组,记录该数组的大小
                len[s[0]] = length;
            }
            //assignment,赋值语句有等号
            else {
                //获取等号右边的值
                getIndexOrNum(eq+1, s.length());
                if(flag) break;	//不合法则退出
                int right = index.top();    //从index栈中取出等号右边的数值
                index.pop();

                //获取等号左边的数组下标
                getIndexOrNum(1, eq);
                if(flag) break;
                char arrayleft = s[0];      //s[0]为待赋值的数组名
                int indexleft = index.top();//从index栈中取出等号左边的数组下标数值
                index.pop();

                //判断等号左边待赋值数组的下标是否越界
                if(indexleft<0 || indexleft>=len[arrayleft]) {
                    flag = true;
                    break;
                }

                //给数组arrayleft赋值
                arr[arrayleft][indexleft] = right;
            }

            if(flag) break;

            //获取下一行输入
            getline(cin, s);
            //行数++
            line++;
        } while(s != ".");

        //如果提前结束,除去多余行
        while(s != ".") getline(cin, s);

        //输出
        if(flag) cout<<line<<endl;
        else     cout<<"0"<<endl;
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值