您的位置:首页 > 其它

UVa 1592 Database(枚举+字符串查重)

2017-04-18 19:29 357 查看

原题地址

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

题意:给定一个数据库的n行和m列,判断是否存在两个不同列c1、c2,使得这两个列下有两个不同行r1、r2对应的内容都相等。即[r1][c1]=[r2][c1]且[r1][c2]==[r2][c2]。如下图的第2行、第3行和第2列、第3列。



解题思路

本题是《算法竞赛入门经典》的例题5-9,是STL的综合应用题,考察枚举和字符串操作。

读懂题意之后一般先想到的都是写O(n^4)的四重循环,枚举c1、c2的组合嵌套枚举r1、r2,但是这样子想来也是不合理的,加上用string进行操作,时间上的代价是很明显的。

书上给出的解决办法意思如下:

只枚举c1、c2,然后从上到下扫描每行,每到一行,就把这行c1和c2列的内容作为key一个二元组存到map里,map的value即这一行的行号,那么下次遍历到某一行r2有相同二元组时,即可查到上次出现时的行号,即r1。

思想还是很巧妙的,虽然时间复杂度还是有O(n^3),但测试的时间要求在9000ms以内(额…),所以也是进步啦。

还有一个问题就是,难道每个矩阵里都要存string,用两个string构造一个二元组?代价也很大,而且字符串比较的问题是很耗时的。

书上又提出了解决方法(实在是666):

在将元素存储到矩阵之前,先为每个字符串分配一个int型的编号,这样数据库中的每个单元格都变成了整数,二元组则可以用pair对表示,这种技巧和之前的UVa 12096 The SetStack Computer中所用的思想类似。

比较繁琐的是字符串的读入问题,由于每个字符串中都有空格,又以逗号分隔,因此只能一个个找逗号的位置,把string截取出来了。

int find(char c, int pos = 0) const;//从pos开始查找字符c在当前字符串的位置
string substr(int pos = 0,int n = npos) const;//返回pos开始的n个字符组成的字符串


AC代码

#include <iostream>
#include <cstdio>
#include <string>
#include <map>

using namespace std;

typedef pair<int, int> Elem; //c1,c2在r行的字符串组成的的二元组

int num; //字符串的编号
map<string, int> IDcache; //字符串到ID的映射
int database[10005][15];

void print_database(int row, int col) //输出
{
for (int i = 0; i<row; ++i)
{
for (int j = 0; j<col; ++j)
cout << database[i][j] << ' ';
cout << endl;
}
}

int getID(const string &s) //为字符串s分配ID
{
if (IDcache.count(s) != 0) //ID已分配
return IDcache[s];
else
return IDcache[s] = num++; //分配新ID
}

void readData(int n, int m)
{
IDcache.clear();
num = 1;
string tmp;
for (int i = 0; i < n; ++i)
{
getline(cin, tmp);
int lastpos = -1; //指向每次停下来的位置
for (int j = 0; j < m; ++j)
{
int p = tmp.find(',', lastpos+1); //从新位置开始找逗号
if (p == string::npos)
p = tmp.length(); //找到末尾
database[i][j] = getID(tmp.substr(lastpos+1, p-1-lastpos)); //截取lastpos+1~p-1的字符串
lastpos = p; //更新停下来的位置
}
}
}

void findDup(int n, int m)
{
for (int c1 = 0; c1<m; ++c1)
for (int c2 = c1+1; c2<m; ++c2)
{
map<Elem, int> pair2row;
for (int r = 0; r < n; ++r) //对相同c1/c2的每一行遍历
{
Elem p = make_pair(database[r][c1], database[r][c2]);//构造ID二元组
if (pair2row.count(p)) //如果之前行出现过ID二元组,输出结束
{
printf("NO\n");
printf("%d %d\n%d %d\n", pair2row[p]+1, r+1, c1+1, c2+1);
return;
}
else
pair2row[p] = r; //二元组p在c1/c2中的所在行为r
}
}
printf("YES\n");
}

int main()
{
//freopen("E:/Personal/Desktop/test.txt", "r", stdin);
int n,m;
string tmp;
while (cin >> n >> m && n && m)
{
getchar(); //吞掉cin的回车
readData(n, m); //输入数据
//print_database(n,m); //检查ID矩阵
findDup(n, m); //查找可能的重复二元组
}
return 0;
}


耗时:1260ms

算法复杂度:O(n^3),主要由遍历列、行造成,而map内部由红黑树实现,查找时间仅为O(logN)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  uva stl map string