CoderByte CaesarCipher Excercise in Ruby - ruby

This is the question : Using the Ruby language, have the function CaesarCipher(str,num) take the str parameter and perform a Caesar Cipher shift on it using the num parameter as the shifting number. A Caesar Cipher works by shifting each letter in the string N places down in the alphabet (in this case N will be num). Punctuation, spaces, and capitalization should remain intact. For example if the string is "Caesar Cipher" and num is 2 the output should be "Ecguct Ekrjgt"
Here is my code
def CaesarCipher(str,num)
alphabet = ("a".."z").to_a.join("")
alphabetupcase = ("a".."z").to_a.join("").upcase
i=0
result = ""
while i < str.length
if alphabet.include?(str[i])
result += alphabet[alphabet.index(str[i]) + num]
elsif alphabetupcase.include?(str[i])
result += alphabetupcase[alphabetupcase.index(str[i]) + num]
else
result += str[i]
end
i += 1
end
# code goes here
return result
end
I keep getting this error (eval):11: (eval):11:in +': can't convert Fixnum into String (TypeError) from (eval):11:inCaesarCipher' from (eval):26
What is the problem with this and How can I fix this code?
Can you suggest a better solution keeping in mind I am a beginner in Ruby ?
Thank you all in advance

Your code does not work, because you miss the modulo operation - if you shift the letter 'z' by 2, you should get 'b'. And your program fails in this case. The algorithm for counting the new letter index is: (index + shift) modulo alphabet_size.
But I would do like this:
def caesar (str, num)
str.split('').collect do |character|
case character
when 'a'..'z', 'A'..'Z'
base_ascii = if character == character.upcase then 'A'.ord else 'a'.ord end
(((character.ord - base_ascii + num) % ('a'..'z').count) + base_ascii).chr
else
character
end
end.join('')
end
First, iterate on every character of the string. If it is a letter, calculate the shift (note the base_ascii, which is the ASCII code for 'A' or 'a', depends if shift is for lower- or uppercase), which is just an index of letter (character.ord - base_ascii) plus the shif (num) modulo number of letters in the alphabet (('a'..'z').count). If the characters is not a letter, so space, punctation, returns it unchanged.

I recommend taking a look at some other Caesar cypher implementations here: https://codereview.stackexchange.com/questions/55049/learning-ruby-caesar-cipher

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"

Capitalize every nth character of each word in a string in Ruby

I need to capitalize every 'nth' character for each word in a string (every multiple of 4-th character in this example, so character 4, 8, 12 etc).
I came up with the code below (not very elegant I know!) but it only works for words which length < 8.
'capitalize every fourth character in this string'.split(' ').map do |word|
word.split('').map.with_index do |l,idx|
idx % 3 == 0 && idx > 0 ? word[idx].upcase : l
end
.join('')
end
.flatten.join(' ')
Anybody could show me how to capitalize every 4th character in words which length > 8?
Thanks!
str = 'capitalize every fourth character in this string'
idx = 0
str.gsub(/./) do |c|
case c
when ' '
idx = 0
c
else
idx += 1
(idx % 4).zero? ? c.upcase : c
end
end
#=> "capItalIze eveRy fouRth chaRactEr in thiS strIng"
As an option, you can just modify the nth character in the string if it exists by accessing the character by index:
'capitalizinga every fourth character in this string'.split(' ').map do |word|
(3..word.length).step(4) do |x|
c = word[x]
word[x] = c.upcase if c
end
word
end.join(' ')
# capItalIzinGa eveRy fouRth chaRactEr in thiS strIng
Here is the method step or Range class is used, so each fourth index could be calculated: 3, 7, 11, etc...
I think the easiest way is to use a regex with substitution:
'capitalize every fourth character in this string'
.gsub(/([\w]{3})(\w)|([\w]{1,3})/) {
"#{$1}#{$2.to_s.upcase}#{$3}"
}
# => capItalIze eveRy fouRth chaRactEr in thiS strIng
This uses 2 alternatives with captured groups - the first alternative matches 4 characters and the second everything with 1 to 3 characters. Group $1 will match exactly three letters and group $2 the fourth letter within a 4-letter block - while group $3 will match remainders of a longer word as well words shorter than 4 characters.
You can then replace group $2 globally with gsub. Also you need to do $2.to_s in case $2 is nil (or catch that scenario with a ternary operator).
You can inspect the regex here and try the code here
> str.split(" ").map{|word|
word.chars.each_with_index{|c,i|
c.upcase! if (i > 0 && (i+1)%4 == 0)}.join}.join(" ")
#=> "capItalIze eveRy fouRth chaRactEr in thiS strIng"
def capitalize_each_nth_char(str, n)
str.chars.each_slice(n).to_a.each { |arr| arr[-1] = arr[-1].upcase if arr.size == n }.join('')
end
Here is the explanation,
str.chars # will give array of characters
str.chars.each_slice(n) # will give an enumerator as, #<Enumerator: ...>
str.chars.each_slice(n).to_a # will give an array of arrays
arr[-1].upcase # it will capitalize the last element i.e. 4th element of each array
if arr.size == n # it will prevent to capitalize last element of sub-array if it's size is less than n(in our case 4)
str.chars.each_slice(n).to_a.each { |arr| arr[-1] = arr[-1].upcase if arr.size == n } # it will give array of subarray where every subarray last element is capital
str.chars.each_slice(n).to_a.each { |arr| arr[-1] = arr[-1].upcase if arr.size == n }.join('') # it will give the final result as, "capItalIze EverY foUrth chaRactEr iN thIs sTrinG"

How to affect only letters, not punctuation in Caesar Cipher code

I am trying to write a Caesar Cipher in Ruby and I hit a snag when trying to change only the letters to a numerical values and not the punctuation marks.
Here is my script so far:
def caesar_cipher(phrase, key)
array = phrase.split("")
number = array.map {|n| n.upcase.ord - (64-key)}
puts number
end
puts "Script running"
caesar_cipher("Hey what's up", 1)
I tried to use select but I couldn't figure out how to select only the punctuation marks or only the letters.
Use String#gsub to match only the characters that you want to replace. In this case it's the letters of the alphabet, so you'll use the regular expression /[a-z]/i.
You can pass a block to gsub which will be called for each match in the string, and the return value of the block will be used as the replacement. For example:
"Hello, world!".gsub(/[a-z]/i) {|chr| (chr.ord + 1).chr }
# => Ifmmp, xpsme!"
Here's a version of your Caesar cipher method that works pretty well:
BASE_ORD = 'A'.ord
def caesar_cipher(phrase, key)
phrase.gsub(/[a-z]/i) do |letter|
orig_pos = letter.upcase.ord - BASE_ORD
new_pos = (orig_pos + key) % 26
(new_pos + BASE_ORD).chr
end
end
caesar_cipher("Hey, what's up?", 1) # => "IFZ, XIBU'T VQ?"
Edit:
% is the modulo operator. Here it's used to make new_pos "wrap around" to the beginning of the alphabet if it's greater than 25.
For example, suppose letter is "Y" and key is 5. The position of "Y" in the alphabet is 24 (assuming "A" is 0), so orig_pos + key will be 29, which is past the end of the alphabet.
One solution would be this:
new_pos = orig_pos + key
if new_pos > 25
new_pos = new_pos - 26
end
This would make new_pos 3, which corresponds to the letter "D," the correct result. We can get the same result more efficiently, however, by taking "29 modulo 26"—expressed in Ruby (and many other languages) as 29 % 26—which returns the remainder of the operation 29 ÷ 26. (because there are 26 letters in the alphabet). 29 % 26 is 3, the same result as above.
In addition to constraining a number to a certain range, as we do here, the modulo operator is also often used to test whether a number is divisible by another number. For example, you can check if n is divisible by 3 by testing n % 3 == 0.

Caesar cypher indexing

Can someone briefly explain me what happens in this line:
new_word += alphabet[alphabet.index(i.downcase) - num]
new_word = current state of new_word variable + what?
This is whole program:
def cipher(word, num)
alphabet = ('a'..'z').to_a.concat(('A'..'Z').to_a)
new_word = ""
word.each_char do |i|
if !alphabet.include?(i)
new_word +=i
else
new_word += alphabet[alphabet.index(i.downcase) - num]
end
end
return new_word.downcase.capitalize
end
puts cipher("Apples? and Oranges!", 2)
new_word is a String, so the value on the right will be appended to it. The expression alphabet[alphabet.index(i.downcase) - num] is just an inefficient way of determining a character that is shifted num places in the alphabet.
alphabet is an Array containing the character values corresponding to the letters of the alphabet, starting with lowercase letters and then followed by uppercase letters.
The index method in this case finds the index of the first occurence of the character value i in alphabet. This index is then decreased by num. The character corresponding to this new position is finally looked up in alphabet, and the result is appended to new_word.
Note also that the result will „wrap around“ in the sense that if the new index is negative, the array will be indexed from the back, resulting in the capital letters if num is not too large. Those potential uppercase letters will be downcased in new_word.downcase.capitalize.
The downcase part is strange, because it means that the „cipher“ is not invertible. Note also that this will not work as you might expect if nums absolute value is so large that the lookup is out of bounds.
It's adding to the new word the letter in the alphabet which is represented by (alphabet.index(i.downcase) - num).
i.downcase just converts the letter to lowercase if it wasn't already.
alphabet.index finds the place in the alphabet where i exists.
The num subtraction is the cipher. It changes the letter that would be added by modifying the index in alphabet where the letter is found. A num of 1 would change 'b's into 'a's, because 'a's come before 'b' in the given alphabet.
So it takes the letter, converts it into lowercase, then an array index, modifies that index by num and adds the letter that that new index represents back onto the word.

Finding if a string is a repeated substring in Ruby

Here is the problem:
A string is called a k-string if it can be represented as k concatenated copies of some string. For example, the string "aabaabaabaab" is at the same time a 1-string, a 2-string and a 4-string, but it is not a 3-string, a 5-string, or a 6-string and so on. Obviously any string is a 1-string.
You are given a string s, consisting of lowercase English letters and a positive integer k. Your task is to reorder the letters in the string s in such a way that the resulting string is a k-string.
Input
The first input line contains integer k (1 ≤ k ≤ 1000). The second line contains s, all characters in s are lowercase English letters. The string length s satisfies the inequality 1 ≤ |s| ≤ 1000, where |s| is the length of string s.
Output
Rearrange the letters in string s in such a way that the result is a k-string. Print the result on a single output line. If there are multiple solutions, print any of them.
If the solution doesn't exist, print "-1" (without quotes).
Here is my code:
k = gets.to_i
str = gets.chomp.split(//)
n = str.length/k
map = Hash.new(0)
map2 = Hash.new(0)
str.each { |i| map[i] += 1 }
x = str.uniq.permutation(n).map(&:join).each do |string|
string.each_char { |c| map2[c] += k }
if map2 == map
puts string*k
exit
end
map2 = Hash.new(0)
end
puts '-1'
To me this solution seems like it should work, but it fails on a test case. Can anyone tell me why?
Here's my solution.
Just create one segment, then output it k times. If a character does not appear k times (or a multiple of it), then stop early and output -1.
k = gets.to_i
str = gets.chomp.split(//)
counts = Hash.new(0)
str.each { |i| counts[i] += 1 }
out = ''
str.uniq.each do |c|
if counts[c] % k != 0
puts -1
exit
end
out = out + c*(counts[c]/k)
end
puts out*k

Resources