📅  最后修改于: 2023-12-03 15:41:41.890000             🧑  作者: Mango
在二进制字符串中有时需要限制一些特定的规则,例如在一个长度为N的二进制字符串中要求没有P个连续的0和Q个连续的1。我们可以通过动态规划和矩阵快速幂的方法来解决这个问题。
使用 dp(i, j, 0/1) 来表示当前已生成长度为 i 的字符串,并且以 j 个连续的 0 或者 1 结尾的方案数。具体地,设 f[i][0/1] 表示只考虑前i位,最后一位为0/1的符合要求字符串数量,g[i][0/1] 表示只考虑前i位,最后两位为00/01或者10/11的符合要求字符串数量。
转移:
当最后一位为0时,f[i][0] = f[i-1][1]+g[i-1][1],表示上一位是 1 或者 01/11,还可以由上一个为01/11转移。
当最后一位为1时,f[i][1] = f[i-1][0]+g[i-1][0],表示上一位是 0 或者 10/00,还可以由上一个为10/00转移。
g[i][0] = f[i-1][0]
g[i][1] = f[i-1][1]
最终要求的是长度为 N 的方案数,所以需要将所有长度为 N 的方案数加起来即可。
时间复杂度为 $O(N)$。
假设我们已经知道了长度为 n 的二进制字符串没有 p 个连续的 0 和 q 个连续的 1 的数量,我们如何计算长度为 $2n$ 的字符串数量呢?
首先,将长度为 $n$ 的所有合法字符串看做一个节点,建立一个长度为 $2n$ 的图,从一个长度为 $n$ 的节点向另一个长度为 $n$ 的节点连边当且仅当这两个节点对应字符串的后 $n-1$ 位相同且最后一位不相同,并且这两个节点对应的字符串长度为 $n-1$ 的前缀中不包含 p 个连续的 0 和 q 个连续的 1。
例如,当 n = 3,p = 2,q = 1 时,节点分别为:
000,001,010,011,100,101,110,111
合法的边连接:
000 -> 001,010,100
001 -> 010,100,110
010 -> 001,100,101
011 -> 101
100 -> 001,010,110
101 -> 010,110,111
110 -> 011,101,111
111 -> 011,101,110
可以看出这个图是一个 DAG(有向无环图),我们需要求出这个图的某一深度的点数,使用矩阵快速幂来解决这个问题。
具体地,设邻接矩阵为 A,其中 A[i][j] = 1 表示从字符串 i 可以到达字符串 j,由于这是一个 DAG,所以邻接矩阵是一个上三角矩阵,将其转化成下三角矩阵方便计算。
设矩阵 B 为一个 n*1 的向量,其中 B[i][0] 表示长度为 $n$ 的,以 $i$ 结尾的合法字符串数量。
根据定义,初始时有:
$$ B[i][0] = \begin{cases} 1 & i是合法字符串 \ 0 & i不是合法字符串 \ \end{cases} $$
那么长度为 $2n$ 的字符串的数量可以表示为 AB,其中 A 为邻接矩阵,B 为长度为 $n$ 的合法字符串数量的向量,即:
$$ A = \begin{bmatrix} 0 & 1 & 1 & 1 & 0 & 0 & 0 & 0 \ 1 & 0 & 1 & 1 & 0 & 0 & 0 & 0 \ 1 & 1 & 0 & 0 & 1 & 0 & 1 & 0 \ 0 & 0 & 1 & 0 & 0 & 1 & 0 & 1 \ 1 & 1 & 0 & 1 & 0 & 0 & 1 & 0 \ 0 & 0 & 1 & 0 & 1 & 0 & 0 & 1 \ 0 & 1 & 0 & 1 & 1 & 0 & 0 & 0 \ 0 & 0 & 0 & 1 & 1 & 1 & 0 & 0 \ \end{bmatrix} $$
根据定义,结果为:
$$ B' = AB $$
时间复杂度为 $O(n^3\log N)$。
动态规划的代码片段如下:
f = [[0] * 2 for i in range(N + 1)]
g = [[0] * 2 for i in range(N + 1)]
f[1][1] = f[1][0] = g[1][1] = g[1][0] = 1
for i in range(2, N+1):
f[i][0] = f[i - 1][1] + g[i - 1][1]
f[i][1] = f[i - 1][0] + g[i - 1][0]
g[i][0] = f[i - 1][0]
g[i][1] = f[i - 1][1]
ans = sum(f[N]) - (p == 0) # 若p为0,则不需要减去以0结尾的方案数
return ans
矩阵快速幂的代码片段如下:
def matrix_multiply(A, B):
n, m, p = len(A), len(A[0]), len(B[0])
C = [[0] * p for i in range(n)]
for i in range(n):
for j in range(p):
for k in range(m):
C[i][j] += A[i][k] * B[k][j]
return C
# 计算 A 的幂次
def matrix_power(A, k):
n = len(A)
res = [[0] * n for i in range(n)]
for i in range(n):
res[i][i] = 1
while k > 0:
if k & 1:
res = matrix_multiply(res, A)
A = matrix_multiply(A, A)
k >>= 1
return res
# 邻接矩阵
A = [[0, 1, 1, 1, 0, 0, 0, 0],
[1, 0, 1, 1, 0, 0, 0, 0],
[1, 1, 0, 0, 1, 0, 1, 0],
[0, 0, 1, 0, 0, 1, 0, 1],
[1, 1, 0, 1, 0, 0, 1, 0],
[0, 0, 1, 0, 1, 0, 0, 1],
[0, 1, 0, 1, 1, 0, 0, 0],
[0, 0, 0, 1, 1, 1, 0, 0]]
# 初始向量
B = [[1 if (i >> j) & 1 == 0 and (i >> (j-1) & 1) != 1 else 0 for j in range(n) ]for i in range(1 << n)]
# 矩阵快速幂
C = matrix_multiply(B, matrix_power(A, m))
# 计算答案
ans = sum(C[i][j] for i in range(1 << n) for j in range(n))
return ans