UVA - 1592:Database

题目的意思是找到两行在两列处相等,主要要做的是记录某个值是否重复出现过。

经过思考,我的思路是:每一列用一个unordered_map>记录单词出现的行数,对于某一行中的两列,如果有两个元素在同一其他行出现了重复,则可以输出结果

例如,第4行的第1个元素在1 3 5 6行都出现了重复,第4行的第2个元素在2 3行出现了重复,则r1 = 3; r2 = 4; c1 = 1; c2 =2;

每一行用一个数据结构保存每一列在哪些行出现了重复,如果某一行被重复了两次,则说明不满足PNF,输出结果。

为了保存每一行中出现重复的列,我们可以使用两种数据结构,一种是数组,一种是unordered_map,保存的键值对的含义是,本行的val列在key行出现了重复。

数组每一行需要清空,复杂度是O(n),键值对的插入的复杂度是O(n)(每一次是常数,最多插入n次),总复杂度是O(n^2)

使用unordered_map的复杂度每一行也是O(n),但是常数会更大一些。

读入可以一个一个读,如果嫌弃复杂可以将所有的,变成\n然后一行一行读,我使用的是C++的正则表达式库regex,挺好用的。

两种数据结构的实现代码如下:
数组

#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

namespace {
    constexpr int MAXN = 1e4 + 5;
    constexpr int MAXM = 1e1 + 5;
    array<int, MAXN> exists;
    array<unordered_map<string, vector<int>>, MAXM> dict;
    int n, m;
    const string YES = "YES";
    const string NO = "NO";
    string line, word;
    regex r("([^,]*),");
    bool flag;
    int r1, r2, c1, c2;
}

int main() {
    while (cin >> n >> m) {
        fill(dict.begin(), dict.end(), unordered_map<string, vector<int>>());
        //读取空行
        getline(cin, line);
        flag = false;
        for (int i = 1; i <= n; ++i) {
            getline(cin, line);
            if (flag) continue;
            fill(exists.begin(), exists.begin() + i, 0);
            line.push_back(',');
            int j = 1;
            for (sregex_iterator it(line.begin(), line.end(), r), end_it; it != end_it; ++it, ++j) {
                word = it->str(1);
//                cout << word << " ";
                auto &exist = dict[j][word];
                for (auto k : exist) {
                    if (exists[k] > 0) {
                        r1 = k; r2 = i;
                        c1 = exists[k]; c2 = j;
                        flag = true;
                        break;
                    } else {
                        exists[k] = j;
                    }
                }
                if (flag) break;
                dict[j][word].push_back(i);
            }
//            cout << "\n";
        }
        if (flag) {
            cout << NO << "\n"
                << r1 << " " << r2 << "\n"
                << c1 << " " << c2 << "\n";
        } else {
            cout << YES << "\n";
        }
    }
}

unordered_map

#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

namespace {
    constexpr int MAXN = 1e4 + 5;
    constexpr int MAXM = 1e1 + 5;
    unordered_map<int, int> exists;
    array<unordered_map<string, vector<int>>, MAXM> dict;
    int n, m;
    const string YES = "YES";
    const string NO = "NO";
    string line, word;
    regex r("([^,]*),");
    bool flag;
    int r1, r2, c1, c2;
}

int main() {
    while (cin >> n >> m) {
        fill(dict.begin(), dict.end(), unordered_map<string, vector<int>>());
        //读取空行
        getline(cin, line);
        flag = false;
        for (int i = 1; i <= n; ++i) {
            getline(cin, line);
            if (flag) continue;
            exists.clear();
            line.push_back(',');
            int j = 1;
            for (sregex_iterator it(line.begin(), line.end(), r), end_it; it != end_it; ++it, ++j) {
                word = it->str(1);
//                cout << word << " ";
                auto &exist = dict[j][word];
                for (auto k : exist) {
                    if (exists.count(k) > 0) {
                        r1 = k; r2 = i;
                        c1 = exists[k]; c2 = j;
                        flag = true;
                        break;
                    } else {
                        exists[k] = j;
                    }
                }
                if (flag) break;
                dict[j][word].push_back(i);
            }
//            cout << "\n";
        }
        if (flag) {
            cout << NO << "\n"
                << r1 << " " << r2 << "\n"
                << c1 << " " << c2 << "\n";
        } else {
            cout << YES << "\n";
        }
    }
}

提交以后发现,还是使用数组快一点,毕竟常数比较小

看了书以后发现书中采用的是完全不同的思路,不过把字符串首先哈希成数字这个思想我觉得挺重要,对上面的代码也是一种优化。以后遇到对这种复杂数据结构的映射处理都应该想到这种哈希方法。

然后遍历每两列,将每一行的两个的值加入unordered_map,如果出现重复则输出。

复杂度是O(nm^2),按道理来讲比上面的代码会快很多,但是实际提交却比上面的还慢,不清楚为什么,觉得OJ的测评也挺奇怪的。

经过思考,我觉得上面O(n^2)的算法中,每一行的复杂度在实际数据中应该比O(n)小很多,导致算法大概就是O(n)的,所以速度更快。

#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

namespace {
    constexpr int MAXN = 1e4 + 5;
    constexpr int MAXM = 1e1 + 5;
    array<array<int, MAXM>, MAXN> dict;
    class StrHash {
        unordered_map<string, int> hash;
        int idx = 0;
    public:
        int operator ()(const string &s);
    };
    int StrHash::operator()(const string &s) {
        if (hash.count(s)) return hash[s];
        return hash[s] = idx++;
    }
    class IntHash {
        int len = 0;
    public:
        IntHash(int _len):len(_len) {}
        int operator() (int a, int b) {
            return a * len + b;
        }
    };
    int n, m, len;
    regex r("([^,]*),");
    string line;
    bool flag;
    int r1, r2, c1, c2;
    unordered_map<int, int> record;
}

int main() {
    ios::sync_with_stdio(false);
    while (cin >> n >> m) {
        flag = false;
        IntHash intHash(n * m);
        StrHash strHash;
        getline(cin, line);
        for (int i = 0; i < n; ++i) {
            getline(cin, line);
            line.push_back(',');
            int j = 0;
            for (sregex_iterator it(line.begin(), line.end(), r), end_it; it != end_it; ++it, ++j) {
//                cout << it->str(1) << endl;
                dict[i][j] = strHash(it->str(1));
            }
        }
        int word;

        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < i; ++j) {
                record.clear();
                for (int k = 0; k < n; ++k) {
                    word = intHash(dict[k][i], dict[k][j]);
                    if (record.count(word) > 0) {
                        flag = true;
                        r1 = record[word] + 1;
                        r2 = k + 1;
                        c1 = j + 1;
                        c2 = i + 1;
                        break;
                    } else {
                        record[word] = k;
                    }
                }
                if (flag) break;
            }
            if (flag) break;
        }
        if (flag) {
            cout << "NO\n"
                << r1 << " " << r2 << "\n"
                << c1 << " " << c2 << "\n";
        } else {
            cout << "YES\n";
//
        }
    }
}

你可能感兴趣的:(#,算法竞赛入门经典)