问题描述
我最近在hackerrank上制作了“中奖彩票”编码挑战。 https://www.hackerrank.com/challenges/winning-lottery-ticket/
想法是计算包含0-9的所有数字的两行的组合,在下面的示例中,总共包含5个组合。
56789
129300455
5559948277
012334556
123456879
此想法是为了更快地更改某些事物的表示形式,以检查是否包含所有数字。 表示示例: 1278-> 01100001100
使用上面的前两行的示例: 56789129300455-> 1111111111
当检查2行的连接中是否包含数字时,如果遇到零,我可以直接中止,因为那将不是全0-9的对。
此逻辑有效,但是在要比较的行数很多时会失败。
// Go code
func winningLotteryTicket(tickets []string) int {
counter := 0
for i := 0; i < len(tickets); i++ {
for j := i + 1; j < len(tickets); j++ {
if err := bitMask(fmt.Sprintf("%v%v",tickets[i],tickets[j])); err == nil {
counter++
}
}
}
return counter
}
func bitMask(s string) error {
for i := 0; i <= 9; i++ {
if !strings.Contains(s,strconv.Itoa(i)) {
return errors.New("No Pair")
}
}
return nil
}
不确定此表示形式是否称为bitMaks,如果不正确,请纠正我,我将调整此职位。
从我的角度来看,在字符串的连接上没有改善性能的方法,因为我必须检查每个组合。
不确定在函数“ bitMask”处的字符串中是否包含数字。 您有一个更好的方法吗?
解决方法
位掩码是整数,而不是一和零的字符串。之所以称为位掩码,是因为我们对这些整数的数值不感兴趣,而仅对位模式感兴趣。我们可以对整数使用按位运算,因为它们是在硬件中直接在CPU中实现的,所以它们真的很快。
这是一个将字符串转换为实际位掩码的函数,每个一位都表明字符串中存在特定数字:
func mask(s string) uint16 {
// We need ten bits,one for each possible decimal digit in s,so int16 and
// uint16 are the smallest possible integer types that fit. For bitmasks it
// is typical to select an unsigned type because the sign bit doesn't have
// any meaning. As I said earlier,mask's numerical value is irrelevant.
var mask uint16
for _,c := range s {
switch c {
case '0':
mask |= 0b0000000001
case '1':
mask |= 0b0000000010
case '2':
mask |= 0b0000000100
case '3':
mask |= 0b0000001000
case '4':
mask |= 0b0000010000
case '5':
mask |= 0b0000100000
case '6':
mask |= 0b0001000000
case '7':
mask |= 0b0010000000
case '8':
mask |= 0b0100000000
case '9':
mask |= 0b1000000000
}
}
return mask
}
这很冗长,但是很明显会发生什么。
请注意,二进制数字文字可以用移位代替:
-
0b0000000001
与1<<0
相同(1向左偏移零次) -
0b0000000010
与1<<1
相同(1向左移动了一次 -
0b0000000100
与1<<2
相同(1向左移动两次),依此类推
使用此信息,并利用字节'0'至'9'本身就是整数(十进制的48至57,由它们在ASCII table中的位置所给定的事实),我们可以缩短此函数像这样:
func mask(s string) uint16 {
var mask uint16
for _,c := range s {
if '0' <= c && c <= '9' {
mask |= 1 << (c - '0')
}
}
return mask
}
要检查两条线,我们要做的就是对这两条线的掩码进行“或”运算,然后与0b1111111111
进行比较(即检查是否设置了所有十位):
package main
import "fmt"
func main() {
a := "56789"
b := "129300455"
mA := mask(a)
mB := mask(b)
fmt.Printf("mask(%11q) = %010b\n",a,mA)
fmt.Printf("mask(%11q) = %010b\n",b,mB)
fmt.Printf("combined = %010b\n",mA|mB)
fmt.Printf("all digits present: %v\n",mA|mB == 0b1111111111)
}
func mask(s string) uint16 {
var mask uint16
for _,c := range s {
if '0' <= c && c <= '9' {
mask |= 1 << (c - '0')
}
}
return mask
}
mask( "56789") = 1111100000
mask("129300455") = 1000111111
combined = 1111111111
all digits present: true