diff --git a/.github/lc_chart.png b/.github/lc_chart.png
index 0d7e18b..2ea25a4 100644
Binary files a/.github/lc_chart.png and b/.github/lc_chart.png differ
diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml
index ce08612..80b7084 100644
--- a/.github/workflows/update.yml
+++ b/.github/workflows/update.yml
@@ -46,22 +46,20 @@ jobs:
- name: Setup dependencies via uv
if: steps.latest_commit.outputs.should_skip == 'false'
- run: uv sync --directory ./.github
+ run: uv sync
- name: Move solution files to their respective folders via lc_move.py
if: steps.latest_commit.outputs.should_skip == 'false'
id: move_step
run: |
- export PYTHONPATH=$GITHUB_WORKSPACE
- python ./scripts/lc_move.py | tee output.log
+ uv run python -B ./scripts/lc_move.py | tee output.log
echo "files_moved=$(grep '\[END\]' output.log | grep -o '[0-9]\+')" >> $GITHUB_OUTPUT
rm output.log
- name: Update progress chart and README via lc_markdown.py
if: steps.latest_commit.outputs.should_skip == 'false' && steps.move_step.outputs.files_moved != '0'
run: |
- export PYTHONPATH=$GITHUB_WORKSPACE
- python ./scripts/lc_markdown.py
+ uv run python -B ./scripts/lc_markdown.py
- name: Check for changed files
if: steps.latest_commit.outputs.should_skip == 'false' && steps.move_step.outputs.files_moved != '0'
@@ -77,7 +75,7 @@ jobs:
if ! git diff --cached --quiet; then
git config user.name $GIT_USERNAME
git config user.email $GIT_EMAIL
- git commit -m "[auto] Update README and move solution files (R.N: ${{ github.run_number }})" \
+ git commit -m "[auto] update README and move solution files (R.N: ${{ github.run_number }})" \
-m "Push commit: ${{ github.sha }} " \
-m "Files moved: ${{ steps.move_step.outputs.files_moved }}" \
-m "Workflow: ${{ github.workflow }}" \
diff --git a/README.md b/README.md
index 8a3db4e..9929ab5 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,18 @@
Reetkode
========
-
-My personal collection of [LeetCode](https://leetcode.com/) solutions done in multiple languages, shared for learning and reference. π
-
-This README is generated using the following custom-built **Python** scripts: [`lc_markdown.py`](scripts/lc_markdown.py), [`lc_chart.py`](scripts/lc_chart.py), and [`lc_stats.py`](scripts/lc_stats.py), as part of an automated **GitHub Actions** [workflow](.github/workflows/main.yml) which runs during checks within pull requests made to the `main` branch, updating the README content and organising solution files prior to merging.
-
-The following diagram illustrates the action flow of using these [scripts](scripts/):
-
-
-***
-## Progress Overview
-
-
+
+My personal collection of [LeetCode](https://leetcode.com/) solutions done in multiple languages, shared for learning and reference.
+
+This README is generated using the following custom-built **Python** scripts: [`lc_markdown.py`](scripts/lc_markdown.py), [`lc_chart.py`](scripts/lc_chart.py), and [`lc_stats.py`](scripts/lc_stats.py), as part of an automated **GitHub Actions** [workflow](.github/workflows/update.yml) which runs during checks within pull requests made to the `main` branch, updating the README content and organising solution files prior to merging.
+
+The following diagram illustrates the action flow of using these [scripts](scripts/):
+
+
+***
+## Progress Overview
+
+
## Solutions Directory
|ID|Difficulty|Type|Title|Solutions|
| :--- | :--- | :--- | :--- | :--- |
@@ -46,6 +46,7 @@ The following diagram illustrates the action flow of using these [scripts](scrip
|**196**|π© Easy|π’οΈ|[Delete Duplicate Emails](https://lcid.cc/196)|
|
|**197**|π© Easy|π’οΈ|[Rising Temperature](https://lcid.cc/197)|
|
|**217**|π© Easy|βοΈ|[Contains Duplicate](https://lcid.cc/217)|
|
+|**230**|π¨ Medium|βοΈ|[Kth Smallest Element In A Bst](https://lcid.cc/230)|
|
|**238**|π¨ Medium|βοΈ|[Product Of Array Except Self](https://lcid.cc/238)|
|
|**242**|π© Easy|βοΈ|[Valid Anagram](https://lcid.cc/242)|
|
|**271**|π¨ Medium|βοΈ|[Encode And Decode Strings](https://lcid.cc/271)|
|
@@ -53,6 +54,7 @@ The following diagram illustrates the action flow of using these [scripts](scrip
|**334**|π¨ Medium|βοΈ|[Increasing Triplet Subsequence](https://lcid.cc/334)|
|
|**345**|π© Easy|βοΈ|[Reverse Vowels Of A String](https://lcid.cc/345)|
|
|**347**|π¨ Medium|βοΈ|[Top K Frequent Elements](https://lcid.cc/347)|
|
+|**366**|π¨ Medium|βοΈ|[Find Leaves Of Binary Tree](https://lcid.cc/366)|
|
|**387**|π© Easy|βοΈ|[First Unique Character String](https://lcid.cc/387)|
|
|**389**|π© Easy|βοΈ|[Find The Difference](https://lcid.cc/389)|
|
|**392**|π© Easy|βοΈ|[Is Subsequence](https://lcid.cc/392)|
|
@@ -112,6 +114,7 @@ The following diagram illustrates the action flow of using these [scripts](scrip
|**1211**|π© Easy|π’οΈ|[Queries Quality And Percentage](https://lcid.cc/1211)|
|
|**1232**|π© Easy|βοΈ|[Check If It Is A Straight Line](https://lcid.cc/1232)|
|
|**1251**|π© Easy|π’οΈ|[Average Selling Price](https://lcid.cc/1251)|
|
+|**1255**|π₯ Hard|βοΈ|[Maximum Score Words Formed By Letters](https://lcid.cc/1255)|
|
|**1280**|π© Easy|π’οΈ|[Students And Examinations](https://lcid.cc/1280)|
|
|**1321**|π¨ Medium|π’οΈ|[Restaurant Growth](https://lcid.cc/1321)|
|
|**1327**|π© Easy|π’οΈ|[List The Products Ordered In A Period](https://lcid.cc/1327)|
|
@@ -175,6 +178,7 @@ The following diagram illustrates the action flow of using these [scripts](scrip
|**1978**|π© Easy|π’οΈ|[Employees Whose Manager Left The Company](https://lcid.cc/1978)|
|
|**1995**|π© Easy|βοΈ|[Count Special Quadruplets](https://lcid.cc/1995)|
|
|**2042**|π© Easy|βοΈ|[Check If Numbers Are Ascending In A Sentence](https://lcid.cc/2042)|
|
+|**2044**|π¨ Medium|βοΈ|[Count Number Of Maximum Bitwise-or Subsets](https://lcid.cc/2044)|
|
|**2073**|π© Easy|βοΈ|[Time Needed To Buy Tickets](https://lcid.cc/2073)|
|
|**2109**|π¨ Medium|βοΈ|[Adding Spaces To A String](https://lcid.cc/2109)|
|
|**2124**|π© Easy|βοΈ|[Check If All As Appears Before All Bs](https://lcid.cc/2124)|
|
@@ -234,6 +238,7 @@ The following diagram illustrates the action flow of using these [scripts](scrip
|**3163**|π¨ Medium|βοΈ|[String Compression III](https://lcid.cc/3163)|
|
|**3173**|π© Easy|βοΈ|[Bitwise Or Of Adjacent Elements](https://lcid.cc/3173)|
|
|**3174**|π© Easy|βοΈ|[Clear Digits](https://lcid.cc/3174)|
|
+|**3211**|π¨ Medium|βοΈ|[Generate Binary Strings Without Adjacent Zeros](https://lcid.cc/3211)|
|
|**3220**|π¨ Medium|π’οΈ|[Odd And Even Transactions](https://lcid.cc/3220)|
|
|**3223**|π¨ Medium|βοΈ|[Minimum Length Of String After Operations](https://lcid.cc/3223)|
|
|**3300**|π© Easy|βοΈ|[Minimum Element After Replacement With Digit Sum](https://lcid.cc/3300)|
|
@@ -254,6 +259,8 @@ The following diagram illustrates the action flow of using these [scripts](scrip
|**3536**|π© Easy|βοΈ|[Maximum Product Of Two Digits](https://lcid.cc/3536)|
|
|**3541**|π© Easy|βοΈ|[Find Most Frequent Vowel And Consonant](https://lcid.cc/3541)|
|
|**3550**|π© Easy|βοΈ|[Smallest Index With Digit Sum Equal To Index](https://lcid.cc/3550)|
|
-
-
+|**3616**|π¨ Medium|βοΈ|[Number Of Student Replacements](https://lcid.cc/3616)|
|
+|**3631**|π¨ Medium|βοΈ|[Sort Threats By Severity And Exploitability](https://lcid.cc/3631)|
|
+
+
####
[πReturn to top](#reetkode)
\ No newline at end of file
diff --git a/.github/pyproject.toml b/pyproject.toml
similarity index 100%
rename from .github/pyproject.toml
rename to pyproject.toml
diff --git a/python/lch_p1255_maximum_score_words_formed_by_letters.py b/python/lch_p1255_maximum_score_words_formed_by_letters.py
new file mode 100644
index 0000000..7a3f029
--- /dev/null
+++ b/python/lch_p1255_maximum_score_words_formed_by_letters.py
@@ -0,0 +1,63 @@
+"""
+LCH 1255. Maximum Score Words Formed by Letters
+
+Given a list of words, list of single letters (might be repeating) and score of every character.
+
+Return the maximum score of any valid set of words formed by using the given letters (words[i] cannot be used two or more times).
+
+It is not necessary to use all characters in letters and each letter can only be used once. Score of letters 'a', 'b', 'c', ... ,'z' is given by score[0], score[1], ... , score[25] respectively
+
+Constraints:
+- 1 <= words.length <= 14
+- 1 <= words[i].length <= 15
+- 1 <= letters.length <= 100
+- letters[i].length == 1
+- score.length == 26
+- 0 <= score[i] <= 10
+- words[i], letters[i] contains only lower case English letters.
+
+Topics:
+- Array
+- String
+- Dynamic Programming
+- Backtracking
+- Bit Manipulation
+- Bitmask
+"""
+
+
+class Solution:
+ def recurse(self, words: List[str], letter_freq: List[int], score: List[int], start: int, curr_score: int) -> int:
+ max_score = curr_score
+
+ for i in range(start, len(words)):
+ word = words[i]
+ word_letter_freq = [0] * 26
+ for ch in word: word_letter_freq[ord(ch) - ord('a')] += 1
+
+ can_create = True
+ temp_score = 0
+ temp_letter_freq = list(letter_freq)
+
+ for j in range(26):
+ if word_letter_freq[j] > temp_letter_freq[j]:
+ can_create = False
+ break
+
+ temp_score += (word_letter_freq[j] * score[j])
+ temp_letter_freq[j] -= word_letter_freq[j]
+
+ if can_create:
+ max_score = max(max_score, self.recurse(words, temp_letter_freq, score, i + 1, curr_score + temp_score))
+
+ return max_score
+
+ def maxScoreWords(self, words: List[str], letters: List[str], score: List[int]) -> int:
+ letter_freq = [0] * 26
+ for letter in letters: letter_freq[ord(letter) - ord('a')] += 1
+
+ return self.recurse(words, letter_freq, score, 0, 0)
+
+
+# Time Complexity: O(n * 2^n) - 11 ms -> 71.76%
+# Space Complexity: O(1) - 17.81 MB -> 73.01%
\ No newline at end of file
diff --git a/python/lcm_p0230_kth_smallest_element_in_a_bst.py b/python/lcm_p0230_kth_smallest_element_in_a_bst.py
new file mode 100644
index 0000000..8cac9c5
--- /dev/null
+++ b/python/lcm_p0230_kth_smallest_element_in_a_bst.py
@@ -0,0 +1,43 @@
+"""
+LCM 230. Kth Smallest Element in a BST
+
+Given the root of a binary search tree, and an integer k, return the kth smallest value (1-indexed) of all the values of the nodes in the tree.
+
+Constraints:
+- The number of nodes in the tree is n.
+- 1 <= k <= n <= 10^4
+- 0 <= Node.val <= 10^4
+
+Topics:
+- Tree
+- Depth-First Search
+- Binary Search Tree
+- Binary Tree
+"""
+
+
+# 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 dfs(self, node: Optional[TreeNode], arr: list[int]) -> list[int]:
+ if node.left:
+ self.dfs(node.left, arr)
+
+ arr.append(node.val)
+
+ if node.right:
+ self.dfs(node.right, arr)
+
+ return arr
+
+ def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:
+ arr = self.dfs(root, [])
+ return arr[k - 1]
+
+
+# Time Complexity: O(n) - 4 ms -> 16.15%
+# Space Complexity: O(n) - 21.20 MB -> 27.36%
diff --git a/python/lcm_p0366_find_leaves_of_binary_tree.py b/python/lcm_p0366_find_leaves_of_binary_tree.py
new file mode 100644
index 0000000..85d5746
--- /dev/null
+++ b/python/lcm_p0366_find_leaves_of_binary_tree.py
@@ -0,0 +1,51 @@
+"""
+LCM 366. Find Leaves of Binary Tree (Premium)
+
+Given the root of a binary tree, collect a tree's nodes as if you were doing this:
+- Collect all the leaf nodes.
+- Remove all the leaf nodes.
+- Repeat until the tree is empty.
+
+Constraints:
+- The number of nodes in the tree is in the range [1, 100].
+- -100 <= Node.val <= 100
+
+Topics:
+- Tree
+- Depth-First Search
+- Binary Tree
+"""
+
+
+# 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:
+ # group nodes based on the maximum depth of their left and right subtrees
+ def findLeaves(self, root: Optional[TreeNode]) -> List[List[int]]:
+ mapper = {}
+
+ def dfs(node: Optional[TreeNode], level: int) -> int:
+ if not node:
+ return level
+
+ left = dfs(node.left, level)
+ right = dfs(node.right, level)
+ level = max(left, right)
+
+ if level not in mapper:
+ mapper[level] = [node.val]
+ else:
+ mapper[level].append(node.val)
+
+ return level + 1
+
+ dfs(root, 0)
+ return list(mapper.values())
+
+
+# Time Complexity: O(n) - 0 ms -> 100.00%
+# Space Complexity: O(n) - 17.28 MB -> 42.81%
diff --git a/python/lcm_p2044_count_number_of_maximum_bitwise-or_subsets.py b/python/lcm_p2044_count_number_of_maximum_bitwise-or_subsets.py
new file mode 100644
index 0000000..54e7c97
--- /dev/null
+++ b/python/lcm_p2044_count_number_of_maximum_bitwise-or_subsets.py
@@ -0,0 +1,44 @@
+"""
+LCM 2044. Count Number of Maximum Bitwise-OR Subsets
+
+Given an integer array nums, find the maximum possible bitwise OR of a subset of nums and return the number of different non-empty subsets with the maximum bitwise OR.
+
+An array a is a subset of an array b if a can be obtained from b by deleting some (possibly zero) elements of b.
+Two subsets are considered different if the indices of the elements chosen are different.
+
+The bitwise OR of an array a is equal to a[0] OR a[1] OR ... OR a[a.length - 1] (0-indexed).
+
+Constraints:
+- 1 <= nums.length <= 16
+- 1 <= nums[i] <= 10^5
+
+Topics:
+- Array
+- Backtracking
+- Bit Manipulation
+- Enumeration
+"""
+
+
+class Solution:
+ def countMaxOrSubsets(self, nums: List[int]) -> int:
+ max_bitwise_or = reduce(lambda x, y: x | y, nums)
+ n = len(nums)
+
+ def backtrack(prev: int, start_idx: int) -> int:
+ count = 0
+
+ for i in range(start_idx, n):
+ curr = prev | nums[i]
+ if curr == max_bitwise_or:
+ count += 1
+
+ count += backtrack(curr, i + 1)
+
+ return count
+
+ return backtrack(0, 0)
+
+
+# Time Complexity: O(2^n) - 271 ms -> 38.30%
+# Space Complexity: O(1) - 17.71 MB -> 72.24%
diff --git a/python/lcm_p3211_generate_binary_strings_without_adjacent_zeros.py b/python/lcm_p3211_generate_binary_strings_without_adjacent_zeros.py
new file mode 100644
index 0000000..a397bad
--- /dev/null
+++ b/python/lcm_p3211_generate_binary_strings_without_adjacent_zeros.py
@@ -0,0 +1,39 @@
+"""
+LCM 3211. Generate Binary Strings Without Adjacent Zeros
+
+You are given a positive integer n.
+
+A binary string x is valid if all substrings of x of length 2 contain at least one "1".
+
+Return all valid strings with length n, in any order.
+
+Constraints:
+- 1 <= n <= 18
+
+Topics:
+- String
+- Backtracking
+- Bit Manipulation
+"""
+
+
+class Solution:
+ def dfs(self, size: int, s: str, lst: list) -> List[str]:
+ if size == len(s):
+ lst.append(s)
+ return
+
+ for i in range(2):
+ if i == 0 and s and s[-1] == "0":
+ continue
+
+ self.dfs(size, s + str(i), lst)
+
+ return lst
+
+ def validStrings(self, n: int) -> List[str]:
+ return self.dfs(n, "", [])
+
+
+# Time Complexity: O(2^n) - 63 ms -> 26.49%
+# Space Complexity: O(2^n) - 19.08 MB -> 83.41%
\ No newline at end of file
diff --git a/python/lcm_p3616_number_of_student_replacements.py b/python/lcm_p3616_number_of_student_replacements.py
new file mode 100644
index 0000000..a23067f
--- /dev/null
+++ b/python/lcm_p3616_number_of_student_replacements.py
@@ -0,0 +1,36 @@
+"""
+LCM 3616. Number of Student Replacements (Premium)
+
+You are given an integer array ranks where ranks[i] represents the rank of the ith student arriving in order. A lower number indicates a better rank.
+
+Initially, the first student is selected by default.
+
+A replacement occurs when a student with a strictly better rank arrives and replaces the current selection.
+
+Return the total number of replacements made.
+
+Constraints:
+- 1 <= ranks.length <= 10^5ββββ
+- 1 <= ranks[i] <= 10^5
+
+Topics:
+- Array
+- Simulation
+"""
+
+
+class Solution:
+ def totalReplacements(self, ranks: List[int]) -> int:
+ curr = ranks[0]
+ ctr = 0
+
+ for i in range(1, len(ranks)):
+ if ranks[i] < curr:
+ curr = ranks[i]
+ ctr += 1
+
+ return ctr
+
+
+# Time Complexity: O(n) - 37 ms -> 24.00%
+# Space Complexity: O(1) - 28.70 MB -> 58.40%
diff --git a/python/lcm_p3631_sort_threats_by_severity_and_exploitability.py b/python/lcm_p3631_sort_threats_by_severity_and_exploitability.py
new file mode 100644
index 0000000..f870c04
--- /dev/null
+++ b/python/lcm_p3631_sort_threats_by_severity_and_exploitability.py
@@ -0,0 +1,37 @@
+"""
+LCM 3631. Sort Threats by Severity and Exploitability (Premium)
+
+You are given a 2D integer array threats, where each threats[i] = [IDi, seviβ, expi]
+
+ - IDi: Unique identifier of the threat.
+ - sevi: Indicates the severity of the threat.
+ - expi: Indicates the exploitability of the threat.
+
+The score of a threat i is defined as: score = 2 Γ sevi + expi
+
+Your task is to return threats sorted in descending order of score.
+
+If multiple threats have the same score, sort them by ascending IDβββββββ.
+
+Constraints:
+- 1 <= threats.length <= 10^5
+- threats[i] == [IDi, sevi, expi]
+- 1 <= IDi <= 10^6
+- 1 <= sevi <= 10^9
+- 1 <= expi <= 10^9
+- All IDi are unique
+
+Topics:
+- Array
+- Sorting
+"""
+
+
+class Solution:
+ def sortThreats(self, threats: List[List[int]]) -> List[List[int]]:
+ threats.sort(key=lambda threat: (-(2 * threat[1] + threat[2]), threat[0]))
+ return threats
+
+
+# Time Complexity: O(n log n) - 227 ms -> 74.14%
+# Space Complexity: O(1) - 63.53 MB -> 81.03%
diff --git a/scripts/lc_constants.py b/scripts/lc_constants.py
index bbc4e58..87d23f3 100644
--- a/scripts/lc_constants.py
+++ b/scripts/lc_constants.py
@@ -4,7 +4,7 @@
REETKODE_FLOW_URL = ".github/lc_flow.png?"
LEETCODE_PROBLEM_URL = "https://lcid.cc/{id}"
-IGNORED_DIRS = (".git", ".github", "icons", "scripts")
+IGNORED_DIRS = (".venv", ".git", ".github", "icons", "scripts")
ACCEPTED_FILETYPES_MAP = {
".c": "c",
".cpp": "cpp",
diff --git a/scripts/lc_markdown.py b/scripts/lc_markdown.py
index 3719adf..6c3fa1b 100644
--- a/scripts/lc_markdown.py
+++ b/scripts/lc_markdown.py
@@ -100,11 +100,11 @@ def markdown_leetcode() -> None:
try:
readme_file = MdUtils(file_name="README.md", title="Reetkode")
- readme_file.new_line(text="My personal collection of [LeetCode](https://leetcode.com/) solutions done in multiple languages, shared for learning and reference. π")
+ readme_file.new_line(text="My personal collection of [LeetCode](https://leetcode.com/) solutions done in multiple languages, shared for learning and reference.")
readme_file.new_line()
readme_file.new_line(text=
"This README is generated using the following custom-built **Python** scripts: [`lc_markdown.py`](scripts/lc_markdown.py), [`lc_chart.py`](scripts/lc_chart.py), and [`lc_stats.py`](scripts/lc_stats.py), " +
- "as part of an automated **GitHub Actions** [workflow](.github/workflows/main.yml) which runs during checks within pull requests made to the `main` branch, updating the README content and organising solution files prior to merging."
+ "as part of an automated **GitHub Actions** [workflow](.github/workflows/update.yml) which runs during checks within pull requests made to the `main` branch, updating the README content and organising solution files prior to merging."
)
readme_file.new_line()
diff --git a/.github/uv.lock b/uv.lock
similarity index 100%
rename from .github/uv.lock
rename to uv.lock