我有一个M
字符,从这些字符开始我需要制作一个长度为N
的序列,这样两个连续的字符就不会相同,并且序列的第一个和最后一个字符也是固定的。所以我需要找到总的方式。
我的方法:
动态编程。 如果第一个和最后一个字符是' 0'和' 1'
dp[1][0]=1 , dp[1][1]=1
for(int i=2;i<N;i++)
for(int j=0;j<M;j++)
for(int k=0;k<M;k++)
if(j!=k) dp[i][j]+=dp[i-1][k]
所以最终答案是summation dp[n-1][i] , i!=1
问题:
此处N
长度在10 ^ 15左右过大而M
在128
左右,如何在不使用数组的情况下找到排列数?
答案 0 :(得分:2)
假设M
已修复。设D(n)
为长度n
的序列数,没有重复的字符,其中第一个和最后一个字符不同(但是是固定的)。设S(n)
为长度n
的序列数,其中第一个和最后一个字符相同(但是固定)。
例如,D(6)
是a????b
形式的字符串数(对于某些a
和b
- 注意到计算它并不重要我们选择的两个字符,以及?
代表其他字符的位置)。同样,S(6)
是a????a
形式的字符串数。
考虑n>3
形式的长度为a....?b
的序列。 ?
可以是m-1
个字符中的任何一个(b
除外)。其中之一是a
。所以D(n) = S(n-1) + (m-2)D(n-1)
。使用类似的参数,可以找出S(n) = (M-1)D(n-1)
。
例如,a??b
形式有多少个字符串?好吧,b
之前的字符可能是a
或其他内容。它是a
时有多少个字符串?嗯,它与a?a
形式的字符串数相同。当它是别的东西时,有多少个字符串?那么它与a?c
形式的字符串数量乘以c
选择的数量相同(即:m-2
- 除了a
之外的所有内容我们已经计算了,b
被规则排除了。
如果n
是奇数,我们可以考虑中间字符。考虑n
形式的长度为a...?...b
的序列。 ?
(位于字符串的中心)可以是a
,b
或其他M-2
个字符之一。因此D(2n+1) = S(n+1)D(n+1) + D(n+1)S(n+1) + (M-2)D(n+1)D(n+1)
。同样,S(2n+1) = S(n+1)S(n+1) + (M-1)D(n+1)D(n+1)
。
适用于小型n
,S(2)=0
,S(3)=M-1
,D(2)=1
,D(3)=M-2
。
我们可以使用上面的等式(偶数n>3
的第一组,奇数n>3
的第二组,以及n=2
或3
的基本情况你需要在O(log N
)算术运算中得到的结果。大概这个问题要求你以模数的形式计算结果(因为结果像O(M^(N-2)
)一样增长,但这很容易融入到结果
使用此方法的工作代码:
def C(n, m, p):
if n == 2:
return 0, 1
if n == 3:
return (m-1)%p, (m-2)%p
if n % 2 == 0:
S, D = C(n-1, m, p)
return ((m-1) * D)%p, (S + (m-2) * D)%p
else:
S, D = C((n-1)//2+1, m, p)
return (S*S + (m-1)*D*D)%p, (2*S*D + (m-2)*D*D)%p
请注意,在此代码中,C(n,m,p)返回两个数字 - S(n)%p和D(n)%p。
例如:
>>> p = 2**64 - 59 # Some large prime
>>> print(C(4, 128, p))
>>> print(C(5, 128, p))
>>> print(C(10**15, 128, p))
(16002, 16003)
(2032381, 2032380)
(12557489471374801501, 12557489471374801502)
看一下这些例子,好像是D(n) = S(n) + (-1)^n
。如果这是真的,我猜想代码可以简化一下。
另一种更有效的方法是使用矩阵和第一组方程。 (抱歉,ascii art - 此图是vector = matrix * vector):
(D(n)) = (M-2 1) * (D(n-1))
(S(n)) = (M-1 0) (S(n-1))
伸缩这个,并使用D(2)= 1,S(2)= 0:
(D(n)) = (M-2 1)^(n-2) (1)
(S(n)) = (M-1 0) (0)
您可以通过在O(log n)时间内平方来使用取幂来执行矩阵幂。
这是工作代码,包括示例(您可以检查产生与上面代码相同的值)。大多数代码实际上是矩阵乘法和矩阵幂 - 如果你使用那个包,你可以用numpy代码替换很多代码。
def mat_mul(M, N, p):
R = [[0, 0], [0, 0]]
for i in range(2):
for j in range(2):
for k in range(2):
R[i][j] += M[i][k] * N[k][j]
R[i][j] %= p
return R
def mat_pow(M, n, p):
if n == 0:
return [[1, 0], [0, 1]]
if n == 1:
return M
if n % 2 == 0:
R = mat_pow(M, n//2, p)
return mat_mul(R, R, p)
return mat_mul(M, mat_pow(M, n-1, p), p)
def Cmat(n, m, p):
M = [((m-2), 1), (m-1, 0)]
M = mat_pow(M, n-2, p)
return M[1][0], M[0][0]
p = 2**64 - 59
print(Cmat(4, 128, p))
print(Cmat(5, 128, p))
print(Cmat(10**15, 128, p))
答案 1 :(得分:1)
您只需要计算可接受序列的数量,而不是明确找到它们。事实证明,大多数角色并不重要。重要的只有4种字符:
换句话说,您不需要迭代所有10^15
个字符。你只需要考虑上面的四种情况,因为大多数字符都可以归结为最后一种情况。