leetcode
leetcode 2801 ~ 2850
翻转数位

翻转数位

难度:

标签:

题目描述

You have an integer and you can flip exactly one bit from a 0 to a 1. Write code to find the length of the longest sequence of 1s you could create.

Example 1:

Input: num = 1775(110111011112)
Output: 8

Example 2:

Input: num = 7(01112)
Output: 4

代码结果

运行时间: 22 ms, 内存: 16.1 MB


/*
 * 思路:
 * 与上述思路类似,但使用Java Stream API来处理数组的转换和计算。
 */

import java.util.Arrays;

public class Solution {
    public int longestSequenceAfterFlip(int num) {
        if (~num == 0) return Integer.BYTES * 8;
        String binaryStr = Integer.toBinaryString(num);
        String[] ones = binaryStr.split("0");
        return Arrays.stream(ones)
                     .mapToInt(String::length)
                     .reduce((max, curr) -> Math.max(max, curr + 1 + (curr > 0 ? 1 : 0)))
                     .orElse(0);
    }
}

解释

方法:

题解采用了动态规划的方式来处理问题。首先,将输入的整数 `num` 转换为二进制表示的列表 `bi`。每个元素 `bi[i]` 代表二进制位上的 0 或 1。动态规划表 `dp` 用于记录到当前位为止的无需翻转和翻转一次后的连续 1 的最大长度。 每个 `dp[i]` 包含三个元素:`dp[i][0]` 表示不翻转当前位时前 `i` 位的最长连续 1 的长度;`dp[i][1]` 表示翻转一次后前 `i` 位的最长连续 1 的长度,`dp[i][2]` 表示是否已经翻转过位值。初始状态和状态转移都针对当前位是否为 1 或 0 进行不同的处理。最后,从 `dp` 中找出最长的连续 1 的长度,并根据 `num` 的正负情况决定是否加一(针对全 1 的情况)。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
题解中的动态规划表`dp`三个元素分别代表什么?如何理解`dp[i][2]`表示的是否已翻转过位值?
`dp[i][0]`表示在第i位不进行翻转时,从开始到当前位的最长连续1的长度。`dp[i][1]`表示如果在第i位或之前的某一位进行了一次翻转,从开始到当前位的最长连续1的长度。`dp[i][2]`是一个布尔值,表示到当前位为止是否已经进行过翻转。如果`dp[i][2]`为True,表示在第i位或之前的某一位已经进行了翻转;如果为False,表示到第i位为止还没有进行翻转。这有助于决定在后续位是否还可以进行翻转。
🦆
为什么初始化`dp[0]`时,即使`bi[0]`为0,翻转后的长度也被设为1?这是否意味着我们默认翻转了第一个位?
初始化`dp[0]`时,如果`bi[0]`为0,则将`dp[0][1]`设为1,这代表我们选择翻转第一个位,从而使得从第一个位开始的连续1的长度为1。这确实意味着我们默认翻转了第一个位,以此来考虑最优解的情况,即使用一次翻转机会获得尽可能长的连续1序列。
🦆
在动态规划的状态转移方程中,对于`bi[i] == 0`的情况,为什么`dp[i][1]`的值是`dp[i-1][0] + 1`而不是`dp[i-1][1] + 1`?这是否意味着我们只考虑从未翻转到翻转的转变?
对于`bi[i] == 0`的情况,`dp[i][1]`的值设置为`dp[i-1][0] + 1`表示我们选择在第i位进行翻转(将0翻转为1),并且在此之前的位没有进行过翻转。这样,我们使用了一次翻转机会,以将当前0位转变为1。这确实意味着我们在此处考虑的是从未翻转的状态转变到翻转的状态,以最大化利用单次翻转的优势。

相关问题