mirror of
https://github.com/TheAlgorithms/Ruby
synced 2024-11-16 19:50:00 +01:00
Merge pull request #198 from aparibocci/feature/boyer_moore_horspool_search
Implementing `Boyer-Moore-Horspool` substring search algorithm
This commit is contained in:
commit
42f63958eb
2 changed files with 80 additions and 0 deletions
60
strings/boyer_moore_horspool_search.rb
Normal file
60
strings/boyer_moore_horspool_search.rb
Normal file
|
@ -0,0 +1,60 @@
|
|||
##
|
||||
# This class represents a table of {bad_match_character => slide_offset}
|
||||
# to be used in Boyer-Moore-Horspool substring finding algorithm.
|
||||
|
||||
class BadMatchTable
|
||||
|
||||
attr_reader :pattern
|
||||
attr_reader :table
|
||||
|
||||
def initialize(pattern)
|
||||
@pattern = pattern
|
||||
@table = {}
|
||||
for i in 0...pattern.size
|
||||
@table[pattern[i]] = pattern.size - 1 - i
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Given a mismatch character belonging to the search string, returns
|
||||
# the offset to be used when sliding the pattern towards the right.
|
||||
|
||||
def slide_offset(mismatch_char)
|
||||
table.fetch(mismatch_char, pattern.size)
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the first starting index of the given pattern's occurrence (as a substring)
|
||||
# in the provided search string if a match is found, -1 otherwise.
|
||||
|
||||
def first_match_index(search_string, pattern)
|
||||
matches = matches_indices(search_string, pattern, true)
|
||||
matches.empty? ? -1 : matches[0]
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the list of starting indices of the given pattern's occurrences (as a substring)
|
||||
# in the provided search string.
|
||||
# If no match is found, an empty list is returned.
|
||||
# If `stop_at_first_match` is provided as `true`, the returned list will contain at most one element,
|
||||
# being the leftmost encountered match in the search string.
|
||||
|
||||
def matches_indices(search_string, pattern, stop_at_first_match=false)
|
||||
table = BadMatchTable.new(pattern)
|
||||
i = pattern.size - 1
|
||||
indices = []
|
||||
while i < search_string.size
|
||||
for j in 0...pattern.size
|
||||
if search_string[i-j] != pattern[pattern.size-1-j]
|
||||
i += table.slide_offset(search_string[i-j])
|
||||
break
|
||||
elsif j == pattern.size-1
|
||||
indices.append(i-j)
|
||||
return indices if stop_at_first_match
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
indices
|
||||
end
|
20
strings/boyer_moore_horspool_search_test.rb
Normal file
20
strings/boyer_moore_horspool_search_test.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
require 'minitest/autorun'
|
||||
require_relative 'boyer_moore_horspool_search'
|
||||
|
||||
class TestBoyerMooreHorspoolSearch < Minitest::Test
|
||||
def test_first_match_returns_negative_index_if_no_match
|
||||
assert first_match_index('abcdefghijk', 'defz') < 0
|
||||
end
|
||||
|
||||
def test_first_match_returns_first_match_index
|
||||
assert first_match_index('abcdefghijkghilmno', 'ghi') == 6
|
||||
end
|
||||
|
||||
def test_match_indices_returns_empty_list_if_no_match
|
||||
assert matches_indices('abcdefghijk', 'defz').empty?
|
||||
end
|
||||
|
||||
def test_match_indices_returns_list_of_match_indices
|
||||
assert matches_indices('abcdefghijkghilmno', 'ghi') == [6, 11]
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue