3

I have a task to create a function that reverse any string character inside the regular bracket sequence, starting from the innermost pair. The string sequence can have, spaces, punctuation marks, letters and brakets. So the result should be sting.

Example

For string

s = "a(bc)de"

the output should be

reverseParentheses(s) = "acbde".

I have wrote the following code to solve this problem:

s_i = s   
for i in range(s.count('(')):
    # reverse letters inside parenthesis
    s_i = s_i.replace(s_i[s_i.rindex('(')+1:s_i.index(')')], s_i[s_i.rindex('(')+1:s_i.index(')')][::-1])
    # delete outward parenthesis
    s_i =s_i[:s_i.rindex('(')] + s_i[s_i.rindex('(')+1:]
    # delete inward parenthesis
    s_i =s_i[:s_i.index(')')] + s_i[s_i.index(')')+1:]
    i += 1
print(s_i)

However, I become false results for following strings:

s = "abc(cba)ab(bac)c"

It should be

abcabcabcabc

I get

abccabbaabcc

And

s = "The ((quick (brown) (fox) jumps over the lazy) dog)"

It should be like this:

The god quick nworb xof jumps over the lazy

But I get only:

The god quick xof nworb jumps over the lazy

How should I correct or adjust my code for becoming right results for last two examples?

Code Adjustment

I have tried to take into consideration answers and hints but I could not use recursion. I adressed the problem with the parantacies when there are just two of them located as: "..(...) (...).., .."

So I made the following code:

def reverse(s):
#ensure parens are in pairs
if '(' not in s and ')' not in s:
    while '(' in s:
            s = s.replace(s[s.rindex('(')+1:s.index(')')], s[s.rindex('(')+1:s.index(')')][::-1])
            s = s[:s.rindex('(')] + s[s.rindex('(')+1:]
            s = s[:s.index(')')] + s[s.index(')')+1:]
    return s
else:
    if (s[s.index(')'):s.rindex('(')+1] == ''):
        while '(' in s:
                s = s.replace(s[s.rindex('(')+1:s.index(')')], s[s.rindex('(')+1:s.index(')')][::-1])
                s = s[:s.rindex('(')] + s[s.rindex('(')+1:]
                s = s[:s.index(')')] + s[s.index(')')+1:]
        return s
    elif (s[s.index(')'):s.rindex('(')+1] != ''):
        betw = s[s.index(')')+1:s.rindex('(')]
        part1 = s[:s.index(')')+1]
        part2 = s[s.rindex('('):]
        part1 = part1.replace(part1[part1.rindex('(')+1:part1.index(')')], part1[part1.rindex('(')+1:part1.index(')')][::-1])
        part1 = part1[:part1.rindex(')')]
        part2 = part2.replace(part2[part2.rindex('(')+1:part2.index(')')], part2[part2.rindex('(')+1:part2.index(')')][::-1])
        part2 = part2[part2.rindex('(')+1:]
        s = part1+betw+part2
        s = s[:s.rindex('(')] + s[s.rindex('(')+1:]
        s = s[:s.index(')')] + s[s.index(')')+1:]
        while '(' in s:
            s = s.replace(s[s.rindex('(')+1:s.index(')')], s[s.rindex('(')+1:s.index(')')][::-1])
            s = s[:s.rindex('(')] + s[s.rindex('(')+1:]
            s = s[:s.index(')')] + s[s.index(')')+1:]
        return s
    else:
        while '(' in s:
            s = s.replace(s[s.rindex('(')+1:s.index(')')], s[s.rindex('(')+1:s.index(')')][::-1])
            s = s[:s.rindex('(')] + s[s.rindex('(')+1:]
            s = s[:s.index(')')] + s[s.index(')')+1:]
        return s

However, I think that it can not perform well for the following example:

s = "abc(147)ab(123)c(12)asd"

The answer should be: "abc741ab321c21asd" but I get "abc12c321ba147asd"

What should be changed in order to get the correct answer?

user21
  • 249
  • 3
  • 13
  • 6
    Since it appears that any number of `()` pairs could occur, I would recommend implementing a recursive function that can be called as long as pairs of parens still exist at a level outside the first – JacobIRR May 16 '17 at 18:03
  • This is basically a lispy language parser. Check this out http://norvig.com/lispy.html –  May 16 '17 at 18:03
  • @JacobIRR thank you. However, how to get parenthesis that are close to each other? F.ex.: `...(brown) (fox)...` . – user21 May 16 '17 at 18:47
  • @JacobIRR I think you should post that as an answer. The question is actually asking how to adjust/correct his code (not asking for the solution to the paren-reversal problem itself) – Matt Messersmith May 16 '17 at 18:58
  • this look like a job for regular exoresions – Copperfield May 17 '17 at 23:29

3 Answers3

1

The reason your solution isn't working is because it is mismatching parentheses:

"abc(cba)ab(bac)c"
"   (cba)ab(bac) "
"       )ab(     "

Your method ultimately won't work: Instead, I recommend you figure out a better way of figuring out which parens match:

def find_paren_set(str):
    # magic
    return left, right

Once you have that capability, you can then just while has_parens(str): through until you're done.

Additional note: Each time you reverse a section, the inner parens will get swapped, ((ob)) will become )bo(.

TemporalWolf
  • 7,727
  • 1
  • 30
  • 50
1

Since it appears that any number of () pairs could occur, I would recommend implementing a recursive function that can be called as long as pairs of parens still exist at a level outside the first:

def reverseParentheses(s):
    # ensure parens are in pairs
    assert '(' in s and ')' in s

    while '(' in s:
        # Go through and swap strings/letters in the innermost parens only.
        # Then reassign `s` to that newly formatted string 
        #  (after taking out those parens)
        # Call the function again until it purges the string of all parens
        reverseParentheses(s)
    return s
JacobIRR
  • 8,545
  • 8
  • 39
  • 68
1

rather than doing this manually, use the regular expression module re that specialize in string manipulation

import re

def reverseParentheses(s):
    def reverse_interior(m):
        s = m.group()
        return s[-2:0:-1]
    old = ""
    while old != s:
        old = s
        s = re.sub(r'(\([^\(\)]*\))',reverse_interior,s)
    return s

assert reverseParentheses("a(bc)de") == "acbde"
assert reverseParentheses("abc(cba)ab(bac)c") == "abcabcabcabc"
assert reverseParentheses("The ((quick (brown) (fox) jumps over the lazy) dog)") == "The god quick nworb xof jumps over the lazy"
assert reverseParentheses("((ob))") == "ob"

here the expression '(\([^\(\)]*\))' would search anything that is between ( and ) that is a one of characters defined in [^\(\)]* which in turn means any numbers character that is not a ( or ), this way it will search the innermost group that match, then I use the function re.sub to replace those in the string with that auxiliary function, that take a string of the form "(xyz)" and return "zyx". As this only work for the innermost group, the process should be repeated while there are changes to be made, hence the loop.

Copperfield
  • 8,131
  • 3
  • 23
  • 29