题目相关
题目链接
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 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)。