LeetCode 1888 使二进制字符串交替的最少翻转次数

LeetCode 1888 使二进制字符串交替的最少翻转次数

题目描述

给你一个二进制字符串 s,你可以进行两种操作:

  1. 翻转任意一个字符(0 变 1,1 变 0)。
  2. 将字符串的第一个字符移动到末尾(即旋转)。

你可以任意次旋转,然后进行若干次翻转,使得最终的字符串变成交替字符串(即相邻字符都不相同)。求最少翻转次数。

解题思路

交替字符串的两种模式

长度为 n 的交替字符串只有两种可能:

  • 模式0 :以 0 开头,偶数下标(0,2,4,...)为 0,奇数下标为 1。
  • 模式1 :以 1 开头,偶数下标为 1,奇数下标为 0。

对于任意一个固定的字符串,我们可以统计偶数位置上 0 的个数 和奇数位置上 0 的个数,然后利用总数推导出与两种模式匹配所需翻转次数:

  • 偶数位置总数 = n - n/2(向上取整),奇数位置总数 = n/2(向下取整)。
  • 与模式0匹配的翻转次数 = (偶数位置总数 - 偶数位0的个数) + 奇数位0的个数 = (n - n/2 - even0) + odd0。
  • 与模式1匹配的翻转次数 = 偶数位0的个数 + (奇数位置总数 - 奇数位0的个数) = even0 + (n/2 - odd0)。
    取两者较小值即为当前字符串的最小翻转次数。

旋转的影响

旋转操作会使字符串的索引发生变化:每旋转一次,原第一个字符移到最后,其余字符的索引减 1。这会导致原本在偶数位置的字符变成奇数位置,反之亦然(除了移到末尾的那个字符)。因此,如果我们能维护当前字符串中偶数位置和奇数位置上 0 的个数,就可以通过模拟旋转过程,计算出所有旋转情况下的最少翻转次数。

核心算法

  1. 先统计原始字符串中偶数下标和奇数下标上 0 的个数 even0 和 odd0。
  2. 初始化答案为 n(最大可能值)。
  3. 循环 n 次,模拟每次旋转后的状态更新:
    • 移除当前第一个字符(原字符串的第 i 个字符,它在当前字符串中位于下标 0)。如果该字符是 '0',则 even0--(因为下标 0 是偶数)。
    • 剩余字符的索引全部减 1,奇偶性互换:交换 odd0 和 even0。
    • 把刚才移除的字符放到末尾,它现在位于下标 n-1。如果该字符是 '0',根据 n-1 的奇偶性增加对应的计数(n-1 为偶数则 even0++,否则 odd0++)。
    • 利用当前的 even0 和 odd0 计算两种模式的最小翻转次数,更新答案。
  4. 返回答案。

代码实现(C++)

cpp 复制代码
class Solution {
public:
    int minFlips(string s) {
        int n = s.length();
        int even0 = 0, odd0 = 0; // 原始字符串中偶数下标、奇数下标上 '0' 的个数
        for (int i = 0; i < n; ++i) {
            if (s[i] == '0') {
                if (i & 1) odd0++;   // 奇数下标
                else even0++;         // 偶数下标
            }
        }

        int ans = n; // 初始化答案为最大值 n
        for (int i = 0; i < n; ++i) {
            // 1. 移除第一个字符(下标 0)
            if (s[i] == '0') even0--;

            // 2. 剩余字符索引减1,奇偶互换
            swap(odd0, even0);

            // 3. 把移出的字符放到末尾(下标 n-1)
            if (s[i] == '0') {
                if ((n - 1) & 1) odd0++; // n-1 为奇数
                else even0++;             // n-1 为偶数
            }

            // 计算当前旋转后的字符串所需最少翻转次数
            // 偶数位置总数 = n - n/2,奇数位置总数 = n/2
            int flips = min(even0 + n / 2 - odd0, odd0 + n - n / 2 - even0);
            ans = min(ans, flips);
        }
        return ans; // ans 一定非负,直接返回
    }
};

复杂度分析

  • 时间复杂度:O(n),只需一次遍历和一次循环,没有额外嵌套。
  • 空间复杂度:O(1),只使用了常数个变量。

总结

本题的关键在于理解旋转对字符奇偶位置的影响,通过维护偶数位和奇数位上 0 的个数,可以 O(n) 时间内求出所有旋转情况的最少翻转次数。这种模拟旋转并动态更新计数的方法避免了显式地旋转字符串,非常高效。

扩展:如果题目要求输出最少翻转次数对应的具体旋转次数,也可以在此算法基础上记录。

相关推荐
Gigavision16 分钟前
DEAP数据集 2026最新Transformer算法 DEAP 数据集 下载
深度学习·算法·transformer
夜之眷属39 分钟前
PriorityQueue:for-each 遍历和 poll 取出,顺序一样吗?
java·服务器·数据结构·后端·算法
地平线开发者1 小时前
不是线程越多越快:用 hrt_model_exec 在 征程6M 上给 YOLOv5x 建立性能基线
算法
姜鱼问生2 小时前
fail2ban 实战:从默认配置到生产可用
后端·算法·架构
垆边人似月.2 小时前
华为题:全排列
算法·深度优先·图论
杜 硕2 小时前
快速排序详解01
数据结构·算法·排序算法
Matlab程序猿2 小时前
【MATLAB源码-第459期】基于matlab的D-H模型与阻尼最小二乘逆运动学的六自由度机械臂五次轨迹规划及动力学前馈PD控制仿真。
人工智能·算法·matlab·d-h建模·六自由度串联机械臂
信奥卷王2 小时前
[GESP202606 六级] 满二叉树
数据结构·算法
积流成海3 小时前
用代码实现哈希计算器:Web Crypto API 实战
前端·算法·哈希算法·哈希函数·盘子工具站
Nil2083 小时前
leetcode 300最长递增子序列
算法·leetcode·职场和发展