1. 程式人生 > 其它 >AtCoder題解 —— AtCoder Beginner Contest 187 —— C - 1-SAT —— 資料結構之雜湊表

AtCoder題解 —— AtCoder Beginner Contest 187 —— C - 1-SAT —— 資料結構之雜湊表

技術標籤:OJ題解# AtCoder題解AtCoder題解ABC187C題1-SAT

題目相關

題目連結

AtCoder Beginner Contest 187 C 題,https://atcoder.jp/contests/abc187/tasks/abc187_c

Problem Statement

Given are N N N strings S 1 , S 2 , … , S N S_1,S_2,…,S_N S1,S2,,SN. Each of these is a non-empty string consisting of lowercase English letters, with zero or one ! !

! added at the beginning.
We say a string T T T to be unsatisfied when it matches one of S 1 , S 2 , … , S N S_1,S_2,…,S_N S1,S2,,SN regardless of whether we add an ! ! ! at the beginning of T T T.
Determine whether there exists an unsatisfied string. If so, present one such string.

Input

Input is given from Standard Input in the following format:

N
S1
.
.
SN

Output

If there exists an unsatisfied string, print one such string.
If there is no unsatisfied string, print satisfiable.

Sample 1

Sample Input 1

6
a
!a
b
!c
d
!d

Sample Output 1

a

Explaination

a a a matches S 1 S_1 S1 as is, and it matches S 2 S_2 S2 when we add an ! ! !, so it is unsatisfied. Besides that, d d

d will also be accepted.

Sample 2

Sample Input 2

10
red
red
red
!orange
yellow
!blue
cyan
!green
brown
!gray

Sample Output 1

satisfiable

Constraints

  • 1 ≤ N ≤ 2 × 1 0 5 1 ≤ N ≤ 2×10^5 1N2×105
  • 1 ≤ |Si| ≤ 10
  • S i S_i Si is a non-empty string consisting of lowercase English letters, with zero or one ! added at the beginning.

題解報告

題目翻譯

N N N 個字串 S 1 , S 2 , … , S N S_1,S_2,…,S_N S1,S2,,SN. 每個字元都是非空,而且只有小寫字母組成,字串的開始位置可以包含 ! ! ! 或者不包含。
我們定義字串 T T T 不滿足,當它是 S 1 , S 2 , … , S N S_1,S_2,…,S_N S1,S2,,SN 其中之一的字串前面加上一個 ! ! !
請找出是否存在一個不滿足字串,如果存在,請輸出其中的任意一個。

題目分析

本題難度其實不大,但是由於 N N N 的最大值為 2e5,這個資料量要 A C AC AC 程式碼,我們必須有一個 O ( N ) O(N) O(N),最壞不能超過 O ( N l o g N ) O(NlogN) O(NlogN) 時間複雜度的程式碼。因此使用雙重迴圈進行遍歷一定是 T L E TLE TLE,因為這樣演算法時間複雜度為 O ( N 2 ) O(N^2) O(N2)。我們需要優化遍歷程式碼。
我們可以考慮計算每個不包含 ! ! ! 的字串雜湊值,因為字串只有小寫字母,我們可以證明每個不同的字串,對應的雜湊值是不一樣的。因此,本題的核心從暴力查詢變為計算每個字串的雜湊值。

計算雜湊值

我們可以考慮將每個字元轉化稱為 30 30 30 進位制的數字。為什麼是 30 30 30 進位制呢,因為小寫英文字母只有 26 26 26 個,我們只需要大於 26 26 26 進位制即可。
比如字串 a b c abc abc,對應的 30 30 30 進位制資料為 0 ∗ 3 0 0 + 1 ∗ 3 0 1 + 2 ∗ 3 0 2 = 0 + 30 + 1800 = 1830 0*30^{0}+1*30^{1}+2*30^{2}=0+30+1800=1830 0300+1301+2302=0+30+1800=1830,因為 a a a 表示數字 0 0 0 b b b 表示數字 1 1 1,以此類推。不需要在意到底左邊是高位還是右邊是高位,反正不管用什麼方法,每個字串對應的資料都是唯一的。
這樣,我們就可以將字串除去 ! ! ! 變成一個唯一的數字。我們只需要使用一個雜湊表來記錄即可。這樣設計的演算法時間複雜度為 O ( N ) O(N) O(N)

演算法設計

讀取資料,儲存到字串陣列中。
遍歷字串陣列,計算所有不包含 ! ! ! 的字串雜湊值。
遍歷字串陣列,計算包含 ! ! ! 的字串雜湊值,並查表看是否已經存在對應的資料。如果存在,輸出結果即可。
遍歷陣列後,輸出 satisfiable。

樣例資料分析

樣例資料 1

根據題目分析。

不含 ! ! ! 字串雜湊表

對應的資料為:

a
b
d

轉換稱為的雜湊表為:

0
1
3

含有 ! ! ! 字串雜湊表

對應的資料為:

!a
!c
!d

轉換稱為的雜湊表為:

0
2
3

通過比對雜湊表,我們可以知道有兩個結果,分別為 a a a d d d

資料範圍分析

由於 S i S_i Si 的最大長度為 10,因此最大的字串為 z z z z z z z z z z zzzzzzzzzz zzzzzzzzzz,對應的雜湊值為: 25 ∗ 3 0 9 + 25 ∗ 3 0 8 + 25 ∗ 3 0 7 + 25 ∗ 3 0 6 + 25 ∗ 3 0 6 + 25 ∗ 3 0 4 + 25 ∗ 3 0 3 + 25 ∗ 3 0 2 + 25 ∗ 3 0 1 + 25 ∗ 3 0 0 ≈ 5.1 ∗ 1 0 14 25*30^9+25*30^8+25*30^7+25*30^6+25*30^6+25*30^4+25*30^3+25*30^2+25*30^1+25*30^0\approx5.1*10^{14} 25309+25308+25307+25306+25306+25304+25303+25302+25301+253005.11014,超過了 int 範圍,在 long long 之內。因此需要使用 long long。

AC 參考程式碼

//https://atcoder.jp/contests/abc187/tasks/abc187_c
//C - 1-SAT
#include <bits/stdc++.h>

using namespace std;

//如果提交到OJ,不要定義 __LOCAL
//#define __LOCAL

typedef long long ll;

const int MAXN=2e5+4;
string s[MAXN];

int main() {
#ifndef __LOCAL
    //這部分程式碼需要提交到OJ,本地除錯不使用
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
#endif
    int n;
    cin>>n;
    for (int i=1; i<=n; i++) {
        cin>>s[i];
    }

    //處理正常的字串
    unordered_map<ll, int> mp;
    for (int i=1; i<=n; i++) {
        if ('!'==s[i][0]) {
            continue;
        }

        ll x=0;
        for (int j=0; j<s[i].length(); j++) {
            x = x*30+(s[i][j]-'a');
        }
        mp[x]=i;
    }

    //處理!開頭的字串
    for (int i=1; i<=n; i++) {
        if ('!'!=s[i][0]) {
            continue;
        }

        ll x=0;
        for (int j=1; j<s[i].length(); j++) {
            x = x*30+(s[i][j]-'a');
        }

        if (mp.count(x)) {
            cout<<s[mp[x]]<<"\n";
#ifdef __LOCAL
    //這部分程式碼不需要提交到OJ,本地除錯使用
            system("pause");
#endif
            return 0;
        }
    }

    cout<<"satisfiable\n";

#ifdef __LOCAL
    //這部分程式碼不需要提交到OJ,本地除錯使用
    system("pause");
#endif
    return 0;
}

在這裡插入圖片描述

時間複雜度

O(N)。

空間複雜度

O(N)。