哈希表题目:猜数字游戏

题目

标题和出处

标题:猜数字游戏

出处:299. 猜数字游戏

难度

4 级

题目描述

要求

你在和朋友一起玩猜数字(Bulls and Cows)游戏。

你写出一个秘密数字,并请朋友猜这个数字是多少。朋友每猜测一次,你就会给他一个包含下述信息的提示:

  • 「公牛」的数量,即猜测数字中有多少位数字在秘密数字中出现且位置正确。
  • 「奶牛」的数量,即猜测数字中有多少位数字在秘密数字中出现但位置错误,即猜测数字中有多少位非公牛数字可以通过重新排列变成公牛数字。

给你一个秘密数字 secret \texttt{secret} secret 和朋友猜测的数字 guess \texttt{guess} guess,请你返回对朋友这次猜测的提示。

提示的格式为 "xAyB" \texttt{"xAyB"} "xAyB" x \texttt{x} x 是公牛个数, y \texttt{y} y 是奶牛个数。请注意 secret \texttt{secret} secret guess \texttt{guess} guess 都可能含有重复数字。

示例

示例 1:

输入: secret   =   "1807",   guess   =   "7810" \texttt{secret = "1807", guess = "7810"} secret = "1807", guess = "7810"
输出: "1A3B" \texttt{"1A3B"} "1A3B"
解释:数字和位置都对(公牛)用 ‘|’ \texttt{`|'} ‘|’ 连接,数字猜对位置不对(奶牛)用下划线表示。
"1807" \texttt{"1807"} "1807"
   | \texttt{~~|}   |
" 7 ‾ 8 10 ‾ " \texttt{"}\underline{\texttt{7}}\texttt{8}\underline{\texttt{10}}\texttt{"} "7810"

示例 2:

输入: secret   =   "1123",   guess   =   "0111" \texttt{secret = "1123", guess = "0111"} secret = "1123", guess = "0111"
输出: "1A1B" \texttt{"1A1B"} "1A1B"
解释:数字和位置都对(公牛)用 ‘|’ \texttt{`|'} ‘|’ 连接,数字猜对位置不对(奶牛)用下划线表示。
"1123" \texttt{"1123"} "1123"
   | \texttt{~~|}   |
"01 1 ‾ 1" \texttt{"01}\underline{\texttt{1}}\texttt{1"} "0111"

"1123" \texttt{"1123"} "1123"
   | \texttt{~~|}   |
"011 1 ‾ " \texttt{"011}\underline{\texttt{1}}\texttt{"} "0111"
注意,两个不匹配的 1 \texttt{1} 1 中,只有一个会算作奶牛(数字猜对位置不对),因为通过重新排列非公牛数字之后只有一个 1 \texttt{1} 1 可以成为公牛数字。

示例 3:

输入: secret   =   "1",   guess   =   "0" \texttt{secret = "1", guess = "0"} secret = "1", guess = "0"
输出: "0A0B" \texttt{"0A0B"} "0A0B"

示例 4:

输入: secret   =   "1",   guess   =   "1" \texttt{secret = "1", guess = "1"} secret = "1", guess = "1"
输出: "1A0B" \texttt{"1A0B"} "1A0B"

数据范围

  • 1 ≤ secret.length,   guess.length ≤ 1000 \texttt{1} \le \texttt{secret.length, guess.length} \le \texttt{1000} 1secret.length, guess.length1000
  • secret.length   =   guess.length \texttt{secret.length = guess.length} secret.length = guess.length
  • secret \texttt{secret} secret guess \texttt{guess} guess 仅由数字组成

解法一

思路和算法

公牛为 secret \textit{secret} secret guess \textit{guess} guess 的相同下标处数字相同的下标个数,统计公牛的方法是:同时遍历 secret \textit{secret} secret guess \textit{guess} guess,对于每个下标,如果 secret \textit{secret} secret guess \textit{guess} guess 在相同下标处的数字相同,则将公牛的数量加 1 1 1

奶牛为 secret \textit{secret} secret guess \textit{guess} guess 的不同下标处数字相同的下标个数,统计奶牛的方法是:使用两个哈希表分别记录 secret \textit{secret} secret guess \textit{guess} guess 中的每个数字的出现次数(排除公牛的情况),同时遍历 secret \textit{secret} secret guess \textit{guess} guess,对于每个下标,如果 secret \textit{secret} secret guess \textit{guess} guess 在相同下标处的数字不同,则将该下标处的两个数字在对应的哈希表中的出现次数分别加 1 1 1,遍历结束之后, 0 0 0 9 9 9 的每个数字在奶牛中的数量为该数字在两个哈希表中的出现次数的较小值,计算每个数字在奶牛中的数量之和,即为奶牛的数量。

由于统计公牛和奶牛都需要同时遍历 secret \textit{secret} secret guess \textit{guess} guess,因此可以在一次遍历中完成统计公牛和奶牛的操作。遍历过程中,如果 secret \textit{secret} secret guess \textit{guess} guess 在相同下标处的数字相同则将公牛的数量加 1 1 1,如果 secret \textit{secret} secret guess \textit{guess} guess 在相同下标处的数字不同则将该下标处的两个数字在 secret \textit{secret} secret guess \textit{guess} guess 中的出现次数分别加 1 1 1,遍历结束之后遍历 0 0 0 9 9 9 secret \textit{secret} secret guess \textit{guess} guess 中的出现次数,统计奶牛的数量。

实现方面,由于数字范围是 0 0 0 9 9 9,因此可以使用两个数组分别记录 secret \textit{secret} secret guess \textit{guess} guess 中的每个数字的出现次数。

代码

class Solution {
    public String getHint(String secret, String guess) {
        int bulls = 0, cows = 0;
        int[] secretCounts = new int[10];
        int[] guessCounts = new int[10];
        int length = secret.length();
        for (int i = 0; i < length; i++) {
            int secretDigit = secret.charAt(i) - '0';
            int guessDigit = guess.charAt(i) - '0';
            if (secretDigit == guessDigit) {
                bulls++;
            } else {
                secretCounts[secretDigit]++;
                guessCounts[guessDigit]++;
            }
        }
        for (int i = 0; i < 10; i++) {
            cows += Math.min(secretCounts[i], guessCounts[i]);
        }
        return String.format("%dA%dB", bulls, cows);
    }
}

复杂度分析

  • 时间复杂度: O ( n + C ) O(n + C) O(n+C),其中 n n n 是字符串 secret \textit{secret} secret guess \textit{guess} guess 的长度, C C C 是数字的个数, C = 10 C = 10 C=10。需要 O ( n ) O(n) O(n) 的时间遍历字符串 secret \textit{secret} secret guess \textit{guess} guess 各一次统计公牛数量和每个数字的出现次数,需要 O ( C ) O(C) O(C) 的时间遍历 0 0 0 9 9 9 的所有数字统计奶牛数量。

  • 空间复杂度: O ( C ) O(C) O(C),其中 C C C 是数字的个数, C = 10 C = 10 C=10。需要使用两个哈希表分别记录 secret \textit{secret} secret guess \textit{guess} guess 中的每个数字的出现次数。

解法二

思路和算法

解法一需要使用两个哈希表分别记录 secret \textit{secret} secret guess \textit{guess} guess 中的每个数字的出现次数,在遍历 secret \textit{secret} secret guess \textit{guess} guess 之后还需要遍历 0 0 0 9 9 9 的每个数字计算奶牛的数量。可以只用一个哈希表,在遍历 secret \textit{secret} secret guess \textit{guess} guess 的同时计算奶牛的数量。

使用一个哈希表记录每个数字的出现次数(排除公牛的情况),遍历 secret \textit{secret} secret guess \textit{guess} guess 的过程中,对于 secret \textit{secret} secret 中出现的每个数字将出现次数加 1 1 1,对于 guess \textit{guess} guess 中出现的每个数字将出现次数减 1 1 1。上述操作的思想是:对于 secret \textit{secret} secret 中的每个数字,只有当 guess \textit{guess} guess 中存在一个不同位置的相同数字时,才是一个奶牛,反之亦然。

当遍历到一个下标时,将 secret \textit{secret} secret guess \textit{guess} guess 在该下标处的数字分别记为 secretDigit \textit{secretDigit} secretDigit guessDigit \textit{guessDigit} guessDigit,如果 secretDigit = guessDigit \textit{secretDigit} = \textit{guessDigit} secretDigit=guessDigit 则将公牛的数量加 1 1 1,如果 secretDigit ≠ guessDigit \textit{secretDigit} \ne \textit{guessDigit} secretDigit=guessDigit 则按照如下操作统计奶牛:

  1. 如果哈希表中 secretDigit \textit{secretDigit} secretDigit 的出现次数小于 0 0 0,则在之前遍历的 guess \textit{guess} guess 的数字中有多余的 secretDigit \textit{secretDigit} secretDigit,因此将奶牛的数量加 1 1 1

  2. 如果哈希表中 guessDigit \textit{guessDigit} guessDigit 的出现次数大于 0 0 0,则在之前遍历的 secret \textit{secret} secret 的数字中有多余的 guessDigit \textit{guessDigit} guessDigit,因此将奶牛的数量加 1 1 1

  3. secretDigit \textit{secretDigit} secretDigit 在哈希表中的出现次数加 1 1 1,将 guessDigit \textit{guessDigit} guessDigit 在哈希表中的出现次数减 1 1 1

遍历结束之后,即可知道奶牛的数量。

代码

class Solution {
    public String getHint(String secret, String guess) {
        int bulls = 0, cows = 0;
        int[] digits = new int[10];
        int length = secret.length();
        for (int i = 0; i < length; i++) {
            int secretDigit = secret.charAt(i) - '0';
            int guessDigit = guess.charAt(i) - '0';
            if (secretDigit == guessDigit) {
                bulls++;
            } else {
                if (digits[secretDigit] < 0) {
                    cows++;
                }
                if (digits[guessDigit] > 0) {
                    cows++;
                }
                digits[secretDigit]++;
                digits[guessDigit]--;
            }
        }
        return String.format("%dA%dB", bulls, cows);
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是字符串 secret \textit{secret} secret guess \textit{guess} guess 的长度。需要遍历字符串 secret \textit{secret} secret guess \textit{guess} guess 各一次统计公牛数量和奶牛数量。

  • 空间复杂度: O ( C ) O(C) O(C),其中 C C C 是数字的个数, C = 10 C = 10 C=10。需要使用一个哈希表记录每个数字的出现次数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

伟大的车尔尼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值