This is a follow-up for here, for bug fixes and applied advice.
Problem
Your friend John uses a lot of emoticons when you talk to him on Messenger. In addition to being a person who likes to express himself through emoticons, he hates unbalanced parenthesis so much that it makes him go :(
Sometimes he puts emoticons within parentheses, and you find it hard to tell if a parenthesis really is a parenthesis or part of an emoticon.
A message has balanced parentheses if it consists of one of the following:
- An empty string ""
- One or more of the following characters: 'a' to 'z', ' ' (a space) or ':' (a colon)
- An open parenthesis '(', followed by a message with balanced parentheses, followed by a close parenthesis ')'.
- A message with balanced parentheses followed by another message with balanced parentheses.
- A smiley face ":)" or a frowny face ":("
- Write a program that determines if there is a way to interpret his message while leaving the parentheses balanced.
I'm working on this balanced smileys checking algorithm, and my current solution is very naive, with just two rules:
- Do a forward scan, at any point, the number of
)
(close) should be less than or equal to the number of(
(open) + number of:(
(frown) - Do a backward scan, at any point, the number of
(
(open) should be less than or equal to the number of)
(close) and:)
(smile)
I'm wondering if there are any bugs in my checking logic. Any advice on algorithm time complexity improvement or code style advice is highly appreciated as well.
def check_balance(source):
pre = ''
left = 0
right = 0
frown = 0 # :(
smile = 0 # :)
# forward scan
for c in source:
if c == ')':
if pre == ':':
smile += 1
else:
right += 1
if right > left + frown:
return False
elif c == '(':
if pre == ':':
frown += 1
else:
left += 1
pre = c
# reverse scan
for i in range(len(source)-1, -1, -1):
if source[i] == ')':
if i > 0 and source[i-1] == ':':
smile += 1
else:
right += 1
elif source[i] == '(':
if i > 0 and source[i-1] == ":":
frown += 1
else:
left += 1
if right + smile < left:
return False
return True
def verify(test_input, expected_result):
actual_result = check_balance(test_input)
assert actual_result == expected_result, "{} expected {} failed".format(test_input, expected_result)
print(test_input, actual_result)
if __name__ == "__main__":
verify(':)(', False)
verify(':()', True)
verify('abc(b:)cdef(:()', True)
verify('a)', False)
verify(')(' , False)