From c1409996a498fdfae31f754e70fe11ae3f064afb Mon Sep 17 00:00:00 2001
From: jelonmusk <jelonmusk@gmail.com>
Date: Sun, 24 Nov 2024 17:02:19 +0530
Subject: [PATCH 1/4] COMMITTING STASHED FILES

---
 java/1220-count-vowels-permutation.java   | 51 +++++++++++----
 kotlin/1143-longest-common-subsequence.kt | 77 +----------------------
 2 files changed, 41 insertions(+), 87 deletions(-)

diff --git a/java/1220-count-vowels-permutation.java b/java/1220-count-vowels-permutation.java
index 1fc65e791..980bc0d55 100644
--- a/java/1220-count-vowels-permutation.java
+++ b/java/1220-count-vowels-permutation.java
@@ -1,3 +1,9 @@
+/*          Bottom-Up Approach
+-----------------------------------------
+    Time Complexity : O(n)
+    Space Complexity : O(n)
+----------------------------------------*/
+
 class Solution {
     int MOD = (int) 1e9+7; 
 
@@ -39,18 +45,41 @@ class Solution {
     int MOD = (int) 1e9+7; 
 
     public int countVowelPermutation(int n) {
-        long[] counts = getBaseCounts();
-        if(n == 1) {
-            return getSum(counts);
-        }
+        long ans = 0;
+        ans = (ans + dfs('a', n, 1)) % MOD;
+        ans = (ans + dfs('e', n, 1)) % MOD;
+        ans = (ans + dfs('i', n, 1)) % MOD;
+        ans = (ans + dfs('o', n, 1)) % MOD;
+        ans = (ans + dfs('u', n, 1)) % MOD;
+        return (int)ans;
+    }
+
+    private int dfs(char c, int n, int l){
+        if(l == n)
+            return 1;
         
-        Map<Integer, List<Integer>> mapNextCounting;
-        mapNextCounting = getNextCountMapping();
-    
-        for(int i=1; i<n; i++) {
-            counts = getNextCounts(counts, mapNextCounting);
+        String key = c + "_" + l;
+        if (memo.containsKey(key)) return memo.get(key);
+
+        long res = 0;
+        if(c == 'a') {
+            res = dfs('e', n, l+1);
+        } else if(c == 'e') {
+            res = (res + dfs('a', n, l+1)) % MOD;
+            res = (res + dfs('i', n, l+1)) % MOD;
+        } else if(c == 'i') {
+            res = (res + dfs('a', n, l+1)) % MOD;
+            res = (res + dfs('e', n, l+1)) % MOD;
+            res = (res + dfs('o', n, l+1)) % MOD;
+            res = (res + dfs('u', n, l+1)) % MOD;
+        } else if(c == 'o') {
+            res = (res + dfs('i', n, l+1)) % MOD;
+            res = (res + dfs('u', n, l+1)) % MOD;
+        } else {
+            res = dfs('a', n, l+1);
         }
-        
-        return getSum(counts);
+
+        memo.put(key, (int)(res % MOD));
+        return (int)(res % MOD);    
     }
 }
diff --git a/kotlin/1143-longest-common-subsequence.kt b/kotlin/1143-longest-common-subsequence.kt
index 55dc1f154..ea744645e 100644
--- a/kotlin/1143-longest-common-subsequence.kt
+++ b/kotlin/1143-longest-common-subsequence.kt
@@ -20,79 +20,4 @@ class Solution {
 		}
 		return dp[M][N]
 	}
-}
-
-/*
- * Different solutions
- */
-
- // Recursion + Memoization, Time Complexity of O(n * m) and space complexity of O(n * m)
-class Solution {
-    fun longestCommonSubsequence(t1: String, t2: String): Int {
-        val n = t1.length
-        val m = t2.length
-        val dp = Array (n) { IntArray (m) { -1 } }
-
-        fun dfs(i: Int, j: Int): Int {
-            if (i == n || j == m) return 0
-            if (dp[i][j] != -1) return dp[i][j]
-
-            if (t1[i] == t2[j])
-                dp[i][j] = 1 + dfs(i + 1, j + 1)
-            else
-                dp[i][j] = maxOf(dfs(i + 1, j), dfs(i, j + 1))
-
-            return dp[i][j]
-        }
-
-        return dfs(0, 0)
-    }
-}
-
-// Top down DP, Time Complexity of O(n * m) and space complexity of O(n * m)
-class Solution {
-    fun longestCommonSubsequence(t1: String, t2: String): Int {
-        val n = t1.length
-        val m = t2.length
-        val dp = Array (n + 1) { IntArray (m + 1) }
-
-        for (i in n - 1 downTo 0) {
-            for (j in m - 1 downTo 0) {
-                if (t1[i] == t2[j])
-                    dp[i][j] = 1 + dp[i + 1][j + 1]
-                else
-                    dp[i][j] = maxOf(dp[i + 1][j], dp[i][j + 1])
-            }
-        }
-
-        return dp[0][0]
-    }
-}
-
-// Optimized DP (Works both for both Top-down and Bottom-up, but here we use bottom-up approach)
-// Time Complexity of O(n * m) and space complexity of O(maxOf(n, m))
-class Solution {
-    fun longestCommonSubsequence(t1: String, t2: String): Int {
-        val m = t1.length
-        val n = t2.length
-        if (m < n) return longestCommonSubsequence(t2, t1)
-
-        var dp = IntArray (n + 1)
-
-        for (i in m downTo 0) {
-            var newDp = IntArray (n + 1)
-            for (j in n downTo 0) {
-                if (i == m || j == n) {
-                    newDp[j] = 0
-                } else if (t1[i] == t2[j]) {
-                    newDp[j] = 1 + dp[j + 1]
-                } else {
-                    newDp[j] = maxOf(dp[j], newDp[j + 1])
-                }
-            }
-            dp = newDp
-        }
-
-        return dp[0]
-    }
-}
+}
\ No newline at end of file

From 97cfe7e79ebb296a10d98385d5f553f3d1895615 Mon Sep 17 00:00:00 2001
From: jelonmusk <jelonmusk@gmail.com>
Date: Wed, 27 Nov 2024 16:50:24 +0530
Subject: [PATCH 2/4] local changes

---
 articles/design-word-search-data-structure.md |  6 +--
 hints/combination-target-sum-ii.md            | 31 +++++++++++++++
 hints/combination-target-sum.md               |  2 +-
 hints/combinations-of-a-phone-number.md       | 31 +++++++++++++++
 hints/design-word-search-data-structure.md    | 31 +++++++++++++++
 hints/implement-prefix-tree.md                | 31 +++++++++++++++
 hints/n-queens.md                             | 31 +++++++++++++++
 hints/palindrome-partitioning.md              | 31 +++++++++++++++
 hints/search-for-word-ii.md                   | 39 +++++++++++++++++++
 hints/search-for-word.md                      | 31 +++++++++++++++
 hints/subsets-ii.md                           | 31 +++++++++++++++
 11 files changed, 291 insertions(+), 4 deletions(-)
 create mode 100644 hints/combination-target-sum-ii.md
 create mode 100644 hints/combinations-of-a-phone-number.md
 create mode 100644 hints/design-word-search-data-structure.md
 create mode 100644 hints/implement-prefix-tree.md
 create mode 100644 hints/n-queens.md
 create mode 100644 hints/palindrome-partitioning.md
 create mode 100644 hints/search-for-word-ii.md
 create mode 100644 hints/search-for-word.md
 create mode 100644 hints/subsets-ii.md

diff --git a/articles/design-word-search-data-structure.md b/articles/design-word-search-data-structure.md
index 9050009e5..2771fc3f8 100644
--- a/articles/design-word-search-data-structure.md
+++ b/articles/design-word-search-data-structure.md
@@ -226,10 +226,10 @@ class WordDictionary {
 
 ### Time & Space Complexity
 
-* Time complexity: $O(1)$ for $addWord()$, $O(m * n)$ for $search()$.
+* Time complexity: $O(n)$ for $addWord()$, $O(m * n)$ for $search()$.
 * Space complexity: $O(m * n)$
 
-> Where $m$ is the number of words added and $n$ is the length of the search string.
+> Where $m$ is the number of words added and $n$ is the length of the string.
 
 ---
 
@@ -631,7 +631,7 @@ class WordDictionary {
 
 ### Time & Space Complexity
 
-* Time complexity: $O(n)$ for $addWord()$, $O(26 ^ n)$ for $search()$.
+* Time complexity: $O(n)$ for $addWord()$, $O(n)$ for $search()$.
 * Space complexity: $O(t + n)$
 
 > Where $n$ is the length of the string and $t$ is the total number of TrieNodes created in the Trie.
\ No newline at end of file
diff --git a/hints/combination-target-sum-ii.md b/hints/combination-target-sum-ii.md
new file mode 100644
index 000000000..ef8aabd56
--- /dev/null
+++ b/hints/combination-target-sum-ii.md
@@ -0,0 +1,31 @@
+<br>
+<details class="hint-accordion">  
+    <summary>Recommended Time & Space Complexity</summary>
+    <p>
+    You should aim for a solution with <code>O(n * (2^n))</code> time and <code>O(n)</code> space, where <code>n</code> is the size of the input array.
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 1</summary>
+    <p>
+    A brute-force solution would be to create a <code>hash set</code>, which is used to detect duplicates, to get combinations without duplicates. Can you think of a better way without using a <code>hash set</code>? Maybe you should sort the input array and observe the recursive calls that are responsible for duplicate combinations.
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 2</summary>
+    <p>
+    We can use backtracking to generate all combinations whose sum equals the given target. When the input array contains duplicate elements, it may result in duplicate combinations. To avoid this, we can sort the array. Why does sorting help? Because as we traverse the array from left to right, we form combinations with the current element. By skipping duplicate elements, we ensure that the same combinations are not repeated for identical elements. How can you implement this?
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 3</summary>
+    <p>
+    We recursively traverse the given array starting at index <code>i</code>, with a variable <code>sum</code> representing the sum of the picked elements. We explore elements from <code>i</code> to the end of the array and extend the recursive path if adding the current element results in <code>sum <= target</code>. When we processed an element, we backtrack and proceed to the next distinct element, skipping any similar elements in the middle to avoid duplicate combinations.
+    </p>
+</details>
\ No newline at end of file
diff --git a/hints/combination-target-sum.md b/hints/combination-target-sum.md
index cf6d8d47d..1eca71f3f 100644
--- a/hints/combination-target-sum.md
+++ b/hints/combination-target-sum.md
@@ -26,6 +26,6 @@
 <details class="hint-accordion">  
     <summary>Hint 3</summary>
     <p>
-    We recursively select elements, increasing the <code>sum</code> and appending the element to the temporary list, which tracks the chosen elements in the current path. At each step, we have the option to consider all elements in the array, but we only proceed with elements that, when added to <code>sum</code>, do not exceed the <code>target</code>. We iterate through the entire array at each step, choosing elements accordingly.
+    We recursively traverse the array starting from index <code>i</code>. At each step, we select an element from <code>i</code> to the end of the array. We extend the recursive path with elements where <code>sum <= target</code> after including that element. This creates multiple recursive paths, and we append the current list to the result whenever the base condition is met.
     </p>
 </details>
\ No newline at end of file
diff --git a/hints/combinations-of-a-phone-number.md b/hints/combinations-of-a-phone-number.md
new file mode 100644
index 000000000..7154a859d
--- /dev/null
+++ b/hints/combinations-of-a-phone-number.md
@@ -0,0 +1,31 @@
+<br>
+<details class="hint-accordion">  
+    <summary>Recommended Time & Space Complexity</summary>
+    <p>
+    You should aim for a solution with <code>O(n * (4^n))</code> time and <code>O(n)</code> space, where <code>n</code> is the length of the input string.
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 1</summary>
+    <p>
+    We can use a hash map to pair all the digits with their corresponding letters. Think of this as a decision tree, where at each step, we have a digit, and we select one of multiple characters to proceed to the next digit in the given string <code>digits</code>. Can you think of an algorithm to generate all combinations of strings? 
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 2</summary>
+    <p>
+    We can use backtracking where we select a character and process it, then backtrack to process another character. We recursively iterate on the given string with index <code>i</code>. At each step, we consider the letters from the hash map that correspond to the digit at the <code>i-th</code> index. Can you think of the base condition to stop this recursive path?
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 3</summary>
+    <p>
+    We initialize an empty string that represents the choices of the characters throughout the current recursive path. When the index <code>i</code> reaches the end of the string, we add the current string to the result list and return.
+    </p>
+</details>
\ No newline at end of file
diff --git a/hints/design-word-search-data-structure.md b/hints/design-word-search-data-structure.md
new file mode 100644
index 000000000..db99718c2
--- /dev/null
+++ b/hints/design-word-search-data-structure.md
@@ -0,0 +1,31 @@
+<br>
+<details class="hint-accordion">  
+    <summary>Recommended Time & Space Complexity</summary>
+    <p>
+    You should aim for a solution with <code>O(n)</code> time for each function call and <code>O(t + n)</code> space, where <code>n</code> is the length of the string and <code>t</code> is the total number of nodes created in the Trie.
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 1</summary>
+    <p>
+    A brute force solution would be to store each added word in a list and search linearly through the list for a word every time. This would be an <code>O(m * n)</code> solution, where <code>m</code> is the size of the list and <code>n</code> is the length of the string. Can you think of a better way? Maybe there is a tree-like data structure.
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 2</summary>
+    <p>
+    We can use a Trie to implement adding and searching for words efficiently. Adding a word follows the standard Trie insertion process. However, when searching for a word containing <code>'.'</code>, which can match any character, we need a different approach. Instead of directly matching, we consider all possible characters at the position of <code>'.'</code> and recursively check the rest of the word for each possibility. How would you implement it?
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 3</summary>
+    <p>
+    We traverse the word with index <code>i</code>, starting at the root of the Trie. For normal characters, we search as usual. When encountering a dot (<code>'.'</code>), we try all possible characters by recursively extending the search in each direction. If any path leads to a valid word, we return <code>true</code>; otherwise, we return <code>false</code>. Although we try all paths for a dot, the time complexity is still <code>O(n)</code> because there are at most two dots (<code>'.'</code>) in the word, making the complexity <code>O((26^2) * n)</code>.
+    </p>
+</details>
\ No newline at end of file
diff --git a/hints/implement-prefix-tree.md b/hints/implement-prefix-tree.md
new file mode 100644
index 000000000..f2908be7f
--- /dev/null
+++ b/hints/implement-prefix-tree.md
@@ -0,0 +1,31 @@
+<br>
+<details class="hint-accordion">  
+    <summary>Recommended Time & Space Complexity</summary>
+    <p>
+    You should aim for a solution with <code>O(n)</code> time for each function call and <code>O(t)</code> space, where <code>n</code> is the length of the given string and <code>t</code> is the total number of nodes created in the Trie.
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 1</summary>
+    <p>
+    A Trie is structured as a tree-like data structure where each node contains a hash map (or an array for fixed character sets) to store references to its child nodes, which represent characters. Each node also includes a boolean flag to indicate whether the current node marks the end of a valid word. The Trie starts with a root node that does not hold any character and serves as the entry point for all operations. The child nodes of the root and subsequent nodes represent unique characters from the words stored in the Trie, forming a hierarchical structure based on the prefixes of the words.
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 2</summary>
+    <p>
+    To insert a word, we iterate through the characters of the word with index <code>i</code>, starting at the root of the Trie as the current node. If the current node already contains <code>word[i]</code>, we continue to the next character and move to the node that <code>word[i]</code> points to. If <code>word[i]</code> is not present, we create a new node for <code>word[i]</code> and continue the process until we reach the end of the word. We mark the boolean variable as true as it is the end of the inserted word.
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 3</summary>
+    <p>
+    Searching for a word is similar to inserting, but instead of creating new nodes, we return <code>false</code> if we don't find a character in the path while iterating or if the end-of-word marker is not set to <code>true</code> when we reach the end of the word.
+    </p>
+</details>
\ No newline at end of file
diff --git a/hints/n-queens.md b/hints/n-queens.md
new file mode 100644
index 000000000..60c5f6fef
--- /dev/null
+++ b/hints/n-queens.md
@@ -0,0 +1,31 @@
+<br>
+<details class="hint-accordion">  
+    <summary>Recommended Time & Space Complexity</summary>
+    <p>
+    You should aim for a solution with <code>O(n!)</code> time and <code>O(n^2)</code> space, where <code>n</code> is the size of the given square board.
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 1</summary>
+    <p>
+    A queen can move in <code>8</code> directions, and no two queens can be in the same row or column. This means we can place one queen per row or column. We iterate column-wise and try to place a queen in each column while ensuring no other queen exists in the same row, left diagonal, or left bottom diagonal. Can you think of a recursive algorithm to find all possible combinations?
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 2</summary>
+    <p>
+    We can use backtracking to traverse through the columns with index <code>c</code> while maintaining a board that represents the current state in the recursive path. We reach the base condition when <code>c == n</code> and we add a copy of the board to the result. How would you implement this?
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 3</summary>
+    <p>
+    We initialize an empty board and recursively go through each column. For each column, we check each cell to see if we can place a queen there. We use a function to check if the cell is suitable by iterating along the left directions and verifying if the same row, left diagonal, or left bottom diagonal are free. If it is possible, we place the queen on the board, move along the recursive path, and then backtrack by removing the queen to continue to the next cell in the column.
+    </p>
+</details>
\ No newline at end of file
diff --git a/hints/palindrome-partitioning.md b/hints/palindrome-partitioning.md
new file mode 100644
index 000000000..a2d81783f
--- /dev/null
+++ b/hints/palindrome-partitioning.md
@@ -0,0 +1,31 @@
+<br>
+<details class="hint-accordion">  
+    <summary>Recommended Time & Space Complexity</summary>
+    <p>
+    You should aim for a solution with <code>O(n * (2^n))</code> time and <code>O(n)</code> space, where <code>n</code> is the length of the input string.
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 1</summary>
+    <p>
+    For a given string there are <code>2^n</code> possible partitions because at each index we have two decisions: we can either partition and start a new string, or continue without partitioning. Can you think of an algorithm to recursively traverse all combinations?
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 2</summary>
+    <p>
+    We can use backtracking to recursively traverse the string with indices <code>j</code> (start of the current substring) and <code>i</code> (current iterating index). At each step, we either skip partitioning at the current index or, if the substring from <code>j</code> to <code>i</code> is a palindrome, make a partition, update <code>j = i + 1</code>, and start a new substring. The base condition to stop the recursion is when <code>j</code> reaches the end of the string. How do you implement this?
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 3</summary>
+    <p>
+    We start with <code>j = 0</code>, <code>i = 0</code> and a temporary list which stores the substrings from the partitions. Then we recursively iterate the string with the index <code>i</code>. At each step we apply the <code>2</code> decisions accordingly. At the base condition of the recursive path, we make a copy of the current partition list and add it to the result.
+    </p>
+</details>
\ No newline at end of file
diff --git a/hints/search-for-word-ii.md b/hints/search-for-word-ii.md
new file mode 100644
index 000000000..205964c7e
--- /dev/null
+++ b/hints/search-for-word-ii.md
@@ -0,0 +1,39 @@
+<br>
+<details class="hint-accordion">  
+    <summary>Recommended Time & Space Complexity</summary>
+    <p>
+    You should aim for a solution with <code>O(m * n * 4 * (3^(t-1)) + s)</code> time and <code>O(s)</code> space, where <code>m</code> is the number of rows, <code>n</code> is the number of columns, <code>t</code> is the maximum length of any word and <code>s</code> is the sum of the lengths of all the words.
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 1</summary>
+    <p>
+    To search for a word in the grid, we can use backtracking by starting at each cell, simultaneously iterating through the word and matching the characters with the cells, recursively visiting the neighboring cells. However, if we are given a list of words and need to search for each one, it becomes inefficient to iterate through each word and run backtracking for each. Can you think of a better way? Perhaps a data structure could help with more efficient word search and insertion.
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 2</summary>
+    <p>
+   We can use a Trie to efficiently search for multiple words. After inserting all words into the Trie, we traverse the grid once. For each character in the grid, we check if it exists in the current Trie node. If not, we prune the search. If we encounter an "end of word" flag in the Trie node, we know a valid word has been found. But how can we add that word to the result list? Maybe you should think about what additional information you can store in the Trie node.
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 3</summary>
+    <p>
+    When we insert a word into the Trie, we can store the word's index. Why? Because when we want to add the word to the result list after finding a valid word, we can easily add it using the index. After adding that word, we put <code>index = -1</code> as we shouldn't add the word multiple times to the result list. How can you implement this?
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 4</summary>
+    <p>
+    We insert all the words into the Trie with their indices marked. Then, we iterate through each cell in the grid. At each cell, we start at the root of the Trie and explore all possible paths. As we traverse, we match characters in the cell with those in the Trie nodes. If we encounter the end of a word, we take the index at that node and add the corresponding word to the result list. Afterward, we unmark that index and continue exploring further paths.
+    </p>
+</details>
\ No newline at end of file
diff --git a/hints/search-for-word.md b/hints/search-for-word.md
new file mode 100644
index 000000000..9dcdf4102
--- /dev/null
+++ b/hints/search-for-word.md
@@ -0,0 +1,31 @@
+<br>
+<details class="hint-accordion">  
+    <summary>Recommended Time & Space Complexity</summary>
+    <p>
+    You should aim for a solution with <code>O(m * (4^n))</code> time and <code>O(n)</code> space, where <code>m</code> is the number of cells in the given <code>board</code> and <code>n</code> is the size of the given <code>word</code>.
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 1</summary>
+    <p>
+    As we can start at any cell on the board, we can explore all paths from that cell. Can you think of an algorithm to do so? Also, you should consider a way to avoid visiting a cell that has already been visited in the current path.
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 2</summary>
+    <p>
+    We can use a hash set to avoid revisiting a cell in the current path by inserting the <code>(row, col)</code> of the visiting cell into the hash set and exploring all paths (four directions, as we can move to four neighboring cells) from that cell. Can you think of the base condition for this recursive path? Maybe you should consider the board boundaries, and also, we can extend a path if the character at the cell matches the character in the word.
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 3</summary>
+    <p>
+    We can use backtracking, starting from each cell on the board with coordinates <code>(row, col)</code> and index <code>i</code> for the given word. We return false if <code>(row, col)</code> is out of bounds or if <code>board[row][col] != word[i]</code>. When <code>i</code> reaches the end of the word, we return true, indicating a valid path. At each step, we add <code>(row, col)</code> to a hash set to avoid revisiting cells. After exploring the four possible directions, we backtrack and remove <code>(row, col)</code> from the hash set.
+    </p>
+</details>
\ No newline at end of file
diff --git a/hints/subsets-ii.md b/hints/subsets-ii.md
new file mode 100644
index 000000000..4001ea052
--- /dev/null
+++ b/hints/subsets-ii.md
@@ -0,0 +1,31 @@
+<br>
+<details class="hint-accordion">  
+    <summary>Recommended Time & Space Complexity</summary>
+    <p>
+    You should aim for a solution as good or better than <code>O(n * (2^n))</code> time and <code>O(n)</code> space, where <code>n</code> is the size of the input array.
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 1</summary>
+    <p>
+    A brute-force solution would involve creating a hash set and inserting every subset into it. Then, converting the hash set to a list and returning it. However, this approach would require extra space of <code>O(2^n)</code>. Can you think of a better way? Maybe you should sort the input array and observe which recusive calls are resposible to make duplicate subsets.
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 2</summary>
+    <p>
+    We can use backtracking to generate subsets of an array. If the input contains duplicates, duplicate subsets may be created. To prevent this, we sort the array beforehand. For example, in <code>[1, 1, 2]</code>, sorting allows us to create subsets using the first <code>1</code> and skip the second <code>1</code>, ensuring unique subsets. How can you implement this?
+    </p>
+</details>
+
+<br>
+<details class="hint-accordion">  
+    <summary>Hint 3</summary>
+    <p>
+    We start by sorting the input array. Then, we recursively iterate through the array from left to right, extending recursive paths by including or excluding each element. To avoid duplicate subsets, we skip an element if it is the same as the previous one. Finally, we return the generated subsets as a list.
+    </p>
+</details>
\ No newline at end of file

From 1fa40e24087e4be8585f3a1c4020c204e8012d1b Mon Sep 17 00:00:00 2001
From: jelonmusk <jelonmusk@gmail.com>
Date: Wed, 27 Nov 2024 16:55:08 +0530
Subject: [PATCH 3/4] Added solution for 404.Sum of Left Leaves

---
 python/0404-sum-of-left-leaves.py | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
 create mode 100644 python/0404-sum-of-left-leaves.py

diff --git a/python/0404-sum-of-left-leaves.py b/python/0404-sum-of-left-leaves.py
new file mode 100644
index 000000000..5eacbb676
--- /dev/null
+++ b/python/0404-sum-of-left-leaves.py
@@ -0,0 +1,27 @@
+# Definition for a binary tree node.
+# class TreeNode:
+#     def __init__(self, val=0, left=None, right=None):
+#         self.val = val
+#         self.left = left
+#         self.right = right
+class Solution:
+    def sumOfLeftLeaves(self, root: Optional[TreeNode]) -> int:
+        sum = 0
+        #edge case: empty tree
+        if not root:
+            return 0
+        queue  = deque([root])
+        while queue:
+            current = queue.popleft()
+# Check if the left child is a leaf
+            if current.left and not current.left.left and not current.left.right:
+                sum+=current.left.val
+                #add children to queueu if they exist
+            if current.left:
+                queue.append(current.left)
+            if current.right:
+                queue.append(current.right)
+        return sum
+        
+
+        
\ No newline at end of file

From 0779e3d703e48c1ea220ab057e1ff3735dafc0cb Mon Sep 17 00:00:00 2001
From: jelonmusk <jelonmusk@gmail.com>
Date: Wed, 27 Nov 2024 17:15:08 +0530
Subject: [PATCH 4/4] Remove 0404-sum-of-left-leaves.py

---
 python/0404-sum-of-left-leaves.py | 27 ---------------------------
 1 file changed, 27 deletions(-)
 delete mode 100644 python/0404-sum-of-left-leaves.py

diff --git a/python/0404-sum-of-left-leaves.py b/python/0404-sum-of-left-leaves.py
deleted file mode 100644
index 5eacbb676..000000000
--- a/python/0404-sum-of-left-leaves.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# Definition for a binary tree node.
-# class TreeNode:
-#     def __init__(self, val=0, left=None, right=None):
-#         self.val = val
-#         self.left = left
-#         self.right = right
-class Solution:
-    def sumOfLeftLeaves(self, root: Optional[TreeNode]) -> int:
-        sum = 0
-        #edge case: empty tree
-        if not root:
-            return 0
-        queue  = deque([root])
-        while queue:
-            current = queue.popleft()
-# Check if the left child is a leaf
-            if current.left and not current.left.left and not current.left.right:
-                sum+=current.left.val
-                #add children to queueu if they exist
-            if current.left:
-                queue.append(current.left)
-            if current.right:
-                queue.append(current.right)
-        return sum
-        
-
-        
\ No newline at end of file
