📌  相关文章
📜  在删除至少包含一个“1”的子字符串“01”和“00”后,计算长度与给定字符串相同的二进制字符串(1)

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

题目描述

给定一个长度为$n(n<=10^6)$的二进制字符串$S$,删除其中至少包含一个"1"的子字符串"01"和"00"后,计算长度与$S$相同的二进制字符串。

解题思路

我们可以观察到,若一个子字符串中包含了'00'或'01',则它必然携带了一位无法被删除的'0'。因此,只要我们将$S$中的所有'1'截去,就可以将问题转化为从剩下的'0'中选出若干个构成字符串的问题。

我们考虑相邻两个'1'之间的'0'的个数,这些'0'中除掉'00'和'01'这两种子字符串,其余的可以直接看作是独立的'0'。因此,我们可以将他们看作是独立的物品,从中选出一个集合,使得集合中的物品的个数为$|S|-1$,并且集合中的物品从左到右的顺序与$S$中的'0'的顺序相同。

考虑使用记忆化搜索或动态规划解决这个问题。设$dp[i][j]$表示前$i$个'0'中选出$j$个的方案数,$cnt_i$表示第$i$个'0'与它后面的'1'之间的'0'的个数。

状态转移方程为:$$dp[i][j]=\begin{cases}dp[i-1][j],&cnt_i>j\dp[i-1][j]+\displaystyle \binom{j-cnt_i-1}{i-cnt_i-1},&cnt_i\leq j\end{cases}$$ 其中$\binom{a}{b}$表示组合数。

复杂度分析

时间复杂度:$O(n^2)$

空间复杂度:$O(n^2)$

参考代码
def count_str(s: str) -> int:
    zeros = [i for i, c in enumerate(s) if c == '0']
    n = len(zeros)
    dp = [[0] * (n + 1) for _ in range(n + 1)]
    dp[0][0] = 1
    for i in range(1, n + 1):
        cnt = zeros[i - 1] - zeros[i - 2] - 1 if i >= 2 else zeros[0]
        for j in range(n + 1):
            dp[i][j] = dp[i - 1][j]
            if cnt <= j:
                dp[i][j] += dp[i - 1][j - cnt] * comb(j - cnt - 1, i - cnt - 1)
    return dp[n][n]
参考资料

AC solution of Python3

计数型DP总结