普通视图

发现新文章,点击刷新页面。
今天 — 2026年3月6日LeetCode 每日一题题解

简单题,简单做(Python/Java/C++/C/Go/JS/Rust)

作者 endlesscheng
2026年3月6日 08:12

注意 $s$ 不含前导零,那么只含一段连续 $\texttt{1}$ 的 $s$ 只有两种情况:

  • $s$ 全是 $\texttt{1}$。
  • $s$ 是 $\texttt{11}\cdots\texttt{100}\cdots\texttt{0}$,一段 $\texttt{1}$ 和一段 $\texttt{0}$。

如果 $s$ 包含多段连续的 $\texttt{1}$,比如示例 1 的 $s = \texttt{1001}$, $\texttt{0}$ 的后面还有 $\texttt{1}$。所以检查 $s$ 是否包含 $\texttt{01}$ 即可。

:只有一个 $\texttt{1}$ 也算一段连续的 $\texttt{1}$。

###py

class Solution:
    def checkOnesSegment(self, s: str) -> bool:
        return "01" not in s

###java

class Solution {
    public boolean checkOnesSegment(String s) {
        return !s.contains("01");
    }
}

###cpp

class Solution {
public:
    bool checkOnesSegment(string s) {
        return s.find("01") == string::npos;
    }
};

###c

bool checkOnesSegment(char* s) {
    return strstr(s, "01") == NULL;
}

###go

func checkOnesSegment(s string) bool {
return !strings.Contains(s, "01")
}

###js

var checkOnesSegment = function(s) {
    return !s.includes("01");
};

###rust

impl Solution {
    pub fn check_ones_segment(s: String) -> bool {
        !s.contains("01")
    }
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(n)$,其中 $n$ 是 $s$ 的长度。
  • 空间复杂度:$\mathcal{O}(1)$。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

每日一题-检查二进制字符串字段🟢

2026年3月6日 00:00

给你一个二进制字符串 s ,该字符串 不含前导零

如果 s 包含 零个或一个由连续的 '1' 组成的字段 ,返回 true 。否则,返回 false

 

示例 1:

输入:s = "1001"
输出:false
解释:由连续若干个 '1' 组成的字段数量为 2,返回 false

示例 2:

输入:s = "110"
输出:true

 

提示:

  • 1 <= s.length <= 100
  • s[i]'0''1'
  • s[0]'1'

【宫水三叶】简单模拟题

作者 AC_OIer
2022年10月3日 08:33

模拟

根据题意进行模拟即可。

代码:

###Java

class Solution {
    public boolean checkOnesSegment(String s) {
        int n = s.length(), cnt = 0, idx = 0;
        while (idx < n && cnt <= 1) {
            while (idx < n && s.charAt(idx) == '0') idx++;
            if (idx < n) {
                while (idx < n && s.charAt(idx) == '1') idx++;
                cnt++;
            }
        }
        return cnt <= 1;
    }
}

###TypeScript

function checkOnesSegment(s: string): boolean {
    let n = s.length, cnt = 0, idx = 0
    while (idx < n && cnt <= 1) {
        while (idx < n && s[idx] == '0') idx++
        if (idx < n) {
            while (idx < n && s[idx] == '1') idx++
            cnt++
        }
    }
    return cnt <= 1
};

###Python

class Solution:
    def checkOnesSegment(self, s: str) -> bool:
        n, cnt, idx = len(s), 0, 0
        while idx < n and cnt <= 1:
            while idx < n and s[idx] == '0':
                idx += 1
            if idx < n:
                while idx < n and s[idx] == '1':
                    idx += 1
                cnt += 1
        return cnt <= 1
  • 时间复杂度:$O(n)$
  • 空间复杂度:$O(1)$

检查二进制字符串字段

2022年9月28日 10:03

方法一:寻找 $01$ 串

思路与算法

题目给定一个长度为 $n$ 的二进制字符串 $s$,并满足该字符串不含前导零。现在我们需要判断字符串中是否只包含零个或一个由连续 $1$ 组成的字段。首先我们依次分析这两种情况:

  • 字符串 $s$ 中包含零个由连续 $1$ 组成的字段,那么整个串的表示为 $00 \cdots 00$。
  • 字符串 $s$ 中只包含一个由连续 $1$ 组成的字段,因为已知字符串 $s$ 不包含前导零,所以整个串的表示为 $1 \cdots 100 \cdots 00$。

那么可以看到两种情况中都不包含 $01$ 串。且不包含的 $01$ 串的一个二进制字符串也有且仅有上面两种情况。所以我们可以通过原字符串中是否有 $01$ 串来判断字符串中是否只包含零个或一个由连续 $1$ 组成的字段。如果有 $01$ 串则说明该情况不满足,否则即满足该情况条件。

代码

###Python

class Solution:
    def checkOnesSegment(self, s: str) -> bool:
        return "01" not in s

###Java

class Solution {
    public boolean checkOnesSegment(String s) {
        return !s.contains("01");
    }
}

###C#

public class Solution {
    public bool CheckOnesSegment(string s) {
        return !s.Contains("01");
    }
}

###C++

class Solution {
public:
    bool checkOnesSegment(string s) {
        return s.find("01") == string::npos;
    }
};

###C

bool checkOnesSegment(char * s){
    return strstr(s, "01") == NULL;
}

###JavaScript

var checkOnesSegment = function(s) {
    return s.indexOf('01') === -1;
};

###go

func checkOnesSegment(s string) bool {
return !strings.Contains(s, "01")
}

复杂度分析

  • 时间复杂度:$O(n)$,其中 $n$ 为字符串 $s$ 的长度。
  • 空间复杂度:$O(1)$,仅适用常量空间。
昨天 — 2026年3月5日LeetCode 每日一题题解

每日一题-生成交替二进制字符串的最少操作数🟢

2026年3月5日 00:00

给你一个仅由字符 '0''1' 组成的字符串 s 。一步操作中,你可以将任一 '0' 变成 '1' ,或者将 '1' 变成 '0'

交替字符串 定义为:如果字符串中不存在相邻两个字符相等的情况,那么该字符串就是交替字符串。例如,字符串 "010" 是交替字符串,而字符串 "0100" 不是。

返回使 s 变成 交替字符串 所需的 最少 操作数。

 

示例 1:

输入:s = "0100"
输出:1
解释:如果将最后一个字符变为 '1' ,s 就变成 "0101" ,即符合交替字符串定义。

示例 2:

输入:s = "10"
输出:0
解释:s 已经是交替字符串。

示例 3:

输入:s = "1111"
输出:2
解释:需要 2 步操作得到 "0101" 或 "1010" 。

 

提示:

  • 1 <= s.length <= 104
  • s[i]'0''1'

【宫水三叶】简单模拟题

作者 AC_OIer
2022年11月29日 09:18

模拟

最终结果只有「从 0 开始的交替串」和「从 1 开始的交替串」两种。

对于一个长度为 n 的未知序列 A 而言,假设我们需要花费 cnt 次操作将其变为「从 0 开始的交替串」,那么我们想要将其变为「从 1 开始的交替串」则需要 n - cnt 次操作:原本操作的 cnt 个位置不能动,而原本没操作的位置则都需要翻转,从而确保两种交替串对应位均相反。

代码:

###Java

class Solution {
    public int minOperations(String s) {
        int n = s.length(), cnt = 0;
        for (int i = 0; i < n; i++) cnt += (s.charAt(i) - '0') ^ (i & 1);
        return Math.min(cnt, n - cnt);
    }
}

###TypeScript

function minOperations(s: string): number {
    let n = s.length, cnt = 0
    for (let i = 0; i < n; i++) cnt += (s.charCodeAt(i) - '0'.charCodeAt(0)) ^ (i & 1)
    return Math.min(cnt, n - cnt)
}

###Python3

class Solution:
    def minOperations(self, s: str) -> int:
        n, cnt = len(s), 0
        for i, c in enumerate(s):
            cnt += (ord(c) - ord('0')) ^ (i & 1)
        return min(cnt, n - cnt)
  • 时间复杂度:$O(n)$
  • 空间复杂度:$O(1)$

最后

如果有帮助到你,请给题解点个赞和收藏,让更多的人看到 ~ ("▔□▔)/

也欢迎你 关注我,提供写「证明」&「思路」的高质量题解。

所有题解已经加入 刷题指南,欢迎 star 哦 ~

生成交替二进制字符串的最少操作数

2022年11月28日 09:58

方法一:模拟

思路

根据题意,经过多次操作,$s$ 可能会变成两种不同的交替二进制字符串,即:

  • 开头为 $0$,后续交替的字符串;
  • 开头为 $1$,后续交替的字符串。

注意到,变成这两种不同的交替二进制字符串所需要的最少操作数加起来等于 $s$ 的长度,我们只需要计算出变为其中一种字符串的最少操作数,就可以推出另一个最少操作数,然后取最小值即可。

代码

###Python

class Solution:
    def minOperations(self, s: str) -> int:
        cnt = sum(int(c) != i % 2 for i, c in enumerate(s))
        return min(cnt, len(s) - cnt)

###Java

class Solution {
    public int minOperations(String s) {
        int cnt = 0;
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (c != (char) ('0' + i % 2)) {
                cnt++;
            }
        }
        return Math.min(cnt, s.length() - cnt);
    }
}

###C#

public class Solution {
    public int MinOperations(string s) {
        int cnt = 0;
        for (int i = 0; i < s.Length; i++) {
            char c = s[i];
            if (c != (char) ('0' + i % 2)) {
                cnt++;
            }
        }
        return Math.Min(cnt, s.Length - cnt);
    }
}

###C++

class Solution {
public:
    int minOperations(string s) {
        int cnt = 0;
        for (int i = 0; i < s.size(); i++) {
            char c = s[i];
            if (c != ('0' + i % 2)) {
                cnt++;
            }
        }
        return min(cnt, (int)s.size() - cnt);
    }
};

###C

#define MIN(a, b) ((a) < (b) ? (a) : (b))

int minOperations(char * s) {
    int cnt = 0, len = strlen(s);
    for (int i = 0; i < len; i++) {
        char c = s[i];
        if (c != ('0' + i % 2)) {
            cnt++;
        }
    }
    return MIN(cnt, len - cnt);
}

###JavaScript

var minOperations = function(s) {
    let cnt = 0;
    for (let i = 0; i < s.length; i++) {
        const c = s[i];
        if (c !== (String.fromCharCode('0'.charCodeAt() + i % 2))) {
            cnt++;
        }
    }
    return Math.min(cnt, s.length - cnt);
};

###go

func minOperations(s string) int {
    cnt := 0
    for i, c := range s {
        if i%2 != int(c-'0') {
            cnt++
        }
    }
    return min(cnt, len(s)-cnt)
}

func min(a, b int) int {
    if a > b {
        return b
    }
    return a
}

复杂度分析

  • 时间复杂度:$O(n)$,其中 $n$ 为输入 $s$ 的长度,仅需遍历一遍字符串。

  • 空间复杂度:$O(1)$,只需要常数额外空间。

一个判断(炒鸡简单):两种情况比大小,小的就是答案辽

2021年2月14日 12:12

两种情况:
1.偶数位为0,奇数位为1
这种情况下,任意位的值和索引奇偶性相同,即s[i]%2==i%2,若不满足,即需要变动该位,则计数cnt1++
2.偶数位为1,奇数位为0
这种情况下,任意位的值和索引奇偶性不同,即s[i]%2!=i%2,若不满足,即需要变动该位,则计数cnt2++

比较哪种需要变动的位数小

class Solution{
public:
    int minOperations(string s) {
      int n=s.size(),cnt1=0,cnt2=0;
      for(int i=0;i<n;i++){
        if(s[i]%2!=i%2)  cnt1++; 
        else cnt2++;
      }
      return min(cnt1,cnt2);
    }
};
昨天以前LeetCode 每日一题题解

每日一题-找出第 N 个二进制字符串中的第 K 位🟡

2026年3月3日 00:00

给你两个正整数 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

从递归到 O(1) 数学公式(Python/Java/C++/C/Go/JS/Rust)

作者 endlesscheng
2026年2月26日 08:55

方法一:递归 / 迭代

我们需要确定第 $k$ 个字符位于 $S_n$ 的左半、正中间还是右半。为此,首先要知道 $S_n$ 的长度。

用 $|s|$ 表示字符串 $s$ 的长度。根据题意,$|S_1| = 1$,$|S_n| = 2|S_{n-1}| + 1$,所以有

$$
|S_n| + 1 = 2(|S_{n-1}| + 1)
$$

所以 ${|S_n| + 1}$ 是个首项为 $2$,公比为 $2$ 的等比数列,得

$$
|S_n| = 2^n - 1
$$

所以 $|S_{n-1}| = 2^{n-1} - 1$,这说明 $S_n$ 的左半是第 $1$ 个字符到第 $2^{n-1}-1$ 个字符,正中间是第 $2^{n-1}$ 个字符,右半是第 $2^{n-1} + 1$ 个字符到第 $2^n-1$ 个字符。

分类讨论:

  • 如果 $k < 2^{n-1}$,那么第 $k$ 个字符位于 $S_n$ 的左半,问题变成 $S_{n-1}$ 的第 $k$ 个字符。这可以递归解决。
  • 如果 $k > 2^{n-1}$,那么第 $k$ 个字符位于 $S_n$ 的右半,问题变成 $S_{n-1}$ 反转后的第 $k-2^{n-1}$ 个字符,即反转前的第 $2^{n-1}-(k-2^{n-1}) = 2^n-k$ 个字符(比如 $k=2^n-1$ 对应反转前的第 $1$ 个字符)。这个字符再翻转,即为 $S_n$ 的第 $k$ 个字符。这也可以递归解决。

递归边界:

  • 如果 $n=1$,那么返回 $S_1$ 唯一的字符 $\texttt{0}$。
  • 如果 $k = 2^{n-1}$,那么返回 $S_n$ 正中间的字符 $\texttt{1}$。

递归写法

class Solution:
    def findKthBit(self, n: int, k: int) -> str:
        if n == 1:
            return '0'
        if k == 1 << (n - 1):
            return '1'
        if k < 1 << (n - 1):
            return self.findKthBit(n - 1, k)
        res = self.findKthBit(n - 1, (1 << n) - k)
        return '0' if res == '1' else '1'
class Solution {
    public char findKthBit(int n, int k) {
        if (n == 1) {
            return '0';
        }
        if (k == 1 << (n - 1)) {
            return '1';
        }
        if (k < 1 << (n - 1)) {
            return findKthBit(n - 1, k);
        }
        char res = findKthBit(n - 1, (1 << n) - k);
        return (char) (res ^ 1);
    }
}
class Solution {
public:
    char findKthBit(int n, int k) {
        if (n == 1) {
            return '0';
        }
        if (k == 1 << (n - 1)) {
            return '1';
        }
        if (k < 1 << (n - 1)) {
            return findKthBit(n - 1, k);
        }
        return findKthBit(n - 1, (1 << n) - k) ^ 1;
    }
};
char findKthBit(int n, int k) {
    if (n == 1) {
        return '0';
    }
    if (k == 1 << (n - 1)) {
        return '1';
    }
    if (k < 1 << (n - 1)) {
        return findKthBit(n - 1, k);
    }
    return findKthBit(n - 1, (1 << n) - k) ^ 1;
}
func findKthBit(n, k int) byte {
if n == 1 {
return '0'
}
if k == 1<<(n-1) {
return '1'
}
if k < 1<<(n-1) {
return findKthBit(n-1, k)
}
return findKthBit(n-1, 1<<n-k) ^ 1
}
var findKthBit = function(n, k) {
    if (n === 1) {
        return '0';
    }
    if (k === 1 << (n - 1)) {
        return '1';
    }
    if (k < 1 << (n - 1)) {
        return findKthBit(n - 1, k);
    }
    return findKthBit(n - 1, (1 << n) - k) === '1' ? '0' : '1';
};
impl Solution {
    pub fn find_kth_bit(n: i32, k: i32) -> char {
        if n == 1 {
            return '0';
        }
        if k == 1 << (n - 1) {
            return '1';
        }
        if k < 1 << (n - 1) {
            return Self::find_kth_bit(n - 1, k);
        }
        (Self::find_kth_bit(n - 1, (1 << n) - k) as u8 ^ 1) as _
    }
}

迭代写法

class Solution:
    def findKthBit(self, n: int, k: int) -> str:
        rev = 0  # 翻转次数的奇偶性
        while True:
            if n == 1:
                return '1' if rev else '0'
            if k == 1 << (n - 1):
                return '0' if rev else '1'
            if k > 1 << (n - 1):
                k = (1 << n) - k
                rev ^= 1
            n -= 1
class Solution {
    public char findKthBit(int n, int k) {
        int rev = 0; // 翻转次数的奇偶性
        while (true) {
            if (n == 1) {
                return (char) ('0' ^ rev);
            }
            if (k == 1 << (n - 1)) {
                return (char) ('1' ^ rev);
            }
            if (k > 1 << (n - 1)) {
                k = (1 << n) - k;
                rev ^= 1;
            }
            n--;
        }
    }
}
class Solution {
public:
    char findKthBit(int n, int k) {
        int rev = 0; // 翻转次数的奇偶性
        while (true) {
            if (n == 1) {
                return '0' ^ rev;
            }
            if (k == 1 << (n - 1)) {
                return '1' ^ rev;
            }
            if (k > 1 << (n - 1)) {
                k = (1 << n) - k;
                rev ^= 1;
            }
            n--;
        }
    }
};
char findKthBit(int n, int k) {
    int rev = 0; // 翻转次数的奇偶性
    while (true) {
        if (n == 1) {
            return '0' ^ rev;
        }
        if (k == 1 << (n - 1)) {
            return '1' ^ rev;
        }
        if (k > 1 << (n - 1)) {
            k = (1 << n) - k;
            rev ^= 1;
        }
        n--;
    }
}
func findKthBit(n, k int) byte {
rev := byte(0) // 翻转次数的奇偶性
for {
if n == 1 {
return '0' ^ rev
}
if k == 1<<(n-1) {
return '1' ^ rev
}
if k > 1<<(n-1) {
k = 1<<n - k
rev ^= 1
}
n--
}
}
var findKthBit = function(n, k) {
    let rev = 0; // 翻转次数的奇偶性
    while (true) {
        if (n === 1) {
            return rev ? '1' : '0';
        }
        if (k === 1 << (n - 1)) {
            return rev ? '0' : '1';
        }
        if (k > 1 << (n - 1)) {
            k = (1 << n) - k;
            rev ^= 1;
        }
        n--;
    }
};
impl Solution {
    pub fn find_kth_bit(mut n: i32, mut k: i32) -> char {
        let mut rev = 0; // 翻转次数的奇偶性
        loop {
            if n == 1 {
                return (b'0' ^ rev) as _;
            }
            if k == 1 << (n - 1) {
                return (b'1' ^ rev) as _;
            }
            if k > 1 << (n - 1) {
                k = (1 << n) - k;
                rev ^= 1;
            }
            n -= 1;
        }
    }
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(n)$。
  • 空间复杂度:$\mathcal{O}(1)$。

方法二:数学公式

奇数位

$S_4 = \texttt{011100110110001}$,只看奇数位(下标从 $1$ 开始)的字符,是 $\texttt{01010101}$,这是一个 $\texttt{01}$ 交替序列,为什么?

只看奇数位:

  • $S_1 = \texttt{0}$。
  • $S_2$ 把 $\texttt{0}$ 反转再翻转,得到 $\texttt{1}$,拼起来是 $\texttt{01}$。
  • $S_3$ 把 $\texttt{01}$ 反转再翻转,得到 $\texttt{01}$,拼起来是 $\texttt{0101}$。
  • $S_4$ 把 $\texttt{0101}$ 反转再翻转,得到 $\texttt{0101}$,拼起来是 $\texttt{01010101}$。

一般地,由于 $\texttt{01}$ 交替序列反转再翻转,结果不变,所以从 $S_{i-1}$ 到 $S_i\ (i\ge 3)$,其中奇数位相当于复制了一份自身,拼在了自身后面,得到的仍然是 $\texttt{01}$ 交替序列。

所以,当 $k$ 是奇数时,可以立刻得出答案:

  • 设 $k' = \dfrac{k-1}{2}$。这会把 $k=1,3,5,7,\ldots$ 变成 $k'=0,1,2,3,\ldots$
  • 如果 $k'$ 是偶数,那么答案是 $\texttt{0}$。
  • 如果 $k'$ 是奇数,那么答案是 $\texttt{1}$。
  • 一般地,答案为 $k'\bmod 2$ 对应的字符。

偶数位

奇数位的字符,都发源于 $S_1 = \texttt{0}$。

偶数位的字符呢?都发源于 $S_i\ (i\ge 2)$ 正中间的那个 $\texttt{1}$,即位置为 $2,4,8,16,\ldots$ 的字符 $\texttt{1}$。

根据方法一的结论,$S_{n-1}$ 的第 $k$ 个字符,反转后,是 $S_n$ 的第 $2^n-k$ 个字符。

$2^n-k$ 有什么性质?

比如二进制 $10000 - 100 = 1100$,去掉末尾的两个 $0$,相当于 $100 - 1 = 11$,结果最低位一定是 $1$,所以 $100$ 和 $1100$ 的尾零个数相同。一般地,$k$ 和 $2^n-k$ 的尾零个数是相同的,这是个不变量!我们可以根据 $k$ 的尾零个数,找到 $k$ 发源于哪个 $S_i$ 正中间的 $\texttt{1}$。

以 $S_2$ 的中间字符(第 $2$ 个字符)为例:

  • 我们把 $S_2$ 的第 $2$ 个字符反转到了 $S_3$ 的第 $8-2=6$ 个字符。把 $\texttt{1}$ 反转再翻转,得到 $\texttt{0}$,拼起来是 $\texttt{10}$。
  • 我们把 $S_3$ 的第 $2,6$ 个字符反转到了 $S_4$ 的第 $14,10$ 个字符。把 $\texttt{10}$ 反转再翻转,得到 $\texttt{10}$,拼起来是 $\texttt{1010}$。注意 $2,6,10,14$ 的二进制尾零个数都是 $1$,且这些位置上的字符拼起来是一个 $\texttt{10}$ 交替序列。

一般地,设 $t$ 为 $k$ 去掉尾零后的值,即 $k = t\cdot 2^x$ 且 $t$ 是奇数。比如 $k=2,6,10,14,\ldots$ 对应着 $t=1,3,5,7,\ldots$

  • 设 $t' = \dfrac{t-1}{2}$。这会把 $t=1,3,5,7,\ldots$ 变成 $t'=0,1,2,3,\ldots$
  • 如果 $t'$ 是偶数,那么答案是 $\texttt{1}$。
  • 如果 $t'$ 是奇数,那么答案是 $\texttt{0}$。
  • 一般地,答案为 $1 - t'\bmod 2$ 对应的字符。

如何去掉 $k$ 的尾零?把 $k$ 除以其 $\text{lowbit}$ 即可。关于 $\text{lowbit}$ 的原理,请看 从集合论到位运算,常见位运算技巧分类总结

class Solution:
    def findKthBit(self, _, k: int) -> str:
        if k % 2:
            return str(k // 2 % 2)
        k //= k & -k  # 去掉 k 的尾零
        return str(1 - k // 2 % 2)
class Solution {
    public char findKthBit(int n, int k) {
        if (k % 2 > 0) {
            return (char) ('0' + k / 2 % 2);
        }
        k /= k & -k; // 去掉 k 的尾零
        return (char) ('1' - k / 2 % 2);
    }
}
class Solution {
public:
    char findKthBit(int, int k) {
        if (k % 2) {
            return '0' + k / 2 % 2;
        }
        k /= k & -k; // 去掉 k 的尾零
        return '1' - k / 2 % 2;
    }
};
char findKthBit(int, int k) {
    if (k % 2) {
        return '0' + k / 2 % 2;
    }
    k /= k & -k; // 去掉 k 的尾零
    return '1' - k / 2 % 2;
}
func findKthBit(_, k int) byte {
if k%2 > 0 {
return '0' + byte(k/2%2)
}
k /= k & -k // 去掉 k 的尾零
return '1' - byte(k/2%2)
}
var findKthBit = function(_, k) {
    if (k % 2) {
        return (k - 1) / 2 % 2 ? '1' : '0';
    }
    k /= k & -k; // 去掉 k 的尾零
    return (k - 1) / 2 % 2 ? '0' : '1';
};
impl Solution {
    pub fn find_kth_bit(_: i32, mut k: i32) -> char {
        if k % 2 > 0 {
            return (b'0' + k as u8 / 2 % 2) as _;
        }
        k /= k & -k; // 去掉 k 的尾零
        (b'1' - k as u8 / 2 % 2) as _
    }
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(1)$。
  • 空间复杂度:$\mathcal{O}(1)$。

相似题目

见下面回溯题单的「五、其他递归/分治」。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

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

2020年8月20日 20:51

方法一:递归

观察二进制字符串 $S_n$,可以发现,当 $n>1$ 时,$S_n$ 是在 $S_{n-1}$ 的基础上形成的。用 $\text{len}n$ 表示 $S_n$ 的长度,则 $S_n$ 的前 $\text{len}{n-1}$ 个字符与 $S_{n-1}$ 相同。还可以发现,当 $n>1$ 时,$\text{len}n=\text{len}{n-1} \times 2 + 1$,根据 $\text{len}_1=1$ 可知 $\text{len}_n=2^n-1$。

由于 $S_1=``0"$,且对于任意 $n \ge 1$,$S_n$ 的第 $1$ 位字符也一定是 $0'$,因此当 $k=1$ 时,直接返回字符 $0'$。

当 $n>1$ 时,$S_n$ 的长度是 $2^n-1$。$S_n$ 可以分成三个部分,左边 $2^{n-1}-1$ 个字符是 $S_{n-1}$,中间 $1$ 个字符是 $1'$,右边 $2^{n-1}-1$ 个字符是 $S_{n-1}$ 翻转与反转之后的结果。中间的字符 $1'$ 是 $S_n$ 的第 $2^{n-1}$ 位字符,因此如果 $k=2^{n-1}$,直接返回字符 $`1'$。

当 $k \ne 2^{n-1}$ 时,考虑以下两种情况:

  • 如果 $k<2^{n-1}$,则第 $k$ 位字符在 $S_n$ 的前半部分,即第 $k$ 位字符在 $S_{n-1}$ 中,因此在 $S_{n-1}$ 中寻找第 $k$ 位字符;

  • 如果 $k>2^{n-1}$,则第 $k$ 位字符在 $S_n$ 的后半部分,由于后半部分为前半部分进行翻转与反转之后的结果,因此在前半部分寻找第 $2^n-k$ 位字符,将其反转之后即为 $S_n$ 的第 $k$ 位字符。

上述过程可以通过递归实现。

###Java

class Solution {
    public char findKthBit(int n, int k) {
        if (k == 1) {
            return '0';
        }
        int mid = 1 << (n - 1);
        if (k == mid) {
            return '1';
        } else if (k < mid) {
            return findKthBit(n - 1, k);
        } else {
            k = mid * 2 - k;
            return invert(findKthBit(n - 1, k));
        }
    }

    public char invert(char bit) {
        return (char) ('0' + '1' - bit);
    }
}

###JavaScript

const invert = (bit) => bit === '0' ? '1' : '0';

var findKthBit = function(n, k) {
    if (k == 1) {
        return '0';
    }
    const mid = 1 << (n - 1);
    if (k == mid) {
        return '1';
    } else if (k < mid) {
        return findKthBit(n - 1, k);
    } else {
        k = mid * 2 - k;
        return invert(findKthBit(n - 1, k));
    }
};

###C++

class Solution {
public:
    char findKthBit(int n, int k) {
        if (k == 1) {
            return '0';
        }
        int mid = 1 << (n - 1);
        if (k == mid) {
            return '1';
        } else if (k < mid) {
            return findKthBit(n - 1, k);
        } else {
            k = mid * 2 - k;
            return invert(findKthBit(n - 1, k));
        }
    }

    char invert(char bit) {
        return (char) ('0' + '1' - bit);
    }
};

###Python

class Solution:
    def findKthBit(self, n: int, k: int) -> str:
        if k == 1:
            return "0"
        
        mid = 1 << (n - 1)
        if k == mid:
            return "1"
        elif k < mid:
            return self.findKthBit(n - 1, k)
        else:
            k = mid * 2 - k
            return "0" if self.findKthBit(n - 1, k) == "1" else "1"

###C#

public class Solution {
    public char FindKthBit(int n, int k) {
        if (k == 1) {
            return '0';
        }
        int mid = 1 << (n - 1);
        if (k == mid) {
            return '1';
        } else if (k < mid) {
            return FindKthBit(n - 1, k);
        } else {
            k = mid * 2 - k;
            return Invert(FindKthBit(n - 1, k));
        }
    }

    private char Invert(char bit) {
        return (char)('0' + '1' - bit);
    }
}

###Go

func findKthBit(n int, k int) byte {
    if k == 1 {
        return '0'
    }
    mid := 1 << (n - 1)
    if k == mid {
        return '1'
    } else if k < mid {
        return findKthBit(n - 1, k)
    } else {
        k = mid*2 - k
        return invert(findKthBit(n - 1, k))
    }
}

func invert(bit byte) byte {
    if bit == '0' {
        return '1'
    }
    return '0'
}

###C

char invert(char bit) {
    return '0' + '1' - bit;
}

char findKthBit(int n, int k) {
    if (k == 1) {
        return '0';
    }
    int mid = 1 << (n - 1);
    if (k == mid) {
        return '1';
    } else if (k < mid) {
        return findKthBit(n - 1, k);
    } else {
        k = mid * 2 - k;
        return invert(findKthBit(n - 1, k));
    }
}

###TypeScript

function findKthBit(n: number, k: number): string {
    if (k === 1) {
        return '0';
    }
    const mid = 1 << (n - 1);
    if (k === mid) {
        return '1';
    } else if (k < mid) {
        return findKthBit(n - 1, k);
    } else {
        k = mid * 2 - k;
        return invert(findKthBit(n - 1, k));
    }
}

function invert(bit: string): string {
    return bit === '0' ? '1' : '0';
}

###Rust

impl Solution {
    pub fn find_kth_bit(n: i32, k: i32) -> char {
        Self::find_kth_bit_recursive(n, k)
    }
    
    fn find_kth_bit_recursive(n: i32, k: i32) -> char {
        if k == 1 {
            return '0';
        }
        let mid = 1 << (n - 1);
        if k == mid {
            return '1';
        } else if k < mid {
            return Self::find_kth_bit_recursive(n - 1, k);
        } else {
            let new_k = mid * 2 - k;
            return Self::invert(Self::find_kth_bit_recursive(n - 1, new_k));
        }
    }
    
    fn invert(bit: char) -> char {
        if bit == '0' {
            '1'
        } else {
            '0'
        }
    }
}

复杂度分析

  • 时间复杂度:$O(n)$。字符串 $S_n$ 的长度为 $2^n-1$,每次递归调用可以将查找范围缩小一半,因此时间复杂度为 $O(\log 2^n)=O(n)$。

  • 空间复杂度:$O(n)$。空间复杂度主要取决于递归调用产生的栈空间,递归调用层数不会超过 $n$。

递归——双百(logn)

作者 233999
2020年8月9日 12:13

解题思路

递归 将时间复杂度降到logn
力扣.png

代码

###cpp

class Solution {
private:
    char ch_not(char ch) {
        if(ch == '0') { return '1'; }
        else          { return '0'; }
    }
public:
    char findKthBit(int n, int k) {
        if(n == 1) { return '0'; }
        int mid = (1<<(n-1));
        if(k == mid) { return '1'; }
        if(k < mid) { return findKthBit(n-1, k); }
        return ch_not(findKthBit(n-1, (1<<n) - k)); 
    }
};

每日一题-十-二进制数的最少数目🟡

2026年3月1日 00:00

如果一个十进制数字不含任何前导零,且每一位上的数字不是 0 就是 1 ,那么该数字就是一个 十-二进制数 。例如,1011100 都是 十-二进制数,而 1123001 不是。

给你一个表示十进制整数的字符串 n ,返回和为 n十-二进制数 的最少数目。

 

示例 1:

输入:n = "32"
输出:3
解释:10 + 11 + 11 = 32

示例 2:

输入:n = "82734"
输出:8

示例 3:

输入:n = "27346209830709182346"
输出:9

 

提示:

  • 1 <= n.length <= 105
  • n 仅由数字组成
  • n 不含任何前导零并总是表示正整数

脑筋急转弯(Python/Java/C++/C/Go/JS/Rust)

作者 endlesscheng
2026年2月24日 15:21

例如 $n=321$,其中最大的数字是 $3$。这个 $3$ 至少要拆分成 $3$ 个 $1$,即 $321=1__ + 1__ + 1__$。对于 $n$ 中的其余数字 $d$,可以拆分成 $d$ 个 $1$ 和 $3-d$ 个 $0$,即 $2=1+1+0$ 和 $1=1+0+0$,填到对应的位置上,得到 $321 = 111 + 110 + 100$。

一般地,设 $m$ 为 $n$ 中的最大数字,那么答案为 $m$。构造方案为:设 $n$ 的第 $i$ 个数字为 $n_i$,那么拆分出的这 $m$ 个数的第 $i$ 位上,有 $n_i$ 个 $1$ 和 $m-n_i$ 个 $0$(填入顺序随意)。

###py

class Solution:
    def minPartitions(self, n: str) -> int:
        return int(max(n))

###java

class Solution {
    public int minPartitions(String n) {
        int mx = 0;
        for (char ch : n.toCharArray()) {
            mx = Math.max(mx, ch);
        }
        return mx - '0';
    }
}

###cpp

class Solution {
public:
    int minPartitions(string n) {
        return ranges::max(n) - '0';
    }
};

###c

#define MAX(a, b) ((b) > (a) ? (b) : (a))

int minPartitions(char* n) {
    char mx = 0;
    for (int i = 0; n[i]; i++) {
        mx = MAX(mx, n[i]);
    }
    return mx - '0';
}

###go

func minPartitions(n string) int {
ans := rune(0)
for _, ch := range n {
ans = max(ans, ch)
}
return int(ans - '0')
}

###js

var minPartitions = function(n) {
    return Number(_.max(n));
};

###rust

impl Solution {
    pub fn min_partitions(n: String) -> i32 {
        (n.as_bytes().iter().max().unwrap() - b'0') as _
    }
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(|n|)$,其中 $|n|$ 表示 $n$ 的长度。
  • 空间复杂度:$\mathcal{O}(1)$。

专题训练

见下面贪心与思维题单的「§5.2 脑筋急转弯」。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

c++/python3 均一行代码 本质:找最大的数字

作者 HanXin_HanXin
2021年4月15日 18:10

思路和心得:

1.因为分解时,长度是不限的

2.好比有一片连绵的山。每次最多消掉一层,消掉时宽度不限

最少的次数取决于peak的高度

class Solution:
    def minPartitions(self, n: str) -> int:
        ###### 本质: 找最大的数字
        return int(max(n))
class Solution 
{
public:
    int minPartitions(string n) 
    {
        ////////// 本质:找最大的数字
        return *max_element(n.begin(), n.end()) - '0';
    }
};

十_二进制

作者 zhu-freshzhu
2020年12月13日 16:28

解题思路

把每一个数字分解成若干个1,竖着将所有分解的1排列起来,以最下方的数字作为基准,上方空白的地方全部补零。
即:行数即为最小数目。也即,字符串中最大的数字就是最少数目。
    接下来的任务就是找到字符串中最大的数字是多少,可在遍历数组时利用flag记录较大数字的值。

例如:                  3 2
    可分解成=>          1 1   ····第一行
                        1 1   ····第二行
                        1 0   ····第三行
上面的0和1可随意排列组合。

代码

###cpp

class Solution {
public:
    int minPartitions(string n) {
        int flag=n[0]-'0';
        for(int i=0;i<n.length();i++){
            if(flag<(n[i]-'0'))
            flag=n[i]-'0';
        }
        return flag;
    }
};

每日一题-连接连续二进制数字🟡

2026年2月28日 00:00

给你一个整数 n ,请你将 1 到 n 的二进制表示连接起来,并返回连接结果对应的 十进制 数字对 109 + 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 <= 105

Java Beat 100%

作者 civitas
2020年12月6日 13:28

原理:

  1. 使用位运算思想
  2. 假设n=3
    • n=1 二进制为1
    • n=2 二进制为10
    • n=3 二进制为11

假设res为最终结果值,我们一般会想到先将1-n的二进制字符串都求出来然后拼接,再转为十进制,比较暴力。

但其实有更简单的算法,因为,拼接的结果无非是res二进制向左移 $x_i$ 位得到的值与所拼接二进制字符串值的和。
因此,事实上,$x_i$ 的值便是求解的关键。

容易看出,$x_i$ 即为值i表示的二进制字符串的位数,如何求得二进制字符串的位数呢?
类比于十进制,10的整数次幂所表示的值的10进制位数刚好差距为1,如$10^0$,$10^1$,$10^2$,$10^3$
类似的,$2^0$,$2^1$,$2^2$,$2^3$所表示的二进制的位数也刚好差距为1
我们可以利用这一点来求解。
如以n=3为例:
n=1时,二进制为1,res向左移动1位,与1相加,res值为1;
n=2时,二进制为10,res向左移动2位,与2相加,res值为6;
n=3时,二进制为11,res向左移动2位,与3相加,res值为27.

因此,我们只需要判断当前n值是否为为2的幂,如果是,位数偏移在之前的基础上加1,否则位数偏移不变
判断n值是否为2的幂方法有很多,在这里我采用了一种比较简单的方法i & (i-1)是否等于0,如果i是2的幂,说明仅某一位为1,其余均为0,那么i-1即为其余位均为1,自然与运算为0。如果难以理解可以想象99和100的关系。

下面是全部代码:

###java

public class Solution {
    private static final int MOD = 1000000007;

    public int concatenatedBinary(int n) {
        int res = 0, shift = 0;
        for (int i = 1; i <= n; i++) {
            if ((i & (i - 1)) == 0) {
                // 说明是2的幂,则进位
                shift++;
            }
            res = (int) ((((long) res << shift) + i) % MOD);
        }
        return res;
    }
}

Golang 简洁写法

作者 endlesscheng
2020年12月6日 12:32

用位运算模拟这个过程:每拼接一个数 $i$,就把之前拼接过的数左移 $i$ 的二进制长度,然后加上 $i$。

由于左移后空出的位置全为 $0$,加法运算也可以写成或运算。

###go

func concatenatedBinary(n int) (ans int) {
    for i := 1; i <= n; i++ {
        ans = (ans<<bits.Len(uint(i)) | i) % (1e9 + 7)
    }
    return
}
❌
❌