红宝石,相当快,但这取决于输入
现在,通过从字符串转换为整数,速度提高了2〜2.5倍。
用法:
cat <input> | ruby this.script.rb
例如。
mad_gaksha@madlab ~/tmp $ ruby c50138.rb < c50138.inp2
number of matches: 298208861472
took 0.05726237 s
通过二项式系数可以很容易地计算出单个掩模的匹配数。因此,例如122020
需要2
填充3 s,即1 0
和2 1
。因此,存在nCr(3,2)=nCr(3,1)=3!/(2!*1!)=3
与该掩码匹配的不同二进制字符串。
n个蒙版m_1,m_2,... m_n之间的交集是一个蒙版q,因此仅当二进制字符串s匹配所有蒙版m_i时,它才与q匹配。
如果我们采用两个遮罩m_1和m_2,则它们的交点很容易计算。如果m_1 [i] == 2,只需设置m_1 [i] = m_2 [i]。122020
和之间的交集111222
是111020
:
122020 (matched by 3 strings, 111000 110010 101010)
111222 (matched by 1 string, 111000)
111020 (matched by 1 string, 111000)
两个单独的遮罩由3 + 1 = 4个字符串匹配,交叉遮罩由一个字符串匹配,因此有3 + 1-1 = 3个唯一的字符串与一个或两个遮罩匹配。
我称N(m_1,m_2,...)匹配所有m_i的字符串数。应用与上述相同的逻辑,我们可以计算出至少一个掩码匹配的唯一字符串的数量,这由包含排除原理给出,并且也如下所示:
N(m_1) + N(m_2) + ... + N(m_n) - N(m_1,m_2) - ... - N(m_n-1,m_n) + N(m_1,m_2,m_3) + N(m_1,m_2,m_4) + ... N(m_n-2,m_n-1,m_n) - N(m_1,m_2,m_3,m_4) -+ ...
服用的组合有很多很多,例如200面罩中有30面罩。
因此,此解决方案假设输入遮罩不存在许多高阶交集。n> 2个掩码的大多数n元组将没有任何公共匹配。
在此处使用代码,ideone上的代码可能已过时。
我添加了一个函数remove_duplicates
,该函数可用于预处理输入并删除掩码m_i
,以便与之匹配的所有字符串也都与另一个掩码匹配m_j
。对于当前输入,由于没有这样的掩码(或数量不多),实际上这会花费更长的时间,因此该功能尚未在下面的代码中应用于数据。
码:
# factorial table
FAC = [1]
def gen_fac(n)
n.times do |i|
FAC << FAC[i]*(i+1)
end
end
# generates a mask such that it is matched by each string that matches m and n
def diff_mask(m,n)
(0..m.size-1).map do |i|
c1 = m[i]
c2 = n[i]
c1^c2==1 ? break : c1&c2
end
end
# counts the number of possible balanced strings matching the mask
def count_mask(m)
n = m.size/2
c0 = n-m.count(0)
c1 = n-m.count(1)
if c0<0 || c1<0
0
else
FAC[c0+c1]/(FAC[c0]*FAC[c1])
end
end
# removes masks contained in another
def remove_duplicates(m)
m.each do |x|
s = x.join
m.delete_if do |y|
r = /\A#{s.gsub(?3,?.)}\Z/
(!x.equal?(y) && y =~ r) ? true : false
end
end
end
#intersection masks of cn masks from m.size masks
def mask_diff_combinations(m,n=1,s=m.size,diff1=[3]*m[0].size,j=-1,&b)
(j+1..s-1).each do |i|
diff2 = diff_mask(diff1,m[i])
if diff2
mask_diff_combinations(m,n+1,s,diff2,i,&b) if n<s
yield diff2,n
end
end
end
# counts the number of balanced strings matched by at least one mask
def count_n_masks(m)
sum = 0
mask_diff_combinations(m) do |mask,i|
sum += i%2==1 ? count_mask(mask) : -count_mask(mask)
end
sum
end
time = Time.now
# parse input
d = STDIN.each_line.map do |line|
line.chomp.strip.gsub('2','3')
end
d.delete_if(&:empty?)
d.shift
d.map!{|x|x.chars.map(&:to_i)}
# generate factorial table
gen_fac([d.size,d[0].size].max+1)
# count masks
puts "number of matches: #{count_n_masks(d)}"
puts "took #{Time.now-time} s"
这就是所谓的包含排除原理,但是在有人指出这一点之前,我有自己的证明,因此就可以了。自己做某事感觉很棒。
让我们考虑2个掩码的情况,然后分别调用0
和和1
。我们采用每个平衡的二进制字符串,并根据其匹配的掩码进行分类。c0
是那些只匹配掩盖的数量0
,c1
那些仅匹配的nunber 1
,c01
那些匹配掩模0
和1
。
设s0
每个掩码的匹配数的总和(它们可能重叠)。让s1
是匹配的对的掩模的每对(2-组合)的数量的总和。让s_i
成为掩模的每个第(i + 1)的组合匹配的数目的总和。n个掩码的匹配数是与所有掩码匹配的二进制字符串的数。
如果存在n个掩码,则所需的输出为所有值的总和c
,即。c = c0+...+cn+c01+c02+...+c(n-2)(n-1)+c012+...+c(n-3)(n-2)(n-1)+...+c0123...(n-2)(n-1)
。程序计算的是所有值的总和s
,即。s = s_0-s_1+s_2-+...+-s_(n-1)
。我们希望证明这一点s==c
。
n = 1很明显。考虑n = 2。计数掩模的所有比赛0
给出c0+c01
(串的匹配仅0 +那些匹配两者的数量0
和1
),计数所有比赛的1
给出c1+c02
。我们可以举例说明如下:
0: c0 c01
1: c1 c10
根据定义,s0 = c0 + c1 + c12
。s1
是的每个2个组合的匹配总数[0,1]
。所有uniqye c_ij
s。请记住c01=c10
。
s0 = c0 + c1 + 2 c01
s1 = c01
s = s0 - s1 = c0 + c1 + c01 = c
因此s=c
对于n = 2。
现在考虑n = 3。
0 : c0 + c01 + c02 + c012
1 : c1 + c01 + c12 + c012
2 : c2 + c12 + c02 + c012
01 : c01 + c012
02 : c02 + c012
12 : c12 + c012
012: c012
s0 = c0 + c1 + c2 + 2 (c01+c02+c03) + 3 c012
s1 = c01 + c02 + c12 + 3 c012
s2 = c012
s0 = c__0 + 2 c__1 + 3 c__2
s1 = c__1 + 3 c__2
s2 = c__2
s = s0 - s1 + s2 = ... = c0 + c1 + c2 + c01 + c02 + c03 + c012 = c__0 + c__1 + c__2 = c
因此s=c
对于n = 3。c__i
代表c
具有(i + 1)个索引的所有s的,例如c__1 = c01
对于n = 2和c__1 = c01 + c02 + c12
n == 3。
对于n = 4,模式开始出现:
0: c0 + c01 + c02 + c03 + c012 + c013 + c023 + c0123
1: c1 + c01 + c12 + c13 + c102 + c103 + c123 + c0123
2: c2 + c02 + c12 + c23 + c201 + c203 + c213 + c0123
3: c3 + c03 + c13 + c23 + c301 + c302 + c312 + c0123
01: c01 + c012 + c013 + c0123
02: c02 + c012 + c023 + c0123
03: c03 + c013 + c023 + c0123
12: c11 + c012 + c123 + c0123
13: c13 + c013 + c123 + c0123
23: c23 + c023 + c123 + c0123
012: c012 + c0123
013: c013 + c0123
023: c023 + c0123
123: c123 + c0123
0123: c0123
s0 = c__0 + 2 c__1 + 3 c__2 + 4 c__3
s1 = c__1 + 3 c__2 + 6 c__3
s2 = c__2 + 4 c__3
s3 = c__3
s = s0 - s1 + s2 - s3 = c__0 + c__1 + c__2 + c__3 = c
因此s==c
对于n = 4。
通常,我们得到这样的二项式系数(↓是i,→是j):
0 1 2 3 4 5 6 . . .
0 1 2 3 4 5 6 7 . . .
1 1 3 6 10 15 21 . . .
2 1 4 10 20 35 . . .
3 1 5 15 35 . . .
4 1 6 21 . . .
5 1 7 . . .
6 1 . . .
. .
. .
. .
要看到这一点,请考虑对于i
和j
,有:
- x = ncr(n,i + 1):n中(i + 1)个遮罩的交集的组合C
- y = ncr(ni-1,ji):对于上述每个组合C,(j + 2)个蒙版的交集有y个不同的组合,其中包含C
- z = ncr(n,j + 1):n中(j + 1)个遮罩的交集的不同组合
听起来可能会造成混淆,这是对示例的定义。对于i = 1,j = 2,n = 4,它看起来像这样(参见上文):
01: c01 + c012 + c013 + c0123
02: c02 + c012 + c023 + c0123
03: c03 + c013 + c023 + c0123
12: c11 + c012 + c123 + c0123
13: c13 + c013 + c123 + c0123
23: c23 + c023 + c123 + c0123
所以这里x = 6(01,02,03,12,13,23),y = 2(每个组合都有两个索引的两个c),z = 4(c012,c013,c023,c123)。
总共有(j + 1)个索引的x*y
系数,并且系数不同,因此每个系数出现的时间称为系数。通过简单的代数,我们得到。c
z
x*y/z
k_ij
k_ij = ncr(n,i+1) ncr(n-i-1,j-i) / ncr(n,j+1) = ncr(j+1,i+1)
因此,索引由下式给出:k_ij = nCr(j+1,i+1)
如果您想起所有定义,我们需要显示的是每列的交替和为1。
因此,交替总和s0 - s1 + s2 - s3 +- ... +- s(n-1)
可以表示为:
s_j = c__j * ∑[(-1)^(i+j) k_ij] for i=0..n-1
= c__j * ∑[(-1)^(i+j) nCr(j+1,i+1)] for i=0..n-1
= c__j * ∑[(-1)^(i+j) nCr(j+1,i)]{i=0..n} - (-1)^0 nCr(j+1,0)
= (-1)^j c__j
s = ∑[(-1)^j s_j] for j = 0..n-1
= ∑[(-1)^j (-1)^j c__j)] for j=0..n-1
= ∑[c__j] for j=0..n-1
= c
因此s=c
对于所有n = 1,2,3,...