def is_valid(s):
if not s:
return True
char = "x"
count = 0
for c in s:
if c != char:
char, count = c, 1
else:
count += 1
if count == 3:
return False
return True
# https://stackoverflow.com/a/69322213/2034787
# Code by MTO
def solve(_str):
opposite = {"a": "b", "b": "a"}
curr_idx = 0
output = []
lookahead = lambda x: None if ((curr_idx + x < 0) or (curr_idx + x >= len(_str))) else _str[curr_idx + x]
lookbehind = lambda x: None if curr_idx-x< 0 else output[curr_idx - x]
matches = lambda x, y: x == y or x == None or y == None
is_replacement = lambda x: x == '?'
while curr_idx < len(_str):
curr = lookbehind(1) or 'b'
i = 0
next = lookahead(i)
while is_replacement(next):
i += 1
next = lookahead(i)
if next == None:
# Found the end of the string.
# Append characters opposite to the previous for each ?
for _ in range(i, 0, -1):
curr = opposite[curr]
output.append( curr )
break
if (i > 1):
# Found multiple successive ?s
# Handle the first half of the ?s by
# Append alternating characters from the previous character.
j = 0
while j < i / 2:
curr = opposite[curr]
output.append( curr )
j += 1
# Then handle the second half of the ?s
# append alternating characters to the next character after the ?s.
while j < i:
output.append( next if (j%2) == (i%2) else opposite[next] )
j += 1
elif i == 1:
# Found a single ?
prev = lookbehind(2)
if curr == prev and curr == opposite[next] and next == lookahead(2):
# No solution.
return None
if curr == prev or matches(curr, next):
output.append( opposite[curr] )
else:
output.append( curr )
if next != None:
# Output the next non-? character.
output.append( next )
curr_idx += i + 1
return ''.join(output)
# My greedy solution
def f(s):
if len(s) == 2:
return s.replace('?', 'a')
aa = ['a', 'a']
bb = ['b', 'b']
out = []
i = 0
a, b = 0, 0
while i < len(s):
if s[i] == 'a' or (s[i] == '?' and b == 2):
if s[i] == 'a' and a == 2:
if s[i-3:i-1] == "??":
out[i-3], out[i-2] = out[i-2], out[i-3]
a -= 1
else:
return ""
out.append('a')
a, b = a + 1, 0
i += 1
elif s[i] == 'b' or (s[i] == '?' and a == 2):
if s[i] == 'b' and b == 2:
if s[i-3:i-1] == "??":
out[i-3], out[i-2] = out[i-2], out[i-3]
b -= 1
else:
return ""
out.append('b')
a, b = 0, b + 1
i += 1
elif i == len(s) - 1:
out.append('a' if b else 'b')
i += 1
elif i == len(s) - 2:
if s[i+1] == '?':
out.extend(aa if b else bb)
else:
out.append('a' if b else 'b')
out.append(s[i+1])
i += 2
elif s[i+1] == '?':
if a:
out.append('a')
a, b = a + 1, 0
else:
out.append('b')
a, b = 0, b + 1
i += 1
elif s[i+1] == 'a':
if a or b < 2:
out.append('b')
a, b = 0, b + 1
else:
out.append('a')
a, b = a + 1, 0
i += 1
elif s[i+1] == 'b':
if b or a < 2:
out.append('a')
a, b = a + 1, 0
else:
out.append('b')
a, b = 0, b + 1
i += 1
return ''.join(out)
strs = [
"a?bb", # "aabb"
"??abb", # "ababb" or "bbabb" or "baabb"
"a?b?aa", # "aabbaa"
"?bb?",
"aa?bb", # NO SOLUTION
"aa??aa", # "aabbaa"
"ab???bb?",
"????",
"??",
"?????",
"??????",
"?",
"a?",
"a??",
"a???",
"b?",
"b??",
"b???",
"a?a",
"a?b",
"b?a",
"b?b",
"a??a",
"a??b",
"b??a",
"b??b",
"aa?",
"ba?",
"a?bb",
"?bb?",
"??abb",
"a?b?aa",
"ab???bb?",
"aa?bb"
]
for s in strs:
_solve = solve(s)
_f = f(s)
print("%s\n%s, %s, %s, %s\n" % (s, is_valid(_f), is_valid(_solve), _solve, _f))
import random
def get_random(n):
char = 'x'
count = 0
out = []
not_c = lambda c: 'a' if c == 'b' else 'b'
for _ in range(n):
c = ['a', 'b'][int(random.random() * 2)]
if c != char:
out.append(c)
char, count = c, 1
elif count == 2:
out.append(not_c(c))
char, count = not_c(c), 1
else:
out.append(c)
count += 1
for i in range(n):
if int(random.random() * 2):
out[i] = '?'
return ''.join(out)
num_tests = 1000
n = 20
print("Starting random tests...")
for _ in range(num_tests):
s = get_random(n)
_solve = solve(s)
_f = f(s)
valid_solve = is_valid(_solve)
valid_f = is_valid(_f)
if not valid_f or not valid_solve:
print(s, valid_f, valid_solve, _f, _solve)
break
print("Done testing")
ZGVmIGlzX3ZhbGlkKHMpOgogIGlmIG5vdCBzOgogICAgcmV0dXJuIFRydWUKICBjaGFyID0gIngiCiAgY291bnQgPSAwCiAgZm9yIGMgaW4gczoKICAgIGlmIGMgIT0gY2hhcjoKICAgICAgY2hhciwgY291bnQgPSBjLCAxCiAgICBlbHNlOgogICAgICBjb3VudCArPSAxCiAgICAgIGlmIGNvdW50ID09IDM6CiAgICAgICAgcmV0dXJuIEZhbHNlCiAgcmV0dXJuIFRydWUKCgojIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vYS82OTMyMjIxMy8yMDM0Nzg3CiMgQ29kZSBieSBNVE8KZGVmIHNvbHZlKF9zdHIpOgogIG9wcG9zaXRlID0geyJhIjogImIiLCAiYiI6ICJhIn0KICBjdXJyX2lkeCA9IDAKICBvdXRwdXQgPSBbXQogIAogIGxvb2thaGVhZCAgPSBsYW1iZGEgeDogTm9uZSBpZiAoKGN1cnJfaWR4ICsgeCA8IDApIG9yIChjdXJyX2lkeCArIHggPj0gbGVuKF9zdHIpKSkgZWxzZSBfc3RyW2N1cnJfaWR4ICsgeF0KICBsb29rYmVoaW5kID0gbGFtYmRhIHg6IE5vbmUgaWYgY3Vycl9pZHgteDwgMCBlbHNlIG91dHB1dFtjdXJyX2lkeCAtIHhdCiAgbWF0Y2hlcyA9IGxhbWJkYSB4LCB5OiB4ID09IHkgb3IgeCA9PSBOb25lIG9yIHkgPT0gTm9uZQogIGlzX3JlcGxhY2VtZW50ID0gbGFtYmRhIHg6IHggPT0gJz8nCiAgCiAgd2hpbGUgY3Vycl9pZHggPCBsZW4oX3N0cik6CiAgICBjdXJyID0gbG9va2JlaGluZCgxKSBvciAnYicKICAgIGkgPSAwCiAgICBuZXh0ID0gbG9va2FoZWFkKGkpCiAgICB3aGlsZSBpc19yZXBsYWNlbWVudChuZXh0KToKICAgICAgaSArPSAxCiAgICAgIG5leHQgPSBsb29rYWhlYWQoaSkKICAgIAogICAgaWYgbmV4dCA9PSBOb25lOgogICAgICAjIEZvdW5kIHRoZSBlbmQgb2YgdGhlIHN0cmluZy4KICAgICAgIyBBcHBlbmQgY2hhcmFjdGVycyBvcHBvc2l0ZSB0byB0aGUgcHJldmlvdXMgZm9yIGVhY2ggPwogICAgICBmb3IgXyBpbiByYW5nZShpLCAwLCAtMSk6CiAgICAgICAgY3VyciA9IG9wcG9zaXRlW2N1cnJdCiAgICAgICAgb3V0cHV0LmFwcGVuZCggY3VyciApCiAgICAgIGJyZWFrCgogICAgaWYgKGkgPiAxKToKICAgICAgIyBGb3VuZCBtdWx0aXBsZSBzdWNjZXNzaXZlID9zCiAgICAgICMgSGFuZGxlIHRoZSBmaXJzdCBoYWxmIG9mIHRoZSA/cyBieQogICAgICAjIEFwcGVuZCBhbHRlcm5hdGluZyBjaGFyYWN0ZXJzIGZyb20gdGhlIHByZXZpb3VzIGNoYXJhY3Rlci4KICAgICAgaiA9IDAKICAgICAgd2hpbGUgaiA8IGkgLyAyOgogICAgICAgIGN1cnIgPSBvcHBvc2l0ZVtjdXJyXQogICAgICAgIG91dHB1dC5hcHBlbmQoIGN1cnIgKQogICAgICAgIGogKz0gMQogICAgICAjIFRoZW4gaGFuZGxlIHRoZSBzZWNvbmQgaGFsZiBvZiB0aGUgP3MKICAgICAgIyBhcHBlbmQgYWx0ZXJuYXRpbmcgY2hhcmFjdGVycyB0byB0aGUgbmV4dCBjaGFyYWN0ZXIgYWZ0ZXIgdGhlID9zLgogICAgICB3aGlsZSBqIDwgaToKICAgICAgICBvdXRwdXQuYXBwZW5kKCBuZXh0IGlmIChqJTIpID09IChpJTIpIGVsc2Ugb3Bwb3NpdGVbbmV4dF0gKQogICAgICAgIGogKz0gMQogIAogICAgZWxpZiBpID09IDE6CiAgICAgICMgRm91bmQgYSBzaW5nbGUgPwogICAgICBwcmV2ID0gbG9va2JlaGluZCgyKQogICAgICBpZiBjdXJyID09IHByZXYgYW5kIGN1cnIgPT0gb3Bwb3NpdGVbbmV4dF0gYW5kIG5leHQgPT0gbG9va2FoZWFkKDIpOgogICAgICAgICMgTm8gc29sdXRpb24uCiAgICAgICAgcmV0dXJuIE5vbmUKCiAgICAgIGlmIGN1cnIgPT0gcHJldiBvciBtYXRjaGVzKGN1cnIsIG5leHQpOgogICAgICAgIG91dHB1dC5hcHBlbmQoIG9wcG9zaXRlW2N1cnJdICkKICAgICAgZWxzZToKICAgICAgICBvdXRwdXQuYXBwZW5kKCBjdXJyICkKCiAgICBpZiBuZXh0ICE9IE5vbmU6CiAgICAgICMgT3V0cHV0IHRoZSBuZXh0IG5vbi0/IGNoYXJhY3Rlci4KICAgICAgb3V0cHV0LmFwcGVuZCggbmV4dCApCgogICAgY3Vycl9pZHggKz0gaSArIDEKICAKICByZXR1cm4gJycuam9pbihvdXRwdXQpCgoKIyBNeSBncmVlZHkgc29sdXRpb24KZGVmIGYocyk6CiAgaWYgbGVuKHMpID09IDI6CiAgICByZXR1cm4gcy5yZXBsYWNlKCc/JywgJ2EnKQogICAgCiAgYWEgPSBbJ2EnLCAnYSddCiAgYmIgPSBbJ2InLCAnYiddCgogIG91dCA9IFtdCiAgaSA9IDAKICBhLCBiID0gMCwgMAoKICB3aGlsZSBpIDwgbGVuKHMpOgogICAgaWYgc1tpXSA9PSAnYScgb3IgKHNbaV0gPT0gJz8nIGFuZCBiID09IDIpOgogICAgICBpZiBzW2ldID09ICdhJyBhbmQgYSA9PSAyOgogICAgICAgIGlmIHNbaS0zOmktMV0gPT0gIj8/IjoKICAgICAgICAgIG91dFtpLTNdLCBvdXRbaS0yXSA9IG91dFtpLTJdLCBvdXRbaS0zXQogICAgICAgICAgYSAtPSAxCiAgICAgICAgZWxzZToKICAgICAgICAgIHJldHVybiAiIgogICAgICBvdXQuYXBwZW5kKCdhJykKICAgICAgYSwgYiA9IGEgKyAxLCAwCiAgICAgIGkgKz0gMQogICAgZWxpZiBzW2ldID09ICdiJyBvciAoc1tpXSA9PSAnPycgYW5kIGEgPT0gMik6CiAgICAgIGlmIHNbaV0gPT0gJ2InIGFuZCBiID09IDI6CiAgICAgICAgaWYgc1tpLTM6aS0xXSA9PSAiPz8iOgogICAgICAgICAgb3V0W2ktM10sIG91dFtpLTJdID0gb3V0W2ktMl0sIG91dFtpLTNdCiAgICAgICAgICBiIC09IDEKICAgICAgICBlbHNlOgogICAgICAgICAgcmV0dXJuICIiCiAgICAgIG91dC5hcHBlbmQoJ2InKQogICAgICBhLCBiID0gMCwgYiArIDEKICAgICAgaSArPSAxCiAgICBlbGlmIGkgPT0gbGVuKHMpIC0gMToKICAgICAgb3V0LmFwcGVuZCgnYScgaWYgYiBlbHNlICdiJykKICAgICAgaSArPSAxCiAgICBlbGlmIGkgPT0gbGVuKHMpIC0gMjoKICAgICAgaWYgc1tpKzFdID09ICc/JzoKICAgICAgICBvdXQuZXh0ZW5kKGFhIGlmIGIgZWxzZSBiYikKICAgICAgZWxzZToKICAgICAgICBvdXQuYXBwZW5kKCdhJyBpZiBiIGVsc2UgJ2InKQogICAgICAgIG91dC5hcHBlbmQoc1tpKzFdKQogICAgICBpICs9IDIKICAgIGVsaWYgc1tpKzFdID09ICc/JzoKICAgICAgaWYgYToKICAgICAgICBvdXQuYXBwZW5kKCdhJykKICAgICAgICBhLCBiID0gYSArIDEsIDAKICAgICAgZWxzZToKICAgICAgICBvdXQuYXBwZW5kKCdiJykKICAgICAgICBhLCBiID0gMCwgYiArIDEKICAgICAgaSArPSAxCiAgICBlbGlmIHNbaSsxXSA9PSAnYSc6CiAgICAgIGlmIGEgb3IgYiA8IDI6CiAgICAgICAgb3V0LmFwcGVuZCgnYicpCiAgICAgICAgYSwgYiA9IDAsIGIgKyAxCiAgICAgIGVsc2U6CiAgICAgICAgb3V0LmFwcGVuZCgnYScpCiAgICAgICAgYSwgYiA9IGEgKyAxLCAwCiAgICAgIGkgKz0gMQogICAgZWxpZiBzW2krMV0gPT0gJ2InOgogICAgICBpZiBiIG9yIGEgPCAyOgogICAgICAgIG91dC5hcHBlbmQoJ2EnKQogICAgICAgIGEsIGIgPSBhICsgMSwgMAogICAgICBlbHNlOgogICAgICAgIG91dC5hcHBlbmQoJ2InKQogICAgICAgIGEsIGIgPSAwLCBiICsgMQogICAgICBpICs9IDEKCiAgcmV0dXJuICcnLmpvaW4ob3V0KQoKCnN0cnMgPSBbCiAgImE/YmIiLCAjICJhYWJiIgogICI/P2FiYiIsICMgImFiYWJiIiBvciAiYmJhYmIiIG9yICJiYWFiYiIKICAiYT9iP2FhIiwgIyAiYWFiYmFhIgogICI/YmI/IiwKICAiYWE/YmIiLCAjIE5PIFNPTFVUSU9OCiAgImFhPz9hYSIsICMgImFhYmJhYSIKICAiYWI/Pz9iYj8iLAogICI/Pz8/IiwKICAiPz8iLAogICI/Pz8/PyIsCiAgIj8/Pz8/PyIsCiAgIj8iLAogICJhPyIsCiAgImE/PyIsCiAgImE/Pz8iLAogICJiPyIsCiAgImI/PyIsCiAgImI/Pz8iLAogICJhP2EiLAogICJhP2IiLAogICJiP2EiLAogICJiP2IiLAogICJhPz9hIiwKICAiYT8/YiIsCiAgImI/P2EiLAogICJiPz9iIiwKICAiYWE/IiwKICAiYmE/IiwKICAiYT9iYiIsCiAgIj9iYj8iLAogICI/P2FiYiIsCiAgImE/Yj9hYSIsCiAgImFiPz8/YmI/IiwKICAiYWE/YmIiCl0KCgpmb3IgcyBpbiBzdHJzOgogIF9zb2x2ZSA9IHNvbHZlKHMpCiAgX2YgPSBmKHMpCiAgcHJpbnQoIiVzXG4lcywgJXMsICVzLCAlc1xuIiAlIChzLCBpc192YWxpZChfZiksIGlzX3ZhbGlkKF9zb2x2ZSksIF9zb2x2ZSwgX2YpKQoKCmltcG9ydCByYW5kb20KCmRlZiBnZXRfcmFuZG9tKG4pOgogIGNoYXIgPSAneCcKICBjb3VudCA9IDAKICBvdXQgPSBbXQogIG5vdF9jID0gbGFtYmRhIGM6ICdhJyBpZiBjID09ICdiJyBlbHNlICdiJwoKICBmb3IgXyBpbiByYW5nZShuKToKICAgIGMgPSBbJ2EnLCAnYiddW2ludChyYW5kb20ucmFuZG9tKCkgKiAyKV0KICAgIGlmIGMgIT0gY2hhcjoKICAgICAgb3V0LmFwcGVuZChjKQogICAgICBjaGFyLCBjb3VudCA9IGMsIDEKICAgIGVsaWYgY291bnQgPT0gMjoKICAgICAgb3V0LmFwcGVuZChub3RfYyhjKSkKICAgICAgY2hhciwgY291bnQgPSBub3RfYyhjKSwgMQogICAgZWxzZToKICAgICAgb3V0LmFwcGVuZChjKQogICAgICBjb3VudCArPSAxCgogIGZvciBpIGluIHJhbmdlKG4pOgogICAgIGlmIGludChyYW5kb20ucmFuZG9tKCkgKiAyKToKICAgICAgb3V0W2ldID0gJz8nCgogIHJldHVybiAnJy5qb2luKG91dCkKCm51bV90ZXN0cyA9IDEwMDAKbiA9IDIwCgpwcmludCgiU3RhcnRpbmcgcmFuZG9tIHRlc3RzLi4uIikKCmZvciBfIGluIHJhbmdlKG51bV90ZXN0cyk6CiAgcyA9IGdldF9yYW5kb20obikKICBfc29sdmUgPSBzb2x2ZShzKQogIF9mID0gZihzKQogIHZhbGlkX3NvbHZlID0gaXNfdmFsaWQoX3NvbHZlKQogIHZhbGlkX2YgPSBpc192YWxpZChfZikKICBpZiBub3QgdmFsaWRfZiBvciBub3QgdmFsaWRfc29sdmU6CiAgICBwcmludChzLCB2YWxpZF9mLCB2YWxpZF9zb2x2ZSwgX2YsIF9zb2x2ZSkKICAgIGJyZWFrCgpwcmludCgiRG9uZSB0ZXN0aW5nIik=
a?bb
True, True, aabb, aabb
??abb
True, True, ababb, bbabb
a?b?aa
True, True, aabbaa, aabbaa
?bb?
True, True, abba, abba
aa?bb
True, True, None,
aa??aa
True, True, aabbaa, aabbaa
ab???bb?
True, True, abababba, abbaabba
????
True, True, abab, bbab
??
True, True, ab, aa
?????
True, True, ababa, bbabb
??????
True, True, ababab, bbaaba
?
True, True, a, b
a?
True, True, ab, aa
a??
True, True, aba, abb
a???
True, True, abab, aaba
b?
True, True, ba, ba
b??
True, True, bab, baa
b???
True, True, baba, bbab
a?a
True, True, aba, aba
a?b
True, True, aab, abb
b?a
True, True, bba, baa
b?b
True, True, bab, bab
a??a
True, True, abba, aaba
a??b
True, True, abab, aabb
b??a
True, True, baba, bbaa
b??b
True, True, baab, bbab
aa?
True, True, aab, aab
ba?
True, True, bab, bab
a?bb
True, True, aabb, aabb
?bb?
True, True, abba, abba
??abb
True, True, ababb, bbabb
a?b?aa
True, True, aabbaa, aabbaa
ab???bb?
True, True, abababba, abbaabba
aa?bb
True, True, None,
Starting random tests...
Done testing