Skip to content

Instantly share code, notes, and snippets.

@datt
Created July 31, 2020 16:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save datt/f321718efdec3ec69fd65144ccf74a55 to your computer and use it in GitHub Desktop.
Save datt/f321718efdec3ec69fd65144ccf74a55 to your computer and use it in GitHub Desktop.
Finding length of longest substring code in Ruby. Uses Ruby's core methods, may not be efficient for other languages. Probably one of the fastest solution implemented in Ruby, suggestions are welcomed.
# Rough Algo
# 1. Initialize last_distinct with first character
# 2. Iterate character by character
# 3. if no repeating char, Push current character in last_distinct
# 4. Repeat 3 until repeating char found
# 5 if repeating char found
# 6 if repeating char at first place, drop and rotate
# 7. if repeating char not at first place, reset with char and set longest.
class LongestDistinctSubstring # Finds length of the longest substring
ARG_ERROR = 'ArgumentError:: Expects a valid non blank string'.freeze
attr_reader :longest # just to access the found substrings with index
# @param [String] input string for finding longest substring length
def initialize(input_string)
raise ARG_ERROR if !input_string.is_a?(String) || input_string.length.zero?
@input_string = input_string.downcase # making case insensitive
@longest = { strings: [], length: 0 } # for storing result
@last_distinct = '' # storing reference to current longest string
@last_distinct_length = 0 # storing length of current longest string
end
# Returns the longest distinct substring length
def longest_length
find_longest if @longest[:length] == 0
@longest[:length]
end
private
# The core logic, refer Rough Algo section at top
def find_longest
@input_string.each_char.with_index do |char, idx|
# if duplicate character found, either remove first char or reset
if @last_distinct.include?(char)
# # duplicate character found at first character, drop it from longest
if @last_distinct.index(char).zero?
@last_distinct = @last_distinct.slice(1..-1) << char
# i.e. rotate the first character to the last
@last_distinct_length = @last_distinct.length
else
update_new_longest(idx) if new_longest?
reset_last_distinct(char) # repetitive char found, reset it with char
end
else
@last_distinct << char
@last_distinct_length = @last_distinct.length
update_new_longest(idx) if new_longest? && idx.next == @input_string.length
# handling edge case e.g. 'abcabcd' must return 4 i.e. 'abcd'
end
end
end
# compares new length and current length and
# @return [Boolean]
def new_longest?
@last_distinct_length >= @longest[:length]
end
# resets the last_distinct with sent substring
# @param [String] substring
def reset_last_distinct(substring)
@last_distinct = substring
end
# Updates length of newly substring
# @param [Integer] number which is index of current loop
def update_new_longest(idx)
update_longest_string_data(idx)
@longest[:length] = @last_distinct_length
end
# @todo: Though Getting all the uniq strings of highest length,
# was out of scope this program. It's logic isn't perfect, needs improvement.
def update_longest_string_data(idx)
start_index = idx - @last_distinct_length
string_data = {
substring: @last_distinct,
sindex: start_index
}
if @last_distinct_length > @longest[:length]
@longest[:strings] = [string_data]
else # if string length is same as last_distinct substring
@longest[:strings] << string_data
end
end
end
# simple tests
string = 'abcabcd' #abcd
lds = LongestDistinctSubstring.new(string)
puts "Test Failed => abcabcd" unless lds.longest_length == 4
string = 'technology' # technol
lds = LongestDistinctSubstring.new(string)
puts "Test Failed => technology" unless lds.longest_length == 7
# benchmarking
require 'benchmark'
a = ('a'..'z').to_a
N = 1000
arr = 50.times.map { N.times.map { a.sample }.join }
5.times do
Benchmark.bm do |x|
x.report("LDS") { arr.sum { |s| LongestDistinctSubstring.new(s).longest_length } }
end
end
# user system total real
# LDS 0.034644 0.000502 0.035146 ( 0.037816)
# user system total real
# LDS 0.028382 0.000551 0.028933 ( 0.032161)
# user system total real
# LDS 0.024485 0.000502 0.024987 ( 0.026192)
# user system total real
# LDS 0.025439 0.000338 0.025777 ( 0.026046)
# user system total real
# LDS 0.023968 0.000215 0.024183 ( 0.024395)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment