在问 · 算法题解

299. 猜数字游戏

题目描述

你和朋友玩猜数字游戏(Bulls and Cows)。你心里有一个秘密数字 secret,朋友给出一个猜测 guess,两者长度相同,都由数字字符组成,并且都可能有重复数字。

每猜一次,你要回一个提示,包括两个数:公牛数 x,即数字和位置都对上的位数;奶牛数 y,即在排除公牛位之后,guess 中还有多少位可以通过调整位置变成公牛。重复的数字按个数配对,同一个数字不能被重复计算。

按 "xAyB" 的格式返回提示字符串,其中 A 代表公牛,B 代表奶牛。

示例 1
输入:secret = "1807", guess = "7810"
输出:"1A3B"

解释:第 2 位的 8 是公牛;剩下的 1、0、7 都出现在 secret 中,只是位置不对,是 3 头奶牛。

示例 2
输入:secret = "1123", guess = "0111"
输出:"1A1B"

解释:第 2 位的 1 是公牛;其余位置里 secret 还剩一个 1,guess 还剩两个 1,只能配上一对,所以只有 1 头奶牛。

数据范围

  • 1 <= secret.length, guess.length <= 1000
  • secret.length == guess.length
  • secret 和 guess 只含数字。

解题思路

一遍扫描:同一位置数字相同就是公牛;否则把 secret 的数字计入 cs,guess 的数字计入 cg,这些是“未匹配”的数字。扫完后,对每个数字 d,双方都未匹配的那一部分最多能配成 min(cs[d], cg[d]) 对,求和就是奶牛数。

为什么正确

cs[d] 和 cg[d] 只统计不是公牛的位置,所以公牛不会被重复算成奶牛。

复杂度分析

时间 O(n);空间 O(10)。

易错点

不能对每个 guess 数字去 secret 里“找有没有”:重复数字会被多次计数,公牛也会被重复算成奶牛。必须只统计非公牛位置,再取 min。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def getHint(self, secret: str, guess: str) -> str:
        bulls = 0
        cs, cg = [0] * 10, [0] * 10
        for a, b in zip(secret, guess):
            if a == b:
                bulls += 1
            else:
                cs[int(a)] += 1
                cg[int(b)] += 1
        cows = sum(min(cs[d], cg[d]) for d in range(10))
        return f'{bulls}A{cows}B'

载入编辑器,自己跑一遍 →

字符串 专题的其他题目

查看 字符串 全部题目 →