leetcode
leetcode 951 ~ 1000
易混淆数 II

易混淆数 II

难度:

标签:

题目描述

代码结果

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


/*
 * 题目思路:
 * 通过Java Stream API,我们可以使用递归生成所有可能的数字,
 * 并使用流的过滤功能来筛选出所有有效的易混淆数。
 */
import java.util.*;
import java.util.stream.*;

public class ConfusingNumberIIStream {
    private static final char[] DIGITS = {'0', '1', '6', '8', '9'};
    private static final Map<Character, Character> CONFUSING_MAP = Map.of(
        '0', '0',
        '1', '1',
        '6', '9',
        '8', '8',
        '9', '6'
    );

    public long confusingNumberII(int N) {
        return generateNumbers(0, new StringBuilder())
            .filter(num -> num > 0 && num <= N && isConfusingNumber(Long.toString(num)))
            .count();
    }

    private Stream<Long> generateNumbers(long cur, StringBuilder sb) {
        if (cur > Integer.MAX_VALUE) return Stream.empty();

        Stream<Long> result = Stream.of(cur);
        for (char digit : DIGITS) {
            if (cur == 0 && digit == '0') continue;
            sb.append(digit);
            result = Stream.concat(result, generateNumbers(cur * 10 + (digit - '0'), sb));
            sb.deleteCharAt(sb.length() - 1);
        }
        return result;
    }

    private boolean isConfusingNumber(String num) {
        StringBuilder rotated = new StringBuilder();
        for (char c : num.toCharArray()) {
            rotated.append(CONFUSING_MAP.get(c));
        }
        return !num.equals(rotated.reverse().toString());
    }
}

解释

方法:

这道题目要求找到不超过给定数字 n 的所有'易混淆数'的数量。'易混淆数'是指当一个数字旋转 180 度后,形成一个不同的有效数字。题解中定义了两个方法:confusingNumberIII 和 confusingNumberII。 confusingNumberIII 方法遍历从 1 到 n 的所有数字,首先将包含不可翻转的数字(2, 3, 4, 5, 7)的数字排除。然后,将剩余的数字进行转换:6 和 9 互换,最后将数字翻转。如果翻转后的数字与原数字不同,则是一个易混淆数。 confusingNumberII 方法使用了一种生成方法来直接构造可能的易混淆数,并且计算它们的数量。通过固定数字的一半长度,然后生成所有可能的组合,并将它们翻转拼接来形成完整的数字。然后根据生成的数字是否有效和是否大于 n 来决定是否停止。这种方法更有效地使用了数位的特性来直接计算易混淆数的数量。

时间复杂度:

confusingNumberIII: O(n * k), confusingNumberII: O(5^L)

空间复杂度:

confusingNumberIII: O(k), confusingNumberII: O(L)

代码细节讲解

🦆
在`confusingNumberIII`方法中,为什么选择通过字符串操作来检测数字是否为易混淆数,而不是直接使用数学运算?
在`confusingNumberIII`方法中,选择通过字符串操作来检测数字是否为易混淆数主要是因为字符串操作在处理数字的各个位上的替换和反转时更直观和简单。数学运算处理这类问题需要额外的逻辑来分解数字并逐位进行替换,这不仅增加了实现的复杂性,还可能导致错误率增加。而字符串提供了内置的方法来直接进行字符替换和反转,使得代码更简洁、易于理解和维护。
🦆
在`confusingNumberII`方法中,如何确保生成的数字不会重复计算,并且如何处理大于n的情况?
在`confusingNumberII`方法中,通过使用递归或迭代的方式生成数字,并在生成过程中遵循特定的序列(如'01689'),从而避免重复。生成的数字是基于组合的方式构造,确保每种组合只被使用一次。处理大于n的情况是通过在生成数字后立即进行检查,如果生成的数字超过n,则使用`raise ValueError()`来中断所有进一步的数字生成,这样可以有效地停止不必要的计算和迭代。
🦆
对于`confusingNumberII`方法中的异常处理(`raise ValueError()`),这种方式在什么情况下会触发,以及为什么选择使用异常来控制流程?
在`confusingNumberII`方法中,异常处理(`raise ValueError()`)会在生成的数字超过给定的上限n时触发。这种异常用来立即中断当前的数字生成过程,避免进一步的无效计算。选择使用异常来控制流程是因为它提供了一种简洁有效的方式来从多层嵌套的循环中直接退出,特别是在深度递归或多层迭代的场景中,传统的控制结构如break或return可能不足以直接跳出所有层级。异常处理在这种情况下能够立刻响应错误情况,并将控制权返回到上层逻辑。
🦆
在`confusingNumberII`方法中,为什么在数字的最后一位使用6或9的时候,需要特别处理奇数长度的数字串?
在`confusingNumberII`方法中,数字的最后一位使用6或9时需要特别处理奇数长度的数字串,是因为在生成对称数字时,中间的数字必须是能够自我翻转的(即0、1、8),这样翻转后的数字才是有效的。如果中间的数字是6或9,翻转后会变成另一个数字(9变6,6变9),这会导致整个数字不再对称。因此,在奇数长度的数字串中,最中间的位不能是6或9,以确保翻转后数字的有效性。

相关问题

易混淆数

易混淆数