UVa 1592 Database

来源:互联网 发布:京东抢券软件安卓 编辑:程序博客网 时间:2024/06/05 20:13

其实紫书在第五章STL初步的时候介绍这个题,主要是为了演示STL的各种用法,但是效率会比较低。实践中一般会使用C字符串和哈希表来实现。

我自己先写过一遍,就是先将数据一行行的读入一个string类型的数组中,然后设计一个函数,用来取指定行指定列的子串,然后主程序开始从第一列的第一行一直遍历到最后一行,中间每取出一个子串就将其保存到一个map映射表中,键为该子串,值为该子串所在的行数。若该子串在同一列出现过,我们就对其后面几列进行判断。这样一来我就写了4个循环,结果一提交发现,Time limit exceeded。这样超时了,这种方法行不通。

于是我找到了这个题的刘大爷版本来好好研究:
先看mian()函数:

//全局变量typedef pair<int,int> PII;const int maxr = 10000 + 5;const int maxc = 10 + 5;int m, n, db[maxr][maxc], cnt;map<string, int> id;//主函数int main() {  string s;  while(getline(cin, s)) {    stringstream ss(s);    if(!(ss >> n >> m)) break;    cnt = 0;    id.clear();    for(int i = 0; i < n; i++) {      getline(cin, s);      int lastpos = -1;      for(int j = 0; j < m; j++) {        int p = s.find(',', lastpos+1);        if(p == string::npos) p = s.length();        db[i][j] = ID(s.substr(lastpos+1, p - lastpos - 1));        lastpos = p;      }    }    find();  }  return 0;}

一点一点慢慢的理解学习吧,首先我们看main函数,刘大爷直接使用getline()读取了第一行两个数字并将其保存在s字符串中,再用其创建一个ss的字符串流,然后再读取其中的两个数据。然后再对cnt初始化,清除id映射表中的内容。
(首先这里就值得借鉴,我之前是直接在while的判断条件里写cin >> n >> m,然后再getline()一行一行的读取数据,结果就出问题了,因为cin >> n >> m 后会留下一个’\n’在缓冲区,这样一来我们后面使用getline()的时候就会先读到这个换行符,导致出现一些其他问题)

然后就是循环中的内容了,刘大爷的处理方式是每次读入一行数据,然后对其进行处理,使用lastpos标记目前处理到的位置。处理方式是使用s.find()函数(对string类的一些函数不熟的可以参考https://www.renfei.org/blog/introduction-to-cpp-string.html)该函数作用是查找从s[lastpos+1]到s[s.length()-1]中首次出现字符 ‘,’ 的位置,如p == string::npos (即没找到) 就将p设置为s字符串的长度,接下来就是s.substr(lastpos+1, p - lastpos -1) 函数将还刚所找到的 ‘,’ 字符前到 lastpos+1 位置的字符串取出作为参数传给ID函数
现在看看ID函数

int ID(const string& s) {  if(!id.count(s)) {    id[s] = ++cnt;  }  return id[s];}

ID函数接收一个字符串,检查id映射表中是否存在,若存在则返回其中的值,若不存在则将其存入映射表中,并单独给一个值,是不是很像 “例题5-5 集合栈计算机“ 这个题目中的ID函数。

然后回到main函数,我们将这个子串对应的唯一ID存入这个二维数组中,并以其所在的行和列作为下标。这样一来我们接收一个下面左边这样的数据,就可以得到右边这样的一个数组。

a1,b1,c1,d1           1  2  3  4a2,b2,c2,d2           5  6  7  8a3,b3,c3,d3           9 10 11 12a4,b4,c4,d4          13 14 15 16a5,b5,c5,d5          17 18 19 20a1,b2,c3,d4           1  6 11 16a1,b3,c2,d5           1 10  7 20a1,b4,c5,d2           1 14 19  8a1,b5,c4,d3           1 18 15 12a2,b1,c3,d5           5  2 11 20a2,b3,c1,d4           5 10  3 16a2,b4,c5,d1           5 14 19  4a3,b1,c2,d4           9  2  7 16a3,b2,c1,d5           9  6  3 20a4,b1,c5,d3          13  2 19 12a5,b1,c4,d2          17  2 15  8

这样我们的处理就可以简单些了,看看print()函数:

void find() {  for(int c1 = 0; c1 < m; c1++)    for(int c2 = c1+1; c2 < m; c2++) {      map<PII, int> d;      for(int i = 0; i < n; i++) {        PII p = make_pair(db[i][c1], db[i][c2]);  //将第i行的第c1、c2列中字符串所对应的ID临时组成一个pair        if(d.count(p)) {        //如果该pair在之前出现过,就说明我们找到了这样的c1,c2,r1,r2          printf("NO\n");          printf("%d %d\n", d[p]+1, i+1);          printf("%d %d\n", c1+1, c2+1);          return;        }        d[p] = i; //如果没出现过该pair则存入映射表中。      }    }  printf("YES\n");}

对pair不熟悉可以看看http://blog.csdn.net/calvin_zcx/article/details/6072286 相信熟悉了pair上面的代码也就不难理解了

若上述某些地方,有误或者是不恰当的地方欢迎留言。

原创粉丝点击