๐ ๋ชฉ์ฐจ
์ ๋ชฉ: https://leetcode.com/problems/remove-invalid-parentheses/๐
๐ค ๋ฌธ์ : Remove Invalid Parentheses | LeetCode 301
๋ฌธ์ : https://leetcode.com/problems/remove-invalid-parentheses/
์ฃผ์ด์ง ๋ฌธ์์ด์์ ๊ฐ์ฅ ๊ธด palindrome์ ์ฐพ๋ ๋ฌธ์ ์ ๋๋ค.
๐ก ํ์ด
1. ๊ตฌ๊ตฌ์ ์ .. iteration
์ฃผ์ด์ง s์ ๋ชจ๋ substring์ ๋ํด์, valid parenthesis ์ฌ๋ถ๋ฅผ ํ์ธํ๋ฉด์, ๊ฐ์ฅ ๊ธด ๊ฒ๋ค์ ์ ์ฅํด์ ๋ฐํํด์ผ ๊ฒ ๋ค.
๊ทธ๋ผ TLE ๊ฐ ๋ ๊ฒ ๊ฐ์ผ๋, (์ )์ ๊ฐฏ์๋ฅผ ๋์กฐํด์ ๋ ๋ง์๊ฑธ ๋ง์ ๊ฐฏ์๋งํผ ์ญ์ ํ substring๋จผ์ ํ์ธํ๊ณ .. ๊ฑฐ๊ธฐ์ ์์ผ๋ฉด (์ )๋ฅผ ํ๋์ฉ ๋ ์ญ์ ํ๊ณ ..
๋ฑ๋ฑ์ ๋ก์ง์ ๋ด์ ์ฝ๋๋ฅผ ์์ฑํ์๊ณ ์ด์ฐ์ด์ฐ ํต๊ณผ๋ ๋์๋ค์.
ํ์ง๋ง ๋ค์๋ ์ฝ๊ณ ์ถ์ง ์์ ๊ธธ์ด์ ์ฝ๋๊ฐ ๋์์ผ๋.. ๋ญ๊ฐ ๋จ๋จํ ์๋ชป๋ ๊ฒ ๊ฐ์ฃ ..
from typing import List
# s = "()())()"
# => ["(())()","()()()"]
class Solution:
def removeInvalidParentheses(self, s: str) -> List[str]:
# ์ ๊ฑฐํ๋ ๋ชจ๋ ์กฐํฉ์ ๋ง๋ค์ด์ valid ์ฒดํฌ
# valid ์ฌ๋ถ ํ์ธ, ๋ฌธ์์ด์์ ํฌํจ ์ํ idx ์ง์ - O(N)
def isParenthesisSkip(s: str, skipOrg: List[int]) -> bool:
skip = skipOrg[:]
# ๊ฑด๋๋ธ๊ฒ ๋ฌธ์๋ฉด pass
chars_to_skip = [ (s[idx] == '(' or s[idx] == ')') for idx in skip]
if not all(chars_to_skip):
return False
# ๊ฑด๋๋ธ๊ฒ ๋ฌธ์๋ ํฌํจํ์ง ์๊ณ parenthesis check
stack = []
toskip = skip.pop(0) if len(skip) > 0 else -1
for idx, c in enumerate(s):
if idx == toskip:
toskip = skip.pop(0) if len(skip) > 0 else -1
continue
# print('stack: ', stack, 'skip idx: ', toskip)
if c == '(':
stack.append(c)
elif c == ')':
if len(stack) == 0 or stack[-1] != '(':
return False
stack.pop()
if len(stack) == 0:
return True
return False
def trim(s: str) -> str:
# ๋ฌธ์์ด ์๋ถ๋ถ์ ), ๋ท๋ถ๋ถ์ ( ์ญ์
idx_front = -1
idx_back = -1
for idx, c in enumerate(s):
if c != ')':
idx_front = idx
break
for idx, c in enumerate(reversed(s)):
if c != '(':
idx_back = idx
break
return s[idx_front: len(s)-idx_back]
def powerset(s):
x = len(s)
masks = [1 << i for i in range(x)]
for i in range(1 << x):
yield [ss for mask, ss in zip(masks, s) if i & mask]
def make_string(s, idx_to_remove_org):
# array deep copy
idx_to_remove = idx_to_remove_org[:]
if len(idx_to_remove) == 0:
return s
ret = ""
idx_to_remove_next = idx_to_remove.pop(0)
for idx, char in enumerate(s):
if idx == idx_to_remove_next:
idx_to_remove_next = idx_to_remove.pop(0) if len(idx_to_remove) > 0 else -1
else:
ret += char
return ret
trimed_string = trim(s)
# ( ์ )์ ์์น๋ค์ ์ ์ฅ
position_op = []
position_cp = []
for idx, c in enumerate(trimed_string):
if c =='(':
position_op.append(idx)
elif c == ')':
position_cp.append(idx)
# ( ์ )์ ๊ฐฏ์๋ฅผ ๋น๊ตํ์ฌ ์ฐจ์ด๋ฅผ ๊ณ์ฐ
diff = len(position_op) - len(position_cp)
skip_num_op = max(diff, 0)
skip_num_cp = max(-diff, 0)
# possible skip options
postion_list_op = list(powerset(position_op))
postion_list_cp = list(powerset(position_cp))
result_found = False
result = []
while skip_num_op <= len(position_op) and skip_num_cp <= len(position_cp) and not result_found:
op_skip_list = list(filter(lambda x: len(x) == skip_num_op, postion_list_op))
cp_skip_list = list(filter(lambda x: len(x) == skip_num_cp, postion_list_cp))
if len(op_skip_list) == 0:
for cp_skip in cp_skip_list:
skip_options =sorted(cp_skip)
if isParenthesisSkip(trimed_string, skip_options):
result_found = True
result.append(make_string(trimed_string, skip_options))
elif len(cp_skip_list) == 0:
for op_skip in op_skip_list:
skip_options =sorted(op_skip)
if isParenthesisSkip(trimed_string, skip_options):
result_found = True
result.append(make_string(trimed_string, skip_options))
else:
for op_skip in op_skip_list:
for cp_skip in cp_skip_list:
skip_options =sorted(op_skip+cp_skip)
if isParenthesisSkip(trimed_string, skip_options):
result_found = True
result.append(make_string(trimed_string, skip_options))
skip_num_op += 1
skip_num_cp += 1
return list(set(result))
2. DFS
์ด์ฉ์ง ์ฝ๋๊ฐ ๊ตฌ๊ตฌ์ ์ ํ๋ค ์ถ์ผ๋ฉด.. DFS๋ BFS๋ก ํ์ด์ผ ํ ๊ฑธ iteration์ผ๋ก ํ์ด์ผ ํ ๊ฒฝ์ฐ๊ฐ ๋ง์์ต๋๋ค.
์ด๋ฒ์๋ ์ญ์๋ discussion์ ํ์ธํด๋ณด๋ DFSํ์ด๊ฐ ๋ง์์ต๋๋ค.
DFS๋ก ๋ค์ ์ฝ๋๋ฅผ ์์ฑํด๋ณด์์ต๋๋ค.
from typing import List
# s = "()())()"
# => ["(())()","()()()"]
class Solution:
def removeInvalidParentheses(self, s: str) -> List[str]:
self.longest_str_size = -1
self.result = []
self.isValidParenthesis(s, len(s), '', 0, 0, 0)
return list(set(self.result))
def isValidParenthesis(self, s: str, lens: int, currs: str, idx: int, num_o, num_c):
if num_c > num_o:
return False
# ๋ง์ง๋ง๊น์ง ๋ค ๋ดค์
if lens == idx:
# print('isValidParenthesis', idx, currs, num_c, num_o, self.longest_str_size)
if num_c != num_o:
return False
else:
if len(currs) > self.longest_str_size:
self.longest_str_size = len(currs)
self.result = [currs]
return True
elif len(currs) == self.longest_str_size:
self.result.append(currs)
return True
else:
return False
self.isValidParenthesis(s, lens, currs+s[idx], idx+1, num_o + (1 if s[idx] == "(" else 0), num_c+ (1 if s[idx] == ")" else 0))
self.isValidParenthesis(s, lens, currs, idx+1, num_o, num_c)
ํ๊ฒฐ ๊น๋ํด์ก์ง๋ง ๊ฒฐ๊ณผ๋ TLE.
๋ฌธ์์ด์ ๊ธธ์ด๊ฐ N์ด๋ผ๊ณ ํ ๋ DFS๋ O(2^N) ์ด์ฃ .
3. DFS + pruning
DFS์์ ๋ถ๊ฐ๋ฅํ ๊ฐ์ง๋ค์ ์ฌ์ ์ pruningํ๋ ๋ฐฉ์์ผ๋ก ์ต์ข ๋ต์ ์ ์ถํ์์ต๋๋ค.
์๋ ์ฝ๋์ [2], [4]์ ๋ํ ๋ถ์ฐ์ค๋ช ์ ๋๋ค.
[2] skip๊ฐฏ์ ๊ธฐ๋ฐ pruning
valid parentheses ์ค ๊ฐ์ฅ ๊ธด ๊ฒ์ ์ฐพ๋ ๋ฌธ์ ์ ๋๋ค.
๋ง์ฝ ์ด๋ฏธ ์ฐพ์ valid parentheses๊ฐ ์ญ์ ํ ๋ฌธ์๋ 1๊ฐ์ธ๋ฐ ์ง๊ธ ํ์ธํ๋ ๋ฌธ์์ด์ ์ด๋ฏธ 2๊ฐ๋ฅผ ์ญ์ ํ๋ค๋ฉด, ์ด ๋ฌธ์์ด์ด validํ๋ค ๊ฐ์ฅ ๊ธด valid parentheses๋ ๋ ์ ์์ผ๋ฏ๋ก ์ ๋ต์ด ์๋๋๋ค.
[3] valid parentheses๊ฐ ๋ ๊ฐ๋ฅ์ฑ์ด ์๋์ง ํ์ธ
valid parentheses๋ ( ์ )์ ๊ฐฏ์๊ฐ ๊ฐ๋ค๋ ํน์ง์ด ์์ต๋๋ค.
๋ง์ฝ ํ์ฌ ๋ฌธ์์ด์ (๊ฐ 10๊ฐ์ธ๋ฐ ๋จ์ ) ๋ฅผ ๋ค ์ด๋ค๊ณ ํด๋ 5๊ฐ๋ฐ์ ์๋๋ค๋ฉด ์ด ๋ฌธ์์ด์ valid parentheses๊ฐ ๋ ๊ฐ๋ฅ์ฑ์ด ์์ต๋๋ค.
')'์ ๊ฐฏ์๊ฐ '('๋ณด๋ค ๋ง์ ์์ ์ด ํ๋ฒ์ด๋ผ๋ ์๋ค๋ฉด ์ด๋ฏธ [1]์์ invad parentheses๋ก False๋ฅผ ๋ฆฌํดํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ํด๋น ์ผ์ด์ค๋ ๊ณ ๋ คํ์ง ์์ต๋๋ค.
๋ (์ )์ ๊ฐฏ์๊ฐ ๊ฐ๋ค๋ ํน์ง์ด ์์ต๋๋ค.
from typing import List
class Solution:
def removeInvalidParentheses(self, s: str) -> List[str]:
self.longest_str_size = -1
self.result = []
self.dfs(s, len(s), '', 0, 0, 0, 0, s.count('('), s.count(')'))
return list(set(self.result))
def dfs(self, s: str, lens: int, currs: str, idx: int, num_o, num_c, num_skip, remaining_o, remaining_c):
# [1] valid parenthesis์ธ์ง ํ์ธ
if num_c > num_o:
return False
# [2] skip๊ฐฏ์ ๊ธฐ๋ฐ pruning
if num_skip > lens-self.longest_str_size:
return False
# [3] valid parentheses๊ฐ ๋ ๊ฐ๋ฅ์ฑ์ด ์๋์ง ํ์ธ
if num_o > num_c + remaining_c:
return False
# ๋ง์ง๋ง ์ธ๋ฑ์ค์ธ ๊ฒฝ์ฐ validity check
if lens == idx:
# print('isValidParenthesis', idx, currs, num_c, num_o, self.longest_str_size)
if num_c != num_o:
return False
else:
if len(currs) > self.longest_str_size:
self.longest_str_size = len(currs)
self.result = [currs]
return True
elif len(currs) == self.longest_str_size:
self.result.append(currs)
return True
else:
return False
self.dfs(s, lens, currs+s[idx], idx+1, num_o + (1 if s[idx] == "(" else 0), num_c+ (1 if s[idx] == ")" else 0), num_skip, remaining_o-(1 if s[idx] == "(" else 0), remaining_c-(1 if s[idx] == ")" else 0))
self.dfs(s, lens, currs, idx+1, num_o, num_c, num_skip+1, remaining_o-(1 if s[idx] == "(" else 0), remaining_c-(1 if s[idx] == ")" else 0))
'์๊ณ ๋ฆฌ์ฆ > LEETCODE' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Longest Increasing Subsequence | LeetCode 810 | Python3 ๐ (0) | 2022.03.18 |
---|---|
Task Scheduler | LeetCode 826 | Python3 ๐ (0) | 2022.03.17 |
Majority Element | LeetCode 824 | Python3 ๐ (0) | 2022.03.16 |
Divide Two Integers | LeetCode 820 | Python3 ๐ (0) | 2022.03.15 |
Search a 2D Matrix II | LeetCode 806 | Python3 ๐ (0) | 2022.03.14 |