贪心题目:最少的后缀翻转次数

文章目录

题目

标题和出处

标题:最少的后缀翻转次数

出处:1529. 最少的后缀翻转次数

难度

4 级

题目描述

要求

给定一个长度为 n \texttt{n} n、下标从 0 \texttt{0} 0 开始的二进制字符串 target \texttt{target} target。有另一个长度为 n \texttt{n} n 的二进制字符串 s \texttt{s} s,初始时 s \texttt{s} s 的每一位上都是 0 \texttt{0} 0。目标是让 s \texttt{s} s 和 target \texttt{target} target 相等。

一步操作中,可以选择下标 i \texttt{i} i( 0 ≤ i < n \texttt{0} \le \texttt{i} < \texttt{n} 0≤i<n)并翻转在闭区间 i, n − 1 \texttt{i, n} - \\texttt{1} i, n−1 内的所有位。翻转的含义是 '0' \texttt{`0'} '0' 变为 '1' \texttt{`1'} '1', '1' \texttt{`1'} '1' 变为 '0' \texttt{`0'} '0'。

返回使 s \texttt{s} s 与 target \texttt{target} target 相等需要的最少翻转次数。

示例

示例 1:

输入: target = "10111" \texttt{target = "10111"} target = "10111"

输出: 3 \texttt{3} 3

解释:初始时, s = "00000" \texttt{s = "00000"} s = "00000"。

选择下标 2 \texttt{2} 2: "00000" → "00111" \texttt{"00000"} \rightarrow \texttt{"00111"} "00000"→"00111"

选择下标 0 \texttt{0} 0: "00111" → "11000" \texttt{"00111"} \rightarrow \texttt{"11000"} "00111"→"11000"

选择下标 1 \texttt{1} 1: "11000" → "10111" \texttt{"11000"} \rightarrow \texttt{"10111"} "11000"→"10111"

要达成目标,需要至少 3 \texttt{3} 3 次翻转。

示例 2:

输入: target = "101" \texttt{target = "101"} target = "101"

输出: 3 \texttt{3} 3

解释:初始时, s = "000" \texttt{s = "000"} s = "000"。

选择下标 0 \texttt{0} 0: "000" → "111" \texttt{"000"} \rightarrow \texttt{"111"} "000"→"111"

选择下标 1 \texttt{1} 1: "111" → "100" \texttt{"111"} \rightarrow \texttt{"100"} "111"→"100"

选择下标 2 \texttt{2} 2: "100" → "101" \texttt{"100"} \rightarrow \texttt{"101"} "100"→"101"

要达成目标,需要至少 3 \texttt{3} 3 次翻转。

示例 3:

输入: target = "00000" \texttt{target = "00000"} target = "00000"

输出: 0 \texttt{0} 0

解释:由于 s \texttt{s} s 已经等于目标,所以不需要任何操作。

数据范围

  • n = target.length \texttt{n} = \texttt{target.length} n=target.length
  • 1 ≤ n ≤ 10 5 \texttt{1} \le \texttt{n} \le \texttt{10}^\texttt{5} 1≤n≤105
  • targeti \texttt{targeti} targeti 为 '0' \texttt{`0'} '0' 或 '1' \texttt{`1'} '1'

解法

思路和算法

对于长度为 n n n 的字符串,当翻转以下标 i i i 开始的后缀时,下标范围 i , n − 1 i, n - 1 i,n−1 中的所有字符都会翻转。下标 j j j 处的字符的翻转次数等于开始下标小于等于 j j j 的后缀的翻转次数,翻转结果与翻转顺序无关,因此可以从左到右遍历字符串,计算最少翻转次数。

对于以下标 i i i 开始的后缀,如果将该后缀翻转两次,则翻转前后的字符串相同。为了使翻转次数最少,应满足对于每个下标开始的后缀至多翻转一次,并且只有当必须翻转的时候才执行翻转。

由于下标 0 0 0 的左侧没有其他字符,因此判断以下标 0 0 0 开始的后缀的依据是 target 0 \textit{target}0 target0。如果 target 0 = '1' \textit{target}0 = \text{`1'} target0='1',则需要翻转以下标 0 0 0 开始的后缀,否则不需要翻转以下标 0 0 0 开始的后缀。

按照从左到右的顺序遍历字符串并翻转后缀,用 index \textit{index} index 表示最后一次翻转的后缀的开始下标,则不存在任何一个开始下标大于 index \textit{index} index 的后缀被翻转,因此下标范围 index , n − 1 \\textit{index}, n - 1 index,n−1 中的所有字符都相同。遍历过程中如果发现 target \textit{target} target 的两个相邻字符不同,即存在 i > 0 i > 0 i>0 且 target i ≠ target i − 1 \textit{target}i \ne \textit{target}i - 1 targeti=targeti−1,则需要翻转以下标 i i i 开始的后缀,否则字符串 s s s 的下标 i i i 和 i − 1 i - 1 i−1 处的字符相同,不可能满足 s s s 与 target \textit{target} target 相等。

根据上述思路,首先判断以下标 0 0 0 开始的后缀是否需要翻转,然后从左到右遍历 target \textit{target} target,当遇到 target \textit{target} target 的两个相邻字符不同时需要执行一次翻转,遍历结束时即可得到最少翻转次数。

上述思路为贪心策略,贪心策略的正确性说明如下。

  1. 翻转后满足 s 0 = target 0 s0 = \textit{target}0 s0=target0,且对于任意 0 < i < n 0 < i < n 0<i<n,当 target i = target i − 1 \textit{target}i = \textit{target}i - 1 targeti=targeti−1 时 s i = s i − 1 si = si - 1 si=si−1,当 target i ≠ target i − 1 \textit{target}i \ne \textit{target}i - 1 targeti=targeti−1 时 s i ≠ s i − 1 si \ne si - 1 si=si−1,因此翻转之后 s s s 与 target \textit{target} target 相等。

  2. 对于每个下标开始的后缀至多翻转一次。

  3. 对于 i = 0 i = 0 i=0,只有当 s i ≠ target i si \ne \textit{target}i si=targeti 时才翻转以下标 i i i 开始的后缀;对于 i > 0 i > 0 i>0,只有当 target i ≠ target i − 1 \textit{target}i \ne \textit{target}i - 1 targeti=targeti−1 时才翻转以下标 i i i 开始的后缀。每次翻转都是必不可少的,如果在需要翻转的下标处不翻转,则一定有 s 0 ≠ target 0 s0 \ne \textit{target}0 s0=target0 或者 s s s 的相邻下标字符的关系与 target \textit{target} target 的对应相邻下标字符的关系不同,不可能满足 s s s 与 target \textit{target} target 相等。因此贪心策略可以确保得到最少翻转次数。

代码

java 复制代码
class Solution {
    public int minFlips(String target) {
        int flips = 0;
        char prev = '0';
        int n = target.length();
        for (int i = 0; i < n; i++) {
            char curr = target.charAt(i);
            if (curr != prev) {
                flips++;
                prev = curr;
            }
        }
        return flips;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是字符串 target \textit{target} target 的长度。需要遍历字符串一次计算最少反转次数。

  • 空间复杂度: O ( 1 ) O(1) O(1)。

相关推荐
lvwangshu1 天前
P12960 毒药 题解
动态规划·题解·贪心·技巧·性质题·思维好题
伟大的车尔尼10 天前
贪心题目:优势洗牌
贪心
Logic10110 天前
C语言/数据结构动态规划题解:Kadane算法求最大子数组和——O(n)时间O(1)空间
c语言·数据结构·动态规划·贪心·时间复杂度·算法题·最大子数组和
伟大的车尔尼12 天前
贪心题目:分发饼干
贪心
Tisfy13 天前
LeetCode 2472.不重叠回文子字符串的最大数目:动态规划(贪心)
动态规划·字符串·题解·贪心·dp·回文字符串
伟大的车尔尼17 天前
贪心题目:三角形的最大周长
贪心
青少儿编程课堂18 天前
贪心算法进阶:区间调度与最少资源整合解析
c++·python·算法·贪心·信息学竞赛·区间调度
伟大的车尔尼21 天前
贪心题目:保持城市天际线
贪心
a1879272183122 天前
【算法】动态规划第三篇:背包第一课——贪心之死与正序倒序开关
算法·leetcode·动态规划·贪心·dp·暴力·算法讲解