Palindrome count in a string - ruby

So, I explored www.hackerearth.com today and was solving my first problem statement in ruby: http://www.hackerearth.com/problem/algorithm/palindrome-count-1/
Palindrome count Problem:
Given a string S, count the number of non empty sub strings that are palindromes.
A sub string is any continuous sequence of characters in the string.
A string is said to be palindrome, if the reverse of the string is same as itself.
Two sub strings are different if they occur at different positions in S
Input: Input contains only a single line that contains string S.
Output: Print a single number, the number of sub strings that are palindromes.
Constraints
1 <= |S| <= 50
S contains only lower case latin letters, that is characters a to z.
Sample Input (Plaintext Link): dskjkd
Sample Output (Plaintext Link): 7
Explanation -
The 7 sub strings are d, s, k, j, k, d, kjk.
Time limit 3 sec(s)
Memory limit 256 MB
Source limit 1024 KB
Here is what I did:
chars = gets.chomp.gsub(' ', '').split('')
counts = chars.count
(2..chars.count).each do |len|
chars.combination(len).each do |comb|
string = comb.inject(:<<)
counts += 1 if string.reverse == string
end
end
puts counts
However, this approach seems to be inefficient in terms of the time execution and memory usage. Is there any way to optimize this? Or have any other approach to this solution, algorithm is also welcome as solution! Thanks.
Edit
Since, all the answers are correct. I had to choose the one which is efficient. So, I ran benchmark and here is the result: https://gist.github.com/suryart/7577481
Based on the result you can see this answer is much faster. Thank you for the new approaches/ solution guys! :)

This approach -- in pseudo-code -- should work.
input: String s
// each single letter is palindrome itself
palindromsCount = length(s)
// let count all odd-length palindromes first (palindrome of length 1 already counted)
// we will be checking from the very middle of a sub-string, if it is symmetric
for(int i = 1; i < length(s)-1; i++)
for(int j = 1; ; j++)
if (i - j < 0 || i + j >= length(s) || s[i-j] != s[i+j])
break
else
palindromsCount += 1
// let count in similar way all even-length palindromes
for(int i = 0; i < length(s)-1; i++)
for(int j = 0; ; j++)
if (i - j < 0 || i + j + 1 >= length(s) || s[i-j] != s[i+j+1])
break
else
palindromsCount += 1
EDIT Of course both loops can be combined into a single one -- I did not want to do it for better readability.

using the algorithm to get all subsets of the string from What is the best way to split a string to get all the substrings by Ruby?
count = 0
(0..len-1).each do |i|
(i..len-1).each do |j|
temp = s[i..j]
count = count + 1 if temp == temp.reverse
end
end
puts "found #{count} palindromes"

Enumerable#each_cons is handy here:
str = "momanddadpaddledthekayak"
b = str.chars
(1..b.size).reduce(0) {|t,n| t + b.each_cons(n).reduce(0) \
{|r,e| w = e.join; w==w.reverse ? r + 1 : r}} # => 30
If we want to see the palendromes:
b = str.chars
pals = (1..b.size).each_with_object([]) {|n, a| b.each_cons(n).each \
{|e| w = e.join; a << w if w==w.reverse}}
p pals.size # => 30
p pals # => ["m", "o", "m", "a", "n", "d", "d", "a", "d", "p", "a",\
"d", "d", "l", "e", "d", "t", "h", "e", "k", "a", "y",
"a", "k", "dd", "dd", "mom", "dad", "aya", "kayak"]
Edit: #squiguy made the useful observation that we may not want to count duplicates. If that's the case, my first calculation above could not be used and the second would have to be changed as squiguy suggests (e.g., p a.uniq.size) or changed to build a hash rather than an array:
b = str.chars
pals = (1..b.size).each_with_object({}) {|n,h| b.each_cons(n).each \
{|e| w = e.join; h[w] = 0 if w==w.reverse}}.keys
p pals.size # => 17
p pals# => ["m", "o", "a", "n", "d", "p", "l", "e", "t",\
"h", "k", "y", "dd", "mom", "dad", "aya", "kayak"]
[Edit: replaced each with each_with_object. On rereading the question, it appears that dups are to be counted.]

Related

Ruby. Shuffle the array so that there are no adjacent elements with the same value

An array of hashes is given (10 elements at least):
arr = [{letter: "a", number: "1"}, {letter: "a", number: "3"}, {letter: "b", number: "4"}, {letter: "b", number: "1"}, ..., {letter: "e", number: "2"} ]
The task is to shuffle the array so that there are no adjacent elements with the same 'letter' value.
So, the result should be like the following:
[{letter: "c", number: "4"}, {letter: "a", number: "1"}, {letter: "e", number: "2"}, {letter: "b", number: "1"}, ..., {letter: "a", number: "3"} ]
What is the simplest way to do that?
=== UPDATE ===
The number of repeated letters in the array is precisely known - it's 20% of the array length.
So, the array looks like the following:
[
{letter: "a", number: "1"}, {letter: "a", number: "3"},
{letter: "b", number: "4"}, {letter: "b", number: "1"},
{letter: "c", number: "7"}, {letter: "c", number: "3"},
{letter: "d", number: "6"}, {letter: "d", number: "4"},
{letter: "e", number: "5"}, {letter: "e", number: "2"}
]
Or, its simplified version:
["a", "a", "b", "b", "c", "c", "d", "d", "e", "e"]
Or, for example, there is a simplified array containing 15 elements:
["a", "a", "a", "b", "b", "b", "c", "c", "c", "d", "d", "d", "e", "e", "e"]
The simplest way (without any random):
# Calculate letter frequency
freq = arr.group_by { |h| h[:letter] }.map { |k, v| [k, v.size] }.to_h
# Then check that the most frequent element occurs less that arr.size / 2
center = (arr.size + 1) / 2
if freq.values.max > center
# Impossible
end
# Sort array by frequency to have most frequent first.
sarr = arr.sort_by { |h| freq[h[:letter]] }.reverse
sarr[0..center-1].zip(sarr[center..-1]).flatten.compact
Your problem is a special case of this question. See my answer for the detailed explanation how this works.
We even don't need to sort by letter frequency. It's for corner cases like "abbcccc". We can solve them in another way:
# Works with correct data: most frequent letter occurs <= center times
def f(arr)
arr = arr.sort
center = (arr.size + 1) / 2
arr = arr[0..center-1].zip(arr[center..-1]).flatten.compact
double = (1..arr.size-1).find { |i| arr[i] == arr[i-1] }
double ? arr.rotate(double) : arr # fix for the corner cases
end
puts f(%w[a a a a b b c].shuffle).join
# ababaca
puts f(%w[a a b b b b c].shuffle).join
# bcbabab
puts f(%w[a b b c c c c].shuffle).join
# cacbcbc
The only non-linear part of the algorithm is arr.sort. But as you can see by the link above, we even don't need the sorting. We need letters counts, which could be found in linear time. Therefore, we can reduce the algorithm to O(n).
The number of repeated letters in the array is precisely known - it's 20% of the array length.
With this update, the algorithm is simplified to (as there are no corner cases):
sarr = arr.sort_by { |h| h[:letter] }
center = (arr.size + 1) / 2
sarr[0..center-1].zip(sarr[center..-1]).flatten.compact
The simple and maybe the less effective way could be the brute force.
So on a simplified version of the array, one can do:
ary = %w(a a c c b a s)
loop do
break if ary.shuffle!.slice_when { |a, b| a == b }.to_a.size == 1
end
Some check should be added to assure that a solution exists, to avoid infinite loop.
Other (better?) way is to shuffle then find the permutation (no infinite loop) which satisfy the condition:
ary.shuffle!
ary.permutation.find { |a| a.slice_when { |a, b| a == b }.to_a.size == 1 }
If a solution does not exist, it returns nil.
Run the the benchmark:
def looping
ary = %w(a a c c b a s)
loop do
break if ary.shuffle!.slice_when { |a, b| a == b }.to_a.size == 1
end
ary
end
def shuffle_permute
ary = %w(a a c c b a s)
ary.shuffle!
ary.permutation.lazy.find { |a| a.slice_when { |a, b| a == b }.to_a.size == 1 }
end
require 'benchmark'
n = 500
Benchmark.bm do |x|
x.report { looping }
x.report { shuffle_permute }
end
Code
def reorder(arr)
groups = arr.group_by { |h| h[:letter] }
return nil if 2 * groups.map { |_,v| v.size }.max > arr.size + 1
max_key = groups.max_by { |_,a| a.size }.first
letters = ([max_key] + (groups.keys - [max_key])).cycle
ordered = []
while ordered.size < arr.size
k = letters.next
ordered << groups[k].pop unless groups[k].empty?
end
ordered
end
nilis returned if it is not possible to rearrange the elements in such a way that g[:letter] != h[:letter] for all pairs of consecutive elements g and h.
Note that this method has near linear computational complexity, O(arr.size), "near" because hash lookups are not quite constant time.
If desired, one could call the method with arr randomized: reorder(arr.shuffle).
Example
arr = [
{ letter: "a" }, { letter: "e" }, { letter: "b" }, { letter: "b" },
{ letter: "e" }, { letter: "a" }, { letter: "a" }, { letter: "f" }
]
reorder(arr)
#=> [{:letter=>"a"}, {:letter=>"e"}, {:letter=>"b"}, {:letter=>"f"},
# {:letter=>"a"}, {:letter=>"e"}, {:letter=>"b"}, {:letter=>"a"}]
Proof
The assertion is that if the line
return nil if 2 * groups.map { |_,v| v.size }.max > arr.size + 1
were removed from the method the array returned by the method would have the property that for all pairs of successive elements, g, h, g[:letter] != h[:letter] if and only if
2 * groups.map { |_,v| v.size }.max <= arr.size + 1
The proof has two parts.
The above inequality holds if the method produces a valid array
Compute
max_key = groups.max_by { |_,a| a.size }.first
max_key_freq = groups.map { |_,v| v.size }.max
and assume a valid array is returned. There must be at least one element other than max_key between each successive value of max_key in that array. The number of elements of arr other than max_key must therefore be at least max_key_freq - 1, so that
max_key_freq + max_key_freq - 1 <= arr.size
Hence,
2 * max_key_freq <= arr.size + 1
which is the same as:
2 * groups.map { |_,v| v.size }.max <= arr.size + 1
The above inequality does not hold if the method produces an invalid array
Suppose ordered is returned and it contains successive elements g and h for which both g[:letter] and h[:letter] equal the same letter l.
Because of the way ordered is constructed:
groups[k] must be empty for all keys k in groups for which k != l;
f[:letter] must equal l for all elements of ordered following g (if there are any); and
l must be the first key enumerated by keys, which is a letter that appears with a frequency that is not less than that of any other letter. l has frequency groups.map { |_,v| v.size }.max.
If n = groups.keys.size there must be a non-negative integer k (loosely, the number of rounds of allocations for all keys of groups) such that the number of elements h of arr for which h[:letter] != l equals k*n and the number of elements h of arr for which h[:letter] == l is k*n + 2 + m, where m >= 0. The size of arr is therefore 2*k*n + 2 + m.
In that case,
2 * groups.map { |_,v| v.size }.max > arr.size + 1
-> 2 * (k*n + 2 + m) > (k*n + 2 + m + k*n) + 1
-> 2*k*n + 4 + 2*m > 2*k*n + 3 + m
-> (4-3) + m > 0
-> true
Explanation
For the example,
groups = arr.group_by { |h| h[:letter] }
#=> {"a"=>[{:letter=>"a"}, {:letter=>"a"}, {:letter=>"a"}],
# "e"=>[{:letter=>"e"}, {:letter=>"e"}],
# "b"=>[{:letter=>"b"}, {:letter=>"b"}],
# "f"=>[{:letter=>"f"}]}
The following tells us that a solution exists.
2 * groups.map { |_,v| v.size }.max > arr.size + 1
#=> 2 * [3, 2, 2, 1].max > 8 + 1
#=> 2 * 3 > 9
#=> 6 > 9
#=> false
Next create an enumerator letters.
max_key = groups.max_by { |_,a| a.size }.first
#=> "a"
letters = ([max_key] + (groups.keys - [max_key])).cycle
#=> #<Enumerator: ["a", "e", "b", "f"]:cycle>
The elements of letters are generated as follows.
letters.next #=> "a"
letters.next #=> "e"
letters.next #=> "b"
letters.next #=> "f"
letters.next #=> "a"
letters.next #=> "e"
... ad infinititum
See Array#cycle.
I can best explain the remaining calculations by salting the method with puts statements before running the method. Note that arr.size #=> 8.
def reorder(arr)
groups = arr.group_by { |h| h[:letter] }
puts "groups = #{groups}"
return nil if 2 * groups.map { |_,v| v.size }.max > arr.size + 1
max_key = groups.max_by { |_,a| a.size }.first
letters = ([max_key] + (groups.keys - [max_key])).cycle
ordered = []
while ordered.size < arr.size
puts "\nordered.size = #{ordered.size} < #{arr.size} = #{ordered.size < arr.size}"
k = letters.next
puts "k = #{k}"
puts "groups[#{k}].empty? = #{groups[k].empty?}"
ordered << groups[k].pop unless groups[k].empty?
puts "ordered = #{ordered}"
puts "groups = #{groups}"
end
ordered
end
reorder(arr)
#=> [{:letter=>"a"}, {:letter=>"e"}, {:letter=>"b"}, {:letter=>"f"},
# {:letter=>"a"}, {:letter=>"e"}, {:letter=>"b"}, {:letter=>"a"}]
The following is displayed.
groups = {"a"=>[{:letter=>"a"}, {:letter=>"a"}, {:letter=>"a"}],
"e"=>[{:letter=>"e"}, {:letter=>"e"}],
"b"=>[{:letter=>"b"}, {:letter=>"b"}],
"f"=>[{:letter=>"f"}]}
ordered.size = 0 < 8 = true
k = a
groups[a].empty? = false
ordered = [{:letter=>"a"}]
groups = {"a"=>[{:letter=>"a"}, {:letter=>"a"}],
"e"=>[{:letter=>"e"}, {:letter=>"e"}],
"b"=>[{:letter=>"b"}, {:letter=>"b"}],
"f"=>[{:letter=>"f"}]}
ordered.size = 1 < 8 = true
k = e
groups[e].empty? = false
ordered = [{:letter=>"a"}, {:letter=>"e"}]
groups = {"a"=>[{:letter=>"a"}, {:letter=>"a"}],
"e"=>[{:letter=>"e"}],
"b"=>[{:letter=>"b"}, {:letter=>"b"}],
"f"=>[{:letter=>"f"}]}
ordered.size = 2 < 8 = true
k = b
groups[b].empty? = false
ordered = [{:letter=>"a"}, {:letter=>"e"}, {:letter=>"b"}]
groups = {"a"=>[{:letter=>"a"}, {:letter=>"a"}],
"e"=>[{:letter=>"e"}],
"b"=>[{:letter=>"b"}],
"f"=>[{:letter=>"f"}]}
ordered.size = 3 < 8 = true
k = f
groups[f].empty? = false
ordered = [{:letter=>"a"}, {:letter=>"e"}, {:letter=>"b"}, {:letter=>"f"}]
groups = {"a"=>[{:letter=>"a"}, {:letter=>"a"}],
"e"=>[{:letter=>"e"}], "b"=>[{:letter=>"b"}],
"f"=>[]}
ordered.size = 4 < 8 = true
k = a
groups[a].empty? = false
ordered = [{:letter=>"a"}, {:letter=>"e"}, {:letter=>"b"}, {:letter=>"f"},
{:letter=>"a"}]
groups = {"a"=>[{:letter=>"a"}],
"e"=>[{:letter=>"e"}],
"b"=>[{:letter=>"b"}],
"f"=>[]}
ordered.size = 5 < 8 = true
k = e
groups[e].empty? = false
ordered = [{:letter=>"a"}, {:letter=>"e"}, {:letter=>"b"}, {:letter=>"f"},
{:letter=>"a"}, {:letter=>"e"}]
groups = {"a"=>[{:letter=>"a"}],
"e"=>[],
"b"=>[{:letter=>"b"}],
"f"=>[]}
ordered.size = 6 < 8 = true
k = b
groups[b].empty? = false
ordered = [{:letter=>"a"}, {:letter=>"e"}, {:letter=>"b"}, {:letter=>"f"},
{:letter=>"a"}, {:letter=>"e"}, {:letter=>"b"}]
groups = {"a"=>[{:letter=>"a"}], "e"=>[], "b"=>[], "f"=>[]}
ordered.size = 7 < 8 = true
k = f
groups[f].empty? = true
ordered = [{:letter=>"a"}, {:letter=>"e"}, {:letter=>"b"}, {:letter=>"f"},
{:letter=>"a"}, {:letter=>"e"}, {:letter=>"b"}]
groups = {"a"=>[{:letter=>"a"}], "e"=>[], "b"=>[], "f"=>[]}
ordered.size = 7 < 8 = true
k = a
groups[a].empty? = false
ordered = [{:letter=>"a"}, {:letter=>"e"}, {:letter=>"b"}, {:letter=>"f"},
{:letter=>"a"}, {:letter=>"e"}, {:letter=>"b"}, {:letter=>"a"}]
groups = {"a"=>[], "e"=>[], "b"=>[], "f"=>[]}
Refering to the revised question, if
arr = ["a", "a", "b", "b", "c", "c", "d", "d", "e", "e"]
one could simply write:
arr.each_slice(arr.index { |s| s != arr.first }.to_a.transpose.flatten
#=> ["a", "b", "c", "d", "e", "a", "b", "c", "d", "e"]
or
arr.each_slice(arr.count(arr.first)).to_a.transpose.flatten
This sounds a lot like backtracking.
I would build the "shuffled" array from left to right.
Assume that there are N elements in the array. Say that at some point during the algorithm, you have already the first k elements arranged to fulfil the condition.
Now you pick from the remaining (N-k) elements the first one, which you can append to your result array, without breaking the condition.
If you can find one, you repeat the process recursively, now having an result array of (k+1) elements.
If you can not find one, you return a failure indicator and let the caller (i.e. the previous recursion) try another choice.

How can I improve the performance of this small Ruby function?

I am currently doing a Ruby challenge and get the error Terminated due to timeout
for some testcases where the string input is very long (10.000+ characters).
How can I improve my code?
Ruby challenge description
You are given a string containing characters A and B only. Your task is to change it into a string such that there are no matching adjacent characters. To do this, you are allowed to delete zero or more characters in the string.
Your task is to find the minimum number of required deletions.
For example, given the string s = AABAAB, remove A an at positions 0 and 3 to make s = ABAB in 2 deletions.
My function
def alternatingCharacters(s)
counter = 0
s.chars.each_with_index { |char, idx| counter += 1 if s.chars[idx + 1] == char }
return counter
end
Thank you!
This could be faster returning the count:
str.size - str.chars.chunk_while{ |a, b| a == b }.to_a.size
The second part uses String#chars method in conjunction with Enumerable#chunk_while.
This way the second part groups in subarrays:
'aababbabbaab'.chars.chunk_while{ |a, b| a == b}.to_a
#=> [["a", "a"], ["b"], ["a"], ["b", "b"], ["a"], ["b", "b"], ["a", "a"], ["b"]]
Trivial if you can use squeeze:
str.length - str.squeeze.length
Otherwise, you could try a regular expression that matches those A (or B) that are preceded by another A (or B):
str.enum_for(:scan, /(?<=A)A|(?<=B)B/).count
Using enum_for avoids the creation of the intermediate array.
The main issue with:
s.chars.each_with_index { |char, idx| counter += 1 if s.chars[idx + 1] == char }
Is the fact that you don't save chars into a variable. s.chars will rip apart the string into an array of characters. The first s.chars call outside the loop is fine. However there is no reason to do this for each character in s. This means if you have a string of 10.000 characters, you'll instantiate 10.001 arrays of size 10.000.
Re-using the characters array will give you a huge performance boost:
require 'benchmark'
s = ''
options = %w[A B]
10_000.times { s << options.sample }
Benchmark.bm do |x|
x.report do
counter = 0
s.chars.each_with_index { |char, idx| counter += 1 if s.chars[idx + 1] == char }
# create a character array for each iteration ^
end
x.report do
counter = 0
chars = s.chars # <- only create a character array once
chars.each_with_index { |char, idx| counter += 1 if chars[idx + 1] == char }
end
end
user system total real
8.279767 0.000001 8.279768 ( 8.279655)
0.002188 0.000003 0.002191 ( 0.002191)
You could also make use of enumerator methods like each_cons and count to simplify the code, this doesn't increase performance cost a lot, but makes the code a lot more readable.
Benchmark.bm do |x|
x.report do
counter = 0
chars = s.chars
chars.each_with_index { |char, idx| counter += 1 if chars[idx + 1] == char }
end
x.report do
s.each_char.each_cons(2).count { |a, b| a == b }
# ^ using each_char instead of chars to avoid
# instantiating a character array
end
end
user system total real
0.002923 0.000000 0.002923 ( 0.002920)
0.003995 0.000000 0.003995 ( 0.003994)

Decompose words into letters with Ruby

In my language there are composite or compound letters, which consists of more than one character, eg "ty", "ny" and even "tty" and "nny". I would like to write a Ruby method (spell) which tokenize words into letters, according to this alphabet:
abc=[*%w{tty ccs lly ggy ssz nny dzs zzs sz zs cs gy ny dz ty ly q w r t z p l k j h g f d s x c v b n m y}.map{|z| [z,"c"]},*"eéuioöüóőúűáía".split(//).map{|z| [z,"v"]}].to_h
The resulting hash keys shows the existing letters / composite letters of the alphabet and also shows which letter is a consonant ("c") and which one is a vowel ("v"), becase later I would like to use this hash to decompose words into syllables. Cases of compound words when accidentally composite letters are formed at the words common boundary shoudn't be resolved by the method of course.
Examples:
spell("csobolyó") => [ "cs", "o", "b", "o", "ly", "ó" ]
spell("nyirettyű") => [ "ny", "i", "r", "e", "tty", "ű" ]
spell("dzsesszmuzsikus") => [ "dzs", "e", "ssz", "m", "u", "zs", "i", "k", "u", "s" ]
You might be able to get started looking at String#scan, which appears to be giving decent results for your examples:
"csobolyó".scan(Regexp.union(abc.keys))
# => ["cs", "o", "b", "o", "ly", "ó"]
"nyirettyű".scan(Regexp.union(abc.keys))
# => ["ny", "i", "r", "e", "tty", "ű"]
"dzsesszmuzsikus".scan(Regexp.union(abc.keys))
# => ["dzs", "e", "ssz", "m", "u", "zs", "i", "k", "u", "s"]
The last case doesn't match your expected output, but it matches your statement in the comments
I sorted the letters in the alphabet: if a letter appears earlier, then it should be recognized instead of its simple letters. When a word contains "dzs" it should be considered to "dzs" and not to "d" and "zs"
I didn't use the preference in which you sorted, rather I used higher character word will have higher preference than lower character word.
def spell word
abc=[*%w{tty ccs lly ggy ssz nny dzs zzs sz zs cs gy ny dz ty ly q w r t z p l k j h g f d s x c v b n m y}.map{|z| [z,"c"]},*"eéuioöüóőúűáía".split(//).map{|z| [z,"v"]}].to_h
current_position = 0
maximum_current_position = 2
maximum_possible_position = word.length
split_word = []
while current_position < maximum_possible_position do
current_word = set_current_word word, current_position, maximum_current_position
if abc[current_word] != nil
current_position, maximum_current_position = update_current_position_and_max_current_position current_position, maximum_current_position
split_word.push(current_word)
else
maximum_current_position = update_max_current_position maximum_current_position
current_word = set_current_word word, current_position, maximum_current_position
if abc[current_word] != nil
current_position, maximum_current_position = update_current_position_and_max_current_position current_position, maximum_current_position
split_word.push(current_word)
else
maximum_current_position = update_max_current_position maximum_current_position
current_word = set_current_word word, current_position, maximum_current_position
if abc[current_word] != nil
current_position, maximum_current_position = update_current_position_and_max_current_position current_position, maximum_current_position
split_word.push(current_word)
else
puts 'This word cannot be formed in the current language'
break
end
end
end
end
split_word
end
def update_max_current_position max_current_position
max_current_position = max_current_position - 1
end
def update_current_position_and_max_current_position current_position,max_current_position
current_position = max_current_position + 1
max_current_position = current_position + 2
return current_position, max_current_position
end
def set_current_word word, current_position, max_current_position
word[current_position..max_current_position]
end
puts "csobolyó => #{spell("csobolyó")}"
puts "nyirettyű => #{spell("nyirettyű")}"
puts "dzsesszmuzsikus => #{spell("dzsesszmuzsikus")}"
Output
csobolyó => ["cs", "o", "b", "o", "ly", "ó"]
nyirettyű => ["ny", "i", "r", "e", "tty", "ű"]
dzsesszmuzsikus => ["dzs", "e", "ssz", "m", "u", "zs", "i", "k", "u", "s"]
Meanwhile I managed to write a method which works, but 5x slower than String#scan:
abc=[*%w{tty ccs lly ggy ssz nny dzs zzs sz zs cs gy ny dz ty ly q w r t z p l k j h g f d s x c v b n m y}.map{|z| [z,"c"]},*"eéuioöüóőúűáía".split(//).map{|z| [z,"v"]}].to_h
def spell(w,abc)
s=w.split(//)
p=""
t=[]
for i in 0..s.size-1 do
p << s[i]
if i>=s.size-2 then
if abc[p]!=nil then
t.push p
p=""
elsif abc[p[0..-2]]!=nil then
t.push p[0..-2]
p=p[-1]
elsif abc[p[0]]!=nil then
t.push p[0]
p=p[1..-1]
end
elsif p.size==3 then
if abc[p]!=nil then
t.push p
p=""
elsif abc[p[0..-2]]!=nil then
t.push p[0..-2]
p=p[-1]
elsif abc[p[0]]!=nil then
t.push p[0]
p=p[1..-1]
end
end
end
if p.size>0 then
if abc[p]!=nil then
t.push p
p=""
elsif abc[p[0..-2]]!=nil then
t.push p[0..-2]
p=p[-1]
end
end
if p.size>0 then
t.push p
end
return t
end

My return statement will not exit a recursive method

Simple Roman Numerals Algorithm solved using recursion.
I know the code get's into my if statement holding my base case. When I check the values using 'pry' everything is fine. However it just skips over my return statement and get's stuck in an infinite loop
ROMAN_HASH = {
1000 => "M",
900 => "CM",
400 => "CD",
500 => "D",
100 => "C",
90 => "XC",
50 => "L",
40 => "XL",
10 => "X",
9 => "IX",
5 => "V",
4 => "IV",
1 => "I"
}
def roman(num, output="")
return output if num <= 1
else
ROMAN_HASH.each do |k,v|
roman(num - k, output+v) if num >= k
end
end
end
You need to return out of your ROMAN_HASH.each loop, or your recursive functions never end (at least, not for huge numbers of iterations). The problem is that you (seemingly) intended to return the largest value found in the ROMAN_HASH hash, but instead you iterate over all values, recursively calling roman for each one that is greater than k, do nothing with the result, and then return the .each iterator.
You're also misusing if/else. You can't mix post-if with an else expression.
Finally, you're stripping off the last digit by returning if num <= 1. You need to return output when num < 1, and if output is equal to 1 you should return output + 'I', or just let the else branch handle this case:
def roman(num, output="")
if num < 1
return output
else
ROMAN_HASH.each do |k, v|
return roman(num - k, output+v) if num >= k
end
end
end

Tree Level-Order Traversal of Elements in a Vector

I am looking for an algorithm to take a list of x values and loop through them starting in the middle then the middle of the left then the middle of the right, then the middle of the middle of the left...like a tree.
I don't think recursion will work because it will traverse down one side completely before getting to the other side. I need to parse through evenly.
Pretend this is a list of 50 numbers:
.................................................. (50)
Need to find the 25th element first
........................1......................... (lvl1)
Then the 12th, then 38th
...........2.........................3............ (lvl2)
Then the 6,18 31,44
.....4...........5.............6...........7...... (lvl3)
Then the 3,9,15,21 28,34,41,48
..8.....9.....a......b.....c.......d.....e.....f.. (lvl4)
etc... until all the values have been traversed. So by the time lvl4 is hit, i've seen 1,2,3,4,5,6,7,8,9,a,b,c,d,e,f in that order.
All my attempts have flopped to do this iteratively.
Efficiency is not critical as it won't be run often.
Hopefully my question is clear. Thank-you
You can solve this via a queue data structure and some math.
Start by pushing in the tuple (0, 25, 49). This indicates that this is a node at position 25, splitting the range 0-49. So the queue should look like this:
[(0, 25, 49)]
Now at each point, remove the front of the queue, print the element at the index, and push in the descendants. So, for example, when you pop (0, 25, 49), how to track the descendants? The left descendant is the middle of the range 0-24, so you would push in (0, 12, 24). The right descendant is the middle of the range 26-49, so you would push in (26, 38, 49). So the queue should look like this:
[(0, 13, 23), (26, 38, 49)].
Et cetera.
(The solution that follows is written in Swift, but I hope you can follow it and translate to your favourite language of choice, in case you wish to make use of it)
We can quite easily come up with a solution that works in the special case where your number of array values describe a full(/proper) binary tree, i.e., if numElements = 2^(lvl-1)+1, where lvl is the level of your tree. See function printFullBinaryTree(...) below.
Now, we can also somewhat with ease expand any array into one that describes a full binary tree, see expandToFullBinary. '
By combining these two methods, we have a general method for input arrays of any size.
Expand any array into one that describes a full binary tree:
/* given 'arr', returns array expanded to full binary tree (if necessary) */
func expandToFullBinary(arr: [String], expandByCharacter: String = "*") -> [String] {
let binLength = Int(pow(2.0,Double(Int(log2(Double(arr.count)))+1)))-1
if arr.count == binLength {
return arr
}
else {
let diffLength = binLength - arr.count
var arrExpanded = [String](count: binLength, repeatedValue: expandByCharacter)
var j = 0
for i in 0 ..< arr.count {
if i < (arr.count - diffLength) {
arrExpanded[i] = arr[i]
}
else {
arrExpanded[i+j] = arr[i]
j = j+1
}
}
return arrExpanded
}
}
Print array (that describes a full binary tree) as a binary tree according to your question specifications:
/* assumes 'arr' describes a full binary tree */
func printFullBinaryTree(arr: [String]) {
var posVectorA : [Int] = [arr.count/2]
var posVectorB : [Int]
var splitSize : Int = arr.count/2
var elemCount = 0
if arr.count < 2 {
print("\(arr.first ?? "")")
}
else {
while elemCount < arr.count {
posVectorB = []
splitSize = splitSize/2
for i in posVectorA {
if elemCount == arr.count {
print("noo")
break
}
print(arr[i], terminator: " ")
elemCount = elemCount + 1
posVectorB.append(i-splitSize-1)
posVectorB.append(i+splitSize+1)
}
print("")
posVectorA = posVectorB
}
}
}
Example for a vector describing a full binary tree as well as one describing a non-full binary tree:
/* Example */
var arrFullBinary : [String] = ["8", "4", "9", "2", "a", "5", "b", "1", "c", "6", "d", "3", "e", "7", "f"]
var arrNonFullBinary : [String] = ["g", "8", "h", "4", "i", "9", "j", "2", "a", "5", "b", "1", "c", "6", "d", "3", "e", "7", "f"]
printFullBinaryTree(expandToFullBinary(arrFullBinary, expandByCharacter: ""))
/* 1
2 3
4 5 6 7
8 9 a b c d e f */
printFullBinaryTree(expandToFullBinary(arrNonFullBinary, expandByCharacter: ""))
/* 1
2 3
4 5 6 7
8 9 a b c d e f
g h i j */

Resources