加载中...
1977-划分数字的方案数(Number of Ways to Separate Numbers)
发表于:2021-12-03 | 分类: 困难
字数统计: 1.1k | 阅读时长: 5分钟 | 阅读量:

原文链接: https://leetcode-cn.com/problems/number-of-ways-to-separate-numbers

英文原文

You wrote down many positive integers in a string called num. However, you realized that you forgot to add commas to seperate the different numbers. You remember that the list of integers was non-decreasing and that no integer had leading zeros.

Return the number of possible lists of integers that you could have written down to get the string num. Since the answer may be large, return it modulo 109 + 7.

 

Example 1:

Input: num = "327"
Output: 2
Explanation: You could have written down the numbers:
3, 27
327

Example 2:

Input: num = "094"
Output: 0
Explanation: No numbers can have leading zeros and all numbers must be positive.

Example 3:

Input: num = "0"
Output: 0
Explanation: No numbers can have leading zeros and all numbers must be positive.

Example 4:

Input: num = "9999999999999"
Output: 101

 

Constraints:

  • 1 <= num.length <= 3500
  • num consists of digits '0' through '9'.

中文题目

你写下了若干 正整数 ,并将它们连接成了一个字符串 num 。但是你忘记给这些数字之间加逗号了。你只记得这一列数字是 非递减 的且 没有 任何数字有前导 0 。

请你返回有多少种可能的 正整数数组 可以得到字符串 num 。由于答案可能很大,将结果对 109 + 7 取余 后返回。

 

示例 1:

输入:num = "327"
输出:2
解释:以下为可能的方案:
3, 27
327

示例 2:

输入:num = "094"
输出:0
解释:不能有数字有前导 0 ,且所有数字均为正数。

示例 3:

输入:num = "0"
输出:0
解释:不能有数字有前导 0 ,且所有数字均为正数。

示例 4:

输入:num = "9999999999999"
输出:101

 

提示:

  • 1 <= num.length <= 3500
  • num 只含有数字 '0' 到 '9' 。

通过代码

高赞题解

方法一:动态规划

本题较难。

容易想到$\mathcal{O}(N^3)$的动态规划:用$dp[i][j]$表示到第$i$个位置,最后一个数字的长度为$j$时的方案数,则其对应的上一个数的结尾应该是$i-j$,我们就可以枚举$dp[i-j][k]$,$k\le j$。其中,$k<j$的部分可以直接加上(因为前面的数字长度较短,所以一定更小),但$k=j$时,我们需要判断两个数字的大小关系。

利用前缀和的方法可以将$k<j$的部分优化到$\mathcal{O}(1)$时间,但此时整体的复杂度还是$\mathcal{O}(N^3)$,因为最坏情况下,所有数字都相同(比如3500个9),则我们对于每一个$k=j$的情形都需要进行$\mathcal{O}(N)$时间的比较。

如何优化比较呢?这里,我们可以进行一次预处理的动态规划来加速比较。

令$c[i][j]$表示第一个串从$i$开始,第二个串从$j$开始,且满足第一个串不大于第二个串的最长长度。显然$c[i][j]$可以由$c[i+1][j+1]$转移得到。

求得$c[i][j]$之后,我们在进行比较时就只需要判断$c[i-2j][i-j]\ge j$是否成立即可。

  • 时间复杂度$\mathcal{O}(|S|^2)$。
  • 空间复杂度$\mathcal{O}(|S|^2)$。

参考代码(C++)

using ll = long long;
const ll MOD = 1e9 + 7;

class Solution {
public:
    int numberOfCombinations(string num) {
        if (num[0] == '0')
            return 0;
        
        int n = num.size();
        vector<vector<ll>> dp(n + 1, vector<ll>(n + 1));
        dp[1] = vector<ll>(n + 1, 1);
        dp[1][0] = 0;
        
        vector<vector<int>> c(n, vector<int>(n));
        for (int len = 1; len <= n; ++len)
            for (int i = n - 1 - len; i >= 0; --i) {
                if (num[i] < num[i + len])
                    c[i][i + len] = n - i - len;
                else if (num[i] == num[i + len]) {
                    if (i + len == n - 1)
                        c[i][i + len] = 1;
                    else
                        c[i][i + len] = c[i + 1][i + len + 1] + 1;
                }
            }
        
        auto cmp = [&](int l, int r, int len) {
            if (l < 0)
                return false;
            return c[l][r] >= len;
        };
        
        for (int i = 2; i <= n; ++i) {
            dp[i][i] = 1;
            for (int j = 1; j < i; ++j) {
                if (num[i - j] == '0')
                    continue;
                if (cmp(i - 2 * j, i - j, j))
                    dp[i][j] = (dp[i][j] + dp[i - j][j]) % MOD;
                else
                    dp[i][j] = (dp[i][j] + dp[i - j][j - 1]) % MOD;   
            }
            for (int j = 1; j <= n; ++j)
                dp[i][j] = (dp[i][j] + dp[i][j - 1]) % MOD;
        }
        
        return dp[n][n];
    }
};

统计信息

通过次数 提交次数 AC比率
821 3236 25.4%

提交历史

提交时间 提交结果 执行时间 内存消耗 语言
上一篇:
1976-到达目的地的方案数(Number of Ways to Arrive at Destination)
下一篇:
1961-检查字符串是否为数组前缀(Check If String Is a Prefix of Array)
本文目录
本文目录