Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to find the number of occurrence in a string by Ruby [closed]

Tags:

ruby

I am new to Ruby and learning it by solving IOI questions.

The first line of sample input file is the number of 'O'. If it is 1 then it needs to find 'IOI', if it is 2, then 'IOIOI' etc.

The second number 13 tells the number of character in the third line. The question is to find the number of occurrence of 'IOI'.

The following sample should give 4.

I made a method and using if statement in a while loop. But it gives an error with ioioi.rb:14: syntax error, unexpected keyword_end (SyntaxError). What I am trying in the while loop is to find if the first 3 characters are equal and if it is increase a count by one. and delete the first character and repeat the process.

Sample input file

1
13
OOIOIOIOIIOII

Output should be

4

My class Ioioi

def self.frequency(file_name)
    file = File.new(file_name, 'r').each_line.map(&:strip)
    count = 0
    o_str = file.shift # '1'
    o_num = o_str.to_i 
    findme = "IO" * o_num + "I"
    length = file.shift # '13'
    input = file.join
    while file.size > 0 do
      if file[0..(2*o_num)].eql?(findme)
        count +=
      end
        file = file[1..-1] # delete the first letter
    end
    count
  end
like image 541
shin Avatar asked Dec 09 '25 03:12

shin


2 Answers

Using regular expressions:

 def countem(n, str)
  str.scan(/I(?=#{'OI' * n})/).size
end

p countem(1, 'OOIOIOIOIIOII')    # => 4
p countem(2, 'OOIOIOIOIIOII')    # => 2
p countem(3, 'OOIOIOIOIIOII')    # => 1
p countem(4, 'OOIOIOIOIIOII')    # => 0

The regular expression looks for "I" (and consumes it), and then matches the rest of the string using positive lookahead, so that it is not consumed. This leaves the remainder of the string available for more matches.

like image 145
Wayne Conrad Avatar answered Dec 10 '25 18:12

Wayne Conrad


@Kyle has answered your question. Here's one way to do it in a more Ruby-like way.

Edit: @steenslag has made two good suggestions for improvements to my code, which I'm happy to adopt. Originally I had:

def countem(n, str)
  target = ('IO'*n + 'I').split('')
  str.split('').each_cons(2*n+1).reduce(0) {|tot,e| tot + (e==target ? 1 : 0)}
end

His main suggestion was that I use count rather than reduce, but also to construct target in a more direct way. Those changes are reflected below.

Code

The key here is to make use of the method Enumerable#each_cons:

def countem(n, str)
  target = ['I','O']*n << 'I'
  str.split('').each_cons(2*n+1).count { |e| e==target }
end

Example

str = "OOIOIOIOIIOII"
countem(1, str) #=> 4
countem(2, str) #=> 2
countem(3, str) #=> 1

Explanation

n = 1
str = "OOIOIOIOIIOII"

target = ['I','O']*n << 'I'
  #=> ["I", "O", "I"]

a = str.split('')
  #=> ["O", "O", "I", "O", "I", "O", "I", "O", "I", "I", "O", "I", "I"]
b = a.each_cons(2*n+1) #=> a.each_cons(3)
  #=> #<Enumerator: ["O", "O", "I", "O", "I", "O", "I", "O",
  #                  "I", "I", "O", "I", "I"]:each_cons(3)>

To view the contents of the enumerator:

b.to_a
  #=> [["O", "O", "I"], ["O", "I", "O"], ["I", "O", "I"], ["O", "I", "O"],
  #    ["I", "O", "I"], ["O", "I", "O"], ["I", "O", "I"], ["O", "I", "I"],
  #    ["I", "I", "O"], ["I", "O", "I"], ["O", "I", "I"]]

Lastly, count the elements of b that equal target:

b.count { |e| e==target }
  #=> 4
like image 40
Cary Swoveland Avatar answered Dec 10 '25 17:12

Cary Swoveland



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!