Finding if a string is a repeated substring in Ruby - 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

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"

Finding Longest Substring No Duplicates - Help Optimizing Code [Ruby]

So I've been trying to solve a Leetcode Question, "Given a string, find the length of the longest substring without repeating characters."
For example
Input: "abcabcbb"
Output: 3
Explanation: The answer is "abc", with the length of 3.
Currently I optimized my algorithm when it comes to figuring out if the substring is unique by using a hash table. However my code still runs in O(n^2) runtime, and as a result exceeds the time limit during submissions.
What i try to do is to essentially go through every single possible substring and check if it has any duplicate values. Am I as efficient as it gets when it comes to the brute force method here? I know there's other methods such as a sliding window method but I'm trying to get the brute force method down first.
# #param {String} s
# #return {Integer}
def length_of_longest_substring(s)
max_length = 0
max_string = ""
n = s.length
for i in (0..n-1)
for j in (i..n-1)
substring = s[i..j]
#puts substring
if unique(substring)
if substring.length > max_length
max_length = substring.length
max_string = substring
end
end
end
end
return max_length
end
def unique(string)
hash = Hash.new(false)
array = string.split('')
array.each do |char|
if hash[char] == true
return false
else
hash[char] = true
end
end
return true
end
Approach
Here is a way of doing that with a hash that maps characters to indices. For a string s, suppose the characters in the substring s[j..j+n-1] are unique, and therefore the substring is a candidate for the longest unique substring. The next element is therefore e = s[j+n] We wish to determine if s[j..j+n-1] includes e. If it does not we can append e to the substring, keeping it unique.
If s[j..j+n-1] includes e, we determine if n (the size of the substring) is greater than the length of the previously-known substring, and update our records if it is. To determine if s[j..j+n-1] includes e, we could perform a linear search of the substring, but it is faster to maintain a hash c_to_i whose key-value pairs are s[i]=>i, i = j..j_n-1. That is, c_to_i maps the characters in the substring to their indices in full string s. That way we can merely evaluate c_to_i.key?(e) to see if the substring contains e. If the substring includes e, we use c_to_i to determine its index in s and add one: j = c_to_i[e] + 1. The new substring is therefore s[j..j+n-1] with the new value of j. Note that several characters of s may be skipped in this step.
Regardless of whether the substring contained e, we must now append e to the (possibly-updated) substring, so that it becomes s[j..j+n].
Code
def longest_no_repeats(str)
c_to_i = {}
longest = { length: 0, end: nil }
str.each_char.with_index do |c,i|
j = c_to_i[c]
if j
longest = { length: c_to_i.size, end: i-1 } if
c_to_i.size > longest[:length]
c_to_i.reject! { |_,k| k <= j }
end
c_to_i[c] = i
end
c_to_i.size > longest[:length] ? { length: c_to_i.size, end: str.size-1 } :
longest
end
Example
a = ('a'..'z').to_a
#=> ["a", "b",..., "z"]
str = 60.times.map { a.sample }.join
#=> "ekgdaxxzlwbxixhlfbpziswcoelplhobivoygmupdaexssbuuawxmhprkfms"
longest = longest_no_repeats(str)
#=> {:length=>14, :end=>44}
str[0..longest[:end]]
#=> "ekgdaxxzlwbxixhlfbpziswcoelplhobivoygmupdaexs"
str[longest[:end]-longest[:length]+1,longest[:length]]
#=> "bivoygmupdaexs"
Efficiency
Here is a benchmark comparison to #mechnicov's code:
require 'benchmark/ips'
a = ('a'..'z').to_a
arr = 50.times.map { 1000.times.map { a.sample }.join }
Benchmark.ips do |x|
x.report("mechnicov") { arr.sum { |s| max_non_repeated(s)[:length] } }
x.report("cary") { arr.sum { |s| longest_no_repeats(s)[:length] } }
x.compare!
end
displays:
Comparison:
cary: 35.8 i/s
mechnicov: 0.0 i/s - 1198.21x slower
From your link:
Input: "pwwkew"
Output: 3
Explanation: The answer is "wke", with the length of 3.
That means you need first non-repeated substring.
I suggest here is such method
def max_non_repeated(string)
max_string = string.
each_char.
map.with_index { |_, i| string[i..].split('') }.
map do |v|
ary = []
v.each { |l| ary << l if ary.size == ary.uniq.size }
ary.uniq.join
end.
max
{
string: max_string,
length: max_string.length
}
end
max_non_repeated('pwwkew')[:string] #=> "wke"
max_non_repeated('pwwkew')[:length] #=> 3
In Ruby < 2.6 use [i..-1] instead of [i..]

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"

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

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