一.题目
题目链接: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;
}