AtCoder题解 —— AtCoder Beginner Contest 187 —— C - 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)。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力的老周

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值