루비, 꽤 빠르지 만 입력에 따라 다릅니다.
이제 문자열에서 정수로 전환하여 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
3 과 2
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 개의 고유 한 문자열이 있습니다.
모든 m_i와 일치하는 문자열 수를 N (m_1, m_2, ...)이라고합니다. 위와 동일한 논리를 적용하여 포함 제외 원칙 에 따라 하나 이상의 마스크와 일치하는 고유 문자열의 수를 계산하고 다음과 같이 확인할 수 있습니다.
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- 튜플에는 공통 일치 항목이 없습니다.
여기 코드를 사용하십시오. 아이디어 코드가 오래되었을 수 있습니다.
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 조합)에 대한 일치하는 수의 합. LET 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
이야. 명심하십시오 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에 대해 C를 포함하는 마스크와 (j + 2) 마스크의 교차점에 대해 서로 다른 조합이 있습니다.
- 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, ...