2351. 第一个出现两次的字母
题目描述:
给你一个由小写英文字母组成的字符串 s
,请你找出并返回第一个出现 两次 的字母。
注意:
- 如果
a
的 第二次 出现比b
的 第二次 出现在字符串中的位置更靠前,则认为字母a
在字母b
之前出现两次。 s
包含至少一个出现两次的字母。
示例 1:
输入:s = "abccbaacz"
输出:"c"
解释:
字母 'a' 在下标 0 、5 和 6 处出现。
字母 'b' 在下标 1 和 4 处出现。
字母 'c' 在下标 2 、3 和 7 处出现。
字母 'z' 在下标 8 处出现。
字母 'c' 是第一个出现两次的字母,因为在所有字母中,'c' 第二次出现的下标是最小的。
示例 2:
输入:s = "abcdd"
输出:"d"
解释:
只有字母 'd' 出现两次,所以返回 'd' 。
提示:
2 <= s.length <= 100
s
由小写英文字母组成s
包含至少一个重复字母
解题分析及思路:
本题解法有两种,其中最容易让人想到的就是使用hash,另一种解法就是利用位运算来判断字母是否重复出现。
首先,为每一个字母进行编码,将每个字母都在一个32位的二进制数字上进行标记,例如:
字母a
可以标记为 0000 0000 0000 0000 0000 0000 0000 0001
,即0001
字母b
可以标记为 0000 0000 0000 0000 0000 0000 0000 0010
,即0010
字母c
可以标记为 0000 0000 0000 0000 0000 0000 0000 0100
,即0100
那么,怎么保存每个字母是否已经出现过呢?
因为每个字母在32位的二进制上表达的位数不一样,我们恰好可以利用这一特性,将之前出现过的字母保存在某个32位的二进制数上。
定义一个数 res 0000 0000 0000 0000 0000 0000 0000 0000
;
若a
出现过,将第最后一位标为1
,即 res | 0000 0000 0000 0000 0000 0000 0000 0001
= 0000 0000 0000 0000 0000 0000 0000 0001
;
若b
出现过,将第倒数第二位标为1
,即 res | 0000 0000 0000 0000 0000 0000 0000 0010
= 0000 0000 0000 0000 0000 0000 0000 0011
;
以此类推。
那么,怎么判断某个字母出现两次呢?
我们知道,如果一个不为零的二进制数与自己进行且运算,那么这个结果一定不为零。
例如: 0000 0000 0000 0000 0000 0000 0000 0001
& 0000 0000 0000 0000 0000 0000 0000 0001
= 0000 0000 0000 0000 0000 0000 0000 0001
而其他位数上的数不影响这个结果,例如0000 0000 0000 0000 0000 0000 0000 1111
& 0000 0000 0000 0000 0000 0000 0000 0001
= 0000 0000 0000 0000 0000 0000 0000 0001
所以,若要判断一个数是否重现出现可以判断 res
& num
是否大于0。
最后,来看一下范围,26个字母,使用32位数来表达绰绰有余。
func repeatedCharacter(s string) byte {
var res int
for index := range s {
temp := 1 << (s[index] - 'a')
if res&temp > 0 {
return s[index]
}
res |= temp
}
return ' '
}
复杂度:
- 时间复杂度:O(n)
- 空间复杂度:O(1)
执行结果:
- 执行用时:0 ms, 在所有 Go 提交中击败了100.00%的用户
- 内存消耗:2.3 MB, 在所有 Go 提交中击败了90.91%的用户