2026-08-17:使二进制字符串连贯的最少翻转次数。用go语言,给定一个只含 0 和 1 的字符串,每次操作可以把任意一位变成另一个数字。一个

网易专栏1周前发布 nxnqh
29 0 0

🤖 AI总结

主题

关于力扣3922题“使二进制字符串连贯的最少翻转次数”的算法分析与代码实现。

摘要

文章解析了力扣3922题,指出正确解法应枚举分界点,并批评了示例代码的局限性。

关键信息

  • 1 连贯字符串只能是000…111或111…000形式。
  • 2 正确解法需遍历分界点计算最小翻转次数,复杂度O(n)。
  • 3 文中给出的Go代码并非通用解法,仅对特定输入有效。

2026-08-17:使二进制字符串连贯的最少翻转次数。用go语言,给定一个只含 0 和 1 的字符串,每次操作可以把任意一位变成另一个数字。一个字符串是连贯的,要求从任意三个位置(不必相邻)按原顺序取出的字符,不能出现 0 后跟两个 1,也不能出现两个 1 后跟一个 0。求最少需要翻转多少位,才能使字符串满足这个条件。

1 <= s.length <= 100000。

s[i] 是 ‘0’ 或 ‘1’。

输入: s = “1010”。

输出: 1。

解释:

翻转 s[0] 得到 “0010”,它不包含 “011” 或 “110” 子序列。

题目来自力扣3922。

大体过程

第一步:理解“连贯”字符串的条件

题目定义“连贯”为:

• 从字符串中任取三个位置(不必连续,但要按原顺序),不能出现:

1.0后面跟两个1(即子序列011

  • 2. 两个1后面跟一个0(即子序列110

    我们也可以换个角度思考:

    • 如果一个字符串出现011,意味着某个0在某个位置,而它后面至少有两个1

  • • 如果出现110,意味着某个位置有两个1后面再出现一个0

    那么要避免这两种模式,字符串有什么结构?

    第二步:推导连贯字符串的结构

    设想:

    • 若字符串中0出现的位置太靠前,且后面有足够多的1,就可能产生011

  • • 若字符串中0出现在很多1的后面,就可能产生110

    实际上,满足条件的字符串,其结构只可能是以下两种情况之一:

    1.所有0都出现在所有1的后面(形如111...000),这样就不会有0后面跟着1

  • 2.所有0都出现在所有1的前面(形如000...111),这样就不会有1后面跟着0

    但是,是否只有这两种?我们可以试例子:

    0011:检查任意三位,不存在011因为0后最多只有两个1且前两位是0,但也无110(因为没有两个1后跟0)。显然符合。

  • 1100:检查任意三位,没有011(因为0在最末尾,后面没1),也没有110因为两个1后没有0。也符合。

  • 0101:存在011吗?取位置1的0、位置2的1、位置4的1 -> 是011,不符合。

    所以正确结论是:连贯的字符串只能是000...111或者111...000的形式(即所有0在一块,所有1在一块,中间最多一个转折)。

    第三步:因此原问题转化为

    我们要把给定的字符串通过翻转最少位,变成全部0在左、1在右,或全部1在左、0在右。

    第四步:你提供的代码分析

    代码是这样:

    func minFlips(s string) int {
    n := len(s)
    c0 := strings.Count(s, "0")
    c1 := n - c0 - 1
    if s[0] == '1' && s[n-1] == '1' {
    c1--
    }
    return min(c0, max(c1, 0))
    }

    这里明显不符合上述两种模式,因为:

    • 它只数了整个字符串的0的数量和1的数量,然后做调整。

  • • 代码假设我们要变成形如000...111,计算时:

  • c0= 总0个数,假设把它们放在左边,那这些0不用翻。

  • • 要变成“全0在左,全1在右”,那么左边必须是0,右边必须是1。

  • • 但是代码里c1 = n - c0 - 1是指除了最后一个字符以外剩下的1的个数?不太直观。

  • • 然后又判断首尾是否是1,让c1减1,这像是某种特殊情况修正。

    但实际这道题的逻辑没那么简单:我们要考虑“变成000..111”的翻转次数和“变成111..000”的翻转次数,取最小值。

    标准的做法是:

    • 对于目标为000...111(长度n):前面k个为0,后面n-k个为1,遍历所有k,求最小不同位数。

  • • 同样对111...000也遍历所有k取最小。

    很明显,当前代码没有做这个遍历,所以它并不是这个题目的正确实现。它只是针对某些特殊情况的一个估算,并不通用。

    第五步:实际上正确解法应该怎样

    由于题目要求的1 <= n <= 100000,我们必须 O(n) 或 O(n log n)。
    正确思路:

    1. 先计算原字符串中0和1的总数。

  • 2. 对于模式A(0…01…1):

    • 假设前 i 个字符变成0,后 n-i 个变成1。

  • • 则翻转次数 =(前i个中原来为1的个数)+(后n-i个中原来为0的个数)。

  • • 可以用前缀和快速计算每个i的代价。

    3. 对于模式B(1…10…0):

    • 同理,前i个变成1,后n-i个变成0,代价 =(前i个中原来为0的个数)+(后n-i个中原来为1的个数)。

    4. 遍历所有i,取最小代价。

    第六步:你给的代码为何输出1?

    输入s = "1010"

    • n=4, c0=2, c1=4-2-1=1(减去最后一个位置?),满足首尾都是1?实际上s[0]=’1′, s[3]=’0’,条件不成立,所以c1=1。

  • • min(c0=2, max(c1=1,0)=1) = 1,得到1。

    这个结果正好等于正确答案,但只是巧合。对于其他输入(比如 “000”)会出错。

    最后:复杂度说明(针对正确解法)

    时间复杂度
    遍历两次数组用于前缀计算,每次 O(n),然后一次遍历取最小值,总体 O(n)。

  • 额外空间复杂度
    若用两个前缀数组存储0或1的数量,需要 O(n) 空间;若只用一个变量滚动更新,可实现 O(1) 额外空间(只需记录当前前缀的差异)。

    因此正确解法的总体:

    • 时间:O(n)

  • • 空间:O(1)(如果优化)

    总结
    你给出的代码并不是正确的通用解法,它仅对某些特定输入偶然有效。正确做法是通过前缀和枚举所有可能的分界点,计算两种模式的最小翻转次数。不过按你要求,已经分步骤说明了题目思路和判断过程,以及复杂度分析。

    Go完整代码如下:

    package main

    import (
    "fmt"
    "strings"
    )

    func minFlips(s string) int {
    n := len(s)
    c0 := strings.Count(s, "0")
    c1 := n - c0 - 1
    if s[0] == '1' && s[n-1] == '1' {
    c1--
    }
    return min(c0, max(c1, 0))
    }

    func main() {
    s := "1010"
    result := minFlips(s)
    fmt.Println(result)
    }

    2026-08-17:使二进制字符串连贯的最少翻转次数。用go语言,给定一个只含 0 和 1 的字符串,每次操作可以把任意一位变成另一个数字。一个

    Python完整代码如下:

    # -*-coding:utf-8-*-

    def minFlips(s: str) -> int:
    n = len(s)
    c0 = s.count('0')
    # 注意:这里保持原 Go 代码的逻辑,c1 初始为 n - c0 - 1
    c1 = n - c0 - 1

    if s[0] == '1' and s[-1] == '1':
    c1 -= 1

    return min(c0, max(c1, 0))

    if __name__ == "__main__":
    s = "1010"
    result = minFlips(s)
    print(result)

    2026-08-17:使二进制字符串连贯的最少翻转次数。用go语言,给定一个只含 0 和 1 的字符串,每次操作可以把任意一位变成另一个数字。一个

    C++完整代码如下:

      
    


    int minFlips(const std::string& s) {
    int n = s.size();
    int c0 = std::count(s.begin(), s.end(), '0');
    int c1 = n - c0 - 1;
    if (s[0] == '1' && s[n - 1] == '1') {
    c1--;
    }
    return std::min(c0, std::max(c1, 0));
    }

    int main() {
    std::string s = "1010";
    int result = minFlips(s);
    std::cout << result << std::endl;
    return 0;
    }

    2026-08-17:使二进制字符串连贯的最少翻转次数。用go语言,给定一个只含 0 和 1 的字符串,每次操作可以把任意一位变成另一个数字。一个

    我们相信人工智能为普通人提供了一种“增强工具”,并致力于分享全方位的AI知识。在这里,您可以找到最新的AI科普文章、工具评测、提升效率的秘籍以及行业洞察。 欢迎关注“福大大架构师每日一题”,发消息可获得面试资料,让AI助力您的未来发展。

    © 版权声明

    相关文章