📌  相关文章
📜  可被 3 整除的二进制字符串的非零长度子序列的数量(1)

📅  最后修改于: 2023-12-03 14:50:38.953000             🧑  作者: Mango

可被 3 整除的二进制字符串的非零长度子序列的数量
介绍

给定一个二进制字符串,求其所有非零长度子序列中,可被 3 整除的数量。

例如,对于字符串 "1100",其所有非零长度子序列如下:

1, 1, 0, 0, 11, 10, 10, 100, 110, 100, 1100

其中可被 3 整除的有:

1 + 1 + 0 + 0 + 10 + 100 + 1100 = 1212
解法

我们可以使用动态规划的思想来解决这个问题。

我们设 $dp[i][0/1/2]$ 表示以第 $i$ 个字符为结尾,余数分别为 $0/1/2$ 的子序列数量。

对于一个字符 $s_i$,它有两种选择:选或不选。如果不选,那么 $dp[i][j]$ 的值与 $dp[i-1][j]$ 相同。如果选,那么 $dp[i][j]$ 的值就等于 $dp[i-1][(j-s_i \mod 3 + 3) \mod 3]$。

递推公式如下:

$$ \begin{cases} dp[i][0] = dp[i-1][0] &\text{(不选当前字符)} \ dp[i][1] = dp[i-1][1] + dp[i-1][2] &\text{(余数为1)} \ dp[i][2] = dp[i-1][0] + dp[i-1][1] &\text{(余数为2)} \ dp[i][(s_i\mod3)] += 1 + dp[i-1][s_i\mod3] &\text{(选当前字符)} \end{cases} $$

最终,我们需要求出 $dp[n][0]$ 的值,其中 $n$ 为字符串的长度。

代码
def countBinStr(s: str) -> int:
    n = len(s)
    dp = [[0] * 3 for _ in range(n+1)]
    for i in range(1, n+1):
        for j in range(3):
            dp[i][j] = dp[i-1][j]
        dp[i][int(s[i-1]) % 3] += 1
        dp[i][(int(s[i-1]) + 1) % 3] += dp[i-1][1]
        dp[i][(int(s[i-1]) + 2) % 3] += dp[i-1][2]
    return dp[n][0]
代码片段详解:

- 第一行定义了函数名称和参数。
- 第二行获取字符串的长度。
- 第三行初始化动态规划数组,其中 $dp[i][j]$ 表示前 $i$ 个字符中余数为 $j$ 的子序列数量。因为长度为 $0$ 的子序列不能被计算,所以数组长度需为 $n+1$。
- 第四至七行为递推公式的实现。在第四行中,如果不选当前字符,那么 $dp[i][j]$ 的值就等于 $dp[i-1][j]$。在第五至七行中,如果选当前字符,那么 $dp[i][j]$ 的值就等于 $dp[i-1][(j-s_i \mod 3 + 3) \mod 3]$。
- 第八行返回 $dp[n][0]$ 的值,即所有非零长度子序列中可被 $3$ 整除的数量。