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/): - -![Script flow](.github/lc_flow.png?) -*** -## Progress Overview -![LeetCode stats](.github/lc_chart.png?) - + +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/): + +![Script flow](.github/lc_flow.png?) +*** +## Progress Overview +![LeetCode stats](.github/lc_chart.png?) + ## 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