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
!
!
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
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 1≤N≤2×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
0∗300+1∗301+2∗302=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} 25∗309+25∗308+25∗307+25∗306+25∗306+25∗304+25∗303+25∗302+25∗301+25∗300≈5.1∗1014,超過了 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)。