Going from a top-down to a bottom-up algorithm (DP) - ruby

I have created this algorithm to compute the longest palindrome subsequence (word that is the same when mirrors, ie "aba", "racecar"), and have done so using a recursive top-down approach. I know that it's possible to turn these into iterative algorithms working from the bottom-up, but I am having trouble seeing how this could be accoplished
My code
def palindrome(string, r = {})
return 1 if string.length == 1
return 2 if string[0] == string[1] and string.length == 2
return r[string] if r.include?(string)
n = string.length
if string[0] == string[n-1]
r[string] = palindrome(string[1..n-2],r) + 2
else
r[string] = [palindrome(string[0..n-2],r), palindrome(string[1..n-1],r)].max
end
end

When you use negative numbers when fetching an item from an array, the array counts the elements from the end, so you don't have to keep the n variable
if string[0] == string[-1] # <= same as string[n-1]
r[string] = palindrome(string[1..-2],r) + 2 # <= same as string[1..n-2]
I don't know how performant this is, but here is a top-down suggestion:
def palindrome(string)
chars = string.chars
chars.length.downto(1).find do |length|
chars.each_cons(length).any? { |cons| cons == cons.reverse }
end
end

Related

Ruby - Find the longest non-repeating substring in any given string

I am working on an assignment where I have to take user input of a string and search through it to find the longest non-repeating string in it. So for example:
If the string is:
"abcabcabcdef"
My output needs to be:
"abcdef is the longest substring at the value of 6 characters"
Here is my poorly made code:
class Homework_4
puts "Enter any string of alphabetical characters: "
user_input = gets
longest_str = 0
empty_string = ""
map = {}
i = 0
j = 0
def long_substr()
while j < str_len
if map.key?(user_input[j])
i = [map[user_input[j]], i].max
end
longest_str = [longest_str, j - i + 1].max
map[user_input[j]] = j + 1
j += 1
end
longest_str
end
long_substr(user_input)
end
I have been working on this for over 6 hours today and I just can't figure it out. It seems like the internet has many ways to do it. Almost all of them confuse me greatly and don't really explain what they're doing. I don't understand the syntax they use or any of the variables or conditions.
All I understand is that I need to create two indicators that go through the inputted string searching for a non-repeating substring (sliding window method). I don't understand how to create them, what to make them do or even how to make them find and build the longest substring. It is very confusing to try and read the code that is full of random letters, symbols, and conditions. I'm sure my code is all sorts of messed up but any help or tips that could point me in the right direction would be greatly appreciated!
def uniq?(s)
# All letters of s uniq?
return s.chars.uniq == s.chars
end
def subs(s)
# Return all substrings in s.
(0..s.length).inject([]){|ai,i|
(i..s.length - i).inject(ai){|aj,j|
aj << s[i,j]
}
}.uniq
end
def longest_usub(s)
# Return first longest substring of s.
substrings(s).inject{|res, s| (uniq?(s) and s.length > res.length) ? s : res}
end
ruby's inject is actually a reduce function, where inject(optional_start_value){<lambda expression>} - and the lambda expression is similar to Python's lambda x, y: <return expression using x and y> just that lambda expressions are strangely written in Ruby as {|x, y| <return expression using x and y>}.
Python's range(i, y) is Ruby's i..y.
Python's slicing s[i:j] is in Ruby s[i..j] or s[i,j].
<< means add to end of the array.
Second solution (inspired by #Rajagopalan's answer)
def usub(s)
# Return first chunk of uniq substring in s
arr = []
s.chars do |char|
break if arr.include? char
arr << char
end
arr.join
end
def usubs(s)
# Return each position's usub() in s
(0..s.length).to_a.map{|i| usub(s[i,s.length])}
end
def longest_usub(s)
# return the longest one of the usubs() over s
usubs(s).max_by(&:length)
end
then you can do:
longest_usub("abcabcabcdef")
## "abcdef"
I have asssumed that a string is defined to be repeating if it contains a substring s of one or one more characters that is followed by the same substring s, and that a string is non-repeating if it is not repeating.
A string is seen to be repeating if and only if it matches the regular expression
R = /([a-z]+)\1/
Demo
The regular expression reads, "match one or more letters that are saved to capture group one, then match the content of capture group 1".
For convenience we can construct a simple helper method.
def nonrepeating?(str)
!str.match? R
end
I will perform a binary search to find the longest non-repeating string. First, I need a second helper method:
def find_nonrepeating(str, len)
0.upto(str.size-len) do |i|
s = str[i,len]
return s if nonrepeating?(s)
end
nil
end
find_nonrepeating("abababc", 7) #=> nil
find_nonrepeating("abababc", 6) #=> nil
find_nonrepeating("abababc", 5) #=> nil
find_nonrepeating("abababc", 4) #=> "babc"
find_nonrepeating("abababc", 3) #=> "aba"
find_nonrepeating("abababc", 2) #=> "ab"
find_nonrepeating("abababc", 1) #=> "a"
We may now implement the binary search.
def longest(str)
longest = ''
low = 0
high = str.size - 1
while low < high
mid = (low + high)/2
s = find_nonrepeating(str, mid)
if s
longest = s
low = mid + 1
else
high = mid - 1
end
end
longest
end
longest("dabcabcdef")
#=> "bcabcdef"
a = "abcabcabcdef"
arr = []
words = []
b=a
a.length.times do
b.chars.each do |char|
break if arr.include? char
arr << char
end
words << arr.join
arr.clear
b=b.chars.drop(1).join
end
p words.map(&:chars).max_by(&:length).join
Output
"abcdef"

Speeding up solution to algorithm

Working on the following algorithm:
Given an array of non-negative integers, you are initially positioned
at the first index of the array.
Each element in the array represents your maximum jump length at that
position.
Determine if you are able to reach the last index.
For example:
A = [2,3,1,1,4], return true.
A = [3,2,1,0,4], return false.
Below is my solution. It tries every single potential step, and then memoizes accordingly. So if the first element is three, the code takes three steps, two steps, and one step, and launches three separate functions from there. I then memoized with a hash. My issue is that the code works perfectly fine, but it's timing out for very large inputs. Memoizing helped, but only a little bit. Am I memoizing correctly or is backtracking the wrong approach here?
def can_jump(nums)
#memo = {}
avail?(nums, 0)
end
def avail?(nums, index)
return true if nums.nil? || nums.empty? || nums.length == 1 || index >= nums.length - 1
current = nums[index]
true_count = 0
until current == 0 #try every jump from the val to 1
#memo[index + current] ||= avail?(nums, index + current)
true_count +=1 if #memo[index + current] == true
current -= 1
end
true_count > 0
end
Here's a 𝑂(𝑛) algorithm:
Initialize 𝑚𝑎𝑥 to 0.
For each number 𝑛𝑖 in 𝑁:
If 𝑖 is greater than 𝑚𝑎𝑥, neither 𝑛𝑖 nor any subsequent number can be reached, so
return false.
If 𝑛𝑖+𝑖 is greater than 𝑚𝑎𝑥, set 𝑚𝑎𝑥 to 𝑛𝑖+𝑖.
If 𝑚𝑎𝑥 is greater than or equal to the last index in 𝑁
return true.
Otherwise return false.
Here's a Ruby implementation:
def can_jump(nums)
max_reach = 0
nums.each_with_index do |num, idx|
return false if idx > max_reach
max_reach = [idx+num, max_reach].max
end
max_reach >= nums.size - 1
end
p can_jump([2,3,1,1,4]) # => true
p can_jump([3,2,1,0,4]) # => false
See it on repl.it: https://repl.it/FvlV/1
Your code is O(n^2), but you can produce the result in O(n) time and O(1) space. The idea is to work backwards through the array keeping the minimum index found so far from which you can reach index n-1.
Something like this:
def can_jump(nums)
min_index = nums.length - 1
for i in (nums.length - 2).downto(0)
if nums[i] + i >= min_index
min_index = i
end
end
min_index == 0
end
print can_jump([2, 3, 1, 1, 4]), "\n"
print can_jump([3, 2, 1, 0, 4]), "\n"

Ruby - Finding the longest palindromic substring in a string

I understand how to find if one string is a palindrome
string1 == string1.reverse
It's a little more difficult though with multiple palindromes in a string
"abcdxyzyxabcdaaa"
In the above string, there are 4 palindromes of length greater than 1
"xyzyx", "yzy", "aaa" and "aa"
In this case, the longest palindrome is "xyxyx", which is 5 characters long.
How would I go about solving this problem though.
I know of the array#combination method, but that won't work in this case.
I was thinking of implementing something like this
def longest_palindrome(string)
palindromes = []
for i in 2..string.length-1
string.chars.each_cons(i).each {|x|palindromes.push(x) if x == x.reverse}
end
palindromes.map(&:join).max_by(&:length)
end
If your just looking for the largest palindrome substring, Here is a quick and dirty solution.
def longest_palindrome(string, size)
string.size.times do |start| # loop over the size of the string
break if start + size > string.size # bounds check
reverse = string[start, size].reverse
if string.include? reverse #look for palindrome
return reverse #return the largest palindrome
end
end
longest_palindrome(string, size - 1) # Palindrome not found, lets look for the next smallest size
end
def longest_palindrome(string)
longest = ''
i = 0
while i < string.length
j = 1
while (i + j) <= string.length
x = string.slice(i, j)
if (x.length > longest.length) && (x == x.reverse)
longest = x
end
j += 1
end
i += 1
end
longest
end
The slice method is handy to have for solving this problem. Test each substring with the classic double while loop approach with (i, j) representing a starting index and length of the substring respectively. string.slice(start_index, substring_length)
The String#slice method works like this:
"bdehannahc".slice(3, 8) == "hannah" # which is a palindrome and would be
# found by the method introduced above
This checks if the entire string str is a palindrome. If it is, we're finished; if not, check all substrings of length str.size-1. If one is a palindrome, we're finished; if not, check substrings of length str.size-1, and so on.
def longest_palindrome(str)
arr = str.downcase.chars
str.length.downto(1) do |n|
ana = arr.each_cons(n).find { |b| b == b.reverse }
return ana.join if ana
end
end
longest_palindrome "abcdxyzyxabcdaaa"
#=> "xyzyx"
longest_palindrome "abcdefghba"
#=> "a"
The key method here is Enumerable#each_cons.
Here is another solution, using less features of Ruby and iteration instead of recursion:
def longest_palindrome(string)
# to find the longest palindrome, start with whole thing
substr_start = 0
substr_length = string.length
while substr_length > 0 # 1 is a trivial palindrome and the end case
# puts 'substr_length is:' + substr_length.to_s
while substr_start <= string.length - substr_length
# puts 'start is: ' + substr_start.to_s
if palindrome?(string.slice(substr_start,substr_length))
puts 'found palindrome: ' + string.slice(substr_start,substr_length)
return string.slice(substr_start,substr_length)
end
substr_start += 1
end
substr_start = 0 # inner loop ctr reset
substr_length -= 1
end
puts 'null string tested?'
return ''
end

Ruby coding: List out all factors of numbers from 1-100

Write a function that prints out all the factors for each of the numbers 1 through 100.
Really amateur coder but here's my attempt so far.
def factors_numbers(n1,n2)
(n1..n2).each do |n|
factors = []
factors << 1 ##every number has a factor of 1
factors << n ##every number is a factor of itself
i = 1
while i < n
new_number = n % (n-i)
if new_number == 0 #if 0, divisible and that means two numbers are factors
factors << new_number
factors << (n-i)
end
i += 1
end
return factors
end
end
Here is an improved version of your code:
def factors_numbers(n1,n2)
all_factors = {}
(n1..n2).each do |n|
factors = []
(1..Math.sqrt(n).floor).each do |i|
remainder = n % i
if remainder == 0 #if 0, divisible and that means two numbers are factors
factors << i
factors << n/i
end
end
factors = factors.sort.uniq
puts "Factors of #{n}: #{factors.join(',')}"
all_factors[n]=[factors]
end
return all_factors
end
Do you want unique factors? That is, in the range 1-100, should I get the number 1 a hundred times, or only once?
The easiest way to do this is by leveraging the "inject" Enumerable method.
def find_all_factors_between(n1,n2)
(n1..n2).inject([]) do |factors, num|
factors + (1..num).inject([]) { |arry, test| num % test == 0 ? arry + [test] : arry }
end
end
One final thing to note is that Ruby has implicit returns; that is, as long as the output of the last line of your method is your factors variable, you don't have to say return factors.
And my entry would be:
def find_all_factors_between(n1, n2)
factors = -> (n) { (1..n).select {|i| n % i == 0} }
(n1..n2).each { |n| puts "Factors of #{n}: #{factors.(n).join(', ')}" }
end
find_all_factors_between(1,100)
Well, if you wanted to do it with enumerables, there's always
def factor_numbers(rng)
factors = rng.map do |n|
(1..Math.sqrt(n).floor) # search numbers <= square root
.select { |d| n % d == 0 } # find factors <= square root
.flat_map { |x| [x, n / x] } # combine with factors >= square root
.sort # order from least to greatest
.uniq # remove dupes (basically the square root)
end
Hash[rng.zip(factors)] # rng = keys, factors = values
end
puts factor_numbers(1..100)
It's not the most efficient, but my point is just that many of the for/while constructs you'd see in languages like C or JavaScript can be expressed in other ways in Ruby.
(n1..n2).each{|x| print "#{x}: #{(1..x).select{|y| x % y == 0}}\n"}
That oughta do it :)
edit: Implemented Cary Swoveland's suggestion
def factor_nums(n1,n2)
all_factors = {}
(n1..n2).each do |n|
factors = []
(1..n).each do |i|
remainder = n % i
factors << i if remainder == 0
end
all_factors[n] = factors
end
return all_factors
end

optimize this ruby code

So this code will count the total number of pairs of numbers whose difference is K. it is naive method and I need to optimize it. suggestions?
test = $stdin.readlines
input = test[0].split(" ")
numbers = test[1].split(" ")
N = input[0]
K = input[1]
count = 0
for i in numbers
current = i.to_i
numbers.shift
for j in numbers
difference = (j.to_i - current).abs
if (difference == K)
count += 1
end
end
end
puts count
Would have been nice for you to give some examples of input and output, but I think this is correct.
require 'set'
def count_diff(numbers, difference)
set = Set.new numbers
set.inject 0 do |count, num|
set.include?(num+difference) ? count+1 : count
end
end
difference = gets.split[1].to_i
numbers = gets.split.map { |num| num.to_i }
puts count_diff(numbers, difference)
Untested, hopefully actual Ruby code
Documentation for Set: http://www.ruby-doc.org/stdlib/libdoc/set/rdoc/classes/Set.html
require 'set'
numbers_set = Set.new
npairs = 0
numbers.each do |number|
if numbers_set.include?(number + K)
npairs += 1
end
if numbers_set.include?(number - K)
npairs += 1
end
numbers_set.add(number)
end
Someone deleted his post, or his post was deleted... He had the best solution, here it is :
test = $stdin.readlines
input = test[0].split(" ")
numbers = test[1].split(" ")
K = input[1]
count = 0
numbers.combination(2){|couple| couple.inject(:-).abs == K ? count++}
puts count
You don't even need N.
I do not know Ruby so I'll just give you the big idea:
Get the list
Keep a boolean array (call it arr), marking off numbers as true if the number exists in the list
Loop through the list and see if arr[num-K] and/or arr[num+K] is true where num is a number in your list
This uses up quite a bit of memory though so another method is to do the following:
Keep a hash map from an integer n to an integer count
Go through your list, adding num+K and num-K to the hash map, incrementing count accordingly
Go through your list and see if num is in the hash map. If it is, increment your counter by count

Resources