LeetCode 1545. Find Kth Bit in Nth Binary String
题目描述
Given two positive integers n and k, the binary string S n S_n Sn is formed as follows:
S
1
=
"
0
"
S_1 = "0"
S1="0"
S
i
=
S
i
−
1
+
"
1
"
+
r
e
v
e
r
s
e
(
i
n
v
e
r
t
(
S
i
−
1
)
)
S_i = S_{i-1} + "1" + reverse(invert(S_{i-1}))
Si=Si−1+"1"+reverse(invert(Si−1)) for i > 1
Where + denotes the concatenation operation, reverse(x) returns the reversed string x, and invert(x) inverts all the bits in x (0 changes to 1 and 1 changes to 0).
For example, the first 4 strings in the above sequence are:
S
1
=
"
0
"
S_1 = "0"
S1="0"
S
2
=
"
011
"
S_2 = "011"
S2="011"
S
3
=
"
0111001
"
S_3 = "0111001"
S3="0111001"
S
4
=
"
011100110110001
"
S_4 = "011100110110001"
S4="011100110110001"
Return the
k
k
kth bit in
S
n
S_n
Sn. It is guaranteed that k is valid for the given n.
Example 1:
Input: n = 3, k = 1
Output: “0”
Explanation:
S
3
S_3
S3 is “0111001”. The first bit is “0”.
Example 2:
Input: n = 4, k = 11
Output: “1”
Explanation:
S
4
S_4
S4 is “011100110110001”. The 11th bit is “1”.
Example 3:
Input: n = 1, k = 1
Output: “0”
Example 4:
Input: n = 2, k = 3
Output: “1”
Constraints:
1 <= n <= 20
1 <= k <= 2n - 1
思路
8月8日周赛第二题,根据题目描述,后一步是前一步对称扩展来的。对称中心是一个"1",两边完全对称,只不过右侧做了01反转。题目数据量只有20,从题目描述中我们知道下一项的长度是当前长度的2倍再加上对称中心的那个"1"即: l e n ( S n ) = 2 ∗ l e n ( S n − 1 ) + 1 len(S_n) = 2*len(S_{n-1})+1 len(Sn)=2∗len(Sn−1)+1,直觉上每次都翻倍并且加1,是指数级别增长(后面我们证明)。那 S 20 S_{20} S20也就是 2 20 2^{20} 220数量级,约100万,通过暴力构造也是可以通过的。
这里简单证明一下长度是指数增长的
观察样例中给出的前4项目,长度分别是1, 3, 7, 15,大胆推测一下第n项的长度是
2
n
−
1
2^n-1
2n−1
证明:
数学归纳法:
由
S
1
=
"
0
"
S_1="0"
S1="0"得出
l
e
n
(
S
1
)
=
1
=
2
1
−
1
len(S_1)=1=2^1-1
len(S1)=1=21−1符合结论
假设
l
e
n
(
S
k
)
=
2
k
−
1
len(S_k)=2^k-1
len(Sk)=2k−1
那么
l
e
n
(
S
k
+
1
)
=
2
∗
l
e
n
(
S
k
)
+
1
=
2
∗
(
2
k
−
1
)
+
1
=
2
k
+
1
−
1
len(S_{k+1})=2*len(S_k)+1=2*(2^k-1)+1=2^{k+1}-1
len(Sk+1)=2∗len(Sk)+1=2∗(2k−1)+1=2k+1−1符合结论,证毕
我们还能顺便得出下面这个弱智结论
- l e n ( S n ) len(S_n) len(Sn)一定是个奇数,这个结论也没什么用。。。
利用构造对称性
假设
S
n
−
1
=
x
1
x
2
x
1
x
3
x
4
x
5
x
6
x
7
S_{n-1}=x_1x_2x_1x_3x_4x_5x_6x_7
Sn−1=x1x2x1x3x4x5x6x7
S
n
=
x
1
x
2
x
1
x
3
x
4
x
5
x
6
x
7
1
y
7
y
6
y
5
y
4
y
3
y
2
y
1
S_{n}=x_1x_2x_1x_3x_4x_5x_6x_7\boldsymbol{1}y_7y_6y_5y_4y_3y_2y_1
Sn=x1x2x1x3x4x5x6x71y7y6y5y4y3y2y1
其中
y
i
=
i
n
v
e
r
s
e
(
x
i
)
y_i=inverse(x_i)
yi=inverse(xi)
现在我们要找
S
n
S_n
Sn的第k个字符,我们就要看k落在哪里了
- k落在中间那个1上,那不好意思了,直接输出1
- k落在左半区(中间那个1的左边),根据构造规则,左半区完全是从 S n − 1 S_{n-1} Sn−1抄来的,所以就相当于在 S n − 1 S_{n-1} Sn−1中找到第k个。例如k = 5,求 S n S_n Sn的第5个和求 S n − 1 S_{n-1} Sn−1的第5个没有任何不同。
- k落在右半边(中间那个1的右边),根据对称性,我们可以求出它在左半边对称位置的字符,然后再根据反转规则进行取反就可以得出我们要求的位置了。比如我们要求 y 2 y_2 y2,可以先求出 x 2 x_2 x2然后做个取反操作。为什么总是要先转换到左半边去求呢?因为左半边都是抄来的呀,完全照抄前一项的内容,改都没改。这么一来我们就相当于把问题推个了前一项,要知道前一项的长度可是短了很多。
代码
class Solution {
public:
char findKthBit(int n, int k) {
if ( n == 1 ) return '0'; //base case S_1 = “0”
int len = (1 << n) - 1; //我们前面证明过的指数长度结论
int m = len / 2 + 1; //中间那个1的位置(1 indexed)
if ( k == m ) return '1'; //k落在中间那个1
else if ( k < m ) return findKthBit(n - 1, k);//k落在左半边
else return findKthBit(n - 1, len + 1 - k) ^ 1;//k落在右半边
}
};
对称位置的计算问题
这里解释一下代码最后一行的那个len + 1 - k是怎么算出来的。并且通过这道题回顾一下求对称位置的方法
原理:用头尾index的和守恒
举例:一个数组,头的位置是h,尾的位置是t,求x的对称位置
假设结果是y
h
+
t
=
x
+
y
h + t=x+y
h+t=x+y
得到
y
=
h
+
t
−
x
y=h+t-x
y=h+t−x,对于这道题目,h就是1,t就是len,x是k
这个方法同样适用于求某一段的对称性,h和t并不一定非得是字符串的首尾,可以是任意两个点。
代码最后一步的异或操作
char(‘0’)^1 = char(‘1’)
char(‘1’)^1 = char(‘0’)’
'0’的ASCII值是0x30,'1’是0x31,刚好是最后一个bit不一样,异或1刚好能反转最后一个bit
复杂度分析
时间o(n)
空间o(n)递归空间