rubinrot, ziemlich schnell, aber es kommt auf die Eingabe an
Beschleunigen Sie jetzt um den Faktor 2 bis 2,5, indem Sie von Zeichenfolgen zu Ganzzahlen wechseln.
Verwendungszweck:
cat <input> | ruby this.script.rb
Z.B.
mad_gaksha@madlab ~/tmp $ ruby c50138.rb < c50138.inp2
number of matches: 298208861472
took 0.05726237 s
Die Anzahl der Übereinstimmungen für eine einzelne Maske wird leicht durch den Binomialkoeffizienten berechnet. So 122020
braucht zum Beispiel 3 2
s gefüllt, 1 0
und 2 1
. Daher gibt es nCr(3,2)=nCr(3,1)=3!/(2!*1!)=3
verschiedene binäre Zeichenfolgen, die zu dieser Maske passen.
Ein Schnittpunkt zwischen n Masken m_1, m_2, ... m_n ist eine Maske q, so dass eine Binärzeichenfolge s nur dann mit q übereinstimmt, wenn sie mit allen Masken m_i übereinstimmt.
Wenn wir zwei Masken m_1 und m_2 nehmen, kann der Schnittpunkt leicht berechnet werden. Setzen Sie einfach m_1 [i] = m_2 [i], wenn m_1 [i] == 2. Der Schnittpunkt zwischen 122020
und 111222
ist 111020
:
122020 (matched by 3 strings, 111000 110010 101010)
111222 (matched by 1 string, 111000)
111020 (matched by 1 string, 111000)
Die zwei einzelnen Masken werden durch 3 + 1 = 4 Zeichenfolgen abgeglichen, die Schnittmaske wird durch eine Zeichenfolge abgeglichen, daher gibt es 3 + 1-1 = 3 eindeutige Zeichenfolgen, die mit einer oder beiden Masken übereinstimmen.
Ich werde N (m_1, m_2, ...) nennen, die Anzahl der Zeichenfolgen, die mit allen m_i übereinstimmen. Unter Anwendung der gleichen Logik wie oben können wir die Anzahl der eindeutigen Zeichenfolgen berechnen, die mit mindestens einer Maske übereinstimmen, die durch das Einschlussausschlussprinzip gegeben ist, und siehe auch unten wie folgt:
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) -+ ...
Es gibt viele, viele, viele Kombinationen von etwa 30 von 200 Masken.
Diese Lösung geht also davon aus, dass nicht viele Schnittpunkte höherer Ordnung der Eingabemasken existieren, d. H. Die meisten n-Tupel mit n> 2 Masken haben keine gemeinsamen Übereinstimmungen.
Verwenden Sie den Code hier, der Code bei ideone ist möglicherweise veraltet.
Ich habe eine Funktion hinzugefügt remove_duplicates
, mit der die Eingabe vorverarbeitet und Masken gelöscht werden können m_i
, sodass alle übereinstimmenden Zeichenfolgen auch mit einer anderen Maske übereinstimmen m_j
. Für die aktuelle Eingabe dauert dies tatsächlich länger, da es keine solchen Masken gibt (oder nicht viele). Daher wird die Funktion im folgenden Code noch nicht auf die Daten angewendet.
Code:
# 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"
Dies wird als Einschluss-Ausschluss-Prinzip bezeichnet, aber bevor mich jemand darauf hingewiesen hatte, hatte ich meinen eigenen Beweis. Etwas selbst zu tun fühlt sich aber großartig an.
Betrachten wir den Fall von 2 Masken, rufen Sie dann 0
und 1
zuerst an. Wir nehmen jede ausgeglichene Binärzeichenfolge und klassifizieren sie nach den übereinstimmenden Masken. c0
diejenigen , die Anzahl der , die nur Maske entsprechen 0
, c1
die nunber von denen , die nur entsprechen 1
, c01
diejenigen , die Spiel - Maske 0
und 1
.
Sei s0
die Zahlensumme der Anzahl der Übereinstimmungen für jede Maske (sie können sich überlappen). Sei s1
die Summe der Übereinstimmungen für jedes Maskenpaar (2-Kombinationen). Sei s_i
die Summe der Anzahl der Übereinstimmungen für jede (i + 1) Maskenkombination. Die Anzahl der Übereinstimmungen von n-Masken ist die Anzahl der Binärzeichenfolgen, die allen Masken entsprechen.
Wenn es n Masken gibt, ist die gewünschte Ausgabe die Summe aller c
, dh. c = c0+...+cn+c01+c02+...+c(n-2)(n-1)+c012+...+c(n-3)(n-2)(n-1)+...+c0123...(n-2)(n-1)
. Was das Programm berechnet, ist die alternierende Summe aller s
, dh. s = s_0-s_1+s_2-+...+-s_(n-1)
. Das möchten wir beweisen s==c
.
n = 1 ist offensichtlich. Betrachte n = 2. Das Zählen aller Übereinstimmungen von Masken 0
gibt c0+c01
(die Anzahl der Zeichenfolgen, die nur mit 0 übereinstimmen + die mit beiden übereinstimmen 0
und 1
), das Zählen aller Übereinstimmungen von 1
gibt c1+c02
. Wir können dies wie folgt veranschaulichen:
0: c0 c01
1: c1 c10
Per Definition s0 = c0 + c1 + c12
. s1
ist die Summe der Übereinstimmungen jeder 2-Kombination von [0,1]
, dh. alle uniqye c_ij
s. Denken Sie daran c01=c10
.
s0 = c0 + c1 + 2 c01
s1 = c01
s = s0 - s1 = c0 + c1 + c01 = c
Also s=c
für n = 2.
Betrachten Sie nun 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
Also s=c
für n = 3. c__i
repräsentiert das aller c
s mit (i + 1) Indizes, zB c__1 = c01
für n = 2 und c__1 = c01 + c02 + c12
für n == 3.
Für n = 4 beginnt ein Muster aufzutauchen:
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
Also s==c
für n = 4.
Im Allgemeinen erhalten wir Binomialkoeffizienten wie diese (↓ ist i, → ist 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 . . .
. .
. .
. .
Um dies zu sehen, bedenken Sie, dass es für einige i
und folgende j
gibt:
- x = ncr (n, i + 1): Kombinationen C für den Schnittpunkt der (i + 1) Maske aus n
- y = ncr (ni-1, ji): Für jede Kombination C oben gibt es y verschiedene Kombinationen für den Schnittpunkt von (j + 2) Masken aus denen, die C enthalten
- z = ncr (n, j + 1): verschiedene Kombinationen für den Schnittpunkt von (j + 1) Masken aus n
Da dies verwirrend klingen mag, ist hier die Definition eines Beispiels. Für i = 1, j = 2, n = 4 sieht es so aus (vgl. Oben):
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
Hier ist also x = 6 (01, 02, 03, 12, 13, 23), y = 2 (zwei c mit drei Indizes für jede Kombination), z = 4 (c012, c013, c023, c123).
Insgesamt gibt es x*y
Koeffizienten c
mit (j + 1) -Indizes, und es gibt z
verschiedene, so dass jedes x*y/z
Mal auftritt , was wir den Koeffizienten nennen k_ij
. Durch einfache Algebra erhalten wirk_ij = ncr(n,i+1) ncr(n-i-1,j-i) / ncr(n,j+1) = ncr(j+1,i+1)
.
Der Index ist also gegeben durch k_ij = nCr(j+1,i+1)
Wenn Sie sich an alle Definitionen erinnern, müssen wir nur zeigen, dass die alternierende Summe jeder Spalte 1 ergibt.
Die alternierende Summe s0 - s1 + s2 - s3 +- ... +- s(n-1)
kann somit ausgedrückt werden als:
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
Also s=c
für alle n = 1,2,3, ...