Here's an O(n)
algorithm for a different problem for when you have a string instead of a list of words as input.. It was my oversight, but will leave the solution here cause I don't feel like deleting it :)
Since we are only interested in characters, it makes the problem a lot easier. Maintain a map of each character [a-z]
to its position in the string. This map alone is sufficient do determine if we have a pangram and what's its length.
1. Initialize a map of all alphabets to null
2. Initialize shortest_pangram to { length: ∞, value: undefined }
3. Loop through each "character" in given string
3.1 Update the value of map[character] to current string index
3.2 If we have a pangram, and its the shortest so far, record its length/value
4. shortest_pangram should have our result
The map we created is enough to determine if we have a pangram - if all values in our map are non null, we have a pangram.
To find the length of the current pangram, subtract the max value from the min value in our map. Remember that before finding the length, we must check if it is a pangram.
Here's a naive non-optimized implementation in Ruby:
class Pangram
def initialize(string)
@input = string.downcase.split('')
@map = {}
('a'..'z').each { |c| @map[c] = nil }
infinity = 1.0/0.0
@best = { :length => infinity, :string => nil }
end
def shortest
@input.each_with_index do |c, index|
@map[c] = index if @map.key?(c)
if pangram? and length < @best[:length]
@best[:length] = length
@best[:string] = value
end
end
@best
end
def pangram?
@map.values.all? { |value| !value.nil? }
end
def length
@map.values.max - @map.values.min
end
def value
@input[@[email protected]].join('')
end
end
To use, instantiate the class and pass it the entire string. Call .shortest to find the length of the shortest pangram and the matching substring.
pangram = Pangram.new("..")
print pangram.shortest