leetcode - 1680 - 连接连续二进制数字 - 同余 - 二进制

文章目录

    • 题目描述
    • 题目剖析&信息挖掘
    • 解题思路
      • 方法一 模拟运算法
        • 思路
        • 注意
        • 知识点
        • 复杂度
        • 参考
        • 代码实现

题目描述

[1680] 连接连续二进制数字

给你一个整数 n ,请你将 1 到 n 的二进制表示连接起来,并返回连接结果对应的 十进制 数字对 10^9 + 7 取余的结果。

示例 1:

输入:n = 1
输出:1
解释:二进制的 “1” 对应着十进制的 1 。
示例 2:

输入:n = 3
输出:27
解释:二进制下,1,2 和 3 分别对应 “1” ,“10” 和 “11” 。
将它们依次连接,我们得到 “11011” ,对应着十进制的 27 。
示例 3:

输入:n = 12
输出:505379714
解释:连接结果为 “1101110010111011110001001101010111100” 。
对应的十进制数字为 118505380540 。
对 109 + 7 取余后,结果为 505379714 。

提示:

1 <= n <= 10^5

Related Topics
  • 二进制
  • 同余

题目剖析&信息挖掘

此题为模拟题,需要用到同余定理 a*b%c = (a%c)*(b%c)%c

解题思路

方法一 模拟运算法

思路

  • 引入一些符号, o(x) 为x的二进制输出形式,比如 o(5)=101, l(x) 为 x的二进制长度,如l(8)=3。
  • 对于1到n个数字的二进制连接结果如下
  • o(1)o(2)…o(n)
  • 转化成数学公式o(1)o(2)…o(n) = ((((o(1)<
  • 这个结果明显是一个大数,那么要取余,其中的每一个左移和或操作都有可能超出10^9+7
  • 这里就要用到同余定理
  • a<
  • (a<
  • 所以就是对以上每步操作都取余即可
  • 再说一下l(x)的计算
  • 一种是通过除2法获取到长度, 这种大家应该都会
  • 还有一种利用动态规划的思想求得,先观察一下1到n的二进制长度规律1(0),2(10),3(11), 4(100), 5(101), 6(110), 7(111), 8(1000)…
  • 可以看出大部分情况下都l(x)=l(x-1), 只有在最高位进1的时候位数+1,其实就是形如x= 1<
  • 总结一下l(1)=1, l(x)={l(x-1) (x&(x-1)>0),l(x-1)+1 ((x&(x-1))=0)}
func concatenatedBinary(n int) int {
	leftBit := uint(0) // 数字长度
	res :=uint64(0)
	for i:=uint64(1);i<=uint64(n);i++ {
		if (i-1)&i==0 { // 判断长度是否增加
			leftBit++
		}
		res <<= leftBit
		res |= i //这里都在uint64范围内,所以可以不用取模,但有可能已经超出int范围了。
		res %= MOD
	}
	
	return int(res)
}

注意

  • 移位后数据会超出int范围
  • 同余取模

知识点

  • 二进制
  • 位运算
  • 同余

复杂度

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

参考

代码实现

func concatenatedBinary(n int) int {
	leftBit := uint(0) // 数字长度
	res :=uint64(0)
	for i:=uint64(1);i<=uint64(n);i++ {
		if (i-1)&i==0 { // 判断长度是否增加
			leftBit++
		}
		res <<= leftBit
		res |= i //这里都在uint64范围内,所以可以不用取模,但有可能已经超出int范围了。
		res %= MOD
	}
	
	return int(res)
}

在这里插入图片描述

你可能感兴趣的:(leetcode,leetcode,算法)