Here are my solutions. They are very slow ...
Not elegant at all, but at least they can find the solution.
(1) simple breadth-first
路路路
------------------------------------------------------------------------
def search( start_word, end_word, dictionary )
聽聽# check pre-condition
聽聽raise "invalid argument" if start_word.nil? ||
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽end_word.nil? ||
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽dictionary.nil? ||
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽start_word.size != end_word.size
聽聽return [strat_word, end_word] if start_word == end_word
聽聽return nil unless dictionary.delete(start_word)
聽聽return nil unless dictionary.delete(end_word)
聽聽
聽聽paths = [[start_word]]
聽聽while !paths.empty?
聽聽聽聽new_paths = []
聽聽聽聽paths.each do |path|
聽聽聽聽聽聽word = path.last
聽聽聽聽聽聽word.size.times do |i|
聽聽聽聽聽聽聽聽('a'..'z').each do |c|
聽聽聽聽聽聽聽聽聽聽new_word = word.dup
聽聽聽聽聽聽聽聽聽聽new_word[i] = c
聽聽聽聽聽聽聽聽聽聽return (path << new_word) if new_word == end_word
聽聽聽聽聽聽聽聽聽聽next if !dictionary.delete(new_word)
聽聽聽聽聽聽聽聽聽聽new_paths << (path.dup << new_word)
聽聽聽聽聽聽聽聽end
聽聽聽聽聽聽end
聽聽聽聽end
聽聽聽聽paths = new_paths
聽聽end
聽聽nil
end
dictionary = 'words.txt'
if d_index = ARGV.index('-d')
聽聽ARGV.delete_at(d_index)
聽聽dictionary = ARGV.delete_at(d_index)
end
start_word = ARGV[0]
end_word = ARGV[1]
if start_word.nil? || end_word.nil? || dictionary.nil?
聽聽puts "Usage: ruby #$0 [ -d dictionary ] start_word end_word"
聽聽exit
end
puts "Loading dictionary..."
words = []
File.open(dictionary) { |f|
聽聽while word = f.gets
聽聽聽聽word.chomp!
聽聽聽聽words << word if word.size == start_word.size
聽聽end
}
puts "Building chain..."
result = search(start_word, end_word, words)
puts( result ? result : "No solution." )
------------------------------------------------------------------------
(2) simple bidirectional breadth-first
------------------------------------------------------------------------
def search( start_word, end_word, dictionary )
聽聽# check pre-condition
聽聽raise "invalid argument" if start_word.nil? ||
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽end_word.nil? ||
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽dictionary.nil? ||
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽start_word.size != end_word.size
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽
聽聽return [strat_word, end_word] if start_word == end_word
聽聽return nil unless dictionary.delete(start_word)
聽聽return nil unless dictionary.delete(end_word)
聽聽word_length = start_word.length
聽聽start_paths = [[start_word]]
聽聽end_paths = [[end_word]]
聽聽paths = start_paths
聽聽last_new_words = [end_word]
聽聽loop do
聽聽聽聽new_paths = []
聽聽聽聽new_words = []
聽聽聽聽paths.each do |path|
聽聽聽聽聽聽word = path.last
聽聽聽聽聽聽word_length.times do |i|
聽聽聽聽聽聽聽聽('a'..'z').each do |c|
聽聽聽聽聽聽聽聽聽聽new_word = word.dup
聽聽聽聽聽聽聽聽聽聽new_word[i] = c
聽聽聽聽聽聽聽聽聽聽if index = last_new_words.index(new_word)
聽聽聽聽聽聽聽聽聽聽聽聽if paths == start_paths
聽聽聽聽聽聽聽聽聽聽聽聽聽聽return path.push(*end_paths[index].reverse)
聽聽聽聽聽聽聽聽聽聽聽聽else
聽聽聽聽聽聽聽聽聽聽聽聽聽聽return start_paths[index].push(*path.reverse)
聽聽聽聽聽聽聽聽聽聽聽聽end
聽聽聽聽聽聽聽聽聽聽end
聽聽聽聽聽聽聽聽聽聽next unless dictionary.delete(new_word)
聽聽聽聽聽聽聽聽聽聽new_paths << (path.dup << new_word)
聽聽聽聽聽聽聽聽聽聽new_words << new_word
聽聽聽聽聽聽聽聽end
聽聽聽聽聽聽end
聽聽聽聽end
聽聽聽聽
聽聽聽聽return nil if new_paths.empty?
聽聽聽聽
聽聽聽聽if paths == start_paths
聽聽聽聽聽聽start_paths = new_paths
聽聽聽聽聽聽paths = end_paths
聽聽聽聽else
聽聽聽聽聽聽end_paths = new_paths
聽聽聽聽聽聽paths = start_paths
聽聽聽聽end
聽聽聽聽last_new_words = new_words
聽聽end
end
dictionary = 'words.txt'
if d_index = ARGV.index('-d')
聽聽ARGV.delete_at(d_index)
聽聽dictionary = ARGV.delete_at(d_index)
end
start_word = ARGV[0]
end_word = ARGV[1]
if start_word.nil? || end_word.nil? || dictionary.nil?
聽聽puts "Usage: ruby #$0 [ -d dictionary ] start_word end_word"
聽聽exit
end
puts "Loading dictionary..."
words = []
File.open(dictionary) { |f|
聽聽while word = f.gets
聽聽聽聽word.chomp!
聽聽聽聽words << word if word.size == start_word.size
聽聽end
}
puts "Building chain..."
result = search(start_word, end_word, words)
puts( result ? result : "No solution." )
------------------------------------------------------------------------