I wish to offer a solution in Ruby. Considering that Ruby and Python have many similarities most readers should be able to at least get a gist of what I am doing. I am posting this answer in the chance that a reader might look upon it favourably and post an equivalent, if not improved-upon, solution in Python.
First create a constant holding a regular expression.
RGX = /(.)\1/
This expression matches a pair of the same character (e.g., "aa"
). .
matches any character (other than a line terminator), which is saved to capture group 1. \1
matches the content of capture group 1.
Next I will define a constant H
that holds a hash that maps pairs of letters into the "successor" of both, such as "aa"->"b"
and "zz"->"a"
.
a = ('a'..'z').to_a
#=> ["a", "b",..., "y", "z"]
H = a.map { |c| c*2 }.zip(a.rotate).to_h
#=> {"aa"=>"b", "bb"=>"c",..., "yy"=>"z", "zz"=>"a"}
We may now define a method that takes as its argument the string and returns the desired string.
def doit(str)
loop do
break str if str.sub!(RGX, H).nil?
end
end
Let's try it.
doit("barrsxffzzdggh")
#=> "batxgadi"
It can be seen that value of str
at each iteration is as follows.
bassxffzzdggh
batxffzzdggh
batxgzzdggh
batxgadggh
batxgadhh
batxgadi
I will now break down each step of the method.
First create an array containing the letters of the alphabet.
a = ('a'..'z').to_a
#=> ["a", "b",..., "y", "z"]
'a'..'z'
denotes a range and the method Range#to_a maps it into an array.
The hash H
is constructed in four steps.
x = a.map { |c| c*2 }
#=> ["aa", "bb", "cc",..., "yy", "zz"]
b = a.rotate
#=> ["b", "c",..., "z", "a"]
c = x.zip(b)
#=> [["aa", "b"], ["bb", "c"],..., ["yy", "z"], ["zz", "a"]]
H = c.to_h
#=> {"aa"=>"b", "bb"=>"c",..., "yy"=>"z", "zz"=>"a"}
These steps use the methods Array#map, String#*, Array#rotate, Array#zip and Array#to_h.
Now assign the given string to the variable str
.
str = "barrsxffzzdggh"
Ruby's method Kernel#loop more-or-less loops to its end
statement until the break
keyword is encountered. The single statement within the loop is the following.
str.sub!(RGX, H)
#=> "bassxffzzdggh"
This uses the form of Ruby's method String#sub! which matches substrings with its first argument (a string or a regular expression), and uses its second argument, a hash, for making substitutions. This method modifies the string in place. If a replacement is made the string is returned; else nil
is returned. (Ruby has a non-destructive counterpart to this methods, sub
, and methods gsub!
and gsub
for making multiple replacements.)
Initially an attempt is made to match str
with the regular expression RGX
. It matches "rr"
. As H["rr"] #=> "s"
, "s"
is substituted for "rr"
and the modified string is returned (as well as changed in place), so we repeat the loop. This continues until sub!
returns nil
, at which time we are finished, so we break out of the loop and return the current contents of str
.