LeetCode-Problems / 1209. Remove All Adjacent Duplicates in String II / 1209. Remove All Adjacent Duplicates in String II.py
1209. Remove All Adjacent Duplicates in String II.py
Raw
# String slicing to the rescue. Seems pretty simple
# using a two pointer solution.
# Oh wait, collapsing strings can cause a sort of
# chain combo effect that precededs the left most
# pointer. I think we may need to go back k or
# possibly k-1 spaces after each delete to recheck
# previous areas. That or check backwars for matches
# each time, but that seems tedious to deal with direction
# and is probably only marginally more efficient?
# I guess for high k its actually significantly better
# as a single test can break the check, where jumping back
# requires each and every char to be checked again...


class Solution:
    def removeDuplicates(self, s: str, k: int) -> str:
        stack = []
        for each in s:
            if len(stack) == 0:
                stack.append([each, 1])
            elif each == stack[-1][0]:
                stack[-1][1] += 1
                if stack[-1][1] == k:
                    del stack[-1]  # pop the k dups
            else:
                stack.append([each, 1])
        return "".join([x[0] * x[1] for x in stack])


# Ok, im going to give up on the following overly clever solution:
# I think it might be done, but now it times out so posisbly an infinite loop
# I liked the idea of a drunkards walk, rather than just jumping back
# k spaces once you delete a match. For LONG matches that would be super wasteful,
# we know there is no reason to check the previous K letters unless the two halves
# coming together are the same char, otherwise the previous k matches would have already
# been deleted... so why not count and match backwards after each delete, and jump to the
# right pointer if we fail a match? MUCH more efficient for large sparse K... which is not
# at all what the test cases have. This is a LOT of work for small k...


# class Solution:
#     def removeDuplicates(self, s: str, k: int) -> str:
#         l_ptr = 0
#         r_ptr = min(l_ptr + 1, len(s) -1)

#         rev_dir = 1
#         t  = ""

#         while l_ptr != r_ptr:

#             zzL = s[l_ptr]
#             zzR = s[r_ptr]

#             if s[l_ptr] == s[r_ptr]:
#                 if r_ptr - l_ptr + 1 == k:
#                     t = s[:l_ptr]
#                     if r_ptr + 1 < len(s):       #stupid req of string slicing...
#                         t += s[r_ptr + 1:]
#                     s = t[:]                    #does this assign by val instead of ref? YES
#                     l_ptr = max(l_ptr - 1, 0)   # Prevent l_ptr going before beginning of s
#                     r_ptr = min(l_ptr + 1, len(s) -1) # Prevent r_ptr going after end of s
#                     rev_dir = -1
#                 elif  rev_dir == 1:
#                     while r_ptr < len(s) - 1 and s[l_ptr] == s[r_ptr + 1]and r_ptr - l_ptr + 1 < k:
#                         r_ptr += 1
#                     if r_ptr - l_ptr < k - 1:
#                         l_ptr, r_ptr = min(r_ptr-1, len(s)-1), min(r_ptr, len(s)-1)
#                 elif  rev_dir == -1:
#                     while l_ptr > 0 and s[l_ptr-1] == s[r_ptr] and r_ptr - l_ptr + 1 < k:
#                         l_ptr -= 1
#                     rev_dir = 1
#             else:
#                 rev_dir = 1
#                 l_ptr += 1
#                 r_ptr = min(l_ptr + 1, len(s) -1)
#         return s