From 095994abc46a0822ebce336d30ea513207d12600 Mon Sep 17 00:00:00 2001 From: Dusuna <94776135+dusunax@users.noreply.github.com> Date: Tue, 18 Mar 2025 22:04:22 +0900 Subject: [PATCH 1/4] add solution: subtree-of-another-tree --- subtree-of-another-tree/dusunax.py | 62 ++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 subtree-of-another-tree/dusunax.py diff --git a/subtree-of-another-tree/dusunax.py b/subtree-of-another-tree/dusunax.py new file mode 100644 index 000000000..4f7850994 --- /dev/null +++ b/subtree-of-another-tree/dusunax.py @@ -0,0 +1,62 @@ +''' +# 572. Subtree of Another Tree + +## 1. Recursive +use a recursive function to check if the two trees are identical. +- if they are identical, return True. +- if they are not identical, recursively check the left subtree and right subtree. + +### base case +- isSubtree + - if the current tree is None, return False. + - if the subRoot is None, return True. (empty tree is a subtree of any tree) +- isIdentical + - if both trees are None, return True. (they are identical) + - if one of the trees is None, return False. + - if the values of the current nodes are different, return False. + - left subtree and right subtree's results are must be True. + +## 2. Snapshot +> reference: https://www.algodale.com/problems/subtree-of-another-tree/ + +use a snapshot string of the preorder traversal to check if the subRoot is a subtree of the current tree. +- if the subRoot is a subtree of the current tree, return True. +- if the subRoot is not a subtree of the current tree, return False. +''' +class Solution: + ''' + 1. Recursive + TC: O(n * m), m is the number of nodes in the subRoot. + SC: O(n) (recursive stack space) + ''' + def isSubtree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -> bool: + if not root: + return False + if not subRoot: + return True + + if self.isIdentical(root, subRoot): + return True + + return self.isSubtree(root.left, subRoot) or self.isSubtree(root.right, subRoot) + + def isIdentical(self, s: Optional[TreeNode], t: Optional[TreeNode]) -> bool: + if not s and not t: + return True + if not s or not t: + return False + + return s.val == t.val and self.isIdentical(s.left, t.left) and self.isIdentical(s.right, t.right) + + ''' + 2. Snapshot + TC: O(n + m), n is the number of nodes in the root, m is the number of nodes in the subRoot. + SC: O(n + m) (recursive stack space) + ''' + def isSubtree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -> bool: + def preorder(node): + if not node: + return '#' + return f'({node.val},{preorder(node.left)},{preorder(node.right)})' + + return preorder(subRoot) in preorder(root) From 1574698c255b2a88bc4a80d336e46652ba716644 Mon Sep 17 00:00:00 2001 From: Dusuna <94776135+dusunax@users.noreply.github.com> Date: Tue, 18 Mar 2025 22:38:16 +0900 Subject: [PATCH 2/4] add solution: validate-binary-search-tree --- validate-binary-search-tree/dusunax.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 validate-binary-search-tree/dusunax.py diff --git a/validate-binary-search-tree/dusunax.py b/validate-binary-search-tree/dusunax.py new file mode 100644 index 000000000..cc39aedd9 --- /dev/null +++ b/validate-binary-search-tree/dusunax.py @@ -0,0 +1,26 @@ +''' +# 98. Validate Binary Search Tree + +check if is a valid BST. +- the left value is smaller than the current value +- the right value is greater than the current value + +๐ all left and right subtrees must also valid continuously. +so, we need to check the low & high bound recursively. (not only check current node & immediate children) + +## base case +- if the node is None, return True (empty tree is valid BST) +- valid node value has range of left < node.val < right, if not, return False +- recursively check the left and right subtree, update the low & high. +''' +class Solution: + def isValidBST(self, root: Optional[TreeNode]) -> bool: + def dfs(node, low = float('-inf'), high = float('inf')): + if not node: + return True + if not (low < node.val < high): + return False + + return dfs(node.left, low, node.val) and dfs(node.right, node.val, high) + + return dfs(root) From e2661f121da3dfbe85cf8f7467f4b486b2a3ec15 Mon Sep 17 00:00:00 2001 From: Dusuna <94776135+dusunax@users.noreply.github.com> Date: Fri, 21 Mar 2025 07:49:48 +0900 Subject: [PATCH 3/4] add solution: longest-palindromic-substring --- longest-palindromic-substring/dusunax.py | 135 +++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 longest-palindromic-substring/dusunax.py diff --git a/longest-palindromic-substring/dusunax.py b/longest-palindromic-substring/dusunax.py new file mode 100644 index 000000000..74cfb5d8a --- /dev/null +++ b/longest-palindromic-substring/dusunax.py @@ -0,0 +1,135 @@ +''' +# 5. Longest Palindromic Substring + +DP ํ ์ด๋ธ์ ์ฌ์ฉํ์ฌ ํฐ๋ฆฐ๋๋กฌ ์ฌ๋ถ๋ฅผ ์ ์ฅํ๊ณ ์ต๋ ๊ธธ์ด๋ฅผ ์ฐพ๊ธฐ. +''' +class Solution: + ''' + TC: O(n^2) + SC: O(n^2) + ''' + def longestPalindrome(self, s: str) -> str: + n = len(s) + if n == 1: + return s + + start, length = 0, 1 + dp = [[False] * n for _ in range(n)] # SC: O(n^2) + + # length 1, diagonal elements in 2d array + for i in range(n): # TC: O(n) + dp[i][i] = True + + # length 2, are two elements same + for i in range(n - 1): + if s[i] == s[i + 1]: + dp[i][i + 1] = True + start, length = i, 2 + + # length 3+ + for word_length in range(3, n + 1): # TC: O(n^2) + for i in range(n - word_length + 1): + j = i + word_length - 1 + + if s[i] == s[j] and dp[i + 1][j - 1]: + dp[i][j] = True + start, length = i, word_length + + return s[start:start + length] + +''' +## Check Leetcode Hints +- How can we reuse a previously computed palindrome to compute a larger palindrome? + - use dp table that stores isPalindrome computation. +- If โabaโ is a palindrome, is โxabaxโ a palindrome? Similarly is โxabayโ a palindrome? + - if it is palindrome, we only need to check the outermost chars, that wrapping our palindrome. +- Palindromic checks can be O(1) by reusing prev computations. + - DP! + +## ๋์ ํ๋ก๊ทธ๋๋ฐ ํ์ด +- s[i:j]์ ํฐ๋ฆฐ๋๋กฌ ์ฌ๋ถ๋ฅผ ์ ์ฅํ๊ธฐ ์ํด์๋ 2์ฐจ์ ๋ฐฐ์ด์ ์ฌ์ฉํ๋ ๊ฒ์ด ๊ฐํธํ๋ค. +- ๊ธธ์ด๊ฐ 1์ธ ๊ฒฝ์ฐ, ํฐ๋ฆฐ๋๋กฌ +- ๊ธธ์ด๊ฐ 2์ธ ๊ฒฝ์ฐ, ๋ ๋ฌธ์๊ฐ ๊ฐ๋ค๋ฉด ํฐ๋ฆฐ๋๋กฌ +- 3 ์ด์์ dp ํ ์ด๋ธ์ ํ์ฉํ๋ฉฐ ํ์ธ + - ex) ๊ธธ์ด๊ฐ 5์ธ ๋ฌธ์์ด์ ๋ค์๊ณผ ๊ฐ์ด ํ์ + ``` + len: 3, i: 0, j: 2 + len: 3, i: 1, j: 3 + len: 3, i: 2, j: 4 + len: 4, i: 0, j: 3 + len: 4, i: 1, j: 4 + len: 5, i: 0, j: 4 + ``` + +## ํ๊ตฌ +- can we use sliding window to optimize? +์ฌ๋ผ์ด๋ฉ ์๋์ฐ๋ ์ฐ์์ ์ธ ๊ตฌ๊ฐ์ ์ ์งํ๋ฉฐ ์ต์ ํด๋ฅผ ์ฐพ์ ๋ ์ ์ฉํ์ง๋ง, ํฐ๋ฆฐ๋๋กฌ์ ์ค์ ํ์ฅ๊ณผ ์ด์ ๊ณ์ฐ ์ฌ์ฌ์ฉ์ด ํต์ฌ. + +- can we solve this by counting char? +๋ฌธ์ ๊ฐ์๋ฅผ ์ธ๋ฉด "์ด๋ค ๋ฌธ์๊ฐ ๋ช ๋ฒ ๋์๋์ง"๋ง ์ ์ ์์ง๋ง, ํฐ๋ฆฐ๋๋กฌ ์ฌ๋ถ๋ ๋ฌธ์ ์์๊ฐ ์ค์ํ๋ค. + +- ๊ทธ๋ ๋ค๋ฉด ๊ฐ์ ๋ฐฉ๋ฒ์? +> Manacher's Algorithm: https://www.geeksforgeeks.org/manachers-algorithm-linear-time-longest-palindromic-substring-part-1/ +> +>ํฐ๋ฆฐ๋๋กฌ์ ๋์นญ์ฑ์ ํ์ฉํด ์ค์์ ๊ธฐ์ค์ผ๋ก ํ์ฅํ๋ Manacher's Algorithm์ ์ ์ฉํ ์ ์๋ค. ๋ชจ๋ ๋ฌธ์ ์ฌ์ด์ #์ ๋ฃ์ด ์ง์, ํ์๋ฅผ ๋์ผํ๊ฒ ๋ค๋ฃจ๋ ๊ธฐ๋ฒ์ธ๋ฐ ๊ตฌํ์ด ๋ค์ ๋ณต์กํ๋ค. ์๊ฐ ๋ณต์ก๋๋ O(n)์ด๋ค. +''' +class Solution: + ''' + TC: O(n) + SC: O(n) + ''' + def longestPalindromeManacher(self, s: str) -> str: + ''' + 1. ๋ฌธ์์ด ๋ณํํ๊ธฐ (๊ฐ์ด๋ฐ์ # ์ถ๊ฐ) + 2. ์ ์ฅ๊ณต๊ฐ ์ด๊ธฐํ + 3. ๋ณํ๋ ๋ฌธ์์ด ์ํํ๊ธฐ + (a) ๋ฏธ๋ฌ ์ธ๋ฑ์ค ๊ณ์ฐ + (b) ํฐ๋ฆฐ๋๋กฌ ๋ฐ์ง๋ฆ ํ์ฅ + - i์์ ์์ชฝ ๋ฌธ์๋ค์ ๋น๊ตํด์ ํ์ฅ + (c) ์๋ก์ด ํฐ๋ฆฐ๋๋กฌ ์ฐพ๊ธฐ + - ์๋ก์ด ํฐ๋ฆฐ๋๋กฌ์ i๋ก ์ค์ + - ์๋ก์ด ํฐ๋ฆฐ๋๋กฌ์ ์ค๋ฅธ์ชฝ ๋์ i + p[i]๋ก ์ค์ + (d) ๊ฐ์ฅ ๊ธด ํฐ๋ฆฐ๋๋กฌ ์ ๋ฐ์ดํธ + ''' + transformed = '#' + '#'.join(s) + '#' + n = len(transformed) + p = [0] * n # i ์ค์ฌ์ ํฐ๋ฆฐ๋๋กฌ ํฌ๊ธฐ + c = 0 # ํ์ฌ ํฐ๋ฆฐ๋๋กฌ์ ์ค์ฌ + r = 0 # ํ์ฌ ํฐ๋ฆฐ๋๋กฌ์ ์ค๋ฅธ์ชฝ ๋ + max_len = 0 # ๊ฐ์ฅ ๊ธด ํฐ๋ฆฐ๋๋กฌ์ ๊ธธ์ด + center = 0 # ๊ฐ์ฅ ๊ธด ํฐ๋ฆฐ๋๋กฌ์ ์ค์ฌ + + for i in range(n): + # ํ์ฌ ์์น i์ ๋ฏธ๋ฌ ์ธ๋ฑ์ค (์ค์์ ๊ธฐ์ค์ผ๋ก ๋์นญ๋๋ ์ธ๋ฑ์ค) + # ex) ababbaa + # 0123456 + # i๊ฐ 3์ด๊ณ center๊ฐ 2์ผ ๋, 2*2 - 3 = 1, ๋ฏธ๋ฌ ์ธ๋ฑ์ค๋ 1 + # i๊ฐ 5์ด๊ณ center๊ฐ 4์ผ ๋, 2*4 - 5 = 3, ๋ฏธ๋ฌ ์ธ๋ฑ์ค๋ 3 + mirror = 2 * c - i + + if i < r: + # r - i: ์ผ๋ง๋ ๋ ํ์ฅ ๋ ์ ์๋๊ฐ => ํ์ฌ ํฐ๋ฆฐ๋๋กฌ์ ์ค๋ฅธ์ชฝ ๋์์ ํ์ฌ ์ธ๋ฑ์ค๊น์ง์ ๊ฑฐ๋ฆฌ + # p[mirror]: ๋ฏธ๋ฌ ์ธ๋ฑ์ค์์์ ํฐ๋ฆฐ๋๋กฌ ๋ฐ์ง๋ฆ + p[i] = min(r - i, p[mirror]) + # ์์ ๊ฐ๋งํผ๋ง ํ์ฅ์ด ๊ฐ๋ฅํ๋ค. ์๋ฅผ ๋ค์ด, r - i๊ฐ ๋ ์์ ๊ฐ์ด๋ผ๋ฉด ํฐ๋ฆฐ๋๋กฌ์ ๊ทธ๋งํผ๋ง ํ์ฅํ ์ ์๊ณ , p[mirror]๊ฐ ๋ ์์ ๊ฐ์ด๋ผ๋ฉด ์ด๋ฏธ ๊ทธ๋งํผ ํ์ฅ์ด ๋ ์ํ + # r - i๊ฐ 3์ด๊ณ p[mirror]๊ฐ 2๋ผ๋ฉด ํฐ๋ฆฐ๋๋กฌ์ 2๋งํผ ํ์ฅํ ์ ์๊ณ , r - i๊ฐ 2์ด๊ณ p[mirror]๊ฐ 3์ด๋ผ๋ฉด ํฐ๋ฆฐ๋๋กฌ์ 2๋งํผ ํ์ฅํ ์ ์๋ค. + + # ํ์ฌ ์ค์ฌ์์ ํฐ๋ฆฐ๋๋กฌ์ ํ์ฅ + # ์ ๋์ด ๊ฐ๋ค๋ฉด ํฐ๋ฆฐ๋๋กฌ ๋ฐ์ง๋ฆ p[i]๋ฅผ 1์ฉ ์ฆ๊ฐ + while i + p[i] + 1 < n and i - p[i] - 1 >= 0 and transformed[i + p[i] + 1] == transformed[i - p[i] - 1]: + p[i] += 1 + + # ๊ธฐ์กด์ ์ฐพ์ ํฐ๋ฆฐ๋๋กฌ๋ณด๋ค ๋ ํฐ ํฐ๋ฆฐ๋๋กฌ์ ์ฐพ์ ๊ฒฝ์ฐ + # ํ์ฌ ์ค์ฌ๊ณผ ์ค๋ฅธ์ชฝ ๋์ ์ค์ + if i + p[i] > r: + c = i + r = i + p[i] + + # ๊ฐ์ฅ ๊ธด ํฐ๋ฆฐ๋๋กฌ ์ ๋ฐ์ดํธ + if p[i] > max_len: + max_len = p[i] + center = i + + # ๋ณํ๋ ๋ฌธ์์ด์์ ๊ฐ์ฅ ๊ธด ํฐ๋ฆฐ๋๋กฌ์ ์๋ ๋ฌธ์์ด์์ ์ถ์ถ + start = (center - max_len) // 2 + return s[start:start + max_len] From e3b5bde24e7d8b32d5e09fa7887e6ca91c868762 Mon Sep 17 00:00:00 2001 From: Dusuna <94776135+dusunax@users.noreply.github.com> Date: Fri, 21 Mar 2025 08:26:14 +0900 Subject: [PATCH 4/4] add solution: rotate-image --- rotate-image/dusunax.py | 77 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 rotate-image/dusunax.py diff --git a/rotate-image/dusunax.py b/rotate-image/dusunax.py new file mode 100644 index 000000000..56fe2d32b --- /dev/null +++ b/rotate-image/dusunax.py @@ -0,0 +1,77 @@ +''' +# 48. Rotate Image + +rotate 2d matrix 90 degree in place. +๐ transpose matrix and reverse each row. + +- original matrix +1 2 3 +4 5 6 +7 8 9 + +- transpose matrix (swap i, j) (flip diagonally) +1 4 7 +2 5 8 +3 6 9 + +- reverse each row (horizontal flip) +7 4 1 +8 5 2 +9 6 3 +''' +class Solution: + ''' + TC: O(n^2) + SC: O(1) + ''' + def rotate(self, matrix: List[List[int]]) -> None: + n = len(matrix) + + for i in range(n): + for j in range(i, n): + matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j] + + for i in range(n): + matrix[i].reverse() + +''' +# ๐ก other rotate in-place examples + +## rotate 180 degree + +### solution A. reverse each column (vertical flip) & reverse each row (horizontal flip) + +```python +def rotate180(matrix: List[List[int]]) -> None: + n = len(matrix) + matrix.reverse() + + for i in range(n): + matrix[i].reverse() +``` + +### solution B. (after transpose, reverse each row (horizontal flip)) * 2. + +90 degree * 2 = 180 degree + +```python +def rotate180(matrix: List[List[int]]) -> None: + rotate90(matrix) + rotate90(matrix) +``` + +## rotate -90 degree + +after transpose, reverse each column (vertical flip) + +```python +def rotate90CCW(matrix): + n = len(matrix) + + for i in range(n): + for j in range(i, n): + matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j] + + matrix.reverse() +``` +'''