📌  相关文章
📜  字符串可被 n 整除的子序列数(1)

📅  最后修改于: 2023-12-03 15:09:21.719000             🧑  作者: Mango

字符串可被 n 整除的子序列数

本文将介绍如何计算一个给定字符串中可被 n 整除的子序列数。首先,我们需要明确什么是子序列:子序列是指从原字符串中删除一些字符而不改变其相对顺序得到的新字符串。例如,对于字符串 "abc",它的子序列有 "a"、"b"、"c"、"ab"、"ac"和"abc"。

接下来,我们需要了解一些关于 n 的性质:

  1. 如果一个数能被 n 整除,那么这个数的各个位上的数字之和也能被 n 整除。

  2. 如果两个数分别为 a 和 b,且 a 能被 n 整除,那么 (a*10+b) 能被 n 整除的充要条件是 b 能被 n 整除。

例如,如果 n=3,那么 (a10+b) 能被 n 整除的条件就是 b 能被 3 整除。这是因为 10 能被 3 整除,所以 a10 除以 3 的余数和 a 除以 3 的余数相同,因此 (a*10+b) 除以 3 的余数和 b 除以 3 的余数相同。

基于以上性质,我们可以设计如下算法:

  1. 定义一个二维数组 dp,其中 dp[i][j] 表示字符串的前 i 个字符中有多少个子序列可以被 j 整除。

  2. 初始化 dp[1][x],其中 x 是字符串的第一个字符对 n 取余的结果,dp[1][x]=1。

  3. 对于第 i 个字符,假设它的值为 c,那么对于每个 j,dp[i+1][j] 的值等于 dp[i][j]*2 加上 dp[i][j-c]。其中 dp[i][j]*2 表示不加上当前字符 c 的子序列数,dp[i][j-c] 表示加上当前字符 c 的子序列数。需要注意的是,如果 j-c 小于 0,那么应该加上 n。

  4. 最终的答案就是 dp[n][0],即字符串中能被 n 整除的子序列数。

下面是该算法的 Python 代码实现:

def num_subseq_divisible_by_n(s: str, n: int) -> int:
    dp = [[0] * n for _ in range(len(s) + 1)]
    dp[1][int(s[0]) % n] = 1
    for i in range(1, len(s)):
        c = int(s[i])
        dp[i+1][c % n] += 1  # 单个字符本身能被 n 整除的情况
        for j in range(n):
            dp[i+1][j] += dp[i][j] * 2  # 不加上字符的情况
            dp[i+1][(j+c)%n] += dp[i][j]  # 加上字符的情况
    return dp[-1][0]

该算法的时间复杂度是 $O(nm)$,其中 n 是整除数,m 是字符串的长度。空间复杂度也是 $O(nm)$,需要维护一个二维数组。