找Bug(Bug Hunt)[UVA1596]

UVA 1596 BugHunt,ACM/ICPC Tokyo2007时间限制 3000ms 内存限制 128MB

https://vjudge.net/problem/UVA-1596

这题是书上第五章的习题5-9,按照惯例书上的题就不贴详细的样例输入输出和题目描述了。

这个题首先要考虑的是题意。题目要求模拟一段程序的执行,而所执行的程序本身非常简单,操作对象是数组,数组只有赋值语句和定义语句。所以可以分开考虑赋值和定义语句;对于定义语句,我们就需要为当前执行的程序段构造一个概念上的“数组”。对于赋值语句,需要考虑左右操作数。除此之外,需要注意的地方还有,数组的下标很有可能是另一个表达式(在本题中是另一个数组的值),并且数组的下标越界或者使用了未初始化的值,都是要求输出“错误”的。

注意到这题并非需要构造一个完整的数组,因为题目要求数组的下标最大值能达到$2^{31}-1$,如果使用malloc或者new运算符在堆内存分配一个完整的数组,一是时间上不可能很快;更重要的是,可用的堆内存并不够(一般情况下在OJ中只能使用128MB的内存)。所以需要另辟蹊径。因为程序很短,使用了的数组元素有限,因此可以使用STLmap这一容器,将使用的元素进行映射(从下标到值的一个映射;一般来说,数组本身就是简单的一对一映射关系)。map容器的内部实现是红黑树,其一般操作时间为$O(lgn)$,虽然不如直接用下标访问的数组和散列表的$O(1)$时间快,但也是可以接受的。至于要求判断是否越界,则很简单,只要对这个“数组”增加一个长度的属性来存放定义时数组的长度,在每次赋值操作时检查是否越界即可。因此我在这里定义了一个结构体arrayinfo,用来存放每次新建的一个“数组”;而判断是否初始化(也就是是否赋值)也比较简单,没有赋值,则这个map中没有这个对应关系,也就是利用find()函数返回的是mapend()迭代器。经过一次赋值后,利用insert()方法插入元素,则可以表示这个下标对应的值被“初始化”了。

上面是解决这个问题的一个总体的思路:选择map这一数据容器来模拟程序中的数组。下面则主要说明一下一些细节性的问题:

一是要考虑如何创建一个“数组”。题目中的“数组”名只有一个英文字母,因此可以直接使用其ASCII码来作为下标进行定位,一旦从输入中得到定义“数组”的行为,则在arrayset这个数组中,以其数组名作为下标进行访问,用new运算符新建一个arrayinfo并关联到arrayset上。如果考虑的更符合实际一点,“数组”名字可能是一个串,那就还是需要使用map这一容器将“数组名”与实际的数组对应,具体操作便是使用一个map<string, map<int, int> * >

第二个问题就是如何解析嵌套的数组。从一般的思维上理解,便是从外向内扒的一个过程,所以很容易写出一个递归的方法:当表达式只是常数时,作为递归出口;否则脱去最外层的数组记号,将内层的表达式继续传给自身解析,直到解析到递归出口为止。当然非递归化也是可以的,就是需要手动使用一个栈。至于写入数组的值也很简单,只要稍作修改,利用上述思路得到对应的迭代器,然后通过迭代器修改值即可。

第三个问题便是如何处理输出了。题目要求只要找到一行错误的代码,就要输出,并跳过余下的代码。我使用的是设置返回值的方法(也是传统的C/C++处理方法),利用返回代码判断是否出现错误,如果出现错误利用空循环跳过,直到读取到当前程序结束为止。因为UVA支持C++11,所以别人的代码也有直接使用try catch的结构化异常处理。从一个最早是接触VB.Net的视角来看,在这里使用异常会使得思路会显得更为清晰,这也是很多语言支持结构化异常处理的原因吧(虽然在Effective C++中,并不建议对C++使用异常)。

最后附上我的C++实现(可能是使用new delete使得效率略低,在题目输入下花费了10ms的时间):

  1 #include<iostream>
  2 #include<map>
  3 #include<string>
  4 #include<stack>
  5 using namespace std;
  6 struct arrayinfo
  7 {
  8     map<int, int> data;
  9     size_t length;
 10     arrayinfo(size_t len) :length(len)
 11     {
 12     };
 13 };
 14 arrayinfo * arrayset[256] = { 0 };
 15 int exeline = 0;
 16 
 17 int getvalue(const string & exp)
 18 {
 19     //cout << "  " << exp << endl;
 20     if (exp[0] >= '0'&&exp[0] <= '9')
 21         return atoi(exp.c_str());
 22     size_t begin = exp.find('[');
 23     int id = getvalue(exp.substr(begin + 1, exp.length() - begin - 2));
 24     if (id == -1)return -1;
 25     if (id >= arrayset[exp[0]]->length)
 26     {
 27         //subscript out of range
 28         cout << exeline << endl;
 29         return -1;
 30     }
 31     map<int, int>::iterator it = arrayset[exp[0]]->data.find(id);
 32     if (it == arrayset[exp[0]]->data.end())
 33     {
 34         //uninitialized variable
 35         cout << exeline << endl;
 36         return -1;
 37     }
 38     return it->second;
 39 }
 40 int setvalue(const string & exp, int value)
 41 {
 42     //cout << "  " << exp << endl;
 43     size_t begin = exp.find('[');
 44     int id = getvalue(exp.substr(begin + 1, exp.length() - begin - 2));
 45     if (id == -1)
 46         return -1;
 47     if (id >= arrayset[exp[0]]->length)
 48     {
 49         //subscription out of range
 50         cout << exeline << endl;
 51         return -1;
 52     }
 53     map<int, int>::iterator it = arrayset[exp[0]]->data.find(id);
 54     if (it == arrayset[exp[0]]->data.end())
 55         //not found, means first time to assign value
 56         arrayset[exp[0]]->data.insert(pair<int, int>(id, value));
 57     else
 58         it->second = value;
 59     return 0;
 60 }
 61 
 62 int eval(const string & str)
 63 {
 64     arrayinfo * tmparray;
 65     //determine whether it is declaration or assignment
 66     if (arrayset[str[0]] == 0)
 67     {
 68         //it has not be declared .
 69         tmparray = new arrayinfo(atoi(str.c_str() + 2));        //offset one letter and a '['
 70         arrayset[str[0]] = tmparray;
 71     }
 72     else
 73     {
 74         size_t aoffset = str.find('=');
 75         int r = getvalue(str.substr(aoffset + 1, str.length() - aoffset));
 76         if (r == -1)
 77             return -1;
 78         return setvalue(str.substr(0, aoffset), r);
 79     }
 80     return 0;
 81 }
 82 
 83 
 84 
 85 int main()
 86 {
 87     //freopen("input.txt", "r", stdin);
 88     string buffer;
 89     int err = 0;
 90     bool sdot = false;
 91     while (cin >> buffer)
 92     {
 93         //reach a end , clear memory.
 94         if (buffer[0] == '.')
 95         {
 96         Clear:
 97             if (sdot == true)
 98                 break;
 99             sdot = true;
100             for (int i = 0; i < 256; i++)
101                 if (arrayset[i] != 0)
102                 {
103                     delete arrayset[i];
104                     arrayset[i] = 0;
105                 }
106             exeline = 0;
107             if (err != -1)
108                 cout << "0" << endl;
109             continue;
110         }
111         sdot = false;
112         ++exeline;
113         err = eval(buffer);
114         //cout << " " << err << endl;
115         if (err == -1)
116             for (;;)
117             {
118                 cin >> buffer;
119                 if (buffer[0] == '.')
120                     goto Clear;
121             }
122     }
123     return 0;
124 }
原文地址:https://www.cnblogs.com/ggggg63/p/6665607.html