Uva 1596 - Bug Hunt

一.题目

题目链接:Uva1596


二.思路

因为涉及到多个数组的嵌套,两个最近的[]是一对,先进先出,因此很明显是要用到栈的。我是用双栈来完成嵌套数组的处理的,核心思想就是从最里层的数组开始,计算出它的下标和数组名,判断是否有错误,有错误直接返回,没错误就继续让当前数组名该下标下的值成为下一个下标,然后获取新的数组名,直到栈空。数组的存储我是用map<string,map<int,int>>,来实现的,其中里层的map实现下标到值的映射,这样子判断一个下标是否已初始化就用map的count就可以实现,很快。之前其实使用map<string,vector<int>>来存取的,但是下标的最大值有2^31-1,判断是否初始化的时候直接TLE。。。还需要注意的是等式左右边进行判断的时候有小不同,左边外层不用判断是否初始化,右边需要,详情看代码注释。


三.程序源代码

#include <algorithm>
#include <iostream>
#include <map>
#include <stack>
#include <stdio.h>
#include <vector>
using namespace std;
map<string, int> bound;  //用来记录某个数组的size
map<string, map<int, int> > elements;  //记录某个数组已经初始化了的下标和值
string line, name, lt, rt;
int limit, index1, index2;
stack<char> equation;  //用栈来保存类似a[b[c[0]]]这样的等式并判断是否正确
//判断下标是否越界,注意<0也是越界
bool isOverflow(string n, int pos) { return (pos >= bound[n] || pos < 0); }
//判断下标是否已初始化
bool isInit(string name, int pos) { return (elements[name].count(pos) != 0); }
bool isError(string e, int flag, int &num) {
    //flag参数用来判断是等号左边的数组还是右边的
    //这个num是当前等号右边的值,如果等号右边没错,左边也没错的话,那么要更新等号左边数组的值为num
  if ((flag == 1) && (e.find('[') == e.npos)) {
      //如果右边不是个数组就是一个数字,更新num值,返回
      //atoi函数是将一个c字符串转化成数字,头文件是stdio.h
    num = atoi(e.c_str());
    return false;
  }
  while (!equation.empty())//清空栈
    equation.pop();
  for (int i = 0; i < e.length(); i++)//将当前的嵌套数组入栈
    equation.push(e[i]);
  stack<char> st_temp;  //双栈
  string s_temp = "";
  int pos;
  char temp1, temp2;
  while (!equation.empty()) {//不空就循环
    temp1 = equation.top();   //弹出栈首
    equation.pop();
    if (temp1 != '[') //如果不是[,将该元素放到临时栈
      st_temp.push(temp1); //保存数组名字
    else {  //如果是,从临时栈找到第一个]与等式栈的[匹配,两个[中间的字符就是数组名字
      while (st_temp.top() != ']') {
        temp2 = st_temp.top();
        st_temp.pop();
        s_temp += temp2;
      }
      st_temp.pop(); //记得把]弹出
      if (isalpha(s_temp[0])) {  //因为最里层是一个数字,所以第一次的s_temp不是数组名而是下标,因此这里判断一下
        if (isOverflow(s_temp, pos) || !isInit(s_temp, pos))
        //如果有错,直接返回,没错。获取外层数组的下标
          return true;
        else
          pos = elements[s_temp][pos];
      } else  //第一次s_temp是数字,保存
        pos = atoi(s_temp.c_str());
      s_temp = "";
    }
}
  while (!st_temp.empty()) {//获取最外层的数组的名字
    temp2 = st_temp.top();
    st_temp.pop();
    s_temp += temp2;
  }
  if (flag == 1) {  //如果是等号右边的等式,有错直接返回,没错就更新num值给左边
    if (isOverflow(s_temp, pos) || !isInit(s_temp, pos))
      return true;
    else
      num = elements[s_temp][pos];
  }
  if (flag == 0) {  //如果是右边,有错直接返回,没错更新数组的值
    if (isOverflow(s_temp, pos))
      return true;
    else
      elements[s_temp][pos] = num;
  }
  return false;
}
int main() {
  int n = 0, temp = 0, err_pos = 0, out = 0;//out用于判断退出程序时用的
  bool flag = false;   //是否有错
  while (getline(cin, line)) {
    n++;
    if ((line[0] != '.') && !flag) {
        //如果一组数据里之前就有错了,那么剩下的都不用判断了,因为题目只要求记录第一次错误的行数
      if (line.find("=") == line.npos) {//如果line不含等号,说明是个声明语句
        index1 = line.find("[");
        name = line.substr(0, index1);
        limit = atoi(line.substr(index1 + 1, line.length() - index1 - 2)
                         .c_str()); //要把左右两边的[]排除掉
        bound.insert(make_pair(name, limit));
        map<int, int> temp;
        elements.insert(make_pair(name, temp));
    } else {//否则,将左右两边分离分别判断
        index1 = line.find("=");
        lt = line.substr(0, index1);
        rt = line.substr(index1 + 1, line.length() - index1 - 1);
        //要注意一定要先执行判断右边的函数,获取到了右边的值,再判断左边,如果左边没错就把这个值赋给它
        flag = (isError(rt, 1, temp) || isError(lt, 0, temp));
        if (flag)//如果有错,记得行号
          err_pos = n;
      }
    }
    if (line[0] == '.') {
        //如果是.,out+1,连续两个.会让out变成2,不连续则重置为0,是2就退出程序,
      out++;
      if (out == 2)
        break;
      if (flag)
        cout << err_pos << endl;
      else
        cout << 0 << endl;
        //记得把容器清空
      n = 0;
      err_pos = 0;
      flag = false;
      temp = 0;
      bound.clear();
      elements.clear();
      while (!equation.empty())
        equation.pop();
    } else
      out = 0;
  }
  return 0;
}


  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值