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()
+```
+'''