Problem: 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`.


Python solution:
import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def numberOfCombinations(self, num: str) -> int:
    if num[0] == '0':
      return 0

    kMod = 1_000_000_007
    n = len(num)
    dp = [[0] * (n + 1) for _ in range(n)]
    lcs = [[0] * (n + 1) for _ in range(n + 1)]

    for i in range(n - 1, -1, -1):
      for j in range(i + 1, n):
        if num[i] == num[j]:
          lcs[i][j] = lcs[i + 1][j + 1] + 1

    for i in range(n):
      for k in range(1, i + 2):
        dp[i][k] += dp[i][k - 1]
        dp[i][k] %= kMod
        s = i - k + 1
        if num[s] == '0':
          continue
        if s == 0:
          dp[i][k] += 1
          continue
        if s < k:
          dp[i][k] += dp[s - 1][s]
          continue
        l = lcs[s - k][s]
        if l >= k or num[s - k + l] <= num[s + l]:
          dp[i][k] += dp[s - 1][k]
        else:
          dp[i][k] += dp[s - 1][k - 1]

    return dp[n - 1][n] % kMod


Java solution:
class Solution {
  public int numberOfCombinations(String num) {
    if (num.charAt(0) == '0')
      return 0;

    final int MOD = 1_000_000_007;
    final int n = num.length();
    // dp[i][k] := the number of possible lists of integers ending in num[i] with
    // the length of the last number being 1..k
    long[][] dp = new long[n][n + 1];
    // lcs[i][j] := the number of the same digits in num[i..n) and num[j..n)
    int[][] lcs = new int[n + 1][n + 1];

    for (int i = n - 1; i >= 0; --i)
      for (int j = i + 1; j < n; ++j)
        if (num.charAt(i) == num.charAt(j))
          lcs[i][j] = lcs[i + 1][j + 1] + 1;

    for (int i = 0; i < n; ++i)
      for (int k = 1; k <= i + 1; ++k) {
        dp[i][k] += dp[i][k - 1];
        dp[i][k] %= MOD;
        // The last number is num[s..i].
        final int s = i - k + 1;
        if (num.charAt(s) == '0')
          // the number of possible lists of integers ending in num[i] with the
          // length of the last number being k
          continue;
        if (s == 0) {
          // the whole string
          dp[i][k] += 1;
          continue;
        }
        if (s < k) {
          // The length k is not enough, so add the number of possible lists of
          // integers in num[0..s - 1].
          dp[i][k] += dp[s - 1][s];
          continue;
        }
        final int l = lcs[s - k][s];
        if (l >= k || num.charAt(s - k + l) <= num.charAt(s + l))
          // Have enough length k and num[s - k..s - 1] <= num[j..i].
          dp[i][k] += dp[s - 1][k];
        else
          // Have enough length k but num[s - k..s - 1] > num[j..i].
          dp[i][k] += dp[s - 1][k - 1];
      }

    return (int) dp[n - 1][n] % MOD;
  }
}


C++ solution:
class Solution {
 public:
  int numberOfCombinations(string num) {
    if (num[0] == '0')
      return 0;

    constexpr int kMod = 1'000'000'007;
    const int n = num.size();
    // dp[i][k] := the number of possible lists of integers ending in num[i]
    // with the length of the last number being 1..k
    vector<vector<long>> dp(n, vector<long>(n + 1));
    // lcs[i][j] := the number of the same digits in num[i..n) and num[j..n)
    vector<vector<int>> lcs(n + 1, vector<int>(n + 1));

    for (int i = n - 1; i >= 0; --i)
      for (int j = i + 1; j < n; ++j)
        if (num[i] == num[j])
          lcs[i][j] = lcs[i + 1][j + 1] + 1;

    for (int i = 0; i < n; ++i)
      for (int k = 1; k <= i + 1; ++k) {
        dp[i][k] += dp[i][k - 1];
        dp[i][k] %= kMod;
        // The last number is num[s..i].
        const int s = i - k + 1;
        if (num[s] == '0')
          // the number of possible lists of integers ending in num[i] with the
          // length of the last number being k
          continue;
        if (s == 0) {
          // the whole string
          dp[i][k] += 1;
          continue;
        }
        if (s < k) {
          // The length k is not enough, so add the number of possible lists of
          // integers in num[0..s - 1].
          dp[i][k] += dp[s - 1][s];
          continue;
        }
        const int l = lcs[s - k][s];
        if (l >= k || num[s - k + l] <= num[s + l])
          // Have enough length k and num[s - k..s - 1] <= num[j..i].
          dp[i][k] += dp[s - 1][k];
        else
          // Have enough length k but num[s - k..s - 1] > num[j..i].
          dp[i][k] += dp[s - 1][k - 1];
      }

    return dp[n - 1][n] % kMod;
  }
};