Uva 1596

来源:互联网 发布:qq群发软件免费 编辑:程序博客网 时间:2024/06/03 23:43

一.题目

题目链接: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;  //用来记录某个数组的sizemap<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;}


原创粉丝点击