紫书刷题进行中,题解系列【GitHub|CSDN】
习题5-9 UVA1596 Bug Hunt(55行AC代码)
题目大意
给若干行数组定义和赋值,求第一个出现bug的行号(从1开始),bug判定规则如下:
- 数组下标超过定义限制,比如定义
a[10]
,那么a[0-9]
合法,其余均非法 - 需要引用数组的值时(即作为右值),它必须先被赋值,如下所示
a[10] // 定义数组a,长度10
a[1]=2 // 合法赋值
a[2]=a[1] //右边合法引用a[1]
a[a[1]]=3 // 左边合法引用a[1]
a[2]=a[4] 右边非法引用a[4]
a[a[4]]=3 // 左边非法引用a[4]
思路分析
本题关键在于数组名和下标表达式的分割和下标表达式计算
可定义如下数据结构,分别记录数组的定义长度和对应的已经被赋值的元素
map<string, int> arrlen; // 数组名->长度
map<string, map<int,int> > arr; // 数组名->下标->值(模拟二维数组)
- 使用
string
的find
和substr
可轻易分割数组名和下标表达式 - 利用栈或者递归可完成表达式计算
细节比较多,但不难,认真处理细节即可
AC代码(C++11,字符串处理)
#include<bits/stdc++.h>
using namespace std;
map<string, int> arrlen; // 数组名->长度
map<string, map<int,int> > arr; // 数组名->下标->值(模拟二维数组)
string s, sl, sr;
pair<string, string> getNameExpr(string s) { // 获取数组名和下标
int i = s.find('[');
return {s.substr(0, i), s.substr(i+1, s.size()-i-2)};
}
int eval(string s) { // 对s进行求值,若有bug返回-1,否则返回正确值
int idx;
stack<string> stk; // 存储数组名
string st = s;
while (!isdigit(st[0])) { // 依次分割提取数组表达式
pair<string,string> pr = getNameExpr(st);
stk.push(pr.first);
st = pr.second;
}
idx = stoi(st); // 最内层下标
while (!stk.empty()) { // 出栈求值
st = stk.top();
if (arrlen[st] <= idx || arr[st].find(idx) == arr[st].end()) break; // 下标溢出/未赋值
stk.pop();
idx = arr[st][idx]; // 新的长度
}
return stk.empty() ? idx : -1; // -1表示有bug
}
int main() {
bool isOver = false; // 判断是否结束读取测试用例
while (!isOver) {
arrlen.clear(); arr.clear(); isOver=true;// 初始化
int line=0; bool isBug=false;
while (cin >>s && s[0] != '.') {
if (isBug) continue; // 找到bug,继续读完即可
int i = s.find('='); isOver=false; line ++;
if (i == string::npos) { // 定义表达式
pair<string,string> pr = getNameExpr(s);
arrlen[pr.first] = stoi(pr.second); // 数组长度
}
else { // 赋值表达式
sl = s.substr(0,i); sr = s.substr(i+1); // 左右表达式
int rval = eval(sr); // 右值
if (rval != -1) { // 右部表达式合法
pair<string,string> pr = getNameExpr(sl);
int idx = eval(pr.second);
if (idx != -1 && idx < arrlen[pr.first]) arr[pr.first][idx] = rval; // 下标表达式非法/溢出
else isBug = true; // 出现bug
}
else isBug = true;
}
}
if (!isOver) printf("%d\n", isBug ? line : 0); // 未结束均需输出
}
return 0;
}