普通视图

发现新文章,点击刷新页面。
昨天 — 2025年10月27日LeetCode 每日一题题解

[Python3/Java/C++/Go/TypeScript] 一题一解:逐行统计(清晰题解)

作者 lcbin
2025年10月27日 06:59

方法一:逐行统计

我们可以逐行统计每行的安全设备数量,如果当前行没有安全设备,直接跳过,否则我们将当前行的安全设备数量乘以前一行的安全设备数量,累加到答案中。然后更新前一行的安全设备数量为当前行的安全设备数量。

###python

class Solution:
    def numberOfBeams(self, bank: List[str]) -> int:
        ans = pre = 0
        for row in bank:
            if (cur := row.count("1")) > 0:
                ans += pre * cur
                pre = cur
        return ans

###java

class Solution {
    public int numberOfBeams(String[] bank) {
        int ans = 0, pre = 0;
        for (String row : bank) {
            int cur = 0;
            for (int i = 0; i < row.length(); ++i) {
                cur += row.charAt(i) - '0';
            }
            if (cur > 0) {
                ans += pre * cur;
                pre = cur;
            }
        }
        return ans;
    }
}

###cpp

class Solution {
public:
    int numberOfBeams(vector<string>& bank) {
        int ans = 0, pre = 0;
        for (auto& row : bank) {
            int cur = count(row.begin(), row.end(), '1');
            if (cur) {
                ans += pre * cur;
                pre = cur;
            }
        }
        return ans;
    }
};

###go

func numberOfBeams(bank []string) (ans int) {
pre := 0
for _, row := range bank {
if cur := strings.Count(row, "1"); cur > 0 {
ans += pre * cur
pre = cur
}
}
return
}

###ts

function numberOfBeams(bank: string[]): number {
    let [ans, pre] = [0, 0];
    for (const row of bank) {
        const cur = row.split('1').length - 1;
        if (cur) {
            ans += pre * cur;
            pre = cur;
        }
    }
    return ans;
}

###rust

impl Solution {
    pub fn number_of_beams(bank: Vec<String>) -> i32 {
        let mut ans = 0;
        let mut pre = 0;
        for row in bank {
            let cur = row.chars().filter(|&c| c == '1').count() as i32;
            if cur > 0 {
                ans += pre * cur;
                pre = cur;
            }
        }
        ans
    }
}

###c

int numberOfBeams(char** bank, int bankSize) {
    int ans = 0, pre = 0;
    for (int i = 0; i < bankSize; ++i) {
        int cur = 0;
        for (int j = 0; bank[i][j] != '\0'; ++j) {
            if (bank[i][j] == '1') {
                cur++;
            }
        }
        if (cur) {
            ans += pre * cur;
            pre = cur;
        }
    }
    return ans;
}

时间复杂度 $O(m \times n)$,其中 $m$ 和 $n$ 分别为行数和列数。空间复杂度 $O(1)$。


有任何问题,欢迎评论区交流,欢迎评论区提供其它解题思路(代码),也可以点个赞支持一下作者哈 😄~

每日一题-银行中的激光束数量🟡

2025年10月27日 00:00

银行内部的防盗安全装置已经激活。给你一个下标从 0 开始的二进制字符串数组 bank ,表示银行的平面图,这是一个大小为 m x n 的二维矩阵。 bank[i] 表示第 i 行的设备分布,由若干 '0' 和若干 '1' 组成。'0' 表示单元格是空的,而 '1' 表示单元格有一个安全设备。

对任意两个安全设备而言,如果同时 满足下面两个条件,则二者之间存在 一个 激光束:

  • 两个设备位于两个 不同行r1r2 ,其中 r1 < r2
  • 满足 r1 < i < r2 的 所有 行 i ,都 没有安全设备

激光束是独立的,也就是说,一个激光束既不会干扰另一个激光束,也不会与另一个激光束合并成一束。

返回银行中激光束的总数量。

 

示例 1:

输入:bank = ["011001","000000","010100","001000"]
输出:8
解释:在下面每组设备对之间,存在一条激光束。总共是 8 条激光束:
 * bank[0][1] -- bank[2][1]
 * bank[0][1] -- bank[2][3]
 * bank[0][2] -- bank[2][1]
 * bank[0][2] -- bank[2][3]
 * bank[0][5] -- bank[2][1]
 * bank[0][5] -- bank[2][3]
 * bank[2][1] -- bank[3][2]
 * bank[2][3] -- bank[3][2]
注意,第 0 行和第 3 行上的设备之间不存在激光束。
这是因为第 2 行存在安全设备,这不满足第 2 个条件。

示例 2:

输入:bank = ["000","111","000"]
输出:0
解释:不存在两个位于不同行的设备

 

提示:

  • m == bank.length
  • n == bank[i].length
  • 1 <= m, n <= 500
  • bank[i][j]'0''1'

【C语言】2125. 银行中的激光束数量

作者 youzikun
2022年2月6日 00:18
int getNum(char *row) {
    int sum = 0, len = strlen(row);
    for (int i = 0; i < len; i++) {
        sum += (row[i] - '0');
    }
    return sum;
}

int numberOfBeams(char ** bank, int bankSize){
    int res = 0, lastCnt = 0;
    for (int i = 0; i < bankSize; i++) {
        int cnt = getNum(bank[i]);
        if (cnt != 0) {
            res += lastCnt * cnt;
            lastCnt = cnt;
        }
    }
    return res;
}

微信截图_20220206001610.png

【一次遍历,统计每行数量】JavaScript

作者 lzxjack
2022年1月2日 16:54

解题思路

  • 遍历每行,统计每行的数量
  • 若当前行没设备,直接跳过,进入下一行
  • 两行之间的激光数量为两行数量乘积
  • 注意第一行的处理

代码

###javascript

const numberOfBeams = bank => {
    let res = 0;
    let pre = 0;
    let cur = 0;
    for (let i = 0; i < bank.length; i++) {
        const item = bank[i];
        // 统计当前行,安全设备的数量
        cur = 0;
        for (let i = 0; i < item.length; i++) {
            if (item[i] === '1') cur++;
        }
        // 当前行没设备,直接跳过
        if (cur === 0) continue;
        // 更新res
        if (pre > 0) {
            res += pre * cur;
        }
        pre = cur;
    }
    return res;
};

阅读理解(Python/Java/C++/C/Go/JS/Rust)

作者 endlesscheng
2022年1月2日 12:13

题意:去掉没有安全设备的行,计算相邻行之间的激光束的数量之和。

lc2125.jpg{:width=250px}

示例 1 有 $4$ 行,安全设备的个数分别为 $3,0,2,1$。

去掉没有安全设备的行,剩下 $3,2,1$。

计算相邻行之间的激光束的数量之和,即 $3\times 2+ 2\times 1 = 8$。

class Solution:
    def numberOfBeams(self, bank: List[str]) -> int:
        ans = pre_cnt = 0
        for row in bank:
            cnt = row.count('1')
            if cnt > 0:
                ans += pre_cnt * cnt
                pre_cnt = cnt
        return ans
class Solution {
    public int numberOfBeams(String[] bank) {
        int ans = 0;
        int preCnt = 0;
        for (String row : bank) {
            int cnt = 0;
            for (char ch : row.toCharArray()) {
                cnt += ch - '0';
            }
            if (cnt > 0) {
                ans += preCnt * cnt;
                preCnt = cnt;
            }
        }
        return ans;
    }
}
class Solution {
public:
    int numberOfBeams(vector<string>& bank) {
        int ans = 0, pre_cnt = 0;
        for (auto& row : bank) {
            int cnt = ranges::count(row, '1');
            if (cnt > 0) {
                ans += pre_cnt * cnt;
                pre_cnt = cnt;
            }
        }
        return ans;
    }
};
int numberOfBeams(char **bank, int bankSize) {
    int ans = 0, pre_cnt = 0;
    for (int i = 0; i < bankSize; i++) {
        char* row = bank[i];
        int cnt = 0;
        for (int j = 0; row[j]; j++) {
            cnt += row[j] - '0';
        }
        if (cnt > 0) {
            ans += pre_cnt * cnt;
            pre_cnt = cnt;
        }
    }
    return ans;
}
func numberOfBeams(bank []string) (ans int) {
preCnt := 0
for _, row := range bank {
cnt := strings.Count(row, "1")
if cnt > 0 {
ans += preCnt * cnt
preCnt = cnt
}
}
return
}
var numberOfBeams = function(bank) {
    let ans = 0, preCnt = 0;
    for (const row of bank) {
        const cnt = row.split('1').length - 1;
        if (cnt > 0) {
            ans += preCnt * cnt;
            preCnt = cnt;
        }
    }
    return ans;
};
impl Solution {
    pub fn number_of_beams(bank: Vec<String>) -> i32 {
        let mut ans = 0;
        let mut pre_cnt = 0;
        for row in bank {
            let cnt = row.bytes().filter(|&c| c == b'1').count() as i32;
            if cnt > 0 {
                ans += pre_cnt * cnt;
                pre_cnt = cnt;
            }
        }
        ans
    }
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(mn)$,其中 $m$ 和 $n$ 分别是 $\textit{bank}$ 的行数和列数。
  • 空间复杂度:$\mathcal{O}(1)$。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、二叉树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA/一般树)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

昨天以前LeetCode 每日一题题解

O(1) 等差数列求和(Python/Java/C++/C/Go/JS/Rust)

作者 endlesscheng
2025年10月25日 08:09

设一周的天数为 $D=7$。

根据题意,第一周存的钱数为

$$
\textit{base} = 1+2+\cdots+D = \dfrac{D(D+1)}{2}
$$

第二周每天存的钱都比 $D$ 天前多 $1$,所以第二周存的钱数为

$$
\textit{base} + D
$$

第三周每天存的钱都比 $D$ 天前多 $1$,所以第三周存的钱数为

$$
\textit{base} + 2D
$$

依此类推。我们存了完整的 $w=\left\lfloor\dfrac{n}{D}\right\rfloor$ 周,在第 $w$ 周存的钱数为

$$
\textit{base} + (w-1)\cdot D
$$

$w$ 周一共存的钱数为

$$
\begin{aligned}
\sum_{i=0}^{w-1} \textit{base} + i\cdot D &= w\cdot \textit{base} + \dfrac{w(w-1)}{2}\cdot D \
&= w\cdot \dfrac{D(D+1)}{2} + \dfrac{w(w-1)}{2}\cdot D \
&= \dfrac{wD(w+D)}{2} \
\end{aligned}
$$

如果 $n$ 不是 $D$ 的倍数,还有 $r = n\bmod D$ 天,存的钱数为

$$
(w+1) + (w+2) + \cdots + (w+r) = rw + \dfrac{r(r+1)}{2} = \dfrac{r(2w+r+1)}{2}
$$

最终答案为

$$
\dfrac{wD(w+D)}{2} + \dfrac{r(2w+r+1)}{2} = \dfrac{wD(w+D) + r(2w+r+1)}{2}
$$

其中 $D=7$,$w=\left\lfloor\dfrac{n}{D}\right\rfloor$,$r = n\bmod D$。

class Solution:
    def totalMoney(self, n: int) -> int:
        D = 7
        w, r = divmod(n, D)
        return (w * D * (w + D) + r * (w * 2 + r + 1)) // 2
class Solution {
    public int totalMoney(int n) {
        final int D = 7;
        int w = n / D;
        int r = n % D;
        return (w * D * (w + D) + r * (w * 2 + r + 1)) / 2;
    }
}
class Solution {
public:
    int totalMoney(int n) {
        constexpr int D = 7;
        int w = n / D, r = n % D;
        return (w * D * (w + D) + r * (w * 2 + r + 1)) / 2;
    }
};
int totalMoney(int n) {
    const int D = 7;
    int w = n / D, r = n % D;
    return (w * D * (w + D) + r * (w * 2 + r + 1)) / 2;
}
func totalMoney(n int) int {
const d = 7
w, r := n/d, n%d
return (w*d*(w+d) + r*(w*2+r+1)) / 2
}
var totalMoney = function(n) {
    const D = 7;
    const w = Math.floor(n / D), r = n % D;
    return (w * D * (w + D) + r * (w * 2 + r + 1)) / 2;
};
impl Solution {
    pub fn total_money(n: i32) -> i32 {
        const D: i32 = 7;
        let w = n / D;
        let r = n % D;
        (w * D * (w + D) + r * (w * 2 + r + 1)) / 2
    }
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(1)$。
  • 空间复杂度:$\mathcal{O}(1)$。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

每日一题-计算力扣银行的钱🟢

2025年10月25日 00:00

Hercy 想要为购买第一辆车存钱。他 每天 都往力扣银行里存钱。

最开始,他在周一的时候存入 1 块钱。从周二到周日,他每天都比前一天多存入 1 块钱。在接下来每一个周一,他都会比 前一个周一 多存入 1 块钱。

给你 n ,请你返回在第 n 天结束的时候他在力扣银行总共存了多少块钱。

 

示例 1:

输入:n = 4
输出:10
解释:第 4 天后,总额为 1 + 2 + 3 + 4 = 10 。

示例 2:

输入:n = 10
输出:37
解释:第 10 天后,总额为 (1 + 2 + 3 + 4 + 5 + 6 + 7) + (2 + 3 + 4) = 37 。注意到第二个星期一,Hercy 存入 2 块钱。

示例 3:

输入:n = 20
输出:96
解释:第 20 天后,总额为 (1 + 2 + 3 + 4 + 5 + 6 + 7) + (2 + 3 + 4 + 5 + 6 + 7 + 8) + (3 + 4 + 5 + 6 + 7 + 8) = 96 。

 

提示:

  • 1 <= n <= 1000

[Python/Java/JavaScript/Go] 数学 简单题困难做

作者 himymBen
2022年1月15日 07:44

解题思路

根据题意推导数学公式

代码

###Python3

class Solution:
    def totalMoney(self, n: int) -> int:
        return (div:=n//7) * (1 + 7) * 7 // 2 + (m:=max(div - 1, 0)) * (m + 1) * 7 // 2 +  (1 + (r:=n%7)) * r // 2 + div * r

###Java

class Solution {
    public int totalMoney(int n) {
        int div = n / 7, r = n % 7, m = Math.max(div - 1, 0);
        return div * (1 + 7) * 7 / 2 + m * (m + 1) * 7 / 2 + (r + 1) * r / 2 + div * r;
    }
}

###JavaScript

/**
 * @param {number} n
 * @return {number}
 */
var totalMoney = function(n) {
    const div = Math.floor(n / 7), r = n % 7, m = Math.max(div - 1, 0)
    return div * (1 + 7) * 7 / 2 + m * (m + 1) * 7 / 2 + (r + 1) * r / 2 + div * r
};

###Go

func totalMoney(n int) int {
    div, r, m := n / 7, n % 7, 0
    if div > 0 {
        m = div - 1
    }
    return div * (1 + 7) * 7 / 2 + m * (m + 1) * 7 / 2 + (r + 1) * r / 2 + div * r
}

###C

int totalMoney(int n){
    int div = n / 7, r = n % 7, m = 0;
    if(div > 0) {
        m = div - 1;
    }
    return div * (1 + 7) * 7 / 2 + m * (m + 1) * 7 / 2 + (r + 1) * r / 2 + div * r;
}

稍微整理一下数学公式简化为:

###Python3

class Solution:
    def totalMoney(self, n: int) -> int:
        return (div:=n//7) * (div + 7) * 7 // 2 + (1 + (r:=n%7)) * r // 2 + div * r

###Java

class Solution {
    public int totalMoney(int n) {
        int div = n / 7, r = n % 7;
        return div * (div + 7) * 7 / 2 + (r + 1) * r / 2 + div * r;
    }
}

###JavaScript

/**
 * @param {number} n
 * @return {number}
 */
var totalMoney = function(n) {
    const div = Math.floor(n / 7), r = n % 7
    return div * (div + 7) * 7 / 2 + (r + 1) * r / 2 + div * r
};

###Go

func totalMoney(n int) int {
    div, r := n / 7, n % 7
    return div * (div + 7) * 7 / 2 + (r + 1) * r / 2 + div * r
}

###C

int totalMoney(int n){
    int div = n / 7, r = n % 7;
    return div * (div + 7) * 7 / 2 + (r + 1) * r / 2 + div * r;
}

复杂度

时间复杂度 $o(1)$
空间复杂度 $o(1)$

【宫水三叶】简单模拟题

作者 AC_OIer
2022年1月15日 07:06

模拟

根据题意进行模拟即可,分两步进行计算。

先计算完整周:共 $a = \left \lfloor \frac{n}{7} \right \rfloor$ 周,第一周起始日金额为 $1$,每周的起始日的金额递增 $1$,周内金额可使用「等差数列求和」公式直接求解。

然后再计算最后一周(若有)的金额:共 $b = n \pmod 7$ 天,使用紧接的起始日金额继续进行累加即可。

代码:

###Java

class Solution {
    public int totalMoney(int n) {
        int a = n / 7, b = n % 7;
        int ans = 0, k = 1;
        while (a-- > 0) {
            ans += (k + (k + 6)) * 7 / 2;
            k++;
        }
        while (b-- > 0) ans += k++;
        return ans;
    }
}
  • 时间复杂度:$O(a + b)$

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


(优化)模拟

更进一步,每个完整周的起始日金额相比上周起始日金额多 $1$,同时周内每日金额递增 $1$,因此相邻完整周的金额之和也满足「等差」性质,可直接使用「等差数列求和」公式 $O(1)$ 求解完整周部分的金额之和;最后一周(若有)的金额也是同理。

代码:

###Java

class Solution {
    public int totalMoney(int n) {
        int a = n / 7, b = n % 7;
        int a1 = (1 + 7) * 7 / 2, an = (a + (a + 6)) * 7 / 2;
        int s1 = (a1 + an) * a / 2;
        a++;
        int s2 = (a + (a + b - 1)) * b / 2;
        return s1 + s2;
    }
}
  • 时间复杂度:$O(1)$

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


其他「图论搜索 / 模拟」内容

题太简单?不如来学习热乎的 简单图论搜索题 🍭🍭🍭

或是加练如下「模拟」内容:

题目 题解 难度 推荐指数
1. 两数之和 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
2. 两数相加 LeetCode 题解链接 中等 🤩🤩🤩🤩🤩
5. 最长回文子串 LeetCode 题解链接 中等 🤩🤩🤩🤩🤩
6. Z 字形变换 LeetCode 题解链接 中等 🤩🤩🤩
7. 整数反转 LeetCode 题解链接 简单 🤩🤩🤩
8. 字符串转换整数 (atoi) LeetCode 题解链接 中等 🤩🤩🤩
12. 整数转罗马数字 LeetCode 题解链接 中等 🤩🤩
13. 罗马数字转整数 LeetCode 题解链接 简单 🤩🤩
14. 最长公共前缀 LeetCode 题解链接 简单 🤩🤩🤩🤩
31. 下一个排列 LeetCode 题解链接 中等 🤩🤩🤩
38. 外观数列 LeetCode 题解链接 简单 🤩🤩
43. 字符串相乘 LeetCode 题解链接 中等 🤩🤩🤩🤩
58. 最后一个单词的长度 LeetCode 题解链接 中等 🤩🤩🤩🤩
59. 螺旋矩阵 II LeetCode 题解链接 中等 🤩🤩🤩🤩
65. 有效数字 LeetCode 题解链接 困难 🤩🤩🤩
66. 加一 LeetCode 题解链接 简单 🤩🤩🤩🤩
68. 文本左右对齐 LeetCode 题解链接 困难 🤩🤩🤩
73. 矩阵置零 LeetCode 题解链接 中等 🤩🤩🤩🤩
165. 比较版本号 LeetCode 题解链接 中等 🤩🤩🤩🤩
166. 分数到小数 LeetCode 题解链接 中等 🤩🤩🤩🤩
168. Excel表列名称 LeetCode 题解链接 简单 🤩🤩🤩
171. Excel表列序号 LeetCode 题解链接 简单 🤩🤩🤩
190. 颠倒二进制位 LeetCode 题解链接 简单 🤩🤩🤩
233. 数字 1 的个数 LeetCode 题解链接 困难 🤩🤩🤩🤩
237. 删除链表中的节点 LeetCode 题解链接 简单 🤩🤩🤩
260. 只出现一次的数字 III LeetCode 题解链接 中等 🤩🤩🤩🤩
263. 丑数 LeetCode 题解链接 简单 🤩🤩
268. 丢失的数字 LeetCode 题解链接 简单 🤩🤩🤩🤩
273. 整数转换英文表示 LeetCode 题解链接 困难 🤩🤩🤩🤩
284. 顶端迭代器 LeetCode 题解链接 中等 🤩🤩🤩🤩
299. 猜数字游戏 LeetCode 题解链接 中等 🤩🤩🤩🤩
318. 最大单词长度乘积 LeetCode 题解链接 中等 🤩🤩🤩🤩
335. 路径交叉 LeetCode 题解链接 困难 🤩🤩🤩🤩
345. 反转字符串中的元音字母 LeetCode 题解链接 简单 🤩🤩🤩
383. 赎金信 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
400. 第 N 位数字 LeetCode 题解链接 中等 🤩🤩🤩🤩
405. 数字转换为十六进制数 LeetCode 题解链接 简单 🤩🤩🤩🤩
412. Fizz Buzz LeetCode 题解链接 简单 🤩🤩🤩🤩
413. 等差数列划分 LeetCode 题解链接 中等 🤩🤩🤩🤩
414. 第三大的数 LeetCode 题解链接 中等 🤩🤩🤩🤩
419. 甲板上的战舰 LeetCode 题解链接 中等 🤩🤩🤩🤩
423. 从英文中重建数字 LeetCode 题解链接 中等 🤩🤩🤩🤩
434. 字符串中的单词数 LeetCode 题解链接 简单 🤩🤩🤩🤩
443. 压缩字符串 LeetCode 题解链接 中等 🤩🤩🤩🤩
451. 根据字符出现频率排序 LeetCode 题解链接 中等 🤩🤩🤩🤩
457. 环形数组是否存在循环 LeetCode 题解链接 中等 🤩🤩🤩🤩
482. 密钥格式化 LeetCode 题解链接 简单 🤩🤩🤩🤩
492. 构造矩形 LeetCode 题解链接 简单 🤩🤩🤩🤩
495. 提莫攻击 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
500. 键盘行 LeetCode 题解链接 简单 🤩🤩🤩🤩
506. 相对名次 LeetCode 题解链接 简单 🤩🤩🤩🤩
507. 完美数 LeetCode 题解链接 简单 🤩🤩🤩
520. 检测大写字母 LeetCode 题解链接 简单 🤩🤩🤩🤩
528. 按权重随机选择 LeetCode 题解链接 中等 🤩🤩🤩🤩
541. 反转字符串 II LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
551. 学生出勤记录 I LeetCode 题解链接 简单 🤩🤩🤩
566. 重塑矩阵 LeetCode 题解链接 简单 🤩🤩🤩
594. 最长和谐子序列 LeetCode 题解链接 简单 🤩🤩🤩🤩
598. 范围求和 II LeetCode 题解链接 简单 🤩🤩🤩
645. 错误的集合 LeetCode 题解链接 简单 🤩🤩🤩
709. 转换成小写字母 LeetCode 题解链接 简单 🤩🤩🤩
726. 原子的数量 LeetCode 题解链接 困难 🤩🤩🤩🤩
748. 最短补全词 LeetCode 题解链接 简单 🤩🤩🤩🤩
766. 托普利茨矩阵 LeetCode 题解链接 简单 🤩🤩🤩
794. 有效的井字游戏 LeetCode 题解链接 中等 🤩🤩🤩🤩
846. 一手顺子 LeetCode 题解链接 中等 🤩🤩🤩
859. 亲密字符串 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
867. 转置矩阵 LeetCode 题解链接 简单 🤩🤩🤩🤩
896. 单调数列 LeetCode 题解链接 简单 🤩🤩🤩🤩
997. 找到小镇的法官 LeetCode 题解链接 简单 🤩🤩🤩🤩
1005. K 次取反后最大化的数组和 LeetCode 题解链接 简单 🤩🤩🤩🤩
1047. 删除字符串中的所有相邻重复项 LeetCode 题解链接 简单 🤩🤩🤩🤩
1078. Bigram 分词 LeetCode 题解链接 简单 🤩🤩🤩🤩
1104. 二叉树寻路 LeetCode 题解链接 中等 🤩🤩🤩
1154. 一年中的第几天 LeetCode 题解链接 简单 🤩🤩🤩🤩
1185. 一周中的第几天 LeetCode 题解链接 简单 🤩🤩🤩🤩
1436. 旅行终点站 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
1446. 连续字符 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
1480. 一维数组的动态和 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
1486. 数组异或操作 LeetCode 题解链接 简单 🤩🤩🤩
1518. 换酒问题 LeetCode 题解链接 简单 🤩🤩🤩🤩
1576. 替换所有的问号 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
1583. 统计不开心的朋友 LeetCode 题解链接 中等 🤩🤩🤩🤩
1646. 获取生成数组中的最大值 LeetCode 题解链接 简单 🤩🤩🤩🤩
1720. 解码异或后的数组 LeetCode 题解链接 简单 🤩🤩🤩
1736. 替换隐藏数字得到的最晚时间 LeetCode 题解链接 简单 🤩🤩🤩🤩
1743. 从相邻元素对还原数组 LeetCode 题解链接 中等 🤩🤩🤩🤩
1748. 唯一元素的和 LeetCode 题解链接 简单 🤩🤩
1763. 最长的美好子字符串 LeetCode 题解链接 简单 🤩🤩🤩
1816. 截断句子 LeetCode 题解链接 简单 🤩🤩🤩🤩
1834. 单线程 CPU LeetCode 题解链接 中等 🤩🤩🤩🤩
1893. 检查是否区域内所有整数都被覆盖 LeetCode 题解链接 简单 🤩🤩🤩🤩
1894. 找到需要补充粉笔的学生编号 LeetCode 题解链接 中等 🤩🤩🤩🤩
1995. 统计特殊四元组 LeetCode 题解链接 简单 🤩🤩🤩🤩
2022. 将一维数组转变成二维数组 LeetCode 题解链接 简单 🤩🤩🤩🤩
面试题 10.02. 变位词组 LeetCode 题解链接 中等 🤩🤩🤩🤩

注:以上目录整理来自 wiki,任何形式的转载引用请保留出处。


最后

如果有帮助到你,请给题解点个赞和收藏,让更多的人看到 ~ ("▔□▔)/

也欢迎你 关注我 和 加入我们的「组队打卡」小群 ,提供写「证明」&「思路」的高质量题解。

所有题解已经加入 刷题指南,欢迎 star 哦 ~

计算力扣银行的钱

2022年1月14日 11:49

方法一:暴力计算

记当前的天数是第 $\textit{week}$ 周的第 $\textit{day}$ 天。我们从第一周的星期一开始存钱,记 $\textit{week} = 1$,$\textit{day} = 1$。一周内,每一天比前一天多存 $1$ 块钱。而每个周一,会比前一个周一多存 $1$ 块钱。因此,每天存的钱等于 $\textit{week} + \textit{day} - 1$。把每天存的钱相加就可以得到答案。

###Python

class Solution:
    def totalMoney(self, n: int) -> int:
        week, day = 1, 1
        res = 0
        for i in range(n):
            res += week + day - 1
            day += 1
            if day == 8:
                day = 1
                week += 1
        return res

###Java

class Solution {
    public int totalMoney(int n) {
        int week = 1, day = 1;
        int res = 0;
        for (int i = 0; i < n; ++i) {
            res += week + day - 1;
            ++day;
            if (day == 8) {
                day = 1;
                ++week;
            }
        }
        return res;
    }
}

###C#

public class Solution {
    public int TotalMoney(int n) {
        int week = 1, day = 1;
        int res = 0;
        for (int i = 0; i < n; ++i) {
            res += week + day - 1;
            ++day;
            if (day == 8) {
                day = 1;
                ++week;
            }
        }
        return res;
    }
}

###C++

class Solution {
public:
    int totalMoney(int n) {
        int week = 1, day = 1;
        int res = 0;
        for (int i = 0; i < n; ++i) {
            res += week + day - 1;
            ++day;
            if (day == 8) {
                day = 1;
                ++week;
            }
        }
        return res;
    }
};

###C

int totalMoney(int n){
    int week = 1, day = 1;
    int res = 0;
    for (int i = 0; i < n; ++i) {
        res += week + day - 1;
        ++day;
        if (day == 8) {
            day = 1;
            ++week;
        }
    }
    return res;
}

###go

func totalMoney(n int) (ans int) {
    week, day := 1, 1
    for i := 0; i < n; i++ {
        ans += week + day - 1
        day++
        if day == 8 {
            day = 1
            week++
        }
    }
    return
}

###JavaScript

var totalMoney = function(n) {
    let week = 1, day = 1;
    let res = 0;
    for (let i = 0; i < n; ++i) {
        res += week + day - 1;
        ++day;
        if (day === 8) {
            day = 1;
            ++week;
        }
    }
    return res;
};

复杂度分析

  • 时间复杂度:$O(n)$。需要遍历一次 $n$ 得到答案。

  • 空间复杂度:$O(1)$。只需要用到常数空间。

方法二:等差数列求和进行优化

因为每周七天存的钱之和比上一周多 $7$ 块,因此每周存的钱之和的序列是一个等差数列,我们可以用等差数列求和公式来求出所有完整的周存的钱总和。剩下的天数里,每天存的钱也是一个等差数列,可以用相同的公式进行求和。最后把两者相加可以得到答案。

###Python

class Solution:
    def totalMoney(self, n: int) -> int:
        # 所有完整的周存的钱
        weekNumber = n // 7
        firstWeekMoney = (1 + 7) * 7 // 2
        lastWeekMoney = firstWeekMoney + 7 * (weekNumber - 1)
        weekMoney = (firstWeekMoney + lastWeekMoney) * weekNumber // 2
        # 剩下的不能构成一个完整的周的天数里存的钱
        dayNumber = n % 7
        firstDayMoney = 1 + weekNumber
        lastDayMoney = firstDayMoney + dayNumber - 1
        dayMoney = (firstDayMoney + lastDayMoney) * dayNumber // 2
        return weekMoney + dayMoney

###Java

class Solution {
    public int totalMoney(int n) {
        // 所有完整的周存的钱
        int weekNumber = n / 7;
        int firstWeekMoney = (1 + 7) * 7 / 2;
        int lastWeekMoney = firstWeekMoney + 7 * (weekNumber - 1);
        int weekMoney = (firstWeekMoney + lastWeekMoney) * weekNumber / 2;
        // 剩下的不能构成一个完整的周的天数里存的钱
        int dayNumber = n % 7;
        int firstDayMoney = 1 + weekNumber;
        int lastDayMoney = firstDayMoney + dayNumber - 1;
        int dayMoney = (firstDayMoney + lastDayMoney) * dayNumber / 2;
        return weekMoney + dayMoney;
    }
}

###C#

public class Solution {
    public int TotalMoney(int n) {
        // 所有完整的周存的钱
        int weekNumber = n / 7;
        int firstWeekMoney = (1 + 7) * 7 / 2;
        int lastWeekMoney = firstWeekMoney + 7 * (weekNumber - 1);
        int weekMoney = (firstWeekMoney + lastWeekMoney) * weekNumber / 2;
        // 剩下的不能构成一个完整的周的天数里存的钱
        int dayNumber = n % 7;
        int firstDayMoney = 1 + weekNumber;
        int lastDayMoney = firstDayMoney + dayNumber - 1;
        int dayMoney = (firstDayMoney + lastDayMoney) * dayNumber / 2;
        return weekMoney + dayMoney;
    }
}

###C++

class Solution {
public:
    int totalMoney(int n) {
        // 所有完整的周存的钱
        int weekNumber = n / 7;
        int firstWeekMoney = (1 + 7) * 7 / 2;
        int lastWeekMoney = firstWeekMoney + 7 * (weekNumber - 1);
        int weekMoney = (firstWeekMoney + lastWeekMoney) * weekNumber / 2;
        // 剩下的不能构成一个完整的周的天数里存的钱
        int dayNumber = n % 7;
        int firstDayMoney = 1 + weekNumber;
        int lastDayMoney = firstDayMoney + dayNumber - 1;
        int dayMoney = (firstDayMoney + lastDayMoney) * dayNumber / 2;
        return weekMoney + dayMoney;
    }
};

###C

int totalMoney(int n){
    // 所有完整的周存的钱
    int weekNumber = n / 7;
    int firstWeekMoney = (1 + 7) * 7 / 2;
    int lastWeekMoney = firstWeekMoney + 7 * (weekNumber - 1);
    int weekMoney = (firstWeekMoney + lastWeekMoney) * weekNumber / 2;
    // 剩下的不能构成一个完整的周的天数里存的钱
    int dayNumber = n % 7;
    int firstDayMoney = 1 + weekNumber;
    int lastDayMoney = firstDayMoney + dayNumber - 1;
    int dayMoney = (firstDayMoney + lastDayMoney) * dayNumber / 2;
    return weekMoney + dayMoney;
}

###go

func totalMoney(n int) (ans int) {
    // 所有完整的周存的钱
    weekNum := n / 7
    firstWeekMoney := (1 + 7) * 7 / 2
    lastWeekMoney := firstWeekMoney + 7*(weekNum-1)
    weekMoney := (firstWeekMoney + lastWeekMoney) * weekNum / 2
    // 剩下的不能构成一个完整的周的天数里存的钱
    dayNum := n % 7
    firstDayMoney := 1 + weekNum
    lastDayMoney := firstDayMoney + dayNum - 1
    dayMoney := (firstDayMoney + lastDayMoney) * dayNum / 2
    return weekMoney + dayMoney
}

###JavaScript

var totalMoney = function(n) {
    // 所有完整的周存的钱
    const weekNumber = Math.floor(n / 7);
    const firstWeekMoney = Math.floor((1 + 7) * 7 / 2);
    const lastWeekMoney = firstWeekMoney + 7 * (weekNumber - 1);
    const weekMoney = Math.floor((firstWeekMoney + lastWeekMoney) * weekNumber / 2);
    // 剩下的不能构成一个完整的周的天数里存的钱
    const dayNumber = n % 7;
    const firstDayMoney = 1 + weekNumber;
    const lastDayMoney = firstDayMoney + dayNumber - 1;
    const dayMoney = Math.floor((firstDayMoney + lastDayMoney) * dayNumber / 2);
    return weekMoney + dayMoney;
};

复杂度分析

  • 时间复杂度:$O(1)$。只需要用到常数时间。

  • 空间复杂度:$O(1)$。只需要用到常数空间。

两种方法:枚举 / 倒序贪心 + 0-1 背包(Python/Java/C++/Go)

作者 endlesscheng
2025年10月24日 10:36

方法一:枚举

设 $n$ 的十进制长度为 $m$。

对于本题的数据范围,一定存在十进制长为 $m+1$ 的数值平衡数。

例如 $n=999$,答案为 $1333$。

例如 $n=999999$,答案为 $1224444$。

对于本题的数据范围,$m+1$ 一定可以分解为 $[1,9]$ 中的不同元素之和。

所以枚举 $\mathcal{O}(n)$ 次就能找到答案。

###py

class Solution:
    def nextBeautifulNumber(self, n: int) -> int:
        while True:
            n += 1
            cnt = Counter(str(n))
            if all(int(d) == c for d, c in cnt.items()):
                return n

###java

class Solution {
    public int nextBeautifulNumber(int n) {
        next:
        while (true) {
            n++;

            int[] cnt = new int[10];
            for (int x = n; x > 0; x /= 10) {
                cnt[x % 10]++;
            }

            for (int x = n; x > 0; x /= 10) {
                if (cnt[x % 10] != x % 10) {
                    continue next;
                }
            }

            return n;
        }
    }
}

###cpp

class Solution {
public:
    int nextBeautifulNumber(int n) {
        while (true) {
            n++;

            int cnt[10]{};
            for (int x = n; x > 0; x /= 10) {
                cnt[x % 10]++;
            }

            bool ok = true;
            for (int x = n; x > 0; x /= 10) {
                if (cnt[x % 10] != x % 10) {
                    ok = false;
                    break;
                }
            }
            if (ok) {
                return n;
            }
        }
    }
};

###go

func nextBeautifulNumber(n int) int {
next:
for {
n++
cnt := [10]int{}
for x := n; x > 0; x /= 10 {
cnt[x%10]++
}
for x := n; x > 0; x /= 10 {
if cnt[x%10] != x%10 {
continue next
}
}
return n
}
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(n\log n)$ 或 $\mathcal{O}(n(D + \log n))$,其中 $D=10$。枚举 $\mathcal{O}(n)$ 个数,每个数需要 $\mathcal{O}(\log n)$ 的时间判断是否为数值平衡数。
  • 空间复杂度:$\mathcal{O}(\log n)$ 或 $\mathcal{O}(D)$。

方法二:倒序贪心

如果数据范围扩大到 $n\le 10^{18}$,上面的做法就超时了。

下面介绍一个更快的做法,对于更大的数据范围也能瞬间得出结果,且可以扩展到其他情况,例如值域改成小写字母 $\texttt{a}$ 到 $\texttt{z}$。

请先完成更简单的 3720. 大于目标字符串的最小字典序排列我的题解

本题用同样的方法解决。

先把 $n$ 转成十进制字符串 $s$。为简化代码,在 $s$ 前面加一个前导零。设 $s$(补前导零后)的长度为 $m$。

倒着遍历 $s$,设 $d = s[i]$,尝试把 $s[i]$ 增大为 $d+1,d+2,\ldots,9$。那么:

  1. 对于下标在 $[1,i]$ 中的数字,不能存在数字 $x$ 的出现次数超过 $x$ 的情况。换句话说,设 $\textit{cnt}[x]$ 是数字 $x$ 在 $s$ 的 $[1,i]$ 中的出现次数,不能出现 $\textit{cnt}[x] > x$ 的情况。
  2. 剩余位置 $[i+1,m-1]$ 可以随便填,我们需要补满 $0 < \textit{cnt}[x] < x$ 的数字 $x$,把 $x$ 的出现次数变成 $x$。
  3. 补满数字后,如果还有剩余位置没有填数字,那么从剩余的满足 $\textit{cnt}[x] = 0$ 的非零数字中,选择一个字典序最小的序列,使得序列的和恰好等于剩余位置的个数。这是恰好装满型 0-1 背包。算完 DP 后,如何得到具体方案?类似 1449. 数位成本和为目标值的最大数字,见 我的题解
  4. 把第二步和第三步的数字从小到大排序,填在 $[i+1,m-1]$ 中。

###py

class Solution:
    # 从 a 中选一个字典序最小的、元素和等于 target 的子序列
    # a 已经从小到大排序
    # 无解返回 None
    def zeroOneKnapsack(self, a: List[int], target: int) -> Optional[List[int]]:
        n = len(a)
        f = [[False] * (target + 1) for _ in range(n + 1)]
        f[n][0] = True

        # 倒着 DP,这样后面可以正着(从小到大)选
        for i in range(n - 1, -1, -1):
            v = a[i]
            for j in range(target + 1):
                if j < v:
                    f[i][j] = f[i + 1][j]
                else:
                    f[i][j] = f[i + 1][j] or f[i + 1][j - v]

        if not f[0][target]:
            return None

        ans = []
        j = target
        for i, v in enumerate(a):
            if j >= v and f[i + 1][j - v]:
                ans.append(v)
                j -= v
        return ans

    def nextBeautifulNumber(self, n: int) -> int:
        s = "0" + str(n)  # 补一个前导零,方便处理答案十进制串比 n 的十进制串长的情况
        s = list(map(int, s))  # 避免在后续循环中反复调用 int
        m = len(s)

        MX = 10
        cnt = [0] * MX
        for i in range(1, m):
            cnt[s[i]] += 1

        # 从右往左尝试
        for i in range(m - 1, -1, -1):
            if i > 0:
                cnt[s[i]] -= 1  # 撤销

            # 增大 s[i] 为 j
            for j in range(s[i] + 1, MX):
                cnt[j] += 1

                # 后面 [i+1, m-1] 需要补满 0 < cnt[k] < k 的数字 k,剩余数位可以随便填
                free = m - 1 - i  # 统计可以随便填的数位个数
                for k, c in enumerate(cnt):
                    if k < c:  # 不合法
                        free = -1
                        break
                    if c > 0:
                        free -= k - c
                if free < 0:  # 不合法,继续枚举
                    cnt[j] -= 1
                    continue

                # 对于可以随便填的数位,计算字典序最小的填法
                a = [k for k in range(1, MX) if cnt[k] == 0]
                missing = self.zeroOneKnapsack(a, free)
                if missing is None:  # 无解,继续枚举
                    cnt[j] -= 1
                    continue

                for v in missing:
                    cnt[v] = -v  # 用负数表示可以随便填的数

                s[i] = j
                del s[i + 1:]
                for k, c in enumerate(cnt):
                    s += [k] * (k - c if c > 0 else -c)
                return int(''.join(map(str, s)))

        return -1  # 无解(本题不会发生,但为了可扩展性保留)

###java

class Solution {
    public int nextBeautifulNumber(int n) {
        // 补一个前导零,方便处理答案十进制串比 n 的十进制串长的情况
        char[] s = ("0" + n).toCharArray();
        int m = s.length;

        final int MX = 10;
        int[] cnt = new int[MX];
        for (int i = 1; i < m; i++) {
            cnt[s[i] - '0']++;
        }

        // 从右往左尝试
        for (int i = m - 1; i >= 0; i--) {
            if (i > 0) {
                cnt[s[i] - '0']--; // 撤销
            }

            // 增大 s[i] 为 j
            for (int j = s[i] - '0' + 1; j < MX; j++) {
                cnt[j]++;

                // 后面 [i+1, m-1] 需要补满 0 < cnt[k] < k 的数字 k,剩余数位可以随便填
                int free = m - 1 - i; // 统计可以随便填的数位个数
                for (int k = 0; k < MX; k++) {
                    int c = cnt[k];
                    if (k < c) { // 不合法
                        free = -1;
                        break;
                    }
                    if (c > 0) {
                        free -= k - c;
                    }
                }
                if (free < 0) { // 不合法,继续枚举
                    cnt[j]--;
                    continue;
                }

                // 对于可以随便填的数位,计算字典序最小的填法
                List<Integer> a = new ArrayList<>();
                for (int k = 1; k < MX; k++) {
                    if (cnt[k] == 0) {
                        a.add(k);
                    }
                }

                List<Integer> missing = zeroOneKnapsack(a, free);
                if (missing == null) { // 无解,继续枚举
                    cnt[j]--;
                    continue;
                }

                for (int v : missing) {
                    cnt[v] = -v; // 用负数表示可以随便填的数
                }

                StringBuilder ans = new StringBuilder("0" + n);
                ans.setCharAt(i, (char) ('0' + j));
                ans.setLength(i + 1);
                for (int k = 1; k < MX; k++) {
                    int c = cnt[k];
                    ans.repeat('0' + k, c > 0 ? k - c : -c);
                }
                return Integer.parseInt(ans.toString());
            }
        }
        return -1; // 无解(本题不会发生,但为了可扩展性保留)
    }

    // 从 a 中选一个字典序最小的、元素和等于 target 的子序列
    // a 已经从小到大排序
    // 无解返回 null
    private List<Integer> zeroOneKnapsack(List<Integer> a, int target) {
        int n = a.size();
        boolean[][] f = new boolean[n + 1][target + 1];
        f[n][0] = true;

        // 倒着 DP,这样后面可以正着(从小到大)选
        for (int i = n - 1; i >= 0; i--) {
            int v = a.get(i);
            for (int j = 0; j <= target; j++) {
                if (j < v) {
                    f[i][j] = f[i + 1][j];
                } else {
                    f[i][j] = f[i + 1][j] || f[i + 1][j - v];
                }
            }
        }

        if (!f[0][target]) {
            return null;
        }

        List<Integer> ans = new ArrayList<>();
        int j = target;
        for (int i = 0; i < n; i++) {
            int v = a.get(i);
            if (j >= v && f[i + 1][j - v]) {
                ans.add(v);
                j -= v;
            }
        }
        return ans;
    }
}

###cpp

class Solution {
    // 从 a 中选一个字典序最小的、元素和等于 target 的子序列
    // a 已经从小到大排序
    // 无解返回 {} 和 false
    pair<vector<int>, bool> zeroOneKnapsack(vector<int>& a, int target) {
        int n = a.size();
        vector f(n + 1, vector<int8_t>(target + 1)); 
        f[n][0] = true;

        // 倒着 DP,这样后面可以正着(从小到大)选
        for (int i = n - 1; i >= 0; i--) {
            int v = a[i];
            for (int j = 0; j <= target; j++) {
                if (j < v) {
                    f[i][j] = f[i + 1][j];
                } else {
                    f[i][j] = f[i + 1][j] || f[i + 1][j - v];
                }
            }
        }

        if (!f[0][target]) {
            return {};
        }

        vector<int> ans;
        int j = target;
        for (int i = 0; i < n; i++) {
            int v = a[i];
            if (j >= v && f[i + 1][j - v]) {
                ans.push_back(v);
                j -= v;
            }
        }
        return {ans, true};
    }

public:
    int nextBeautifulNumber(int n) {
        // 补一个前导零,方便处理答案十进制串比 n 的十进制串长的情况
        string s = "0" + to_string(n);
        int m = s.size();

        constexpr int MX = 10;
        int cnt[MX]{};
        for (int i = 1; i < m; i++) {
            cnt[s[i] - '0']++;
        }

        // 从右往左尝试
        for (int i = m - 1; i >= 0; i--) {
            if (i > 0) {
                cnt[s[i] - '0']--; // 撤销
            }

            // 增大 s[i] 为 j
            for (int j = s[i] - '0' + 1; j < MX; j++) {
                cnt[j]++;

                // 后面 [i+1, m-1] 需要补满 0 < cnt[k] < k 的数字 k,剩余数位可以随便填
                int free = m - 1 - i; // 统计可以随便填的数位个数
                for (int k = 0; k < MX; k++) {
                    int c = cnt[k];
                    if (k < c) { // 不合法
                        free = -1;
                        break;
                    }
                    if (c > 0) {
                        free -= k - c;
                    }
                }
                if (free < 0) { // 不合法,继续枚举
                    cnt[j]--;
                    continue;
                }

                // 对于可以随便填的数位,计算字典序最小的填法
                vector<int> a;
                for (int k = 1; k < MX; k++) {
                    if (cnt[k] == 0) {
                        a.push_back(k);
                    }
                }
                auto [missing, ok] = zeroOneKnapsack(a, free);
                if (!ok) { // 无解,继续枚举
                    cnt[j]--;
                    continue;
                }

                for (int v : missing) {
                    cnt[v] = -v; // 用负数表示可以随便填的数
                }

                s[i] = '0' + j;
                s.resize(i + 1);
                for (int k = 1; k < MX; k++) {
                    int c = cnt[k];
                    c = c > 0 ? k - c : -c;
                    s += string(c, '0' + k);
                }
                return stoi(s);
            }
        }
        return -1; // 无解(本题不会发生,但为了可扩展性保留)
    }
};

###go

// 从 a 中选一个字典序最小的、元素和等于 target 的子序列
// a 已经从小到大排序
// 无解返回 nil
func zeroOneKnapsack(a []int, target int) []int {
n := len(a)
f := make([][]bool, n+1)
for i := range f {
f[i] = make([]bool, target+1)
}
f[n][0] = true

// 倒着 DP,这样后面可以正着(从小到大)选
for i := n - 1; i >= 0; i-- {
v := a[i]
for j := range f[i] {
if j < v {
f[i][j] = f[i+1][j]
} else {
f[i][j] = f[i+1][j] || f[i+1][j-v]
}
}
}

if !f[0][target] {
return nil
}

ans := []int{}
j := target
for i, v := range a {
if j >= v && f[i+1][j-v] {
ans = append(ans, v)
j -= v
}
}
return ans
}

func nextBeautifulNumber(n int) int {
// 补一个前导零,方便处理答案十进制比 n 的十进制长的情况
s := "0" + strconv.Itoa(n)
m := len(s)

const mx = 10
cnt := make([]int, mx)
for i := 1; i < m; i++ {
cnt[s[i]-'0']++
}

// 从右往左尝试
for i := m - 1; i >= 0; i-- {
if i > 0 {
cnt[s[i]-'0']-- // 撤销
}

// 增大 s[i] 为 j
for j := s[i] - '0' + 1; j < mx; j++ {
cnt[j]++

// 后面 [i+1, m-1] 需要补满 0 < cnt[k] < k 的数字 k,剩余数位可以随便填
free := m - 1 - i // 统计可以随便填的数位个数
for k, c := range cnt {
if k < c { // 不合法
free = -1
break
}
if c > 0 {
free -= k - c
}
}
if free < 0 { // 不合法,继续枚举
cnt[j]--
continue
}

// 对于可以随便填的数位,计算字典序最小的填法
a := []int{}
for k := 1; k < mx; k++ {
if cnt[k] == 0 {
a = append(a, k)
}
}
missing := zeroOneKnapsack(a, free)
if missing == nil { // 无解,继续枚举
cnt[j]--
continue
}

for _, v := range missing {
cnt[v] = -v // 用负数表示可以随便填的数
}

t := []byte(s[:i+1])
t[i] = '0' + byte(j)
for k, c := range cnt {
if c > 0 {
c = k - c
} else {
c = -c
}
d := []byte{'0' + byte(k)}
t = append(t, bytes.Repeat(d, c)...)
}
ans, _ := strconv.Atoi(string(t))
return ans
}
}
return -1 // 无解(本题不会发生,但为了可扩展性保留)
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(D^2\log^2 n)$,其中 $D=10$,$\log n$ 是 $n$ 的十进制长度。枚举 $\mathcal{O}(D\log n)$ 种把 $s[i]$ 增大的情况,每次需要 $\mathcal{O}(D\log n)$ 的时间计算 0-1 背包。
  • 空间复杂度:$\mathcal{O}(D\log n)$。

相似题目

专题训练

见下面贪心题单的「§3.1 字典序最小/最大」。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

每日一题-下一个更大的数值平衡数🟡

2025年10月24日 00:00

如果整数  x 满足:对于每个数位 d ,这个数位 恰好x 中出现 d 次。那么整数 x 就是一个 数值平衡数

给你一个整数 n ,请你返回 严格大于 n最小数值平衡数

 

示例 1:

输入:n = 1
输出:22
解释:
22 是一个数值平衡数,因为:
- 数字 2 出现 2 次 
这也是严格大于 1 的最小数值平衡数。

示例 2:

输入:n = 1000
输出:1333
解释:
1333 是一个数值平衡数,因为:
- 数字 1 出现 1 次。
- 数字 3 出现 3 次。 
这也是严格大于 1000 的最小数值平衡数。
注意,1022 不能作为本输入的答案,因为数字 0 的出现次数超过了 0 。

示例 3:

输入:n = 3000
输出:3133
解释:
3133 是一个数值平衡数,因为:
- 数字 1 出现 1 次。
- 数字 3 出现 3 次。 
这也是严格大于 3000 的最小数值平衡数。

 

提示:

  • 0 <= n <= 106

【回溯】不是所有打表都叫“打表”

作者 l00
2023年12月9日 14:06

2048. 下一个更大的数值平衡数

吐槽

本并不想写这个题解,这题略显无趣,但是看了官方题解不得不吐槽一下,好歹告诉大家表怎么来是吧?如何非常高级的快速的得到 “表” ,你非要说枚举 n 个数得出,我只能说 6 。直接就源码写表,难道竞赛时、面试时您还能提供这个表给我不成?还是说这种题目枚举前 n 项必然不超时?;

打表制表

动态规划当前状态下能否再填入规定数字,例如我要一个长度是 6 的数字,必然就只有这几种组合 1+2+3,2+4,1+5,6,至于这些数字本身如何排列那就需要递归一个个找出来了;

所以做一个递归函数,把当前可能填入的数字都尝试填入并递归下去求解最后答案,每次操作后回溯复位;

时间复杂度主要是取决于最大 n 的长度这题是 6 故 $6^6$ ,即 $O(m^m)$ ;
查表就是 $logn$ 了

###python

nums = []

def dfs(n: int, cur: int, queue: List[int], pre: int) -> int:
    if cur == 0 and n == 0:
        nums.append(pre)
        return
    for num, cnt in enumerate(queue[1:], 1):
        if cnt == 0: continue
        if num > cnt:
            queue[num] -= 1
            dfs(n, cur - 1, queue, pre * 10 + num)
            queue[num] += 1
        else:
            if num > n: continue
            queue[num] -= 1
            dfs(n - num, cur - 1, queue, pre * 10 + num)
            queue[num] += 1

dfs(1, 1, [0,1], 0)
dfs(2, 2, [0,1,2], 0)
dfs(3, 3, [0,1,2,3], 0)
dfs(4, 4, [0,1,2,3,4], 0)
dfs(5, 5, [0,1,2,3,4,5], 0)
dfs(6, 6, [0,1,2,3,4,5,6], 0)
nums.append(1224444)

class Solution:
    def nextBeautifulNumber(self, n: int) -> int:
        # print(nums)
        i = bisect_right(nums, n)
        return nums[i]

最最最白痴解法

作者 forever-qing
2021年10月24日 12:33

###java

class Solution {
    public int nextBeautifulNumber(int n) {
        if (n<1) {
            return 1;
        }else if (n<22) {
            return 22;
        } else if (n<122) {
            return 122;
        }else if(n<212){
            return 212;
        }else if(n<221){
            return 221;
        }else if(n<333){
            return 333;
        }else if(n<1333){
            return 1333;
        }else if(n<3133){
            return 3133;
        }else if(n<3313){
            return 3313;
        }else if(n<3331){
            return 3331;
        }else if(n<4444){
            return 4444;
        }else if(n<14444){
            return 14444;
        }else if(n<22333){
            return 22333;
        }else if(n<23233){
            return 23233;
        }else if(n<23323){
            return 23323;
        }else if(n<23332){
            return 23332;
        }else if(n<32233){
            return 32233;
        }else if(n<32323){
            return 32323;
        }else if(n<32332){
            return 32332;
        }else if(n<33223){
            return 33223;
        }else if(n<33232){
            return 33232;
        }else if(n<33322){
            return 33322;
        }else if(n<41444){
            return 41444;
        }else if(n<44144){
            return 44144;
        }else if(n<44414){
            return 44414;
        }else if(n<44441){
            return 44441;
        }else if(n<55555){
            return 55555;   
        }else if(n<122333){
            return 122333;
        }else if(n<123233){
            return 123233;
        }else if(n<123323){
            return 123323;
        }else if(n<123332){
            return 123332;
        }else if(n<132233){
            return 132233;
        }else if(n<132323){
            return 132323;
        }else if(n<132332){
            return 132332;
        }else if(n<133223){
            return 133223;
        }else if(n<133232){
            return 133232;
        }else if(n<133322){
            return 133322;
        }else if(n<155555){
            return 155555;
        }else if(n<212333){
            return 212333;
        }else if(n<213233){
            return 213233;
        }else if(n<213323){
            return 213323;
        }else if(n<213332){
            return 213332;
        }else if(n<221333){
            return 221333;
        }else if(n<223133){
            return 223133;
        }else if(n<223313){
            return 223313;
        }else if(n<223331){
            return 223331;
        }else if(n<224444){
            return 224444;
        }else if(n<231233){
            return 231233;
        }else if(n<231323){
            return 231323;
        }else if(n<231332){
            return 231332;
        }else if(n<232133){
            return 232133;
        }else if(n<232313){
            return 232313;
        }else if(n<232331){
            return 232331;
        }else if(n<233123){
            return 233123;
        }else if(n<233132){
            return 233132;
        }else if(n<233213){
            return 233213;
        }else if(n<233231){
            return 233231;
        }else if(n<233312){
            return 233312;
        }else if(n<233321){
            return 233321;
        }else if(n<242444){
            return 242444;
        }else if(n<244244){
            return 244244;
        }else if(n<244424){
            return 244424;
        }else if(n<244442){
            return 244442;
        }else if(n<312233){
            return 312233;
        }else if(n<312323){
            return 312323;
        }else if(n<312332){
            return 312332;
        }else if(n<313223){
            return 313223;
        }else if(n<313232){
            return 313232;
        }else if(n<313322){
            return 313322;
        }else if(n<321233){
            return 321233;
        }else if(n<321323){
            return 321323;
        }else if(n<321332){
            return 321332;
        }else if(n<322133){
            return 322133;
        }else if(n<322313){
            return 322313;
        }else if(n<322331){
            return 322331;
        }else if(n<323123){
            return 323123;
        }else if(n<323132){
            return 323132;
        }else if(n<323213){
            return 323213;
        }else if(n<323231){
            return 323231;
        }else if(n<323312){
            return 323312;
        }else if(n<323321){
            return 323321;
        }else if(n<331223){
            return 331223;
        }else if(n<331232){
            return 331232;
        }else if(n<331322){
            return 331322;
        }else if(n<332123){
            return 332123;
        }else if(n<332132){
            return 332132;
        }else if(n<332213){
            return 332213;
        }else if(n<332231){
            return 332231;
        }else if(n<332312){
            return 332312;
        }else if(n<332321){
            return 332321;
        }else if(n<333122){
            return 333122;
        }else if(n<333212){
            return 333212;
        }else if(n<333221){
            return 333221;
        }else if(n<422444){
            return 422444;
        }else if(n<424244){
            return 424244;
        }else if(n<424424){
            return 424424;
        }else if(n<424442){
            return 424442;
        }else if(n<442244){
            return 442244;
        }else if(n<442424){
            return 442424;
        }else if(n<442442){
            return 442442;
        }else if(n<444224){
            return 444224;
        }else if(n<444242){
            return 444242;
        }else if(n<444422){
            return 444422;
        }else if(n<515555){
            return 515555;
        }else if(n<551555){
            return 551555;
        }else if(n<555155){
            return 555155;
        }else if(n<555515){
            return 555515;
        }else if(n<555551){
            return 555551;
        }else if(n<666666){
            return 666666;
        }
            return 1224444;
    }
}

每日一题-判断操作后字符串中的数字是否相等 I🟢

2025年10月23日 00:00

给你一个由数字组成的字符串 s 。重复执行以下操作,直到字符串恰好包含 两个 数字:

  • 从第一个数字开始,对于 s 中的每一对连续数字,计算这两个数字的和 模 10。
  • 用计算得到的新数字依次替换 s 的每一个字符,并保持原本的顺序。

如果 s 最后剩下的两个数字 相同 ,返回 true 。否则,返回 false

 

示例 1:

输入: s = "3902"

输出: true

解释:

  • 一开始,s = "3902"
  • 第一次操作:
    • (s[0] + s[1]) % 10 = (3 + 9) % 10 = 2
    • (s[1] + s[2]) % 10 = (9 + 0) % 10 = 9
    • (s[2] + s[3]) % 10 = (0 + 2) % 10 = 2
    • s 变为 "292"
  • 第二次操作:
    • (s[0] + s[1]) % 10 = (2 + 9) % 10 = 1
    • (s[1] + s[2]) % 10 = (9 + 2) % 10 = 1
    • s 变为 "11"
  • 由于 "11" 中的数字相同,输出为 true

示例 2:

输入: s = "34789"

输出: false

解释:

  • 一开始,s = "34789"
  • 第一次操作后,s = "7157"
  • 第二次操作后,s = "862"
  • 第三次操作后,s = "48"
  • 由于 '4' != '8',输出为 false

 

提示:

  • 3 <= s.length <= 100
  • s 仅由数字组成。

按题意模拟计算

2025年2月23日 13:12

Problem: 100579. 判断操作后字符串中的数字是否相等 I

[TOC]

思路

按题意模拟计算。

Code

执行用时分布0ms击败100.00%;消耗内存分布7.70MB击败100.00%

###C

bool hasSameDigits(char* s) {
    int len = strlen(s), i = 0;
    while (i < len) s[i ++] &= 0x0f;
    while (-- len > 1)
        for (i = 0; i < len; ++ i)
            s[i] = (s[i] + s[i + 1]) % 10;
    return s[0] == s[1];
}

###Python3

class Solution:
    def hasSameDigits(self, s: str) -> bool:
        s = list(map(int, s))
        for _ in range(len(s) - 2): 
            s = [(x + y) % 10 for x, y in pairwise(s)]
        return s[0] == s[1]

模拟

作者 tsreaper
2025年2月23日 12:30

解法:模拟

数据范围很小,模拟即可。复杂度 $\mathcal{O}(n^2)$。

参考代码(c++)

class Solution {
public:
    bool hasSameDigits(string s) {
        while (s.size() > 2) {
            string t;
            for (int i = 0; i + 1 < s.size(); i++) {
                int x = s[i] - '0', y = s[i + 1] - '0';
                t.push_back((x + y) % 10 + '0');
            }
            s = t;
        }
        return s[0] == s[1];
    }
};
❌
❌