Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -645,3 +645,4 @@ Profile on LeetCode: [fartem](https://leetcode.com/fartem/).
| 32. Longest Valid Parentheses | [Link](https://leetcode.com/problems/longest-valid-parentheses/) | [Link](./lib/hard/32_longest_valid_parentheses.rb) | [Link](./test/hard/test_32_longest_valid_parentheses.rb) |
| 41. First Missing Positive | [Link](https://leetcode.com/problems/first-missing-positive/) | [Link](./lib/hard/41_first_missing_positive.rb) | [Link](./test/hard/test_41_first_missing_positive.rb) |
| 115. Distinct Subsequences | [Link](https://leetcode.com/problems/distinct-subsequences/) | [Link](./lib/hard/115_distinct_subsequences.rb) | [Link](./test/hard/test_115_distinct_subsequences.rb) |
| 126. Word Ladder II | [Link](https://leetcode.com/problems/word-ladder-ii/) | [Link](./lib/hard/126_word_ladder_ii.rb) | [Link](./test/hard/test_126_word_ladder_ii.rb) |
2 changes: 1 addition & 1 deletion leetcode-ruby.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ require 'English'
::Gem::Specification.new do |s|
s.required_ruby_version = '>= 3.0'
s.name = 'leetcode-ruby'
s.version = '7.7.5'
s.version = '7.7.6'
s.license = 'MIT'
s.files = ::Dir['lib/**/*.rb'] + %w[README.md]
s.executable = 'leetcode-ruby'
Expand Down
79 changes: 79 additions & 0 deletions lib/hard/126_word_ladder_ii.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# frozen_string_literal: true

# https://leetcode.com/problems/word-ladder-ii/
# @param {String} begin_word
# @param {String} end_word
# @param {String[]} word_list
# @return {String[][]}
def find_ladders(begin_word, end_word, word_list)
return [] unless (unvisited = word_list.to_set).include?(end_word)

transformations = ::Hash.new { |h, k| h[k] = ::Set.new }

begin_layer = ::Set[begin_word]
end_layer = ::Set[end_word]
unvisited.delete(end_word)
transforms = Array('a'..'z').product(Array(0...begin_word.size))

current_levels = begin_layer
previous_levels = end_layer
loop do
current_levels, previous_levels = previous_levels, current_levels

unvisited.subtract(previous_levels)

next_layer = ::Set.new
current_levels.each do |word|
transforms.each do |c, i|
next if word[i] == c

transform = word.dup.tap { |w| w[i] = c }

is_neighbor = unvisited.include?(transform)

next_layer << transform if is_neighbor

if is_neighbor || previous_levels.include?(transform)
if current_levels.equal?(begin_layer)
transformations[word] << transform
else
transformations[transform] << word
end
end
end
end

break if next_layer.empty? || begin_layer.any? { |w| transformations[w].intersect?(end_layer) }

current_levels.replace(next_layer)
end

dfs_paths(begin_word, end_word, transformations)
end

private

# @param {String} begin_word
# @param {String} end_word
# @param {Hash} word_graph
# @return {String[][]}
def dfs_paths(begin_word, end_word, word_graph)
paths = []
path = []
stack = [[begin_word, 1]]

until stack.empty?
word, level = stack.pop

path.pop until path.size < level
path << word

if word == end_word
paths << path.dup
else
word_graph[word].each { |w| stack << [w, level + 1] }
end
end

paths
end
32 changes: 32 additions & 0 deletions test/hard/test_126_word_ladder_ii.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

require_relative '../test_helper'
require_relative '../../lib/hard/126_word_ladder_ii'
require 'minitest/autorun'

class WordLadderIITest < ::Minitest::Test
def test_default_one
assert_equal(
[
%w[hit hot lot log cog],
%w[hit hot dot dog cog]
],
find_ladders(
'hit',
'cog',
%w[hot dot dog lot log cog]
)
)
end

def test_default_two
assert_equal(
[],
find_ladders(
'hit',
'cog',
%w[hot dot dog lot log]
)
)
end
end
Loading