【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;
}