[2023][ruby][12.2]
change pqluzpmrtkrulmtrqvqnzsuxtutssuqv
commit f96c16a9d3f68edad61a56da9d3e8a194025dbcf
author Alpha Chen <alpha@kejadlen.dev>
date
parent yqzxrkll
diff --git a/2023/ruby/day_12.rb b/2023/ruby/day_12.rb
index 97cfc1d..ddc6494 100644
--- a/2023/ruby/day_12.rb
+++ b/2023/ruby/day_12.rb
@@ -1,14 +1,70 @@
-p ARGF.readlines(chomp: true).map {|row|
+input = ARGF.readlines(chomp: true).map {|row|
   springs, groups = row.split(" ")
   [springs, groups.scan(/\d+/).map(&:to_i)]
-}.map {|springs, groups|
-  unknowns = springs.count(??)
-  (0...2**unknowns).map {|arrangement|
-    arrangement = arrangement.to_s(2).rjust(unknowns, ?0).chars.map { _1 == ?0 ? ?. : ?# }
-    candidate = springs.gsub(??) { arrangement.shift }
-    candidate.chars
-      .slice_when { _1 != _2 }
-      .reject { _1[0] == ?. }
-      .map(&:size) == groups
-  }.count(&:itself)
-}.sum
+}
+
+p input
+  # .map { [ _1.chars, _2 ] }
+  .map { [Array.new(5, _1).join(??).chars, _2 * 5] }
+  .map {|springs, groups|
+    arrangement_counts = Hash.new {|h,k|
+      count, springs, groups = k
+      first, *rest = springs
+      h[k] = case [count, first, groups]
+             in count, _, []
+               if count.nonzero?
+                 0
+               elsif springs.include?(?#)
+                 0
+               else
+                 1
+               end
+             in count, _, groups if count > groups.fetch(0)
+               0 # early exit if the damaged group ever gets too large
+             in count, nil, groups # no more springs
+               if groups.size > 1
+                 0
+               elsif count == groups.fetch(0)
+                 1
+               else
+                 0
+               end
+             in count, ?#, groups # continue a damaged group
+                 h[[count + 1, rest, groups]]
+             in 0, ?., groups
+               h[[0, rest, groups]]
+             in count, ?., groups # finish a damaged group
+               if count == groups.fetch(0)
+                 h[[0, rest, groups[1..]]]
+               else
+                 0
+               end
+             in count, ??, g if count == g.fetch(0) # unknown spring, matched current group
+               h[[0, rest, g[1..]]]
+             in count, ??, g unless count.zero? # unknown spring, ongoing group
+               h[[count + 1, rest, g]]
+             in count, ??, g # unknown spring
+               [
+                 h[[count, rest, g]], # undamaged
+                 h[[count + 1, rest, g]], # damaged
+               ].sum
+             else
+               fail "#{{first:, springs: springs.join, groups:}}"
+             end
+    }
+    arrangement_counts[[0, springs, groups]]
+  }.sum
+
+# original part one
+# p input.map {|springs, groups|
+#   unknowns = springs.count(??)
+#   (0...2**unknowns).map {|arrangement|
+#     arrangement = arrangement.to_s(2).rjust(unknowns, ?0).chars.map { _1 == ?0 ? ?. : ?# }
+#     candidate = springs.gsub(??) { arrangement.shift }
+#     candidate.chars
+#       .slice_when { _1 != _2 }
+#       .reject { _1[0] == ?. }
+#       .map(&:size) == groups
+#   }.count(&:itself)
+# }.sum
+