689 字
3 分钟
1888. 使二进制字符串字符交替的最少反转次数
1888. 使二进制字符串字符交替的最少反转次数
题目描述
给你一个二进制字符串 s,你可以进行两类操作:
- 旋转:将第一个字符移动到末尾(可执行任意次);
- 翻转:把一个字符 0↔1(可执行任意次)。
先做任意次旋转,再做任意次翻转。问:最少需要翻转多少次,才能让字符串变成相邻字符均不同(交替)。
示例 :
输入:s = "111000" → 输出:2输入:s = "010" → 输出:0输入:s = "1110" → 输出:1解法思路
核心思想:旋转 k 次等价于在环形数组上取一个长度为 n 的窗口。
把 s 复制一份拼成 s + s,则”旋转 k 次后再翻转”就等价于”在 s+s 上取起点为 l、长度 n 的窗口”,再把这个窗口翻成某个交替模式。交替模式只有两种:0101...(A)与 1010...(B)。
- 预处理
diff数组:diff[i] = 1当且仅当s2[i]与”以 0 开头、按全局下标奇偶”的目标位不同。即diff[i] = (s2[i] != (i 为偶 ? '0' : '1'))。 - 对于起点
l的长度n窗口,窗口内相对下标0..n-1的奇偶与全局下标l..l+n-1一致需满足l为偶数:l为偶数时:翻成模式 A 的代价 =cur(窗口内 diff 之和),翻成模式 B 的代价 =n - cur;l为奇数时:二者互换(用flips = cur if l%2==0 else n-cur统一表达,同时取min(flips, n-flips)覆盖两种模式)。
- 窗口整体每次右移一位,代价用
cur += diff[l+n] - diff[l]在 O(1) 内更新。取所有窗口的最小值即可。
逐步举例(s = "111000",n = 6):
s2 = "111000111000",diff(以0开头交替,'0'目标位为偶数下标):i0:'1'→1, i1:'1'→0, i2:'1'→1, i3:'0'→1, i4:'0'→0, i5:'0'→1,前 6 位和cur = 4。l=0:flips = cur = 4,取min(4, 6-4=2) = 2。即翻成模式 B 只需 2 次(把下标 1、3 两处对应的位翻掉)。后续所有窗口最小仍是 2,答案2✓。
复杂度分析
- 时间复杂度:O(n),单次线性扫描。
- 空间复杂度:O(n),
diff数组长度 2n。
代码实现
class Solution: def minFlips(self, s: str) -> int: n = len(s) s2 = s + s diff = [0] * (2 * n) for i, ch in enumerate(s2): target = '0' if i % 2 == 0 else '1' diff[i] = 1 if ch != target else 0
cur = sum(diff[:n]) ans = n for l in range(n): flips = cur if l % 2 == 0 else n - cur ans = min(ans, flips, n - flips) cur += diff[l + n] - diff[l] return ansimport sysimport unittestfrom pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))from solution import Solution
class TestMinFlips(unittest.TestCase): def setUp(self): self.solution = Solution()
def test_example1(self): self.assertEqual(self.solution.minFlips("111000"), 2)
def test_example2(self): self.assertEqual(self.solution.minFlips("010"), 0)
def test_example3(self): self.assertEqual(self.solution.minFlips("1110"), 1)
def test_single_char(self): self.assertEqual(self.solution.minFlips("0"), 0) self.assertEqual(self.solution.minFlips("1"), 0)
def test_already_alternating(self): self.assertEqual(self.solution.minFlips("01"), 0) self.assertEqual(self.solution.minFlips("10"), 0) self.assertEqual(self.solution.minFlips("0101"), 0)
def test_all_same(self): self.assertEqual(self.solution.minFlips("000"), 1) self.assertEqual(self.solution.minFlips("111"), 1) self.assertEqual(self.solution.minFlips("0000"), 2)
def test_longer(self): self.assertEqual(self.solution.minFlips("01001001101"), 2)
if __name__ == "__main__": unittest.main() 1888. 使二进制字符串字符交替的最少反转次数
https://h1s97x.github.io/posts/coding/coding-1888-min-flips/