找出第 N 个二进制字符串中的第 K 位

标签: 递归 字符串 模拟

难度: Medium

给你两个正整数 nk,二进制字符串  Sn 的形成规则如下:

  • S1 = "0"
  • i > 1 时,Si = Si-1 + "1" + reverse(invert(Si-1))

其中 + 表示串联操作,reverse(x) 返回反转 x 后得到的字符串,而 invert(x) 则会翻转 x 中的每一位(0 变为 1,而 1 变为 0)。

例如,符合上述描述的序列的前 4 个字符串依次是:

  • S= "0"
  • S= "011"
  • S= "0111001"
  • S4 = "011100110110001"

请你返回  Snk 位字符 ,题目数据保证 k 一定在 Sn 长度范围以内。

 

示例 1:

输入:n = 3, k = 1
输出:"0"
解释:S3 为 "0111001",其第 1 位为 "0" 。

示例 2:

输入:n = 4, k = 11
输出:"1"
解释:S4 为 "011100110110001",其第 11 位为 "1" 。

示例 3:

输入:n = 1, k = 1
输出:"0"

示例 4:

输入:n = 2, k = 3
输出:"1"

 

提示:

  • 1 <= n <= 20
  • 1 <= k <= 2n - 1

Submission

运行时间: 22 ms

内存: 15.9 MB

class Solution:
    def findKthBit(self, n: int, k: int) -> str:
        if n == 1:
            return "0"

        middle = 2 ** (n - 1)

        if k == middle:
            return "1"
        elif k < middle:
            return self.findKthBit(n - 1, k)
        else:
            if self.findKthBit(n - 1, 2 * middle - k) == "1":
                return "0"
            return "1"

Explain

此题解采用了递归的方法来寻找第k位字符,避免了直接构造整个字符串Si的复杂性。首先,每个字符串Si的长度是前一个字符串长度的二倍加一,即len(Si) = 2 * len(Si-1) + 1。对于任何Si,其结构可以分解为Si-1 + '1' + reverse(invert(Si-1))。中间的字符总是'1',位于位置2^(n-1)。基于k的位置,算法将问题分为三种情况:1) 如果k正好是中间位置,则答案是'1'。2) 如果k在中间字符之前,问题就变成在Si-1中找第k位,可以递归调用Si-1。3) 如果k在中间字符之后,需要在反转并翻转的Si-1中寻找对应的位,这转换为在Si-1中找第(2^(n-1)*2 - k)位,且需要根据结果翻转字符。

时间复杂度: O(n)

空间复杂度: O(n)

class Solution:
    def findKthBit(self, n: int, k: int) -> str:
        # 如果是S1,直接返回'0'
        if n == 1:
            return '0'

        # 计算中间位置
        middle = 2 ** (n - 1)

        # 如果k就是中间位置,直接返回'1'
        if k == middle:
            return '1'
        # 如果k在中间位置之前,递归找前半部分
        elif k < middle:
            return self.findKthBit(n - 1, k)
        # 如果k在中间位置之后,递归找后半部分的对应位并翻转
        else:
            # 计算对称位置并递归查找
            if self.findKthBit(n - 1, 2 * middle - k) == '1':
                return '0'
            return '1'

Explore

在这个问题中,字符串Si是由Si-1、'1'和reverse(invert(Si-1))构成的。当k在中间字符之后,它实际上是在reverse(invert(Si-1))部分。这部分字符串是Si-1翻转并反转得到的,所以要找到Si-1中对应的字符位置,需要先计算出对称位置。对称位置计算公式为2^(n-1)*2 - k,即从Si-1的末尾反向计数到k的位置。找到对称位置后,因为这部分是反转的,再根据Si-1的结果反转字符,从而得到正确的字符。

在递归调用中,每次都是基于当前Si的长度来判断和调整k的值。Si的长度通过公式len(Si) = 2 * len(Si-1) + 1计算,确保每次递归时,k的值都是在有效范围内。如果k在中间字符之前,直接递归调用Si-1,此时k是有效的。如果k在中间字符之后,通过计算对称位置2^(n-1)*2 - k,这个值也会保留在Si-1的有效范围内,因为它是从Si的长度中减去k得到的,所以不会超出Si-1的长度。这样每一步的k都是合法且有效的。

在这个问题的上下文中,位置k是从1开始计数的。因此,使用的公式middle = 2^(n-1)指的是从1开始的位置。这意味着Si的中间位置是从字符串的第1个位置开始计算的第2^(n-1)个位置。例如,如果n=2,则middle = 2^(2-1) = 2,表示S2的第2位是中间位。