史上最大のなんとかかんとか

あなたのスキルで飯は食えるか? 史上最大のコーディングスキル判定だそうです。*1

ちょっとやってみました。
開始時間をちゃんと見てなかった…2時間くらい?
一応動いてるんじゃないかな…

3(4)5 みたいな真ん中待つ手があるの忘れてました……

#!/usr/bin/ruby

class ManzInternal
  @@manz = Hash.new
  def self.create(input)
    @@manz[input] ||= ManzInternal.new(input) unless input.empty?
  end

  def initialize(input)
    @input_str = input
    @input_ary = input.split(//)
    @input_ary_uniq = @input_ary.uniq
  end

  def single
    @single ||= @input_ary_uniq.inject({}) { |res, k|
        res[k] = self.class.create(@input_str.sub(k, ''))
        res
    }
  end

  def pair
    @pair ||= (@input_ary.size < 2) ? {} : self.single.inject({}) { |res, (k, v)|
      res[k + k] = self.class.create(v.delete(k)) if v.include?(k)
      res
    }
  end

  def three
    @three ||= (@input_ary.size < 3) ? {} : self.pair.inject({}) { |res, (k, v)|
      k1 = k[0, 1]
      res[k + k1] = self.class.create(v.delete(k1)) if v.include?(k1)
      res
    }
  end

  def straight
    @straight ||= search_straight(3)
  end

  def half_straight
    @half_straight ||= search_straight(2).merge(search_straight(2, 2))
  end

  def search_straight(len, step = 1)
    straight = Hash.new
    if len <= @input_ary.size
      v = @input_ary_uniq.join
      first = @input_ary_uniq[0].to_i
      last = @input_ary_uniq[-1].to_i - (len - 1) * step
      (first..last).each do |i|
        range = (0...len).inject([]) { |r, j| r << (i + j * step) }
        k = range.join
        straight[k] = self.class.create(range.inject(@input_str) { |r, j| r.sub(j.to_s, '') }) if v.include?(k)
      end
    end
    straight
  end

  def to_a
    @input_ary
  end

  def to_s
    @input_str
  end

  def inspect
    '#<%s:"%s">' % [ self.class, @input_str ]
  end

  def include?(str)
    @input_str.include? str
  end

  def delete(str)
    to_s.sub(str, '')
  end

  def all_by_types
    { :single        => self.single,
      :pair          => self.pair,
      :three         => self.three,
      :straight      => self.straight,
      :half_straight => self.half_straight }
  end

  protected
    def internal_search(set, count, searched, result, &block)
      self.all_by_types.each do |type, data|
        data.each do |k, manz|
          new_count = count.merge(type => k) { |_, a, b| a.dup << b }
          next if 1 < new_count[:single].size ||
                  2 < new_count[:pair].size + new_count[:half_straight].size ||
                  1 < new_count[:half_straight].size
          new_set = (set + [k]).sort
          unless searched.include?(search_key = new_set.hash)
            if manz
              manz.internal_search(new_set, new_count, searched, result, &block)
            else
              result << to_win(new_count)
              block.call(result.last) if block
            end
            searched[search_key] = true
          end
        end
      end
    end

  private
    def to_win(count)
      type = (1 == count[:single].size)        ? :single        :
             (2 == count[:pair].size)          ? :pair          :
             (1 == count[:half_straight].size) ? :half_straight :
             nil
      raise 'invalid set. (%s)' % count.values.flatten.join(')(') unless type
      '(%s)[%s]' % [ count.reject { |t, k| type == t }.values.flatten.sort.join(')('), count[type].join('][') ]
    end
end

class Manz < ManzInternal
  def initialize(input)
    raise 'Require 13 numbers 1 to 9.' unless /^[1-9]{13}$/ =~ input.to_s
    super input.to_s.split(//).sort.join
  end

  def search(&block)
    set = []
    count = self.all_by_types.keys.inject({}) { |r, type| r[type] = []; r }
    searched = {}
    result = []
    internal_search(set, count, searched, result, &block)
    result
  end
end

input = ARGV[0] || $stdin.gets
Manz.new(input).search do |win|
  print win + "\n"
end

修正前との差分

--- manz.rb.orig	2010-04-18 06:52:14.000000000 +0900
+++ manz.rb	2010-04-18 06:55:49.000000000 +0900
@@ -9,10 +9,11 @@
   def initialize(input)
     @input_str = input
     @input_ary = input.split(//)
+    @input_ary_uniq = @input_ary.uniq
   end
 
   def single
-    @single ||= @input_ary.uniq.inject({}) { |res, k|
+    @single ||= @input_ary_uniq.inject({}) { |res, k|
         res[k] = self.class.create(@input_str.sub(k, ''))
         res
     }
@@ -38,16 +39,18 @@
   end
 
   def half_straight
-    @half_straight ||= search_straight(2)
+    @half_straight ||= search_straight(2).merge(search_straight(2, 2))
   end
 
-  def search_straight(len)
+  def search_straight(len, step = 1)
     straight = Hash.new
     if len <= @input_ary.size
-      v = self.single.keys.sort.join
-      (1..7).each do |i|
-        range = (i...i+len)
-        k = range.to_a.join
+      v = @input_ary_uniq.join
+      first = @input_ary_uniq[0].to_i
+      last = @input_ary_uniq[-1].to_i - (len - 1) * step
+      (first..last).each do |i|
+        range = (0...len).inject([]) { |r, j| r << (i + j * step) }
+        k = range.join
         straight[k] = self.class.create(range.inject(@input_str) { |r, j| r.sub(j.to_s, '') }) if v.include?(k)
       end
     end

*1:飯を食えるかどうかはスキルに関係ないよね、日本って…