下面是 LeetCode 3869. 统计区间内奇妙数的数目 的 Rust 实现。

解题思路

这道题使用数位 DP(Digit DP)来解决。核心思路:

1. 好数:数位严格递增或严格递减的数(一位数默认是好数)
2. 奇妙数:本身是好数,或者其数位和是好数
3. 用 `calc(r) - calc(l-1)` 计算区间 `[l, r]` 内的答案

数位 DP 状态:`dfs(pos, sum, prev, st, lim)`
- `pos`:当前处理到的数位位置
- `sum`:当前数位和(最大 `16×9=144`)
- `prev`:前一个数位的值
- `st`:单调状态(`0`=初始/前导零, `1`=递增, `2`=递减, `3`=非单调)
- `lim`:是否受上界限制

check 函数优化:由于数位和最大只有 144,判断好数时可以优化:
- `s < 100`:不是 11 的倍数即为好数(因为两位数中 `11, 22, ..., 99` 不是好数)
- `s ≥ 100`:只需检查十位和个位是否满足 `1 < mid < last`

Rust 代码

```rust
impl Solution {
    pub fn count_fancy(l: i64, r: i64) -> i64 {
        Self::calc(r) - Self::calc(l - 1)
    }

    fn calc(x: i64) -> i64 {
        if x < 0 {
            return 0;
        }
        let num = x.to_string();
        let n = num.len();
        // dp[pos][sum][prev][state], -1 表示未计算
        // state: 0=初始(前导零), 1=递增, 2=递减, 3=非单调
        let mut dp = vec![vec![vec![vec![-1i64; 4]; 10]; 9 * n + 1]; n];
        Self::dfs(0, 0, 0, 0, true, &num, n, &mut dp)
    }

    fn dfs(
        pos: usize,
        sum: usize,
        prev: usize,
        st: usize,
        lim: bool,
        num: &str,
        n: usize,
        dp: &mut Vec<Vec<Vec<Vec<i64>>>>,
    ) -> i64 {
        if pos >= n {
            // 如果本身是好数,直接计数
            if st != 3 {
                return 1;
            }
            // 否则检查数位和是否是好数
            return if Self::check(sum) { 1 } else { 0 };
        }

        // 记忆化搜索(不受限状态时)
        if !lim && dp[pos][sum][prev][st] != -1 {
            return dp[pos][sum][prev][st];
        }

        let up = if lim {
            (num.as_bytes()[pos] - b'0') as usize
        } else {
            9
        };

        let mut res: i64 = 0;
        for i in 0..=up {
            // 更新单调状态
            let nxt_st = if st == 0 {
                if prev == 0 {
                    0 // 前导零或第一个数字
                } else if i > prev {
                    1 // 开始递增
                } else if i < prev {
                    2 // 开始递减
                } else {
                    3 // 相等,非单调
                }
            } else if st == 1 {
                if i > prev { 1 } else { 3 }
            } else if st == 2 {
                if i < prev { 2 } else { 3 }
            } else {
                3
            };

            // 更新前一个数位(保持前导零状态)
            let nxt_prev = if st == 0 && prev == 0 && i == 0 {
                0
            } else {
                i
            };

            res += Self::dfs(
                pos + 1,
                sum + i,
                nxt_prev,
                nxt_st,
                lim && i == up,
                num,
                n,
                dp,
            );
        }

        if !lim {
            dp[pos][sum][prev][st] = res;
        }

        res
    }

    // 判断 s 是否是好数(s 为数位和,最大 144)
    fn check(s: usize) -> bool {
        if s < 100 {
            return s % 11 != 0;
        }
        let mid = (s / 10) % 10;
        let last = s % 10;
        mid > 1 && mid < last
    }
}
```

复杂度分析

- 时间复杂度:`O(D³ × log r)`,其中 `D = 10` 为数字范围,状态数为 `pos × sum × prev × st ≈ 16 × 145 × 10 × 4`
- 空间复杂度:`O(D² × log r)`,记忆化数组的大小

 

更多推荐