Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add all_indices option to kmp #56

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
28 changes: 19 additions & 9 deletions lib/algorithms/search.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,19 @@ def self.binary_search(container, item)
# substring in a string. The algorithm calculates the best position to resume searching from if a failure
# occurs.
#
# The method returns the index of the starting position in the string where the substring is found. If there
# is no match, nil is returned.
# The method can either return the index of the first occurrence of the substring in the string or an array
# of all starting positions where the substring is found, depending on the `all_indices` flag.
# If there is no match, nil is returned or an empty array if `all_indices` is true.
#
# Complexity: O(n + k), where n is the length of the string and k is the length of the substring.
#
# Algorithms::Search.kmp_search("ABC ABCDAB ABCDABCDABDE", "ABCDABD") #=> 15
# Algorithms::Search.kmp_search("ABC ABCDAB ABCDABCDABDE", "ABCDEF") #=> nil
def self.kmp_search(string, substring)
# Algorithms::Search.kmp_search(string, "ABC", all_indices: true) #=> [0, 4, 11, 15]
# Algorithms::Search.kmp_search("ABC ABCDAB ABCDABCDABDE", "ABCDEF", all_indices: true) #=> []
def self.kmp_search(string, substring, all_indices: false)
return nil if string.nil? or substring.nil?

# create failure function table
pos = 2
cnd = 0
Expand All @@ -60,25 +63,32 @@ def self.kmp_search(string, substring)
end
end

match_indices = []
m = i = 0
while m + i < string.length
if substring[i] == string[m + i]
i += 1
return m if i == substring.length
if i == substring.length - 1
return m if !all_indices
match_indices << m
m = m + i - failure_table[i]
i = failure_table[i] > -1 ? failure_table[i] : 0
else
i += 1
end
else
m = m + i - failure_table[i]
i = failure_table[i] if i > 0
end
end
return nil
return match_indices.empty? ? nil : match_indices
end

# Allows kmp_search to be called as an instance method in classes that include the Search module.
#
# class String; include Algorithms::Search; end
# "ABC ABCDAB ABCDABCDABDE".kmp_search("ABCDABD") #=> 15
def kmp_search(substring)
Algorithms::Search.kmp_search(self, substring)
def kmp_search(substring, all_indices: false)
Algorithms::Search.kmp_search(self, substring, all_indices: all_indices)
end

end
15 changes: 14 additions & 1 deletion spec/search_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,21 @@ class String; include Algorithms::Search; end
expect(Algorithms::Search.kmp_search(string, "")).to be_nil
expect(Algorithms::Search.kmp_search(nil, "ABCD")).to be_nil
end


it "should use kmp_search to find all substrings if all_indices is true" do
string = "ABC ABCDAB ABCDABCDABDE"
expect(Algorithms::Search.kmp_search(string, "ABC", all_indices: true)).to eql([0, 4, 11, 15])
expect(Algorithms::Search.kmp_search(string, "ABCDEF", all_indices: true)).to be_nil
expect(Algorithms::Search.kmp_search(string, nil, all_indices: true)).to be_nil
expect(Algorithms::Search.kmp_search(string, "", all_indices: true)).to be_nil
expect(Algorithms::Search.kmp_search(nil, "ABCD", all_indices: true)).to be_nil
end

it "should let you include Search in String to enable instance methods" do
expect("ABC ABCDAB ABCDABCDABDE".kmp_search("ABCDABD")).to eql(15)
end

it "should let you include Search in String to enable instance methods with all_indices set to true" do
expect("ABC ABCDAB ABCDABCDABDE".kmp_search("ABC", all_indices: true)).to eql([0, 4, 11, 15])
end
end