UVa 1592 Database

其实紫书在第五章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  4
a2,b2,c2,d2           5  6  7  8
a3,b3,c3,d3           9 10 11 12
a4,b4,c4,d4          13 14 15 16
a5,b5,c5,d5          17 18 19 20
a1,b2,c3,d4           1  6 11 16
a1,b3,c2,d5           1 10  7 20
a1,b4,c5,d2           1 14 19  8
a1,b5,c4,d3           1 18 15 12
a2,b1,c3,d5           5  2 11 20
a2,b3,c1,d4           5 10  3 16
a2,b4,c5,d1           5 14 19  4
a3,b1,c2,d4           9  2  7 16
a3,b2,c1,d5           9  6  3 20
a4,b1,c5,d3          13  2 19 12
a5,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++) {
      mapint> 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上面的代码也就不难理解了

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

你可能感兴趣的:(uva,uva,1592,紫书)