From 134d20f2279a5194d86aa72823578ff430eaf0ac Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 9 Mar 2025 22:50:53 -0700 Subject: [PATCH 001/158] Converts to Python --- .editorconfig | 10 +- .flake8 | 2 + .gitattributes | 2 +- .github/workflows/build-and-test.yml | 34 +- .gitignore | 11 +- .prettierignore | 18 - .vscode/settings.json | 3 - README.md | 40 +- eslint.config.mjs | 153 - package-lock.json | 5442 ----------------- package.json | 49 - poetry.lock | 377 ++ prettier.config.mjs | 16 - pyproject.toml | 33 + script/clean.mjs | 4 - scripts.py | 24 + .../best-time-to-buy-and-sell-stock-ii.ts | 49 - src/array/buildings-with-an-ocean-view.ts | 38 - src/array/design-hit-counter.ts | 69 - .../dot-product-of-two-sparse-vectors.ts | 69 - src/array/group-anagrams.ts | 38 - src/array/longest-consecutive-sequence.ts | 60 - src/array/max-chunks-to-make-sorted.ts | 37 - src/array/maximum-swap.ts | 75 - src/array/merge-sorted-array.ts | 72 - src/array/pascals-triangle.ts | 41 - src/array/top-k-frequent-elements.ts | 36 - src/array/two-sum.ts | 40 - src/heap/README.md | 11 - src/heap/common/binary-search-max-heap.ts | 78 - src/heap/common/simple-max-heap.ts | 63 - src/heap/furthest-building-you-can-reach.ts | 79 - src/heap/kth-largest-element.ts | 60 - src/heap/minimize-deviation-in-array.ts | 77 - src/heap/numbers-of-orders-in-the-backlog.ts | 150 - src/leetcode/__init__.py | 0 .../best_time_to_buy_and_sell_stock_ii.py | 48 + .../array/buildings_with_an_ocean_view.py | 36 + src/leetcode/array/design_hit_counter.py | 46 + .../dot_product_of_two_sparse_vectors.py | 49 + src/leetcode/array/group_anagrams.py | 32 + .../array/longest_consecutive_sequence.py | 59 + .../array/max_chunks_to_make_sorted.py | 39 + src/leetcode/array/maximum_swap.py | 66 + src/leetcode/array/merge_sorted_array.py | 68 + src/leetcode/array/pascals_triangle.py | 33 + src/leetcode/array/top_k_frequent_elements.py | 37 + src/leetcode/array/two_sum.py | 42 + .../binary_search}/README.md | 0 ...ast-position-of-element-in-sorted-array.ts | 0 .../binary_search}/find-peak-element.ts | 0 ...-the-smallest-divisor-given-a-threshold.ts | 0 .../k-th-missing-positive-number.ts | 0 ...-th-smallest-element-in-a-sorted-matrix.ts | 0 .../longest-increasing-subsequence.ts | 0 .../median-of-two-sorted-arrays.ts | 0 .../search-in-rotated-sorted-array.ts | 0 .../dynamic_programming}/README.md | 0 .../arithmetic-slices-ii-subsequences.ts | 0 .../dynamic_programming}/max-subarray.ts | 0 .../dynamic_programming}/word-break-i.ts | 0 .../graph/add-edges-to-make-degrees-even.ts | 0 src/{ => leetcode}/graph/bus-routes.ts | 0 .../graph/nested-list-weighted-sum-ii.ts | 0 .../graph/nested-list-weighted-sum.ts | 0 src/{ => leetcode}/graph/number-of-islands.ts | 0 .../graph/parallel-job-scheduling.ts | 0 .../graph/shortest-path-in-binary-matrix.ts | 0 ...est-greater-multiple-made-of-two-digits.ts | 0 src/{ => leetcode}/graph/word-search-i.ts | 0 src/{ => leetcode}/graph/word-search-ii.ts | 0 src/leetcode/heap/README.md | 11 + .../heap/furthest_building_you_can_reach.py | 70 + .../heap/k-closest-points-to-origin.ts | 0 src/leetcode/heap/kth_largest_element.py | 55 + src/{ => leetcode}/heap/max-stack.ts | 0 .../heap/merge-k-sorted-lists.ts | 0 .../heap/minimize_deviation_in_array.py | 78 + .../heap/number_of_orders_in_the_backlog.py | 132 + src/{ => leetcode}/heap/task-scheduler.ts | 0 .../interval/interval-list-intersections.ts | 0 src/{ => leetcode}/interval/meeting-rooms.ts | 0 .../interval/meeting-scheduler.ts | 0 .../interval/merge-intervals.ts | 0 .../linked_list}/README.md | 0 .../linked_list}/add-two-numbers.ts | 0 .../linked_list}/all-one.ts | 0 .../linked_list}/common/list-node.ts | 0 .../linked_list}/common/nested-integer.ts | 0 .../linked_list}/common/random-node.ts | 0 .../copy-list-with-random-pointer.ts | 0 .../linked_list}/lru-cache.ts | 0 .../linked_list}/merge-two-sorted-lists.ts | 0 .../remove-nth-node-from-end-of-list.ts | 0 src/{ => leetcode}/math/number-of-one-bits.ts | 0 src/{ => leetcode}/math/pow-x-n.ts | 0 src/{ => leetcode}/math/reverse-integer.ts | 0 src/{ => leetcode}/math/rotate-image.ts | 0 src/{ => leetcode}/math/sequential-digits.ts | 0 .../matrix/robot-bounded-in-circle.ts | 0 .../matrix/valid-word-square.ts | 0 .../prefix_sum}/README.md | 0 .../prefix_sum}/continuous-subarray-sum.ts | 0 .../max-sum-obtained-of-any-permutation.ts | 0 .../product-of-array-except-self.ts | 0 .../prefix_sum}/random-pick-with-weight.ts | 0 .../prefix_sum}/range-sum-query-immutable.ts | 0 .../prefix_sum}/running-sum-of-1d-array.ts | 0 .../prefix_sum}/subarray-sum-equals-k.ts | 0 .../recursion/generate_parentheses.py | 49 + ...umber-of-operations-with-the-same-score.ts | 0 .../recursion/optimal-account-balancing.ts | 0 src/{ => leetcode}/recursion/permutations.ts | 0 src/{ => leetcode}/recursion/subsets.ts | 0 src/{ => leetcode}/recursion/word-break-ii.ts | 0 .../sliding_window}/README.md | 0 .../best-time-to-buy-and-sell-stock-i.ts | 0 ...unt-subarrays-where-max-element-appears.ts | 0 .../length-of-longest-substring.ts | 0 .../longest-continuous-subarray.ts | 0 .../max-sum-distinct-subarray-of-size-k.ts | 0 .../minimum-window-substring.ts | 0 .../moving-average-from-data-stream.ts | 0 .../sliding_window}/repeated-dna-sequences.ts | 0 src/{ => leetcode}/stack/README.md | 0 .../stack/basic-calculator-ii.ts | 0 src/{ => leetcode}/stack/basic-calculator.ts | 0 .../key-value-store-nested-transactions.ts | 0 .../stack/longest-absolute-file-path.ts | 0 .../stack/max-chunks-to-make-sorted-ii.ts | 0 src/{ => leetcode}/stack/min-stack.ts | 0 .../minimum-add-to-make-valid-parentheses.ts | 0 ...inimum-remove-to-make-valid-parentheses.ts | 0 src/{ => leetcode}/stack/simplify-path.ts | 0 src/{ => leetcode}/stack/valid-number.ts | 0 src/leetcode/stack/valid_parentheses.py | 40 + .../string/find-the-closest-palindrome.ts | 0 src/{ => leetcode}/string/int-to-roman.ts | 0 .../string/integer-to-english-words.ts | 0 .../letter-combinations-phone-number.ts | 0 .../string/longest-palindromic-substring.ts | 0 .../string/palindrome-number.ts | 0 .../remove-all-adjacent-duplicates-i.ts | 0 .../remove-all-adjacent-duplicates-ii.ts | 0 .../string/reverse-words-in-a-string.ts | 0 src/{ => leetcode}/string/roman-to-int.ts | 0 .../time-needed-to-rearrange-binary-string.ts | 0 .../string/valid_word_abbreviation.py | 67 + .../string/vowel-spellchecker.ts | 0 .../tree/binary-tree-level-order-traversal.ts | 0 .../tree/binary-tree-right-side-view.ts | 0 .../binary-tree-vertical-order-traversal.ts | 0 src/{ => leetcode}/tree/common/parent-node.ts | 0 src/{ => leetcode}/tree/common/quad-tree.ts | 0 src/{ => leetcode}/tree/common/tree-node.ts | 0 .../tree/construct-quad-tree.ts | 0 .../tree/design-in-memory-file-system.ts | 0 .../tree/diameter-of-binary-tree.ts | 0 ...st-common-ancestor-of-a-binary-tree-iii.ts | 0 ...lowest-common-ancestor-of-a-binary-tree.ts | 0 src/{ => leetcode}/tree/range-sum-of-bst.ts | 0 src/{ => leetcode}/tree/same-tree.ts | 0 .../tree/sum-root-to-leaf-numbers.ts | 0 .../two_pointers}/README.md | 0 src/leetcode/two_pointers/bag_of_tokens.py | 69 + .../container-with-most-water.ts | 0 .../two_pointers}/longest-common-prefix.ts | 0 ...ngest-substring-without-repeating-chars.ts | 0 .../remove-duplicates-from-sorted-array.ts | 0 .../two_pointers}/remove-element.ts | 0 .../two_pointers}/string-compression.ts | 0 .../two_pointers}/three-sum-closest.ts | 0 .../two_pointers}/three-sum.ts | 0 .../two_pointers}/trapping-rain-water.ts | 0 .../two-sum-input-array-sorted.ts | 0 .../two_pointers}/valid-palindrome-ii.ts | 0 .../two_pointers}/valid-palindrome.ts | 0 .../two_pointers}/valid-triangle-number.ts | 0 src/recursion/generate-parentheses.ts | 54 - src/stack/valid-parentheses.ts | 44 - src/string/valid-word-abbreviation.ts | 78 - src/two-pointer/bag-of-tokens.ts | 76 - .../__snapshots__/group-anagrams.test.ts.snap | 18 - ...best-time-to-buy-and-sell-stock-ii.test.ts | 11 - .../buildings-with-an-ocean-view.test.ts | 7 - test/array/design-hit-counter.test.ts | 18 - .../dot-product-of-two-sparse-vectors.test.ts | 10 - test/array/group-anagrams.test.ts | 7 - .../longest-consecutive-sequence.test.ts | 7 - test/array/max-chunks-to-make-sorted.test.ts | 7 - test/array/maximum-swap.test.ts | 15 - test/array/merge-sorted-array.test.ts | 25 - test/array/pascals-triangle.test.ts | 7 - .../product-of-array-except-self.test.ts | 8 - test/array/top-k-frequent-elements.test.ts | 7 - test/array/two-sum.test.ts | 9 - .../furthest-building-you-can-reach.test.ts | 18 - test/heap/kth-largest-element.test.ts | 20 - test/heap/minimize-deviation-in-array.test.ts | 21 - .../numbers-of-orders-in-the-backlog.test.ts | 65 - test/jest.config.mjs | 12 - ...best_time_to_buy_and_sell_stock_ii_test.py | 16 + .../buildings_with_an_ocean_view_test.py | 7 + .../leetcode/array/design_hit_counter_test.py | 17 + .../dot_product_of_two_sparse_vectors_test.py | 8 + test/leetcode/array/group_anagrams_test.py | 8 + .../longest_consecutive_sequence_test.py | 8 + .../array/max_chunks_to_make_sorted_test.py | 8 + test/leetcode/array/maximum_swap_test.py | 16 + .../leetcode/array/merge_sorted_array_test.py | 26 + test/leetcode/array/pascals_triangle_test.py | 8 + .../snapshots/snap_group_anagrams_test.py | 10 + .../snapshots/snap_pascals_triangle_test.py | 10 + .../array/top_k_frequent_elements_test.py | 8 + test/leetcode/array/two_sum_test.py | 16 + ...osition-of-element-in-sorted-array.test.ts | 0 .../binary_search}/find-peak-element.test.ts | 0 ...smallest-divisor-given-a-threshold.test.ts | 0 .../k-th-missing-positive-number.test.ts | 0 ...mallest-element-in-a-sorted-matrix.test.ts | 0 .../longest-increasing-subsequence.test.ts | 0 .../median-of-two-sorted-arrays.test.ts | 0 .../search-in-rotated-sorted-array.test.ts | 0 .../arithmetic-slices-ii-subsequences.test.ts | 0 .../dynamic_programming}/max-subarray.test.ts | 0 .../dynamic_programming}/word-break-i.test.ts | 0 .../graph/__data__/bus-routes.test.json | 0 .../parallel-job-scheduling.test.ts.snap | 0 .../__snapshots__/word-search-ii.test.ts.snap | 0 .../add-edges-to-make-degrees-even.test.ts | 0 test/{ => leetcode}/graph/bus-routes.test.ts | 0 .../graph/nested-list-weighted-sum-ii.test.ts | 0 .../graph/nested-list-weighted-sum.test.ts | 0 .../graph/number-of-islands.test.ts | 0 .../graph/parallel-job-scheduling.test.ts | 0 .../shortest-path-in-binary-matrix.test.ts | 0 ...reater-multiple-made-of-two-digits.test.ts | 0 .../graph/word-search-i.test.ts | 0 .../graph/word-search-ii.test.ts | 0 .../__data__/kth-largest-element.test.json | 0 .../furthest_building_you_can_reach_test.py | 16 + .../heap/k-closest-points-to-origin.test.ts | 0 .../leetcode/heap/kth_largest_element_test.py | 12 + .../heap/minimize_deviation_in_array_test.py | 16 + .../number_of_orders_in_the_backlog_test.py | 44 + .../heap/task-scheduler.test.ts | 0 .../interval-list-intersection.test.ts | 0 .../interval/meeting-rooms.test.ts | 0 .../interval/meeting-scheduler.test.ts | 0 .../interval/merge-intervals.test.ts | 0 .../linked_list}/add-two-numbers.test.ts | 0 .../linked_list}/all-one.test.ts | 0 .../linked_list}/lru-cache.test.ts | 0 .../linked_list}/merge-k-sorted-lists.test.ts | 0 .../merge-two-sorted-lists.test.ts | 0 .../math/__data__/task-scheduler.test.json | 0 .../sequential-digits.test.ts.snap | 0 .../math/number-of-one-bits.test.ts | 0 test/{ => leetcode}/math/pow-x-n.test.ts | 0 .../math/reverse-integer.test.ts | 0 test/{ => leetcode}/math/rotate-image.test.ts | 0 .../math/sequential-digits.test.ts | 0 .../matrix/robot-bounded-in-circle.test.ts | 0 .../matrix/valid-word-square.test.ts | 0 .../continuous-subarray-sum.test.ts | 0 ...ax-sum-obtained-of-any-permutation.test.ts | 0 .../range-sum-query-immutable.test.ts | 0 .../running-sum-of-1d-array.test.ts | 0 .../prefix_sum}/subarray-sum-equals-k.test.ts | 0 .../recursion/generate-parentheses.test.ts | 0 .../recursion/generate_parentheses_test.py | 16 + ...-of-operations-with-the-same-score.test.ts | 0 .../optimal-account-balancing.test.ts | 0 .../recursion/permutations.test.ts | 0 .../snap_generate_parentheses_test.py | 27 + test/{ => leetcode}/recursion/subsets.test.ts | 0 .../recursion/word-break-ii.test.ts | 0 ...arrays-where-max-element-appears.test.json | 0 .../best-time-to-buy-and-sell-stock-i.test.ts | 0 ...ubarrays-where-max-element-appears.test.ts | 0 .../length-of-longest-substring.test.ts | 0 .../longest-continuous-subarray.test.ts | 0 ...ax-sum-distinct-subarray-of-size-k.test.ts | 0 .../minimum-window-substring.test.ts | 0 .../repeated-dna-sequences.test.ts | 0 .../stack/basic-calculator-ii.test.ts | 0 .../stack/basic-calculator.test.ts | 0 ...ey-value-store-nested-transactions.test.ts | 0 .../stack/longest-absolute-file-path.test.ts | 0 .../max-chunks-to-make-sorted-ii.test.ts | 0 test/{ => leetcode}/stack/max-stack.test.ts | 0 test/{ => leetcode}/stack/min-stack.test.ts | 0 ...imum-add-to-make-valid-parentheses.test.ts | 0 ...m-remove-to-make-valid-parentheses.test.ts | 0 .../stack/simplify-path.test.ts | 0 .../{ => leetcode}/stack/valid-number.test.ts | 0 .../stack/valid-parentheses.test.ts | 0 .../find-the-closest-palindrome.test.ts | 0 .../string/int-to-roman.test.ts | 0 .../string/integer-to-english-words.test.ts | 0 .../letter-combinations-phone-number.test.ts | 0 .../string/palindrome-number.test.ts | 0 .../remove-all-adjacent-duplicates-i.test.ts | 0 .../remove-all-adjacent-duplicates-ii.test.ts | 0 .../string/reverse-words-in-a-string.test.ts | 0 .../string/roman-to-int.test.ts | 0 ...-needed-to-rearrange-binary-string.test.ts | 0 .../string/valid-word-abbreviation.test.ts | 0 .../leetcode/string/valid_parentheses_test.py | 16 + .../string/valid_word_abbreviation_test.py | 12 + .../string/vowel-spellchecker.test.ts | 0 .../construct-quad-tree.test.ts.snap | 0 .../binary-tree-level-order-traversal.test.ts | 0 .../tree/binary-tree-right-side-view.test.ts | 0 ...nary-tree-vertical-order-traversal.test.ts | 0 .../tree/construct-quad-tree.test.ts | 0 .../tree/design-in-memory-file-system.test.ts | 0 .../tree/diameter-of-binary-tree.test.ts | 0 .../longest-palindromic-substring.test.ts | 0 ...mmon-ancestor-of-a-binary-tree-iii.test.ts | 0 ...t-common-ancestor-of-a-binary-tree.test.ts | 0 .../tree/range-sum-of-bst.test.ts | 0 test/{ => leetcode}/tree/same-tree.test.ts | 0 .../tree/sum-root-to-leaf-numbers.test.ts | 0 .../__snapshots__/three-sum.test.ts.snap | 0 .../two_pointers}/bag-of-tokens.test.ts | 0 .../two_pointers/bag_of_tokens_test.py | 16 + .../container-with-most-water.test.ts | 0 .../longest-common-prefix.test.ts | 0 ...-substring-without-repeating-chars.test.ts | 0 ...emove-duplicates-from-sorted-array.test.ts | 0 .../two_pointers}/remove-element.test.ts | 0 .../two_pointers}/string-compression.test.ts | 0 .../two_pointers}/three-sum-closest.test.ts | 0 .../two_pointers}/three-sum.test.ts | 0 .../two_pointers}/trapping-rain-water.test.ts | 0 .../two-sum-input-array-sorted.test.ts | 0 .../two_pointers}/valid-palindrome-ii.test.ts | 0 .../two_pointers}/valid-palindrome.test.ts | 0 .../valid-triangle-number.test.ts | 0 .../generate-parentheses.test.ts.snap | 30 - .../__snapshots__/permutations.test.ts.snap | 49 - .../__snapshots__/subsets.test.ts.snap | 42 - .../__snapshots__/word-break-ii.test.ts.snap | 16 - tsconfig.json | 19 - 345 files changed, 1944 insertions(+), 7583 deletions(-) create mode 100644 .flake8 delete mode 100644 .prettierignore delete mode 100644 .vscode/settings.json delete mode 100644 eslint.config.mjs delete mode 100644 package-lock.json delete mode 100644 package.json create mode 100644 poetry.lock delete mode 100644 prettier.config.mjs create mode 100644 pyproject.toml delete mode 100644 script/clean.mjs create mode 100644 scripts.py delete mode 100644 src/array/best-time-to-buy-and-sell-stock-ii.ts delete mode 100644 src/array/buildings-with-an-ocean-view.ts delete mode 100644 src/array/design-hit-counter.ts delete mode 100644 src/array/dot-product-of-two-sparse-vectors.ts delete mode 100644 src/array/group-anagrams.ts delete mode 100644 src/array/longest-consecutive-sequence.ts delete mode 100644 src/array/max-chunks-to-make-sorted.ts delete mode 100644 src/array/maximum-swap.ts delete mode 100644 src/array/merge-sorted-array.ts delete mode 100644 src/array/pascals-triangle.ts delete mode 100644 src/array/top-k-frequent-elements.ts delete mode 100644 src/array/two-sum.ts delete mode 100644 src/heap/README.md delete mode 100644 src/heap/common/binary-search-max-heap.ts delete mode 100644 src/heap/common/simple-max-heap.ts delete mode 100644 src/heap/furthest-building-you-can-reach.ts delete mode 100644 src/heap/kth-largest-element.ts delete mode 100644 src/heap/minimize-deviation-in-array.ts delete mode 100644 src/heap/numbers-of-orders-in-the-backlog.ts create mode 100644 src/leetcode/__init__.py create mode 100644 src/leetcode/array/best_time_to_buy_and_sell_stock_ii.py create mode 100644 src/leetcode/array/buildings_with_an_ocean_view.py create mode 100644 src/leetcode/array/design_hit_counter.py create mode 100644 src/leetcode/array/dot_product_of_two_sparse_vectors.py create mode 100644 src/leetcode/array/group_anagrams.py create mode 100644 src/leetcode/array/longest_consecutive_sequence.py create mode 100644 src/leetcode/array/max_chunks_to_make_sorted.py create mode 100644 src/leetcode/array/maximum_swap.py create mode 100644 src/leetcode/array/merge_sorted_array.py create mode 100644 src/leetcode/array/pascals_triangle.py create mode 100644 src/leetcode/array/top_k_frequent_elements.py create mode 100644 src/leetcode/array/two_sum.py rename src/{binary-search => leetcode/binary_search}/README.md (100%) rename src/{binary-search => leetcode/binary_search}/find-first-and-last-position-of-element-in-sorted-array.ts (100%) rename src/{binary-search => leetcode/binary_search}/find-peak-element.ts (100%) rename src/{binary-search => leetcode/binary_search}/find-the-smallest-divisor-given-a-threshold.ts (100%) rename src/{binary-search => leetcode/binary_search}/k-th-missing-positive-number.ts (100%) rename src/{binary-search => leetcode/binary_search}/k-th-smallest-element-in-a-sorted-matrix.ts (100%) rename src/{binary-search => leetcode/binary_search}/longest-increasing-subsequence.ts (100%) rename src/{binary-search => leetcode/binary_search}/median-of-two-sorted-arrays.ts (100%) rename src/{binary-search => leetcode/binary_search}/search-in-rotated-sorted-array.ts (100%) rename src/{dynamic-programming => leetcode/dynamic_programming}/README.md (100%) rename src/{dynamic-programming => leetcode/dynamic_programming}/arithmetic-slices-ii-subsequences.ts (100%) rename src/{dynamic-programming => leetcode/dynamic_programming}/max-subarray.ts (100%) rename src/{dynamic-programming => leetcode/dynamic_programming}/word-break-i.ts (100%) rename src/{ => leetcode}/graph/add-edges-to-make-degrees-even.ts (100%) rename src/{ => leetcode}/graph/bus-routes.ts (100%) rename src/{ => leetcode}/graph/nested-list-weighted-sum-ii.ts (100%) rename src/{ => leetcode}/graph/nested-list-weighted-sum.ts (100%) rename src/{ => leetcode}/graph/number-of-islands.ts (100%) rename src/{ => leetcode}/graph/parallel-job-scheduling.ts (100%) rename src/{ => leetcode}/graph/shortest-path-in-binary-matrix.ts (100%) rename src/{ => leetcode}/graph/smallest-greater-multiple-made-of-two-digits.ts (100%) rename src/{ => leetcode}/graph/word-search-i.ts (100%) rename src/{ => leetcode}/graph/word-search-ii.ts (100%) create mode 100644 src/leetcode/heap/README.md create mode 100644 src/leetcode/heap/furthest_building_you_can_reach.py rename src/{ => leetcode}/heap/k-closest-points-to-origin.ts (100%) create mode 100644 src/leetcode/heap/kth_largest_element.py rename src/{ => leetcode}/heap/max-stack.ts (100%) rename src/{ => leetcode}/heap/merge-k-sorted-lists.ts (100%) create mode 100644 src/leetcode/heap/minimize_deviation_in_array.py create mode 100644 src/leetcode/heap/number_of_orders_in_the_backlog.py rename src/{ => leetcode}/heap/task-scheduler.ts (100%) rename src/{ => leetcode}/interval/interval-list-intersections.ts (100%) rename src/{ => leetcode}/interval/meeting-rooms.ts (100%) rename src/{ => leetcode}/interval/meeting-scheduler.ts (100%) rename src/{ => leetcode}/interval/merge-intervals.ts (100%) rename src/{linked-list => leetcode/linked_list}/README.md (100%) rename src/{linked-list => leetcode/linked_list}/add-two-numbers.ts (100%) rename src/{linked-list => leetcode/linked_list}/all-one.ts (100%) rename src/{linked-list => leetcode/linked_list}/common/list-node.ts (100%) rename src/{linked-list => leetcode/linked_list}/common/nested-integer.ts (100%) rename src/{linked-list => leetcode/linked_list}/common/random-node.ts (100%) rename src/{linked-list => leetcode/linked_list}/copy-list-with-random-pointer.ts (100%) rename src/{linked-list => leetcode/linked_list}/lru-cache.ts (100%) rename src/{linked-list => leetcode/linked_list}/merge-two-sorted-lists.ts (100%) rename src/{linked-list => leetcode/linked_list}/remove-nth-node-from-end-of-list.ts (100%) rename src/{ => leetcode}/math/number-of-one-bits.ts (100%) rename src/{ => leetcode}/math/pow-x-n.ts (100%) rename src/{ => leetcode}/math/reverse-integer.ts (100%) rename src/{ => leetcode}/math/rotate-image.ts (100%) rename src/{ => leetcode}/math/sequential-digits.ts (100%) rename src/{ => leetcode}/matrix/robot-bounded-in-circle.ts (100%) rename src/{ => leetcode}/matrix/valid-word-square.ts (100%) rename src/{prefix-sum => leetcode/prefix_sum}/README.md (100%) rename src/{prefix-sum => leetcode/prefix_sum}/continuous-subarray-sum.ts (100%) rename src/{prefix-sum => leetcode/prefix_sum}/max-sum-obtained-of-any-permutation.ts (100%) rename src/{prefix-sum => leetcode/prefix_sum}/product-of-array-except-self.ts (100%) rename src/{prefix-sum => leetcode/prefix_sum}/random-pick-with-weight.ts (100%) rename src/{prefix-sum => leetcode/prefix_sum}/range-sum-query-immutable.ts (100%) rename src/{prefix-sum => leetcode/prefix_sum}/running-sum-of-1d-array.ts (100%) rename src/{prefix-sum => leetcode/prefix_sum}/subarray-sum-equals-k.ts (100%) create mode 100644 src/leetcode/recursion/generate_parentheses.py rename src/{ => leetcode}/recursion/maximum-number-of-operations-with-the-same-score.ts (100%) rename src/{ => leetcode}/recursion/optimal-account-balancing.ts (100%) rename src/{ => leetcode}/recursion/permutations.ts (100%) rename src/{ => leetcode}/recursion/subsets.ts (100%) rename src/{ => leetcode}/recursion/word-break-ii.ts (100%) rename src/{sliding-window => leetcode/sliding_window}/README.md (100%) rename src/{sliding-window => leetcode/sliding_window}/best-time-to-buy-and-sell-stock-i.ts (100%) rename src/{sliding-window => leetcode/sliding_window}/count-subarrays-where-max-element-appears.ts (100%) rename src/{sliding-window => leetcode/sliding_window}/length-of-longest-substring.ts (100%) rename src/{sliding-window => leetcode/sliding_window}/longest-continuous-subarray.ts (100%) rename src/{sliding-window => leetcode/sliding_window}/max-sum-distinct-subarray-of-size-k.ts (100%) rename src/{sliding-window => leetcode/sliding_window}/minimum-window-substring.ts (100%) rename src/{sliding-window => leetcode/sliding_window}/moving-average-from-data-stream.ts (100%) rename src/{sliding-window => leetcode/sliding_window}/repeated-dna-sequences.ts (100%) rename src/{ => leetcode}/stack/README.md (100%) rename src/{ => leetcode}/stack/basic-calculator-ii.ts (100%) rename src/{ => leetcode}/stack/basic-calculator.ts (100%) rename src/{ => leetcode}/stack/key-value-store-nested-transactions.ts (100%) rename src/{ => leetcode}/stack/longest-absolute-file-path.ts (100%) rename src/{ => leetcode}/stack/max-chunks-to-make-sorted-ii.ts (100%) rename src/{ => leetcode}/stack/min-stack.ts (100%) rename src/{ => leetcode}/stack/minimum-add-to-make-valid-parentheses.ts (100%) rename src/{ => leetcode}/stack/minimum-remove-to-make-valid-parentheses.ts (100%) rename src/{ => leetcode}/stack/simplify-path.ts (100%) rename src/{ => leetcode}/stack/valid-number.ts (100%) create mode 100644 src/leetcode/stack/valid_parentheses.py rename src/{ => leetcode}/string/find-the-closest-palindrome.ts (100%) rename src/{ => leetcode}/string/int-to-roman.ts (100%) rename src/{ => leetcode}/string/integer-to-english-words.ts (100%) rename src/{ => leetcode}/string/letter-combinations-phone-number.ts (100%) rename src/{ => leetcode}/string/longest-palindromic-substring.ts (100%) rename src/{ => leetcode}/string/palindrome-number.ts (100%) rename src/{ => leetcode}/string/remove-all-adjacent-duplicates-i.ts (100%) rename src/{ => leetcode}/string/remove-all-adjacent-duplicates-ii.ts (100%) rename src/{ => leetcode}/string/reverse-words-in-a-string.ts (100%) rename src/{ => leetcode}/string/roman-to-int.ts (100%) rename src/{ => leetcode}/string/time-needed-to-rearrange-binary-string.ts (100%) create mode 100644 src/leetcode/string/valid_word_abbreviation.py rename src/{ => leetcode}/string/vowel-spellchecker.ts (100%) rename src/{ => leetcode}/tree/binary-tree-level-order-traversal.ts (100%) rename src/{ => leetcode}/tree/binary-tree-right-side-view.ts (100%) rename src/{ => leetcode}/tree/binary-tree-vertical-order-traversal.ts (100%) rename src/{ => leetcode}/tree/common/parent-node.ts (100%) rename src/{ => leetcode}/tree/common/quad-tree.ts (100%) rename src/{ => leetcode}/tree/common/tree-node.ts (100%) rename src/{ => leetcode}/tree/construct-quad-tree.ts (100%) rename src/{ => leetcode}/tree/design-in-memory-file-system.ts (100%) rename src/{ => leetcode}/tree/diameter-of-binary-tree.ts (100%) rename src/{ => leetcode}/tree/lowest-common-ancestor-of-a-binary-tree-iii.ts (100%) rename src/{ => leetcode}/tree/lowest-common-ancestor-of-a-binary-tree.ts (100%) rename src/{ => leetcode}/tree/range-sum-of-bst.ts (100%) rename src/{ => leetcode}/tree/same-tree.ts (100%) rename src/{ => leetcode}/tree/sum-root-to-leaf-numbers.ts (100%) rename src/{two-pointer => leetcode/two_pointers}/README.md (100%) create mode 100644 src/leetcode/two_pointers/bag_of_tokens.py rename src/{two-pointer => leetcode/two_pointers}/container-with-most-water.ts (100%) rename src/{two-pointer => leetcode/two_pointers}/longest-common-prefix.ts (100%) rename src/{two-pointer => leetcode/two_pointers}/longest-substring-without-repeating-chars.ts (100%) rename src/{two-pointer => leetcode/two_pointers}/remove-duplicates-from-sorted-array.ts (100%) rename src/{two-pointer => leetcode/two_pointers}/remove-element.ts (100%) rename src/{two-pointer => leetcode/two_pointers}/string-compression.ts (100%) rename src/{two-pointer => leetcode/two_pointers}/three-sum-closest.ts (100%) rename src/{two-pointer => leetcode/two_pointers}/three-sum.ts (100%) rename src/{two-pointer => leetcode/two_pointers}/trapping-rain-water.ts (100%) rename src/{two-pointer => leetcode/two_pointers}/two-sum-input-array-sorted.ts (100%) rename src/{two-pointer => leetcode/two_pointers}/valid-palindrome-ii.ts (100%) rename src/{two-pointer => leetcode/two_pointers}/valid-palindrome.ts (100%) rename src/{two-pointer => leetcode/two_pointers}/valid-triangle-number.ts (100%) delete mode 100644 src/recursion/generate-parentheses.ts delete mode 100644 src/stack/valid-parentheses.ts delete mode 100644 src/string/valid-word-abbreviation.ts delete mode 100644 src/two-pointer/bag-of-tokens.ts delete mode 100644 test/array/__snapshots__/group-anagrams.test.ts.snap delete mode 100644 test/array/best-time-to-buy-and-sell-stock-ii.test.ts delete mode 100644 test/array/buildings-with-an-ocean-view.test.ts delete mode 100644 test/array/design-hit-counter.test.ts delete mode 100644 test/array/dot-product-of-two-sparse-vectors.test.ts delete mode 100644 test/array/group-anagrams.test.ts delete mode 100644 test/array/longest-consecutive-sequence.test.ts delete mode 100644 test/array/max-chunks-to-make-sorted.test.ts delete mode 100644 test/array/maximum-swap.test.ts delete mode 100644 test/array/merge-sorted-array.test.ts delete mode 100644 test/array/pascals-triangle.test.ts delete mode 100644 test/array/product-of-array-except-self.test.ts delete mode 100644 test/array/top-k-frequent-elements.test.ts delete mode 100644 test/array/two-sum.test.ts delete mode 100644 test/heap/furthest-building-you-can-reach.test.ts delete mode 100644 test/heap/kth-largest-element.test.ts delete mode 100644 test/heap/minimize-deviation-in-array.test.ts delete mode 100644 test/heap/numbers-of-orders-in-the-backlog.test.ts delete mode 100644 test/jest.config.mjs create mode 100644 test/leetcode/array/best_time_to_buy_and_sell_stock_ii_test.py create mode 100644 test/leetcode/array/buildings_with_an_ocean_view_test.py create mode 100644 test/leetcode/array/design_hit_counter_test.py create mode 100644 test/leetcode/array/dot_product_of_two_sparse_vectors_test.py create mode 100644 test/leetcode/array/group_anagrams_test.py create mode 100644 test/leetcode/array/longest_consecutive_sequence_test.py create mode 100644 test/leetcode/array/max_chunks_to_make_sorted_test.py create mode 100644 test/leetcode/array/maximum_swap_test.py create mode 100644 test/leetcode/array/merge_sorted_array_test.py create mode 100644 test/leetcode/array/pascals_triangle_test.py create mode 100644 test/leetcode/array/snapshots/snap_group_anagrams_test.py create mode 100644 test/leetcode/array/snapshots/snap_pascals_triangle_test.py create mode 100644 test/leetcode/array/top_k_frequent_elements_test.py create mode 100644 test/leetcode/array/two_sum_test.py rename test/{binary-search => leetcode/binary_search}/find-first-and-last-position-of-element-in-sorted-array.test.ts (100%) rename test/{binary-search => leetcode/binary_search}/find-peak-element.test.ts (100%) rename test/{binary-search => leetcode/binary_search}/find-the-smallest-divisor-given-a-threshold.test.ts (100%) rename test/{binary-search => leetcode/binary_search}/k-th-missing-positive-number.test.ts (100%) rename test/{binary-search => leetcode/binary_search}/k-th-smallest-element-in-a-sorted-matrix.test.ts (100%) rename test/{binary-search => leetcode/binary_search}/longest-increasing-subsequence.test.ts (100%) rename test/{binary-search => leetcode/binary_search}/median-of-two-sorted-arrays.test.ts (100%) rename test/{binary-search => leetcode/binary_search}/search-in-rotated-sorted-array.test.ts (100%) rename test/{dynamic-programming => leetcode/dynamic_programming}/arithmetic-slices-ii-subsequences.test.ts (100%) rename test/{dynamic-programming => leetcode/dynamic_programming}/max-subarray.test.ts (100%) rename test/{dynamic-programming => leetcode/dynamic_programming}/word-break-i.test.ts (100%) rename test/{ => leetcode}/graph/__data__/bus-routes.test.json (100%) rename test/{ => leetcode}/graph/__snapshots__/parallel-job-scheduling.test.ts.snap (100%) rename test/{ => leetcode}/graph/__snapshots__/word-search-ii.test.ts.snap (100%) rename test/{ => leetcode}/graph/add-edges-to-make-degrees-even.test.ts (100%) rename test/{ => leetcode}/graph/bus-routes.test.ts (100%) rename test/{ => leetcode}/graph/nested-list-weighted-sum-ii.test.ts (100%) rename test/{ => leetcode}/graph/nested-list-weighted-sum.test.ts (100%) rename test/{ => leetcode}/graph/number-of-islands.test.ts (100%) rename test/{ => leetcode}/graph/parallel-job-scheduling.test.ts (100%) rename test/{ => leetcode}/graph/shortest-path-in-binary-matrix.test.ts (100%) rename test/{ => leetcode}/graph/smallest-greater-multiple-made-of-two-digits.test.ts (100%) rename test/{ => leetcode}/graph/word-search-i.test.ts (100%) rename test/{ => leetcode}/graph/word-search-ii.test.ts (100%) rename test/{ => leetcode}/heap/__data__/kth-largest-element.test.json (100%) create mode 100644 test/leetcode/heap/furthest_building_you_can_reach_test.py rename test/{ => leetcode}/heap/k-closest-points-to-origin.test.ts (100%) create mode 100644 test/leetcode/heap/kth_largest_element_test.py create mode 100644 test/leetcode/heap/minimize_deviation_in_array_test.py create mode 100644 test/leetcode/heap/number_of_orders_in_the_backlog_test.py rename test/{ => leetcode}/heap/task-scheduler.test.ts (100%) rename test/{ => leetcode}/interval/interval-list-intersection.test.ts (100%) rename test/{ => leetcode}/interval/meeting-rooms.test.ts (100%) rename test/{ => leetcode}/interval/meeting-scheduler.test.ts (100%) rename test/{ => leetcode}/interval/merge-intervals.test.ts (100%) rename test/{linked-list => leetcode/linked_list}/add-two-numbers.test.ts (100%) rename test/{linked-list => leetcode/linked_list}/all-one.test.ts (100%) rename test/{linked-list => leetcode/linked_list}/lru-cache.test.ts (100%) rename test/{linked-list => leetcode/linked_list}/merge-k-sorted-lists.test.ts (100%) rename test/{linked-list => leetcode/linked_list}/merge-two-sorted-lists.test.ts (100%) rename test/{ => leetcode}/math/__data__/task-scheduler.test.json (100%) rename test/{ => leetcode}/math/__snapshots__/sequential-digits.test.ts.snap (100%) rename test/{ => leetcode}/math/number-of-one-bits.test.ts (100%) rename test/{ => leetcode}/math/pow-x-n.test.ts (100%) rename test/{ => leetcode}/math/reverse-integer.test.ts (100%) rename test/{ => leetcode}/math/rotate-image.test.ts (100%) rename test/{ => leetcode}/math/sequential-digits.test.ts (100%) rename test/{ => leetcode}/matrix/robot-bounded-in-circle.test.ts (100%) rename test/{ => leetcode}/matrix/valid-word-square.test.ts (100%) rename test/{prefix-sum => leetcode/prefix_sum}/continuous-subarray-sum.test.ts (100%) rename test/{prefix-sum => leetcode/prefix_sum}/max-sum-obtained-of-any-permutation.test.ts (100%) rename test/{prefix-sum => leetcode/prefix_sum}/range-sum-query-immutable.test.ts (100%) rename test/{prefix-sum => leetcode/prefix_sum}/running-sum-of-1d-array.test.ts (100%) rename test/{prefix-sum => leetcode/prefix_sum}/subarray-sum-equals-k.test.ts (100%) rename test/{ => leetcode}/recursion/generate-parentheses.test.ts (100%) create mode 100644 test/leetcode/recursion/generate_parentheses_test.py rename test/{ => leetcode}/recursion/maximum-number-of-operations-with-the-same-score.test.ts (100%) rename test/{ => leetcode}/recursion/optimal-account-balancing.test.ts (100%) rename test/{ => leetcode}/recursion/permutations.test.ts (100%) create mode 100644 test/leetcode/recursion/snapshots/snap_generate_parentheses_test.py rename test/{ => leetcode}/recursion/subsets.test.ts (100%) rename test/{ => leetcode}/recursion/word-break-ii.test.ts (100%) rename test/{sliding-window => leetcode/sliding_window}/__data__/count-subarrays-where-max-element-appears.test.json (100%) rename test/{sliding-window => leetcode/sliding_window}/best-time-to-buy-and-sell-stock-i.test.ts (100%) rename test/{sliding-window => leetcode/sliding_window}/count-subarrays-where-max-element-appears.test.ts (100%) rename test/{sliding-window => leetcode/sliding_window}/length-of-longest-substring.test.ts (100%) rename test/{sliding-window => leetcode/sliding_window}/longest-continuous-subarray.test.ts (100%) rename test/{sliding-window => leetcode/sliding_window}/max-sum-distinct-subarray-of-size-k.test.ts (100%) rename test/{sliding-window => leetcode/sliding_window}/minimum-window-substring.test.ts (100%) rename test/{sliding-window => leetcode/sliding_window}/repeated-dna-sequences.test.ts (100%) rename test/{ => leetcode}/stack/basic-calculator-ii.test.ts (100%) rename test/{ => leetcode}/stack/basic-calculator.test.ts (100%) rename test/{ => leetcode}/stack/key-value-store-nested-transactions.test.ts (100%) rename test/{ => leetcode}/stack/longest-absolute-file-path.test.ts (100%) rename test/{ => leetcode}/stack/max-chunks-to-make-sorted-ii.test.ts (100%) rename test/{ => leetcode}/stack/max-stack.test.ts (100%) rename test/{ => leetcode}/stack/min-stack.test.ts (100%) rename test/{ => leetcode}/stack/minimum-add-to-make-valid-parentheses.test.ts (100%) rename test/{ => leetcode}/stack/minimum-remove-to-make-valid-parentheses.test.ts (100%) rename test/{ => leetcode}/stack/simplify-path.test.ts (100%) rename test/{ => leetcode}/stack/valid-number.test.ts (100%) rename test/{ => leetcode}/stack/valid-parentheses.test.ts (100%) rename test/{ => leetcode}/string/find-the-closest-palindrome.test.ts (100%) rename test/{ => leetcode}/string/int-to-roman.test.ts (100%) rename test/{ => leetcode}/string/integer-to-english-words.test.ts (100%) rename test/{ => leetcode}/string/letter-combinations-phone-number.test.ts (100%) rename test/{ => leetcode}/string/palindrome-number.test.ts (100%) rename test/{ => leetcode}/string/remove-all-adjacent-duplicates-i.test.ts (100%) rename test/{ => leetcode}/string/remove-all-adjacent-duplicates-ii.test.ts (100%) rename test/{ => leetcode}/string/reverse-words-in-a-string.test.ts (100%) rename test/{ => leetcode}/string/roman-to-int.test.ts (100%) rename test/{ => leetcode}/string/time-needed-to-rearrange-binary-string.test.ts (100%) rename test/{ => leetcode}/string/valid-word-abbreviation.test.ts (100%) create mode 100644 test/leetcode/string/valid_parentheses_test.py create mode 100644 test/leetcode/string/valid_word_abbreviation_test.py rename test/{ => leetcode}/string/vowel-spellchecker.test.ts (100%) rename test/{ => leetcode}/tree/__snapshots__/construct-quad-tree.test.ts.snap (100%) rename test/{ => leetcode}/tree/binary-tree-level-order-traversal.test.ts (100%) rename test/{ => leetcode}/tree/binary-tree-right-side-view.test.ts (100%) rename test/{ => leetcode}/tree/binary-tree-vertical-order-traversal.test.ts (100%) rename test/{ => leetcode}/tree/construct-quad-tree.test.ts (100%) rename test/{ => leetcode}/tree/design-in-memory-file-system.test.ts (100%) rename test/{ => leetcode}/tree/diameter-of-binary-tree.test.ts (100%) rename test/{ => leetcode}/tree/longest-palindromic-substring.test.ts (100%) rename test/{ => leetcode}/tree/lowest-common-ancestor-of-a-binary-tree-iii.test.ts (100%) rename test/{ => leetcode}/tree/lowest-common-ancestor-of-a-binary-tree.test.ts (100%) rename test/{ => leetcode}/tree/range-sum-of-bst.test.ts (100%) rename test/{ => leetcode}/tree/same-tree.test.ts (100%) rename test/{ => leetcode}/tree/sum-root-to-leaf-numbers.test.ts (100%) rename test/{two-pointer => leetcode/two_pointers}/__snapshots__/three-sum.test.ts.snap (100%) rename test/{two-pointer => leetcode/two_pointers}/bag-of-tokens.test.ts (100%) create mode 100644 test/leetcode/two_pointers/bag_of_tokens_test.py rename test/{two-pointer => leetcode/two_pointers}/container-with-most-water.test.ts (100%) rename test/{two-pointer => leetcode/two_pointers}/longest-common-prefix.test.ts (100%) rename test/{two-pointer => leetcode/two_pointers}/longest-substring-without-repeating-chars.test.ts (100%) rename test/{two-pointer => leetcode/two_pointers}/remove-duplicates-from-sorted-array.test.ts (100%) rename test/{two-pointer => leetcode/two_pointers}/remove-element.test.ts (100%) rename test/{two-pointer => leetcode/two_pointers}/string-compression.test.ts (100%) rename test/{two-pointer => leetcode/two_pointers}/three-sum-closest.test.ts (100%) rename test/{two-pointer => leetcode/two_pointers}/three-sum.test.ts (100%) rename test/{two-pointer => leetcode/two_pointers}/trapping-rain-water.test.ts (100%) rename test/{two-pointer => leetcode/two_pointers}/two-sum-input-array-sorted.test.ts (100%) rename test/{two-pointer => leetcode/two_pointers}/valid-palindrome-ii.test.ts (100%) rename test/{two-pointer => leetcode/two_pointers}/valid-palindrome.test.ts (100%) rename test/{two-pointer => leetcode/two_pointers}/valid-triangle-number.test.ts (100%) delete mode 100644 test/recursion/__snapshots__/generate-parentheses.test.ts.snap delete mode 100644 test/recursion/__snapshots__/permutations.test.ts.snap delete mode 100644 test/recursion/__snapshots__/subsets.test.ts.snap delete mode 100644 test/recursion/__snapshots__/word-break-ii.test.ts.snap delete mode 100644 tsconfig.json diff --git a/.editorconfig b/.editorconfig index 56ab1f2..697a547 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,10 +5,8 @@ charset = utf-8 end_of_line = lf indent_size = 2 indent_style = space -# Setting this to true makes VSCode break typing flow. The following ticket mentions that Intellisense dropdowns -# disappear, and that is presumably fixed. However, if you aren't, then the cursor still jumps and typing flow is still -# broken. -# -# See https://github.com/editorconfig/editorconfig-vscode/issues/40 -# insert_final_newline = true +insert_final_newline = true trim_trailing_whitespace = true + +[*.py] +indent_size = 4 diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..a6f727b --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +ignore = E501, W503 diff --git a/.gitattributes b/.gitattributes index 6313b56..fcadb2c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -* text=auto eol=lf +* text eol=lf diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 30ee981..5bde350 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -3,6 +3,7 @@ name: Build and Test on: pull_request: paths-ignore: + - CODEOWNERS - LICENSE - README.md push: @@ -27,19 +28,28 @@ jobs: ref: ${{ github.head_ref }} token: ${{ steps.generate-token.outputs.token }} - - name: Setup NodeJS - uses: actions/setup-node@v3 + - name: Setup Python + uses: actions/setup-python@v5 with: - node-version: 20 + python-version: '3.13' - - name: Install dependencies - run: npm ci + - name: Install poetry + run: python -m pip install poetry==1.8.5 + + - name: Configure poetry + run: poetry config virtualenvs.in-project true - - name: Fix - run: npm run fix + - name: Cache the virtualenv + uses: actions/cache@v4 + with: + path: ./.venv + key: ${{ runner.os }}-venv-${{ hashFiles('**/poetry.lock') }} - - name: Build - run: npm run build + - name: Install dependencies + run: poetry install + + - name: Run format + run: poetry run format - name: Commit if: ${{ github.ref != 'refs/heads/main' }} @@ -50,6 +60,10 @@ jobs: author_email: github-actions[bot]@github.com message: Automatic commit via GitHub Actions + - name: Run lint + if: ${{ steps.commit.outputs.committed == 'false' }} + run: poetry run lint + - name: Test if: ${{ steps.commit.outputs.committed == 'false' }} - run: npm run test + run: poetry run test diff --git a/.gitignore b/.gitignore index a73f32e..a9fc15f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,10 @@ +__pycache__/ +.env/ +.mypy_cache/ +.venv/ build/ -coverage/ dist/ -node_modules/ -*.tgz -.env +*.egg-info/ +*.pyc +*.pyo diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 480be40..0000000 --- a/.prettierignore +++ /dev/null @@ -1,18 +0,0 @@ -build/ -coverage/ -dist/ -node_modules/ - -# Ignore all the dotfiles in the repository root. -*.* - -# Ignore specific file patterns in the repository. -*.snap -LICENSE - -# Unless they are a certain file type or explicitly included. -package.json -tsconfig.json -!*.js -!*.mjs -!*.ts diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index d7207a4..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "typescript.tsdk": "node_modules\\typescript\\lib" -} \ No newline at end of file diff --git a/README.md b/README.md index 3990444..7c440c7 100644 --- a/README.md +++ b/README.md @@ -2,39 +2,9 @@ Problems and solutions for LeetCode. +## Development -## Priority Queue - -Several problems require access to a priority queue; however, TypeScript itself does not have such a built-in data structure. - - -### LeetCode - -LeetCode provides support for [heaps](https://support.leetcode.com/hc/en-us/articles/360011833974-What-are-the-environments-for-the-programming-languages) via [datastructures-js/priority-queue](https://github.com/datastructures-js/priority-queue) at version `5.4.0`. This is the library used, because this repository is for LeetCode submissions. - -A couple of gotchas for version `5.4.0` that don't exist in later versions: - -- LeetCode uses `require` style imports and not `import`. Practically that means `MaxPriorityQueue` and `MinPriorityQueue` do not accept parameterized types. -- Also, `MaxPriorityQueue` and `MinPriorityQueue` are exported as values. To get the type, do `InstanceType`. -- Primitives are wrapped. That is, if you do `heap.enqueue(1)`, then you should `heap.dequeue().element` to get `1` back. -- Objects are not wrapped. That is, if you do `heap.enqueue(foo)`, then you should do `heap.dequeue()` to get `foo` back, if `foo` is an object. - - -### CoderPad - -CoderPad has no official support for priority queues. - - -### CodeSignal - -CodeSignal's official documentation does not claim support for priority queues. However, there is [documentation](https://learn.codesignal.com/preview/lessons/3525/heaps-and-priority-queues-in-javascript?utm_source=chatgpt.com) suggesting that [heap-js](https://github.com/ignlg/heap-js) is available. - - -### Other - -There are a few other options. - -- Insert into an array and sort it afterwards. See [simple-heap.ts](src/heap/simple-heap.ts). -- Use binary search to insert into an array and do not sort. See [bs-search-heap.ts](src/heap/bs-search-heap.ts). -- Roll your own heap during the interview. You beast. -- Give up. +1. Install poetry: `python -m pip install poetry==1.8.5` +1. Configure poetry: `poetry config virtualenvs.in-project true` +1. Install dependencies: `poetry install` +1. Run tests: `poetry run test` diff --git a/eslint.config.mjs b/eslint.config.mjs deleted file mode 100644 index 3f0f6e8..0000000 --- a/eslint.config.mjs +++ /dev/null @@ -1,153 +0,0 @@ -import eslint from '@eslint/js'; -import tselint from 'typescript-eslint'; -import uimports from 'eslint-plugin-unused-imports'; - -export default tselint.config({ - extends: [eslint.configs.recommended, tselint.configs.recommended], - files: ['src/**/*.ts', 'test/**/*.ts', 'script/**/*.js'], - languageOptions: { - parserOptions: { - project: './tsconfig.json', - tsconfigRootDir: import.meta.dirname, - ecmaVersion: 2020, - sourceType: 'module' - } - }, - plugins: { - 'unused-imports': uimports - }, - rules: { - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', - // Permits the violation of naming conventions for unused variables with a leading underscore. - // - // See https://typescript-eslint.io/rules/naming-convention/ - '@typescript-eslint/naming-convention': [ - 'error', - { - selector: 'variable', - format: ['camelCase', 'PascalCase', 'snake_case'], - leadingUnderscore: 'allow', - modifiers: ['unused'] - }, - { - selector: 'parameter', - format: ['camelCase', 'PascalCase', 'snake_case'], - leadingUnderscore: 'allow' - } - ], - // Enabled because one should not assign the result of a void function. - // - // See https://typescript-eslint.io/rules/no-confusing-void-expression/ - '@typescript-eslint/no-confusing-void-expression': 'error', - '@typescript-eslint/no-meaningless-void-operator': 'error', - // Disabled because LeetCode imports datastructures-js/priority-queue via require and not import. - '@typescript-eslint/no-require-imports': 'off', - '@typescript-eslint/no-this-alias': 'off', - '@typescript-eslint/no-unused-expressions': 'error', - '@typescript-eslint/no-unused-vars': 'off', - '@typescript-eslint/no-useless-constructor': 'error', - '@typescript-eslint/no-use-before-define': 'off', - '@typescript-eslint/no-var-requires': 'off', - // Disabled because there's no reason to enable this except to be pedantic. Code organization desires may dictate - // that methods not using this should still be methods. For example, there are situations where it's desirable to - // define only instance methods for ease of use, or you would prefer that the caller not have to know or care that - // a method actually uses instance variables or not. - // - // In other situations, nothing is stopping you from declaring a function outside of a class, which is functionally - // the same as a static method. - // - // See https://eslint.org/docs/latest/rules/class-methods-use-this - 'class-methods-use-this': 'off', - // Disabled because this rule cannot detect if a dependency is only for development. For example, if you have a - // postbuild.js and you include it in tsconfig.json, then this plugin will incorrectly assume that any packages - // required there should be in dependencies. - // - // In that situation, postbuild.js is included in tsconfig.json so that JavaScript files (even ones not included - // in the final build) should be linted according to @typescript-eslint. - // - // See https://stackoverflow.com/questions/61956555/why-is-typescript-eslint-parser-including-files-outside-of-those-configured-in - // See https://www.typescriptlang.org/tsconfig#include - 'import/no-extraneous-dependencies': 'off', - // Disabled because named exports are more explicit. - // - // See https://github.com/airbnb/javascript/issues/1365 - // See https://blog.neufund.org/why-we-have-banned-default-exports-and-you-should-do-the-same-d51fdc2cf2ad - 'import/prefer-default-export': 'off', - 'jest/no-disabled-tests': 'off', - 'max-classes-per-file': 'off', - // Disabled because continue ESLint specifically is concerned about continue being used with labels. Using this - // with labels is akin to a goto statement, which makes code hard to reason about. Proper use of the continue - // statement makes code easier to read. - // - // The ESLint recommendations around avoiding continue involve using if statements, which do actively degrade code - // readability. ESLint, for whatever reason, recommends this even when labels are not required. Good readability, - // in contrast, would call for using continue statements with guard clauses. - // - // See https://eslint.org/docs/latest/rules/no-continue - // See https://refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html - 'no-continue': 'off', - // Disabled because prettier can fix this. - 'no-irregular-whitespace': 'off', - // Disabled because it is handled by @typescript-eslint rules. - 'no-shadow': 'off', - // Disabled for convenience. Enable again if this ends up hurting the project. - // - // See https://eslint.org/docs/latest/rules/no-plusplus - 'no-plusplus': 'off', - 'no-restricted-syntax': [ - 'error', - { - selector: 'ForInStatement', - message: 'for..in loops iterate over the prototype chain, which is virtually never what you want' - }, - { - selector: 'LabeledStatement', - message: 'labels are a form of goto; using them makes code confusing and hard to maintain' - }, - { - selector: 'WithStatement', - message: 'with is disallowed in strict mode because it makes code impossible to predict and optimize' - } - ], - // Disabled because we do want to have underscore prefixed identifiers to indicate a variable is ignored. - // - // See https://eslint.org/docs/latest/rules/no-underscore-dangle - 'no-underscore-dangle': 'off', - 'no-unused-expressions': 'off', - 'no-unused-vars': 'off', - 'no-use-before-define': 'off', - 'no-useless-constructor': 'off', - // Disabled because `let [a] = arr;` or `[a] = arr` is just silly sometimes. - // - // See https://eslint.org/docs/latest/rules/prefer-destructuring - 'prefer-destructuring': [ - 'error', - { - object: false, - array: false - } - ], - 'unused-imports/no-unused-imports': 'error', - 'unused-imports/no-unused-vars': [ - 'warn', - { - args: 'all', - argsIgnorePattern: '^_', - caughtErrorsIgnorePattern: '^_', - vars: 'all', - varsIgnorePattern: '^_' - } - ], - // Enabled because it is useful for some problems. ESLint disables this by default because it assumes that a single - // & or | is a mistyped && or ||. - // - // See https://eslint.org/docs/latest/rules/no-bitwise - 'no-bitwise': 'off', - // Disabled because some solutions can be implemented more naturally with a while (true) loop. - 'no-constant-condition': 'off', - // Disabled because some solutions run more optimally if you do mutate the inputs. This will trigger even if you - // reassign a field in an object, which makes it more convenient if turned off. - 'no-param-reassign': 'off' - } -}); diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 3c29ec0..0000000 --- a/package-lock.json +++ /dev/null @@ -1,5442 +0,0 @@ -{ - "name": "@retiman/leetcode", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@retiman/leetcode", - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@datastructures-js/priority-queue": "5.4.0" - }, - "devDependencies": { - "@eslint/js": "^9.17.0", - "@tsconfig/recommended": "^1.0.8", - "@types/jest": "^29.5.14", - "@types/node": "^22.10.2", - "editorconfig": "^2.0.0", - "eslint": "^9.17.0", - "eslint-plugin-unused-imports": "^4.1.4", - "fs-extra": "^11.2.0", - "jest": "^29.7.0", - "prettier": "^3.4.2", - "ts-jest": "^29.2.5", - "ts-node": "^10.9.2", - "typescript": "^5.7.2", - "typescript-eslint": "^8.18.2" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", - "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", - "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.23.0", - "@babel/helpers": "^7.23.2", - "@babel/parser": "^7.23.0", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", - "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", - "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", - "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", - "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@datastructures-js/heap": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@datastructures-js/heap/-/heap-3.2.0.tgz", - "integrity": "sha512-FcU5ZAyb+VIOZz1HABsJUsbJi2ZyUDO7aoe97hq4d3tK3z8nMgwdxf5bO0gafR0ExFi18YTspntqHLzt4XOgnA==", - "license": "MIT" - }, - "node_modules/@datastructures-js/priority-queue": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@datastructures-js/priority-queue/-/priority-queue-5.4.0.tgz", - "integrity": "sha512-x+EkL8tjbwBMCUyVIwsOiKnWiOvMiFAIrnWQihcO42sUZ8uc4sRsJgHAS164wnibM2kVgQUJcfvhIdssBOzxRg==", - "license": "MIT", - "dependencies": { - "@datastructures-js/heap": "^3.2.0" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", - "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.5", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/core": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz", - "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", - "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "9.17.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", - "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", - "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz", - "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", - "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@one-ini/wasm": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", - "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", - "dev": true - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, - "node_modules/@tsconfig/recommended": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@tsconfig/recommended/-/recommended-1.0.8.tgz", - "integrity": "sha512-TotjFaaXveVUdsrXCdalyF6E5RyG6+7hHHQVZonQtdlk1rJZ1myDIvPUUKPhoYv+JAzThb2lQJh9+9ZfF46hsA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/babel__core": { - "version": "7.20.3", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.3.tgz", - "integrity": "sha512-54fjTSeSHwfan8AyHWrKbfBWiEUrNTZsUwPTDSNaaP1QDQIZbeNUg3a59E9D+375MzUw/x1vx2/0F5LBz+AeYA==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.6", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.6.tgz", - "integrity": "sha512-66BXMKb/sUWbMdBNdMvajU7i/44RkrA3z/Yt1c7R5xejt8qh84iU54yUWCtm0QwGJlDcf/gg4zd/x4mpLAlb/w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.3.tgz", - "integrity": "sha512-ciwyCLeuRfxboZ4isgdNZi/tkt06m8Tw6uGbBSBgWrnnZGNXiEyM27xc/PjXGQLqlZ6ylbgHMnm7ccF9tCkOeQ==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.3.tgz", - "integrity": "sha512-Lsh766rGEFbaxMIDH7Qa+Yha8cMVI3qAK6CHt3OR0YfxOIn5Z54iHiyDRycHrBqeIiqGa20Kpsv1cavfBKkRSw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.8.tgz", - "integrity": "sha512-NhRH7YzWq8WiNKVavKPBmtLYZHxNY19Hh+az28O/phfp68CF45pMFud+ZzJ8ewnxnC5smIdF3dqFeiSUQ5I+pw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-zONci81DZYCZjiLe0r6equvZut0b+dBRPBN5kBDjsONnutYNtJMoWQ9uR2RkL1gLG9NMTzvf+29e5RFfPbeKhQ==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.2.tgz", - "integrity": "sha512-8toY6FgdltSdONav1XtUHl4LN1yTmLza+EuDazb/fEmRNCwjyqNVIQWs2IfC74IqjHkREs/nQ2FWq5kZU9IC0w==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.3.tgz", - "integrity": "sha512-1nESsePMBlf0RPRffLZi5ujYh7IH1BWL4y9pr+Bn3cJBdxz+RTP8bUFljLz9HvzhhOSWKdyBZ4DIivdL6rvgZg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.5.14", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", - "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.10.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", - "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.2.tgz", - "integrity": "sha512-g7CK9nHdwjK2n0ymT2CW698FuWJRIx+RP6embAzZ2Qi8/ilIrA1Imt2LVSeHUzKvpoi7BhmmQcXz95eS0f2JXw==", - "dev": true - }, - "node_modules/@types/yargs": { - "version": "17.0.29", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.29.tgz", - "integrity": "sha512-nacjqA3ee9zRF/++a3FUY1suHTFKZeHba2n8WeDw9cCVdmzmHpIxyzOJBcpHvvEmS8E9KqWlSnWHUkOrkhWcvA==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.2", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.2.tgz", - "integrity": "sha512-5qcvofLPbfjmBfKaLfj/+f+Sbd6pN4zl7w7VSVI5uz7m9QZTuB2aZAa2uo1wHFBNN2x6g/SoTkXmd8mQnQF2Cw==", - "dev": true - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.18.2.tgz", - "integrity": "sha512-YJFSfbd0CJjy14r/EvWapYgV4R5CHzptssoag2M7y3Ra7XNta6GPAJPPP5KGB9j14viYXyrzRO5GkX7CRfo8/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.18.2", - "@typescript-eslint/visitor-keys": "8.18.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.18.2.tgz", - "integrity": "sha512-AB/Wr1Lz31bzHfGm/jgbFR0VB0SML/hd2P1yxzKDM48YmP7vbyJNHRExUE/wZsQj2wUCvbWH8poNHFuxLqCTnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "8.18.2", - "@typescript-eslint/utils": "8.18.2", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.18.2.tgz", - "integrity": "sha512-Z/zblEPp8cIvmEn6+tPDIHUbRu/0z5lqZ+NvolL5SvXWT5rQy7+Nch83M0++XzO0XrWRFWECgOAyE8bsJTl1GQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.18.2.tgz", - "integrity": "sha512-WXAVt595HjpmlfH4crSdM/1bcsqh+1weFRWIa9XMTx/XHZ9TCKMcr725tLYqWOgzKdeDrqVHxFotrvWcEsk2Tg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.18.2", - "@typescript-eslint/visitor-keys": "8.18.2", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.8.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.18.2.tgz", - "integrity": "sha512-Cr4A0H7DtVIPkauj4sTSXVl+VBWewE9/o40KcF3TV9aqDEOWoXF3/+oRXNby3DYzZeCATvbdksYsGZzplwnK/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.18.2", - "@typescript-eslint/types": "8.18.2", - "@typescript-eslint/typescript-estree": "8.18.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.18.2.tgz", - "integrity": "sha512-zORcwn4C3trOWiCqFQP1x6G3xTRyZ1LYydnj51cRnJ6hxBlr/cKPckk+PKPUw/fXmvfKTcw7bwY3w9izgx5jZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.18.2", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz", - "integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", - "dev": true - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", - "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001541", - "electron-to-chromium": "^1.4.535", - "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001554", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001554.tgz", - "integrity": "sha512-A2E3U//MBwbJVzebddm1YfNp7Nud5Ip+IPn4BozBmn4KqVX7AvluoIDFWjsv5OkGnKUXQVmMSoMKLa3ScCblcQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", - "dev": true - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/commander": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", - "dev": true, - "engines": { - "node": ">=16" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", - "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", - "dev": true, - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/editorconfig": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-2.0.0.tgz", - "integrity": "sha512-s1NQ63WQ7RNXH6Efb2cwuyRlfpbtdZubvfNe4vCuoyGPewNPY7vah8JUSOFBiJ+jr99Qh8t0xKv0oITc1dclgw==", - "dev": true, - "dependencies": { - "@one-ini/wasm": "0.1.1", - "commander": "^11.0.0", - "minimatch": "9.0.2", - "semver": "^7.5.3" - }, - "bin": { - "editorconfig": "bin/editorconfig" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.568", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.568.tgz", - "integrity": "sha512-3TCOv8+BY6Ltpt1/CmGBMups2IdKOyfEmz4J8yIS4xLSeMm0Rf+psSaxLuswG9qMKt+XbNbmADybtXGpTFlbDg==", - "dev": true - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.17.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.17.0.tgz", - "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.9.0", - "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.17.0", - "@eslint/plugin-kit": "^0.2.3", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.1", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.2.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-unused-imports": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.1.4.tgz", - "integrity": "sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0", - "eslint": "^9.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", - "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.14.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/expect/node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/expect/node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/expect/node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", - "dev": true, - "license": "ISC" - }, - "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", - "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", - "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jake": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", - "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jake/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/jake/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus/node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus/node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus/node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.2.tgz", - "integrity": "sha512-PZOT9g5v2ojiTL7r1xF6plNHLtOeTpSlDI007As2NlA2aYBMfVom17yqa6QzhmDP8QOhn7LjHTg7DFCVSSa6yg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", - "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/pure-rand": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", - "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ] - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", - "dev": true, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, - "node_modules/ts-jest": { - "version": "29.2.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", - "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "bs-logger": "^0.2.6", - "ejs": "^3.1.10", - "fast-json-stable-stringify": "^2.1.0", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.6.3", - "yargs-parser": "^21.1.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.18.2.tgz", - "integrity": "sha512-KuXezG6jHkvC3MvizeXgupZzaG5wjhU3yE8E7e6viOvAvD9xAWYp8/vy0WULTGe9DYDWcQu7aW03YIV3mSitrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.18.2", - "@typescript-eslint/parser": "8.18.2", - "@typescript-eslint/utils": "8.18.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.18.2.tgz", - "integrity": "sha512-adig4SzPLjeQ0Tm+jvsozSGiCliI2ajeURDGHjZ2llnA+A67HihCQ+a3amtPhUakd1GlwHxSRvzOZktbEvhPPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.18.2", - "@typescript-eslint/type-utils": "8.18.2", - "@typescript-eslint/utils": "8.18.2", - "@typescript-eslint/visitor-keys": "8.18.2", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.18.2.tgz", - "integrity": "sha512-y7tcq4StgxQD4mDr9+Jb26dZ+HTZ/SkfqpXSiqeUXZHxOUyjWDKsmwKhJ0/tApR08DgOhrFAoAhyB80/p3ViuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.18.2", - "@typescript-eslint/types": "8.18.2", - "@typescript-eslint/typescript-estree": "8.18.2", - "@typescript-eslint/visitor-keys": "8.18.2", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" - } - }, - "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, - "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "node_modules/v8-to-istanbul": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz", - "integrity": "sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 8acc737..0000000 --- a/package.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "@retiman/leetcode", - "version": "1.0.0", - "license": "MIT", - "author": "Min Huang", - "email": "min.huang@alumni.usc.edu", - "description": "Leetcode problems with solutions", - "homepage": "https://github.com/retiman/leetcode#README.md", - "repository": { - "type": "git", - "url": "git+https://github.com/retiman/leetcode.git" - }, - "scripts": { - "build": "tsc", - "clean": "node script/clean.mjs", - "fix": "npm run lint:fix && npm run format:fix", - "format": "prettier --check .", - "format:fix": "prettier --write .", - "lint": "eslint .", - "lint:fix": "eslint --fix .", - "snapshot:update": "npm run test -- -u", - "test": "jest --config test/jest.config.mjs", - "all": "npm run clean && npm run fix && npm run build && npm run test" - }, - "main": "dist/index.js", - "directories": { - "src": "src", - "test": "test" - }, - "dependencies": { - "@datastructures-js/priority-queue": "5.4.0" - }, - "devDependencies": { - "@eslint/js": "^9.17.0", - "@tsconfig/recommended": "^1.0.8", - "@types/jest": "^29.5.14", - "@types/node": "^22.10.2", - "editorconfig": "^2.0.0", - "eslint": "^9.17.0", - "eslint-plugin-unused-imports": "^4.1.4", - "fs-extra": "^11.2.0", - "jest": "^29.7.0", - "prettier": "^3.4.2", - "ts-jest": "^29.2.5", - "ts-node": "^10.9.2", - "typescript": "^5.7.2", - "typescript-eslint": "^8.18.2" - } -} diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..d8ab112 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,377 @@ +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. + +[[package]] +name = "black" +version = "24.10.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.9" +files = [ + {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, + {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, + {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, + {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, + {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, + {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, + {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, + {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, + {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, + {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, + {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, + {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, + {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, + {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, + {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, + {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, + {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, + {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, + {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, + {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, + {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, + {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.10)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "click" +version = "8.1.8" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, + {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "fastdiff" +version = "0.3.0" +description = "A fast native implementation of diff algorithm with a pure python fallback" +optional = false +python-versions = "*" +files = [ + {file = "fastdiff-0.3.0-py2.py3-none-any.whl", hash = "sha256:ca5f61f6ddf5a1564ddfd98132ad28e7abe4a88a638a8b014a2214f71e5918ec"}, + {file = "fastdiff-0.3.0.tar.gz", hash = "sha256:4dfa09c47832a8c040acda3f1f55fc0ab4d666f0e14e6951e6da78d59acd945a"}, +] + +[package.dependencies] +wasmer = ">=1.0.0" +wasmer-compiler-cranelift = ">=1.0.0" + +[[package]] +name = "flake8" +version = "7.1.2" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-7.1.2-py2.py3-none-any.whl", hash = "sha256:1cbc62e65536f65e6d754dfe6f1bada7f5cf392d6f5db3c2b85892466c3e7c1a"}, + {file = "flake8-7.1.2.tar.gz", hash = "sha256:c586ffd0b41540951ae41af572e6790dbd49fc12b3aa2541685d253d9bd504bd"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.12.0,<2.13.0" +pyflakes = ">=3.2.0,<3.3.0" + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "mypy" +version = "1.15.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"}, + {file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"}, + {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b"}, + {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3"}, + {file = "mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b"}, + {file = "mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828"}, + {file = "mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f"}, + {file = "mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5"}, + {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e"}, + {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c"}, + {file = "mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f"}, + {file = "mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f"}, + {file = "mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd"}, + {file = "mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f"}, + {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464"}, + {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee"}, + {file = "mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e"}, + {file = "mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22"}, + {file = "mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445"}, + {file = "mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d"}, + {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5"}, + {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036"}, + {file = "mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357"}, + {file = "mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf"}, + {file = "mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078"}, + {file = "mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba"}, + {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5"}, + {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b"}, + {file = "mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2"}, + {file = "mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980"}, + {file = "mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e"}, + {file = "mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43"}, +] + +[package.dependencies] +mypy_extensions = ">=1.0.0" +typing_extensions = ">=4.6.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "packaging" +version = "24.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pycodestyle" +version = "2.12.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3"}, + {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"}, +] + +[[package]] +name = "pyflakes" +version = "3.2.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, +] + +[[package]] +name = "pytest" +version = "8.3.4" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "snapshottest" +version = "1.0.0a1" +description = "Snapshot testing for pytest, unittest, Django, and Nose" +optional = false +python-versions = "*" +files = [ + {file = "snapshottest-1.0.0a1-py3-none-any.whl", hash = "sha256:fff0e1da3825c32d001018777c3b56d1eae1c850fc5b3418618da3d7f2cd152f"}, + {file = "snapshottest-1.0.0a1.tar.gz", hash = "sha256:6ef848ee4d6621baff79df6a36bb1da4d7eddf5013dc6b9ca9c361bc42c605b9"}, +] + +[package.dependencies] +fastdiff = ">=0.1.4,<1" +termcolor = "*" + +[package.extras] +nose = ["nose"] +pytest = ["pytest"] +test = ["django (>=1.10.6)", "nose", "pytest (>=4.6)", "pytest-cov"] + +[[package]] +name = "termcolor" +version = "2.5.0" +description = "ANSI color formatting for output in terminal" +optional = false +python-versions = ">=3.9" +files = [ + {file = "termcolor-2.5.0-py3-none-any.whl", hash = "sha256:37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8"}, + {file = "termcolor-2.5.0.tar.gz", hash = "sha256:998d8d27da6d48442e8e1f016119076b690d962507531df4890fcd2db2ef8a6f"}, +] + +[package.extras] +tests = ["pytest", "pytest-cov"] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "wasmer" +version = "1.1.0" +description = "Python extension to run WebAssembly binaries" +optional = false +python-versions = "*" +files = [ + {file = "wasmer-1.1.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:c2af4b907ae2dabcac41e316e811d5937c93adf1f8b05c5d49427f8ce0f37630"}, + {file = "wasmer-1.1.0-cp310-cp310-manylinux_2_24_x86_64.whl", hash = "sha256:ab1ae980021e5ec0bf0c6cdd3b979b1d15a5f3eb2b8a32da8dcb1156e4a1e484"}, + {file = "wasmer-1.1.0-cp310-none-win_amd64.whl", hash = "sha256:d0d93aec6215893d33e803ef0a8d37bf948c585dd80ba0e23a83fafee820bc03"}, + {file = "wasmer-1.1.0-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:1e63d16bd6e2e2272d8721647831de5c537e0bb08002ee6d7abf167ec02d5178"}, + {file = "wasmer-1.1.0-cp37-cp37m-manylinux_2_24_x86_64.whl", hash = "sha256:85e6a5bf44853e8e6a12e947ee3412da9e84f7ce49fc165ba5dbd293e9c5c405"}, + {file = "wasmer-1.1.0-cp37-none-win_amd64.whl", hash = "sha256:a182a6eca9b46d895b4985fc822fab8da3d2f84fab74ca27e55a7430a7fcf336"}, + {file = "wasmer-1.1.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:214d9a3cfb577ea9449eb2b5f13adceae34c55365e4c3d930066beb86a7f67bc"}, + {file = "wasmer-1.1.0-cp38-cp38-manylinux_2_24_x86_64.whl", hash = "sha256:b9e5605552bd7d2bc6337519b176defe83bc69b98abf3caaaefa4f7ec231d18a"}, + {file = "wasmer-1.1.0-cp38-none-win_amd64.whl", hash = "sha256:20b5190112e2e94a8947967f2bc683c9685855d0f34130d8434c87a55216a3bd"}, + {file = "wasmer-1.1.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:ee442f0970f40ec5e32011c92fd753fb2061da0faa13de13fafc730c31be34e3"}, + {file = "wasmer-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:aa112198b743cff2e391230436813fb4b244a24443e37866522b7197e3a034da"}, + {file = "wasmer-1.1.0-cp39-cp39-manylinux_2_24_x86_64.whl", hash = "sha256:c0b37117f6d3ff51ee96431c7d224d99799b08d174e30fcd0fcd7e2e3cb8140c"}, + {file = "wasmer-1.1.0-cp39-none-win_amd64.whl", hash = "sha256:a0a4730ec4907a4cb0d9d4a77ea2608c2c814f22a22b73fc80be0f110e014836"}, + {file = "wasmer-1.1.0-py3-none-any.whl", hash = "sha256:2caf8c67feae9cd4246421551036917811c446da4f27ad4c989521ef42751931"}, +] + +[[package]] +name = "wasmer-compiler-cranelift" +version = "1.1.0" +description = "The Cranelift compiler for the `wasmer` package (to compile WebAssembly module)" +optional = false +python-versions = "*" +files = [ + {file = "wasmer_compiler_cranelift-1.1.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:9869910179f39696a020edc5689f7759257ac1cce569a7a0fcf340c59788baad"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp310-cp310-manylinux_2_24_x86_64.whl", hash = "sha256:405546ee864ac158a4107f374dfbb1c8d6cfb189829bdcd13050143a4bd98f28"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp310-none-win_amd64.whl", hash = "sha256:bdf75af9ef082e6aeb752550f694273340ece970b65099e0746db0f972760d11"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:7d9c782b7721789b16e303b7e70c59df370896dd62b77e2779e3a44b4e1aa20c"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp37-cp37m-manylinux_2_24_x86_64.whl", hash = "sha256:ff7dd5bd69030b63521c24583bf0f5457cd2580237340b91ce35370f72a4a1cc"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp37-none-win_amd64.whl", hash = "sha256:447285402e366a34667a674db70458c491acd6940b797c175c0b0027f48e64bb"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:55a524985179f6b7b88ac973e8fac5a2574d3b125a966fba75fedd5a2525e484"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp38-cp38-manylinux_2_24_x86_64.whl", hash = "sha256:bd03db5a916ead51b442c66acad38847dfe127cf90b2019b1680f1920c4f8d06"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp38-none-win_amd64.whl", hash = "sha256:157d87cbd1d04adbad55b50cb4bedc28e444caf74797fd96df17390667e58699"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:ff25fc99ebafa04a6c271d08a90d17b927930e3019a2b333c7cfb48ba32c6f71"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9697ae082317a56776df8ff7df8c922eac38488ef38d3478fe5f0ca144c185ab"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp39-cp39-manylinux_2_24_x86_64.whl", hash = "sha256:2a4349b1ddd727bd46bc5ede741839dcfc5153c52f064a83998c4150d5d4a85c"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp39-none-win_amd64.whl", hash = "sha256:32fe38614fccc933da77ee4372904a5fa9c12b859209a2e4048a8284c4c371f2"}, + {file = "wasmer_compiler_cranelift-1.1.0-py3-none-any.whl", hash = "sha256:200fea80609cfb088457327acf66d5aa61f4c4f66b5a71133ada960b534c7355"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.12" +content-hash = "7fb2fde931b96232aad1885131c2deb21feb64ab9edd93a1d3baf1895bf9c472" diff --git a/prettier.config.mjs b/prettier.config.mjs deleted file mode 100644 index 1cf80c1..0000000 --- a/prettier.config.mjs +++ /dev/null @@ -1,16 +0,0 @@ -export default { - arrowParens: 'avoid', - bracketSameLine: true, - bracketSpacing: true, - endOfLine: 'lf', - printWidth: 120, - proseWrap: 'never', - quoteProps: 'as-needed', - semi: true, - singleQuote: true, - tabWidth: 2, - // Prettier and ESLint will fight over trailing commas. The latter has more options for configuring trailing commas, - // so disable here and let ESLint do all the work. - trailingComma: 'none', - useTabs: false -}; diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..86f5acb --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,33 @@ +[tool.poetry] +name = "leetcode" +version = "0.1.0" +description = "Problems and solutions for LeetCode" +authors = ["Min Huang "] +license = "MIT" +readme = "README.md" + +[tool.poetry.scripts] +all = "scripts:run_all" +format = "scripts:run_format" +lint = "scripts:run_lint" +test = "scripts:run_tests" + +[tool.poetry.dependencies] +python = "^3.12" + +[tool.poetry.group.dev.dependencies] +black = "^24.10.0" +flake8 = "^7.1.1" +mypy = "^1.13.0" +pytest = "^8.3.4" +snapshottest = "^1.0.0a0" + +[tool.black] +line-length = 120 + +[tool.mypy] +ignore_missing_imports = true + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/script/clean.mjs b/script/clean.mjs deleted file mode 100644 index 9c1ace0..0000000 --- a/script/clean.mjs +++ /dev/null @@ -1,4 +0,0 @@ -import * as fse from 'fs-extra'; - -fse.emptyDirSync('build'); -fse.emptyDirSync('dist'); diff --git a/scripts.py b/scripts.py new file mode 100644 index 0000000..0965ad6 --- /dev/null +++ b/scripts.py @@ -0,0 +1,24 @@ +import subprocess + + +def run_cmd(cmd): + subprocess.run(cmd, shell=True, check=True) + + +def run_all(): + run_format() + run_lint() + run_tests() + + +def run_lint(): + run_cmd("poetry run flake8 src test") + run_cmd("poetry run mypy src test") + + +def run_format(): + run_cmd("poetry run black src test") + + +def run_tests(): + run_cmd("poetry run pytest test") diff --git a/src/array/best-time-to-buy-and-sell-stock-ii.ts b/src/array/best-time-to-buy-and-sell-stock-ii.ts deleted file mode 100644 index 51fda18..0000000 --- a/src/array/best-time-to-buy-and-sell-stock-ii.ts +++ /dev/null @@ -1,49 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given an integer array prices where prices[i] is the price of a given stock on the ith day. -// -// On each day, you may decide to buy and/or sell the stock. You can only hold at most one share of the stock at any -// time. However, you can buy it then immediately sell it on the same day. -// -// Find and return the maximum profit you can achieve. -// -// See {@link https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii} -export { maxProfit }; - -// SOLUTION: -// -// The question is a bit contrived, as in reality this would never happen. Here, we are assuming we can go backwards -// in time to be able to buy at the low point and sell at the high point. Just keep that in mind: we have a time -// machine. -// -// In the easy version of this problem, we can buy the stock and sell it at some later date, ONE TIME! However, here, -// we can buy and sell as many times as we want. -// -// This actually makes the problem much easier because can simulate buying on every day and add to our profit if there -// is any. Honestly, this should be easy and the other one should be medium. -// -// COMPLEXITY: -// -// Runs in O(n) time. -function maxProfit(prices: number[]): number { - let profit = 0; - - // If we only have one day of price data, we can't make any profits at all. - if (prices.length === 1) { - return profit; - } - - for (let i = 1; i < prices.length; i++) { - // Look at the difference in price between yesterday and today. - const previous = prices[i - 1]; - const current = prices[i]; - const delta = current - previous; - - // If we end up having a profit by buying yesterday, let's sell it. - if (delta > 0) { - profit += delta; - } - } - - return profit; -} diff --git a/src/array/buildings-with-an-ocean-view.ts b/src/array/buildings-with-an-ocean-view.ts deleted file mode 100644 index e4ef23a..0000000 --- a/src/array/buildings-with-an-ocean-view.ts +++ /dev/null @@ -1,38 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// There are n buildings in a line. You are given an integer array heights of size n that represents the heights of the -// buildings in the line. -// -// The ocean is to the right of the buildings. A building has an ocean view if the building can see the ocean without -// obstructions. Formally, a building has an ocean view if all the buildings to its right have a smaller height. -// -// Return a list of indices (0-indexed) of buildings that have an ocean view, sorted in increasing order. -// -// See {@link https://leetcode.com/problems/buildings-with-an-ocean-view/} -export { findBuildings }; - -// SOLUTION: -// -// This is easier to do iterating from right to left (since the ocean is to the right). We can keep track of the -// tallest building we've seen so far, and if we encounter a building that is taller, we can add it to the list with -// ocean views. -// -// COMPLEXITY: -// -// Time complexity is O(n) because we are iterating through the list of buildings once. Space complexity is O(n) -// because we are storing a result array. -function findBuildings(heights: number[]): number[] { - const result: number[] = []; - let tallest = -Infinity; - - for (let i = heights.length - 1; i >= 0; i--) { - const height = heights[i]; - if (height > tallest) { - tallest = height; - result.push(i); - } - } - - // Problem says the buildings can't be in any order; they have to be in increasing order (of index, not height). - return result.reverse(); -} diff --git a/src/array/design-hit-counter.ts b/src/array/design-hit-counter.ts deleted file mode 100644 index e910880..0000000 --- a/src/array/design-hit-counter.ts +++ /dev/null @@ -1,69 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Design a hit counter which counts the number of hits received in the past 5 minutes (i.e., the past 300 seconds). -// -// Your system should accept a timestamp parameter (in seconds granularity), and you may assume that calls are being -// made to the system in chronological order (i.e., timestamp is monotonically increasing). Several hits may arrive -// roughly at the same time. -// -// See {@link https://leetcode.com/problems/design-hit-counter/} -export { HitCounter }; - -// SOLUTION: -// -// To do this efficiently we'll have to use a circular array buffer. This is the same technique used by time series -// databases. -// -// COMPLEXITY: -// -// Both methods run in O(1) time since we fix the array size at 300. -interface HitEvent { - timestamp: number; - hits: number; -} - -class HitCounter { - private readonly events: HitEvent[]; - - constructor() { - // Our granularity is seconds, so in theory, we only need to store only the last 300 seconds. Keep track of both - // hits by timestamp. - this.events = []; - - // Don't use Array.fill() here; you'll fill the array with 300 copies of the same object. - for (let i = 0; i < 300; i++) { - this.events[i] = { - timestamp: 0, - hits: 0 - }; - } - } - - hit(timestamp: number): void { - // Find the event in our circular buffer that would correspond to this timestamp. - const i = timestamp % 300; - const event = this.events[i]; - - // If we've already recorded hits at this timestamp, increment it. - if (event.timestamp === timestamp) { - event.hits++; - return; - } - - // Otherwise, we haven't, so set the hits to 1. - event.timestamp = timestamp; - event.hits = 1; - } - - getHits(timestamp: number): number { - let count = 0; - - for (const event of this.events) { - if (timestamp - event.timestamp < 300) { - count += event.hits; - } - } - - return count; - } -} diff --git a/src/array/dot-product-of-two-sparse-vectors.ts b/src/array/dot-product-of-two-sparse-vectors.ts deleted file mode 100644 index ddf6b20..0000000 --- a/src/array/dot-product-of-two-sparse-vectors.ts +++ /dev/null @@ -1,69 +0,0 @@ -// Given two sparse vectors, compute their dot product. -// -// Implement class SparseVector: -// -// - SparseVector(nums) Initializes the object with the vector nums -// - dotProduct(vec) Compute the dot product between the instance of SparseVector and vec -// -// A sparse vector is a vector that has mostly zero values, you should store the sparse vector efficiently and compute -// the dot product between two SparseVector. -// -// Follow up: What if only one of the vectors is sparse? -// -// See {@link https://leetcode.com/problems/dot-product-of-two-sparse-vectors/} -export { SparseVector }; - -// SOLUTION: -// -// If the vector is sparse, just store the non-zero values in a map of index to value. That will compress for storage; -// to compute the dot product you can either decompress and then do the dot product, or you can do the multiplication -// over the non-zero values directly. -// -// Doing it without decompression is more efficient, and we should start with the smaller vector to minimize the number -// of operations. -class SparseVector { - private readonly map: Map; - - constructor(nums: number[]) { - // This does the compression. - this.map = new Map(); - for (let i = 0; i < nums.length; i++) { - if (nums[i] !== 0) { - this.map.set(i, nums[i]); - } - } - } - - dotProduct(vec: SparseVector): number { - // Get the vectors in order of size; we'll iterate over the smaller vector. - const [a, b] = this.sorted(this.map, vec.map); - - // Iterate over the smaller vector and multiply the values, only if the larger vector has the same index. - let result = 0; - for (const [i, ai] of a) { - if (!b.has(i)) { - continue; - } - - const bi = b.get(i)!; - result += ai * bi; - } - - return result; - } - - // If one vector is dense, don't bother compressing it. Just compute directly. - dotProductDense(b: number[]) { - let result = 0; - for (const [i, ai] of this.map) { - const bi = b[i]; - result += ai * bi; - } - - return result; - } - - private sorted(a: Map, b: Map): [Map, Map] { - return a.size < b.size ? [a, b] : [b, a]; - } -} diff --git a/src/array/group-anagrams.ts b/src/array/group-anagrams.ts deleted file mode 100644 index c6f3a9d..0000000 --- a/src/array/group-anagrams.ts +++ /dev/null @@ -1,38 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an array of strings `strs`, group the anagrams together. You can return the answer in any order. -// -// An anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all -// the original letters exactly once. -// -// See {@link https://leetcode.com/problems/group-anagrams/} -export { groupAnagrams }; - -// SOLUTION: -// -// Each anagram can be rearranged into canonical form by sorting the letters. Then simply map the canonical form to -// each anagram. -// -// COMPLEXITY: -// -// Runs in O(n * m * log m) time, where n is the number of strings and m is the length of the longest string. This -// is because we have to sort each string's characters in O(m * log m), and there are n strings. -function groupAnagrams(texts: string[]): string[][] { - type Canonical = string; - type Anagram = string; - const map = new Map(); - - // Group all the anagrams together by their "canonical" form (aka a sorted version of the string). - for (let i = 0; i < texts.length; i++) { - const text = texts[i]; - const canonical = text.split('').sort().join(''); - const anagrams = map.get(canonical) ?? []; - - anagrams.push(text); - map.set(canonical, anagrams); - } - - // The map.values() function gives you an iterable of string[], so we need to convert it to an array of string[] - // instead. - return Array.from(map.values()); -} diff --git a/src/array/longest-consecutive-sequence.ts b/src/array/longest-consecutive-sequence.ts deleted file mode 100644 index 1ccaa05..0000000 --- a/src/array/longest-consecutive-sequence.ts +++ /dev/null @@ -1,60 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an unsorted array of integers nums, return the length of the longest consecutive elements sequence. -// -// You must write an algorithm that runs in O(n) time. -// -// See {@link https://leetcode.com/problems/longest-consecutive-sequence/} -export { longestConsecutive }; - -// SOLUTION: -// -// The problem is phrased in a very confusing way. The sequence doesn't ACTUALLY need to be consecutive within the -// array; it only needs to be consecutive if you plucked the sequence out of the array and sorted it. -// -// For example, [1, 500, 2, 3, 4] has consecutive elements [1, 2, 3, 4]. The fact that 500 appears in the middle -// is okay; the number 500 begins its own consecutive sequence of length 1. The other elements [1, 2, 3, 4] create -// a sequence of length 4. -// -// Conceptually, we'll do this by throwing all the array elements into a set. Then, for each element `x`, we can figure -// out if it's part of a sequence by repeatedly checking its predecessor `x - 1` in the set. -// -// COMPLEXITY: -// -// Runs in O(n) time. It may appear that the inner loop runs multiple times, but each element in the array is only -// processed once; the inner loop will skip `x - 1` if it was already part of some other sequence from a previous -// iteration. -function longestConsecutive(xs: number[]) { - let longest = 0; - - // Use a set to keep track of all elements in the array; we'll reference it to find out if a predecessor to an - // element exists as we iterate through the array. - const set = new Set(xs); - - for (let i = 0; i < xs.length; i++) { - // Let us consider if element x is part of some sequence. We can do this by considering its predecessor, x - 1. - let x = xs[i]; - - // If x - 1 is not in the set, x must begin some new sequence. Let's find out how long it is by incrementing x - // until we can't find any more consecutive elements. - if (!set.has(x - 1)) { - let length = 1; - x++; - - // Continue to increment x and look for consecutive elements in the set. - while (set.has(x)) { - length++; - x++; - } - - // Once we run out of elements, we have determined the length of the sequence between at x. We'll compare it to - // the longest sequence we've found so far. - longest = Math.max(longest, length); - } else { - // If x - 1 is in the set, we know it is part of some sequence. However, this sequence must've already been - // found by the inner loop above, so we can skip this element. - } - } - - return longest; -} diff --git a/src/array/max-chunks-to-make-sorted.ts b/src/array/max-chunks-to-make-sorted.ts deleted file mode 100644 index 34efbe9..0000000 --- a/src/array/max-chunks-to-make-sorted.ts +++ /dev/null @@ -1,37 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given an integer array arr of length n that represents a permutation of the integers in the range [0, n - 1]. -// -// We split arr into some number of chunks (i.e., partitions), and individually sort each chunk. After concatenating -// them, the result should equal the sorted array. -// -// Return the largest number of chunks we can make to sort the array. -// -// See {@link https://leetcode.com/problems/max-chunks-to-make-sorted/} -export { maxChunksToSorted }; - -// SOLUTION: -// -// Because we know the elements in the array are a permutation of numbers less than n, we can use a greedy approach -// by keeping track of the max element seen so far. -// -// Unlike the other problem, this does not require using a stack. -function maxChunksToSorted(xs: number[]) { - let chunks = 0; - let max = 0; - - for (let i = 0; i < xs.length; i++) { - max = Math.max(max, xs[i]); - - // Because all elements are a permutation of the numbers from 0 to xs.length - 1, if we encounter i === max, - // this means that for sure all elements < i can be sorted to form a chunk. - // - // Again, because all elements to the right of i are going to be greater than max (and i), then we can be sure - // that the next element begins a new chunk. - if (i === max) { - chunks++; - } - } - - return chunks; -} diff --git a/src/array/maximum-swap.ts b/src/array/maximum-swap.ts deleted file mode 100644 index ccea926..0000000 --- a/src/array/maximum-swap.ts +++ /dev/null @@ -1,75 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given an integer num. You can swap two digits at most once to get the maximum valued number. -// -// Return the maximum valued number you can get. -// -// See {@link https://leetcode.com/problems/maximum-swap/} -export { maximumSwap }; - -// SOLUTION: -// -// The simple idea of swapping the largest number with the first digit doesn't always work. It works a lot of the time, -// but a couple of cases mess up this algorithm: -// -// - 98368 would stay 98368; we need to swap the 3 and 8 to get 98863. -// - 1993 could become 9193, but we need to swap the 1 and other 9 to get 9913. -// -// The way to think about this is: -// -// 1. Find the FIRST digit that can be made bigger (larger digits appear later). -// 2. Swap it with the LAST occurrence of the largest digit. -// -// COMPLEXITY: -// -// The time complexity is O(n) where n is the number of digits. We iterate through the digits once to create our array -// and map. We iterate through the digits a second time to find the swap point. There is a nested loop, but the inner -// loop is bounded by the 10 digits, so it doesn't cause O(n^2) time complexity. -// -// The space complexity is O(n) because we store an array and map of the digits. -function maximumSwap(num: number): number { - // First convert the number to an array of digits. - const digits = num.toString().split('').map(Number); - - // Create a map of the last seen index of each digit. Use this to find out the last occurrence of any digit. - type Digit = number; - type LastSeen = number; - const map = new Map(); - for (let i = 0; i < digits.length; i++) { - const digit = digits[i]; - map.set(digit, i); - } - - // Find the first digit that can be made bigger by iterating through the digits list. - for (let i = 0; i < digits.length; i++) { - const smaller = digits[i]; - - // Find a larger digit to swap the smaller digit with. To do so, start at 9 and iterate downwards until we find a - // digit that is larger, and appears later in the number. - for (let larger = 9; larger >= 0; larger--) { - // Skip the digit if it's not larger. - if (larger <= smaller) { - break; - } - - // Skip the digit if it doesn't appear at all. - if (!map.has(larger)) { - continue; - } - - // Skip the digit if it appears before the smaller digit. - const j = map.get(larger)!; - if (j <= i) { - continue; - } - - // Success! We have found the largest digit that occurs as late as possible! Swap the digits and return the - // number. - [digits[i], digits[j]] = [digits[j], digits[i]]; - return Number(digits.join('')); - } - } - - // We made no swaps, so just return the number as is. - return num; -} diff --git a/src/array/merge-sorted-array.ts b/src/array/merge-sorted-array.ts deleted file mode 100644 index 64c356a..0000000 --- a/src/array/merge-sorted-array.ts +++ /dev/null @@ -1,72 +0,0 @@ -// DIFFICULTY: EASY -// -// You are given two integer arrays nums1 and nums2, sorted in non-decreasing order, and two integers m and n, -// representing the number of elements in nums1 and nums2 respectively. -// -// Merge nums1 and nums2 into a single array sorted in non-decreasing order. -// -// The final sorted array should not be returned by the function, but instead be stored inside the array nums1. To -// accommodate this, nums1 has a length of m + n, where the first m elements denote the elements that should be merged, -// and the last n elements are set to 0 and should be ignored. nums2 has a length of n. -// -// See {@link https://leetcode.com/problems/merge-sorted-array/} -export { merge }; - -// SOLUTION: -// -// TLDR: Work backwards. Send the largest elements from the smaller array to the end of the bigger array. -// -// This would be VERY easy, if you could use extra memory. To merge without using extra memory, we do need to merge -// into the bigger array, nums1. -// -// You could go about this in two different ways: -// -// 1. Start from the logical beginning of both arrays and swap elements as needed to maintain the correct order. -// 2. Start from the logical end of both arrays and add largest elements to the end of nums1. -// -// Going with option 1 is more natural, but it's more error prone and more work. There may be elements at the end of -// nums1 that need to be shifted to the right to make room. -// -// Going with option 2 is less natural, but it ensures you have enough space. Since nums1 has size m + n, and nums2 has -// size n, you can reliably fit all of nums2 into nums1 without any collisions or shifting. Additionally, if you use -// up all the elements in nums2, you don't need to do anything with the remaining elements of nums1 since they are -// already sorted. -// -// Pro tip: when asked to do something in place, and you have extra space, consider doing backwards iteration instead of -// forwards to avoid collisions and overwriting elements. Secondly, if you started with forwards iteration and realize -// you might have to shift elements, consider a backwards iteration approach to see if it might work better. -// -// The problem doesn't state this, but we assume using no extra memory either. -function merge(nums1: number[], m: number, nums2: number[], n: number): void { - let i = m - 1; - let j = n - 1; - let last = m + n - 1; - - // Send the largest elements from both arrays to the end of the array nums1. - while (i >= 0 && j >= 0) { - const a = nums1[i]; - const b = nums2[j]; - - if (a < b) { - nums1[last] = nums2[j]; - j--; - last--; - } else { - nums1[last] = nums1[i]; - i--; - last--; - } - } - - // Now we have consumed all elements in one of the arrays. - // - // If there are remaining elements in nums2, we should add them to the array nums1. - while (j >= 0) { - nums1[last] = nums2[j]; - j--; - last--; - } - - // On the other hand, if there are remaining elements in nums2, there's nothing to be done because that slice of the - // array is already sorted. -} diff --git a/src/array/pascals-triangle.ts b/src/array/pascals-triangle.ts deleted file mode 100644 index 4706d8c..0000000 --- a/src/array/pascals-triangle.ts +++ /dev/null @@ -1,41 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an integer numRows, return the first numRows of Pascal's triangle. -// -// In Pascal's triangle, each number is the sum of the two numbers directly above it as shown: -// -// See {@link https://leetcode.com/problems/pascals-triangle/} -export { generate }; - -// SOLUTION: -function generate(numRows: number): number[][] { - const triangle: number[][] = []; - - for (let i = 0; i < numRows; i++) { - // Special case the first row. - if (i === 0) { - triangle.push([1]); - continue; - } - - // The first and last elements of this row have value 1. - const current: number[] = []; - current.push(1); - - // The middle elements gotten by summing pairs of the previous row. - const previous = triangle[i - 1]; - if (previous.length > 1) { - for (let j = 1; j < previous.length; j++) { - current.push(previous[j] + previous[j - 1]); - } - } - - // The first and last elements of this row have value 1. - current.push(1); - - // Push the constructed row onto the triangle. - triangle.push(current); - } - - return triangle; -} diff --git a/src/array/top-k-frequent-elements.ts b/src/array/top-k-frequent-elements.ts deleted file mode 100644 index ad995f2..0000000 --- a/src/array/top-k-frequent-elements.ts +++ /dev/null @@ -1,36 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an integer array nums and an integer k, return the k most frequent elements. You may return the answer in any -// order. -// -// See {@link https://leetcode.com/problems/top-k-frequent-elements/} -export { topKFrequent }; - -// SOLUTION: -// -// Just map each number to its frequency then sort by frequency. Return the first k elements. -// -// COMPLEXITY: -// -// Time complexity dominated by the sort, which is O(n log n). Space complexity is O(n) because we are using a map to -// to store frequency. -function topKFrequent(xs: number[], k: number) { - type Frequency = number; - const map = new Map(); - - // Map each number to its frequency. - for (let i = 0; i < xs.length; i++) { - const x = xs[i]; - const frequency = map.get(x) ?? 0; - map.set(x, frequency + 1); - } - - // Now get all unique elements from the list. - const uniques = [...map.keys()]; - - // Sort the unique values by their frequency. Since we want the most frequent elements, we sort in decreasing order. - uniques.sort((a, b) => map.get(b)! - map.get(a)!); - - // Return the first k elements. - return uniques.slice(0, k); -} diff --git a/src/array/two-sum.ts b/src/array/two-sum.ts deleted file mode 100644 index 75ca38a..0000000 --- a/src/array/two-sum.ts +++ /dev/null @@ -1,40 +0,0 @@ -// DIFFICULTY: EASY -// -// Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to -// target. -// -// You may assume that each input would have exactly one solution, and you may not use the same element twice. -// -// You can return the answer in any order. -// -// See {@link https://leetcode.com/problems/two-sum/} -export { twoSum }; - -// SOLUTION: -// -// Use a hashmap to keep track of values we've already seen. This lets us avoid the O(n^2) solution of checking every -// possible pair of values. -// -// When iterating, you can check if the complement nums[j] = target - nums[i] already exists in the hashmap. If it -// does then we've found the solution and can return the indices immediately. -function twoSum(xs: number[], target: number) { - type Index = number; - type Complement = number; - const m = new Map(); - - for (let i = 0; i < xs.length; i += 1) { - const x = xs[i]; - const y = target - x; - - // If our map has the complementary value that would make up the target, we can return it immediately. - if (m.has(y)) { - return [i, m.get(y)]; - } - - // Otherwise, store the number and its index in the map. If we find the complement later, then this index will be - // the complement's complement and we can return the indices as normal. - m.set(x, i); - } - - return []; -} diff --git a/src/heap/README.md b/src/heap/README.md deleted file mode 100644 index deb08f9..0000000 --- a/src/heap/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Heaps - -## Common Phrases - -These phrases indicate a heap might be useful: - -- 'k closest', 'k largest', 'k smallest' -- 'top k elements' -- 'max k elements', 'min k elements' -- 'priority order' -- 'sorted order with constraints' diff --git a/src/heap/common/binary-search-max-heap.ts b/src/heap/common/binary-search-max-heap.ts deleted file mode 100644 index 68fef87..0000000 --- a/src/heap/common/binary-search-max-heap.ts +++ /dev/null @@ -1,78 +0,0 @@ -export interface Element { - value: T; - priority: number; -} - -// When there isn't time to implement a real heap in an interview, and the @datastructures-js/priority-queue library -// isn't available, you can use binary search insert in place of a real heap. This will be O(1) for popping, but it -// will be O(n) for insert (even though the binary search is O(logn), we do have to resize the array at O(n)). -export class BinarySearchMaxHeap { - private readonly heap: Element[]; - - private readonly compare: (a: number, b: number) => number; - - constructor(compare?: (a: number, b: number) => number) { - this.heap = []; - this.compare = compare ?? ((a, b) => a - b); - } - - public enqueue(value: T, priority?: number): BinarySearchMaxHeap { - const element: Element = { - value, - priority: this.getPriority(value, priority) - }; - - let left = 0; - let right = this.heap.length; - while (left < right) { - const mid = Math.floor((left + right) / 2); - const a = this.heap[mid]; - const b = element; - // You can use < here for non-stable inserts, if you don't care. Switch to <= for stable inserts. - if (this.compare(a.priority, b.priority) < 0) { - left = mid + 1; - } else { - right = mid; - } - } - - this.heap.splice(left, 0, element); - return this; - } - - public dequeue(): T { - if (this.isEmpty()) { - throw new Error('heap is empty'); - } - - return this.heap.pop()!.value; - } - - public front(): T { - if (this.isEmpty()) { - throw new Error('heap is empty'); - } - - return this.heap[this.heap.length - 1].value; - } - - public size(): number { - return this.heap.length; - } - - public isEmpty(): boolean { - return this.heap.length === 0; - } - - private getPriority(value: T, priority?: number): number { - if (priority !== undefined) { - return priority; - } - - if (typeof value === 'number') { - return value; - } - - return 0; - } -} diff --git a/src/heap/common/simple-max-heap.ts b/src/heap/common/simple-max-heap.ts deleted file mode 100644 index d0b896a..0000000 --- a/src/heap/common/simple-max-heap.ts +++ /dev/null @@ -1,63 +0,0 @@ -export interface Element { - value: T; - priority: number; -} - -// When there isn't time to implement a real heap in an interview, and the @datastructures-js/priority-queue library -// isn't available, just insert and sort. This will be O(nlogn) on insert but it's quick and dirty. -export class SimpleMaxHeap { - private readonly heap: Element[]; - - private readonly compare: (a: number, b: number) => number; - - constructor(compare?: (a: number, b: number) => number) { - this.heap = []; - this.compare = compare ?? ((a, b) => a - b); - } - - public enqueue(value: T, priority?: number): SimpleMaxHeap { - const element: Element = { - value, - priority: this.getPriority(value, priority) - }; - this.heap.push(element); - this.heap.sort((a, b) => this.compare(a.priority, b.priority)); - return this; - } - - public dequeue(): T { - if (this.isEmpty()) { - throw new Error('heap is empty'); - } - - return this.heap.pop()!.value; - } - - public front(): T { - if (this.isEmpty()) { - throw new Error('heap is empty'); - } - - return this.heap[this.heap.length - 1].value; - } - - public size(): number { - return this.heap.length; - } - - public isEmpty(): boolean { - return this.heap.length === 0; - } - - private getPriority(value: T, priority?: number): number { - if (priority !== undefined) { - return priority; - } - - if (typeof value === 'number') { - return value; - } - - return 0; - } -} diff --git a/src/heap/furthest-building-you-can-reach.ts b/src/heap/furthest-building-you-can-reach.ts deleted file mode 100644 index 30dfe42..0000000 --- a/src/heap/furthest-building-you-can-reach.ts +++ /dev/null @@ -1,79 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given an integer array heights representing the heights of buildings, some bricks, and some ladders. -// -// You start your journey from building 0 and move to the next building by possibly using bricks or ladders. -// -// While moving from building i to building i+1 (0-indexed), -// -// - If the current building's height is greater than or equal to the next building's height, you do not need a ladder -// or bricks. -// - If the current building's height is less than the next building's height, you can either use one ladder or -// (h[i+1] - h[i]) bricks. -// -// Return the furthest building index (0-indexed) you can reach if you use the given ladders and bricks optimally. -// -// See {@link https://leetcode.com/problems/furthest-building-you-can-reach/} -const { MinPriorityQueue } = require('@datastructures-js/priority-queue'); -export { furthestBuilding }; - -// SOLUTION: -// -// This cannot be solved with the sliding window technique; usually that technique involves finding a fixed size -// sub-string sub-array with constraints. Here we need to dynamically adjust our resource (brick/ladder) usage at -// each step, making a greedy algorithm better to solve the problem. -// -// COMPLEXITY: -// -// We iterate through n heights in O(n). Each iteration, we may push onto the heap, which is O(log(n)). This results in -// O(n * log(n)) time complexity. -function furthestBuilding(heights: number[], bricks: number, ladders: number): number { - // Use a heap/priority queue to store the number of jumps we need to make with either bricks or ladders. - const heap = new MinPriorityQueue(); - - for (let i = 0; i < heights.length - 1; i++) { - const delta = heights[i + 1] - heights[i]; - - // If the height is same level or lower, we can jump across with no penalty. - if (delta <= 0) { - continue; - } - - // If the height is higher by some amount, push it onto the heap and make a determination on whether we use a - // ladder or some bricks to scale the difference. - // - // We should use ladders on the biggest deltas, so if we greedily consume the smallest deltas from the heap, as - // long as we have enough ladders to cover the remaining length of the heap, we can continue. - heap.enqueue(delta); - - // As mentioned, the heap stores how many jumps that consume ladders or bricks at this point. Because ladders - // can represent any number of bricks, this means that we "buy" elements in the heap, or that the heap may have - // an ambient size. - // - // For example, normally each element pushed onto the heap must be popped off. However, if we have 3 ladders, - // the min heap is permitted to contain 3 elements that don't need to be popped off. These 3 elements are the - // biggest deltas, and we use ladders to traverse them. - // - // At this moment, we don't know which 3 elements will be the biggest, but we just treat 3 as the "floor" size of - // the heap and continue if we haven't met this floor. - if (ladders >= heap.size()) { - // Assume that this delta, at height i, will be claimed by a ladder. - continue; - } - - // If, at this point, we've run out of ladders, we'll need to pop off the smallest element and use bricks to cross - // the delta. This will cause the largest element to be different, but the ladder doesn't care how big the - // largest element is. - const smallest = heap.dequeue().element; - bricks -= smallest; - - // If we've run out of bricks, it's okay. But if we've gone negative into bricks, we can't cross this gap and - // we return i (since we can't get to i + 1). - if (bricks < 0) { - return i; - } - } - - // There's no building at heights.length; the last building is at heights.length - 1. - return heights.length - 1; -} diff --git a/src/heap/kth-largest-element.ts b/src/heap/kth-largest-element.ts deleted file mode 100644 index 33b3ce5..0000000 --- a/src/heap/kth-largest-element.ts +++ /dev/null @@ -1,60 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an integer array nums and an integer k, return the kth largest element in the array. -// -// Note that it is the kth largest element in the sorted order, not the kth distinct element. -// -// Can you solve it without sorting? -// -// See {@link https://leetcode.com/problems/kth-largest-element-in-an-array/} -const { MinPriorityQueue } = require('@datastructures-js/priority-queue'); -export { findKthLargest }; - -// SOLUTION: -// -// You can do this with quick select, similar to quick sort. The idea is to eliminate half of the search space at each -// step. You pick a pivot element (using heuristics, or even randomly), then partition the array into two halves: left -// half has elements smaller than the pivot, right half has elements larger than the pivot. -// -// After the partition, you can check if the pivot is the (n - k)th element. If it is, you're done. If not, recurse -// into the left or right partitions depending on the pivot's position. -// -// That said, the leetcode questions seem to be crafted to ruin poor pivot index choices. -// -// The other way to do this is to use a heap. You can do this an efficient way or an inefficient way. The inefficient -// way involves jamming all the elements onto a max heap, then popping off k elements to get the kth largest element. -// -// The efficient way is to maintain a min heap and only ever allow k elements on the heap. This way, each time you pop -// an element off the heap, you're left with ever larger elements. Since the heap size is k, the kth largest element -// will be at the top of the heap. -// -// COMPLEXITY: -// -// The quick select method has an average time complexity of O(n), but a worst case time complexity of O(n^2), -// especially if you pick the wrong pivot each time. However, the space complexity is O(1). -// -// The min heap approach is O(n log k) time complexity, and O(k) space complexity. -// -// The latter offers you more consistent run times. -function findKthLargest(nums: number[], k: number): number { - // Use a min heap because we'll want to pop off the smallest element every time we reach the limit. - const heap = new MinPriorityQueue(); - - // Iterate through the numbers, and jam numbers on the heap until we have k elements. - for (const num of nums) { - heap.enqueue(num); - - // Now we have the k + 1 smallest elements we've seen so far. If we get a new element, pop from the - // heap, which will leave us with larger elements in the heap. - // - // At the end, we'll pop off all the smallest elements, leaving us with the top k largest elements. - if (heap.size() > k) { - heap.dequeue(); - } - } - - // This is a min heap, so the smallest element is at the front. However, there are (k - 1) larger elements in - // elsewhere in the heap. That makes the front element the kth largest element. - const result = heap.front().element; - return result; -} diff --git a/src/heap/minimize-deviation-in-array.ts b/src/heap/minimize-deviation-in-array.ts deleted file mode 100644 index ad609cc..0000000 --- a/src/heap/minimize-deviation-in-array.ts +++ /dev/null @@ -1,77 +0,0 @@ -// DIFFICULTY: HARD -// -// You are given an array nums of n positive integers. -// -// You can perform two types of operations on any element of the array any number of times: -// -// If the element is even, divide it by 2. -// -// For example, if the array is [1,2,3,4], then you can do this operation on the last element, and the array will be -// [1,2,3,2]. -// -// If the element is odd, multiply it by 2. -// -// For example, if the array is [1,2,3,4], then you can do this operation on the first element, and the array will be -// [2,2,3,4]. -// -// The deviation of the array is the maximum difference between any two elements in the array. -// -// Return the minimum deviation the array can have after performing some number of operations. -// -// See {@link https://leetcode.com/problems/minimize-deviation-in-array/} -const { MaxPriorityQueue } = require('@datastructures-js/priority-queue'); -export { minimumDeviation }; - -// SOLUTION: -// -// To solve this problem, we have to multiply elements to make them bigger, or divide elements to make them smaller. -// We continue to do this until the minimum and maximum elements are as close as possible. -// -// To make this problem easier, we try to minimize the deviation by making bigger numbers smaller, instead of -// simultaneously trying to make numbers bigger and smaller. To do this, we multiply all odd numbers by 2, so that -// they all become even. Afterwards, we can choose to perform a division or not to make it smaller. -// -// COMPLEXITY: -// -// Runs in O(n * log(n)) time due to the heap operations. -function minimumDeviation(nums: number[]): number { - // Initialize a max heap of all the numbers in this array. - const heap = new MaxPriorityQueue(); - - // Normalize all the numbers so that they are even. Now we can consider only division as a way to make numbers - // smaller and closer to each other. If a previously odd number was too big, we will eventually resize it smaller - // by division if necessary. - for (let i = 0; i < nums.length; i++) { - if (nums[i] % 2 === 1) { - nums[i] *= 2; - } - - heap.enqueue(nums[i]); - } - - let min = Math.min(...nums); - let deviation = Infinity; - - // Calculate the current deviation using the max element in the array, then half the max element and return it to - // the heap. Then repeat to bring the deviation down more and more. - while (true) { - // Calculate current deviation. - const max = heap.dequeue().element; - deviation = Math.min(deviation, max - min); - - // Oh no! If the max value was odd, we can't halve it and re-insert it into the heap. This means whatever the - // deviation is now, we are stuck, as halving smaller even values will not change the deviation. - if (max % 2 === 1) { - break; - } - - // Halve the max value and return it into the heap for re-processing. - const value = max / 2; - heap.enqueue(value); - - // Update the minimum value in case we've changed the minimum value by manipulating the max value. - min = Math.min(value, min); - } - - return deviation; -} diff --git a/src/heap/numbers-of-orders-in-the-backlog.ts b/src/heap/numbers-of-orders-in-the-backlog.ts deleted file mode 100644 index d915c6c..0000000 --- a/src/heap/numbers-of-orders-in-the-backlog.ts +++ /dev/null @@ -1,150 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given a 2D integer array orders, where each orders[i] = [pricei, amounti, orderTypei] denotes that amounti -// orders have been placed of type orderTypei at the price pricei. The orderTypei is: -// -// 0 if it is a batch of buy orders, or -// 1 if it is a batch of sell orders. -// -// Note that orders[i] represents a batch of amounti independent orders with the same price and order type. All orders -// represented by orders[i] will be placed before all orders represented by orders[i+1] for all valid i. -// -// There is a backlog that consists of orders that have not been executed. The backlog is initially empty. When an order -// is placed, the following happens: -// -// If the order is a buy order, you look at the sell order with the smallest price in the backlog. If that sell order's -// price is smaller than or equal to the current buy order's price, they will match and be executed, and that sell order -// will be removed from the backlog. Else, the buy order is added to the backlog. -// -// Vice versa, if the order is a sell order, you look at the buy order with the largest price in the backlog. If that -// buy order's price is larger than or equal to the current sell order's price, they will match and be executed, and -// that buy order will be removed from the backlog. Else, the sell order is added to the backlog. -// -// Return the total amount of orders in the backlog after placing all the orders from the input. Since this number can -// be large, return it modulo 10^9 + 7. -// -// See {@link https://leetcode.com/problems/number-of-orders-in-the-backlog/} -const { MaxPriorityQueue, MinPriorityQueue } = require('@datastructures-js/priority-queue'); -export { getNumberOfBacklogOrders }; - -// SOLUTION: -// -// This problem looks like it can be solved by maintaining a sorted list of buy orders and sell orders (aka heaps). -// For a sell order, you want buy orders in largest to smallest (max heap). For a buy order, you want sell orders -// from smallest to largest (min heap). -// -// COMPLEXITY: -// -// The time complexity is O(n * log(n)) because we are using heaps to maintain the order of the orders. -function getNumberOfBacklogOrders(orders: number[][]): number { - // Order = [price, amount]. - type Order = [number, number]; - - // Order by [price, _] for buys and sells. - const buys = new MaxPriorityQueue({ priority: (order: Order) => order[0] }); - const sells = new MinPriorityQueue({ priority: (order: Order) => order[0] }); - - function handleBuy(price: number, amount: number) { - while (amount > 0) { - // No more sellers, push buy order to the backlog. - if (sells.size() === 0) { - buys.enqueue([price, amount]); - return; - } - - const order = sells.front().element; - - // If the buy price is lower than the minimum selling price, we cannot find a match, so let's just put the buy - // order on the backlog. - if (price < order[0]) { - buys.enqueue([price, amount]); - return; - } - - // Otherwise, we have found a match, so let's execute the order. - const a = Math.min(amount, order[1]); - amount -= a; - order[1] -= a; - - // If the sell order has been exhausted, pop it from the heap. - if (order[1] === 0) { - sells.dequeue(); - } - - // If the buy order has been exhausted, we've processed the buy order, so return. - if (amount === 0) { - return; - } - } - } - - function handleSell(price: number, amount: number) { - while (amount > 0) { - // No more buyers, push sell order to the backlog. - if (buys.size() === 0) { - sells.enqueue([price, amount]); - return; - } - - const order = buys.front().element; - - // If the sell price is higher than the maximum buy price, we cannot find a buyer, so push onto the backlog. - if (price > order[0]) { - sells.enqueue([price, amount]); - return; - } - - // Otherwise, we have found a match, so let's execute the order. - const a = Math.min(amount, order[1]); - amount -= a; - order[1] -= a; - - // If the buy order has been exhausted, pop it from the heap. - if (order[1] === 0) { - buys.dequeue(); - } - - // If the buy order has been exhausted, we've processed the buy order, so return. - if (amount === 0) { - return; - } - } - } - - for (const order of orders) { - const [price, amount, orderType] = order; - - // Nothing to buy or sell here. - if (amount === 0) { - continue; - } - - // Buy order; match with the cheapest seller. - if (orderType === 0) { - handleBuy(price, amount); - continue; - } - - // Sell order; match with the highest buyer. - if (orderType === 1) { - handleSell(price, amount); - continue; - } - } - - // Calculate *amount* of total orders remaining in the backlog. - const modulus = 1e9 + 7; - let total = 0; - - for (const item of buys.toArray()) { - const [_, amount] = item.element; - total = (total + amount) % modulus; - } - - for (const item of sells.toArray()) { - const [_, amount] = item.element; - total = (total + amount) % modulus; - } - - return total; -} diff --git a/src/leetcode/__init__.py b/src/leetcode/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/leetcode/array/best_time_to_buy_and_sell_stock_ii.py b/src/leetcode/array/best_time_to_buy_and_sell_stock_ii.py new file mode 100644 index 0000000..2e9f9c4 --- /dev/null +++ b/src/leetcode/array/best_time_to_buy_and_sell_stock_ii.py @@ -0,0 +1,48 @@ +# DIFFICULTY: MEDIUM +# +# You are given an integer array prices where prices[i] is the price of a given stock on the ith day. +# +# On each day, you may decide to buy and/or sell the stock. You can only hold at most one share of the stock at any +# time. However, you can buy it then immediately sell it on the same day. +# +# Find and return the maximum profit you can achieve. +# +# See https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii +class Solution: + def maxProfit(self, prices: list[int]) -> int: + """ + SOLUTION: + + The question is a bit contrived, as in reality this would never happen. Here, we are assuming we can go + backwards in time to be able to buy at the low point and sell at the high point. Just keep that in mind: we + have a time machine. + + In the easy version of this problem, we can buy the stock and sell it at some later date, ONE TIME! However, + here, we can buy and sell as many times as we want. + + This actually makes the problem much easier because can simulate buying on every day and add to our profit if + there is any. Honestly, this should be easy and the other one should be medium. + + COMPLEXITY: + + Time complexity is O(n). + + Space complexity is O(1). + """ + profit = 0 + + # If we only have one day of price data, we can't make any profits at all. + if len(prices) == 1: + return profit + + for i in range(1, len(prices)): + # Look at the difference in price between yesterday and today. + previous = prices[i - 1] + current = prices[i] + delta = current - previous + + # If we end up having a profit by buying yesterday, let's sell it. + if delta > 0: + profit += delta + + return profit diff --git a/src/leetcode/array/buildings_with_an_ocean_view.py b/src/leetcode/array/buildings_with_an_ocean_view.py new file mode 100644 index 0000000..c2a33e9 --- /dev/null +++ b/src/leetcode/array/buildings_with_an_ocean_view.py @@ -0,0 +1,36 @@ +# DIFFICULTY: MEDIUM +# +# There are n buildings in a line. You are given an integer array heights of size n that represents the heights of the +# buildings in the line. +# +# The ocean is to the right of the buildings. A building has an ocean view if the building can see the ocean without +# obstructions. Formally, a building has an ocean view if all the buildings to its right have a smaller height. +# +# Return a list of indices (0-indexed) of buildings that have an ocean view, sorted in increasing order. +# +# See https://leetcode.com/problems/buildings-with-an-ocean-view +class Solution: + def findBuildings(self, heights: list[int]) -> list[int]: + """ + SOLUTION: + + This is easier to do iterating from right to left (since the ocean is to the right). We can keep track of the + tallest building we've seen so far, and if we encounter a building that is taller, we can add it to the list + with ocean views. + + COMPLEXITY: + + Time complexity is O(n) because we are iterating through the list of buildings once, and reversing once. + + Space complexity is O(n) because we are storing a result array. + """ + result: list[int] = [] + tallest = float("-inf") + + for i in reversed(range(len(heights))): + height = heights[i] + if height > tallest: + tallest = height + result.append(i) + + return result[::-1] diff --git a/src/leetcode/array/design_hit_counter.py b/src/leetcode/array/design_hit_counter.py new file mode 100644 index 0000000..7f3d39a --- /dev/null +++ b/src/leetcode/array/design_hit_counter.py @@ -0,0 +1,46 @@ +# DIFFICULTY: MEDIUM +# +# Design a hit counter which counts the number of hits received in the past 5 minutes (i.e., the past 300 seconds). +# +# Your system should accept a timestamp parameter (in seconds granularity), and you may assume that calls are being +# made to the system in chronological order (i.e., timestamp is monotonically increasing). Several hits may arrive +# roughly at the same time. +# +# See https://leetcode.com/problems/design-hit-counter +class HitCounter: + def __init__(self): + """ + SOLUTION: + + To do this efficiently we'll have to use a circular array buffer. This is the same technique used by time + series databases. + + COMPLEXITY: + + Both methods run in O(1) time since we fix the array size at 300. + """ + # Each event is a [timestamp, hit_count]. Since our granularity is in seconds, we only need to track the last + # 300 seconds. + self.timestamps = [0 for _ in range(300)] + self.hits = [0 for _ in range(300)] + + def hit(self, timestamp: int) -> None: + # Find the index of the event in our circular buffer that would correspond to this timestamp. + i = timestamp % 300 + + # If we've already recorded this timestamp, increment it. + if self.timestamps[i] == timestamp: + self.hits[i] += 1 + # If we haven't, set the hits to 1. + else: + self.timestamps[i] = timestamp + self.hits[i] = 1 + + def getHits(self, timestamp: int) -> int: + count = 0 + + for i, t in enumerate(self.timestamps): + if timestamp - t < 300: + count += self.hits[i] + + return count diff --git a/src/leetcode/array/dot_product_of_two_sparse_vectors.py b/src/leetcode/array/dot_product_of_two_sparse_vectors.py new file mode 100644 index 0000000..4a06ba9 --- /dev/null +++ b/src/leetcode/array/dot_product_of_two_sparse_vectors.py @@ -0,0 +1,49 @@ +# Given two sparse vectors, compute their dot product. +# +# Implement class SparseVector: +# +# - SparseVector(nums) Initializes the object with the vector nums +# - dotProduct(vec) Compute the dot product between the instance of SparseVector and vec +# +# A sparse vector is a vector that has mostly zero values, you should store the sparse vector efficiently and compute +# the dot product between two SparseVector. +# +# Follow up: What if only one of the vectors is sparse? +# +# See https://leetcode.com/problems/dot-product-of-two-sparse-vectors +class SparseVector: + def __init__(self, nums: list[int]): + """ + SOLUTION: + + If the vector is sparse, just store the non-zero values in a map of index to value. That will compress for + storage; to compute the dot product you can either decompress and then do the dot product, or you can do the + multiplication over the non-zero values directly. + + Doing it without decompression is more efficient, and we should start with the smaller vector to minimize the + number of operations. + + COMPLEXITY: + + Best case both vectors are sparse and we get O(k) where k is the number of non-zero elements. But in the worst + case scenario, it could still be O(n) if both vectors are dense. + """ + # This compresses the vector so that non-zero values are mapped. + self.map: dict[int, int] = {i: num for i, num in enumerate(nums) if num != 0} + + def dotProduct(self, vec: "SparseVector") -> int: + # Get the vectors in order of size; we'll iterate over the smaller vector. + smaller, larger = self.__ordered(self.map, vec.map) + + result = 0 + for i, value in smaller.items(): + if i in larger: + result += value * larger[i] + + return result + + def __ordered(self, a: dict[int, int], b: dict[int, int]) -> tuple[dict[int, int], dict[int, int]]: + if len(a) < len(b): + return (a, b) + else: + return (b, a) diff --git a/src/leetcode/array/group_anagrams.py b/src/leetcode/array/group_anagrams.py new file mode 100644 index 0000000..6faf14b --- /dev/null +++ b/src/leetcode/array/group_anagrams.py @@ -0,0 +1,32 @@ +# DIFFICULTY: MEDIUM +# +# Given an array of strings `strs`, group the anagrams together. You can return the answer in any order. +# +# An anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all +# the original letters exactly once. +# +# See https://leetcode.com/problems/group-anagrams +from collections import defaultdict + + +class Solution: + def groupAnagrams(self, texts: list[str]) -> list[list[str]]: + """ + SOLUTION: + + Each anagram can be rearranged into canonical form by sorting the letters. Then simply map the canonical form to + each anagram. + + COMPLEXITY: + + Runs in O(n * m * log m) time, where n is the number of strings and m is the length of the longest string. This + is because we have to sort each string's characters in O(m * log m), and there are n strings. + """ + # Define a map of canonical representation -> list of anagrams. + map: dict[str, list[str]] = defaultdict(list) + + for text in texts: + canonical = "".join(sorted(text)) + map[canonical].append(text) + + return list(map.values()) diff --git a/src/leetcode/array/longest_consecutive_sequence.py b/src/leetcode/array/longest_consecutive_sequence.py new file mode 100644 index 0000000..ee82062 --- /dev/null +++ b/src/leetcode/array/longest_consecutive_sequence.py @@ -0,0 +1,59 @@ +# DIFFICULTY: MEDIUM +# +# Given an unsorted array of integers nums, return the length of the longest consecutive elements sequence. +# +# You must write an algorithm that runs in O(n) time. +# +# See https://leetcode.com/problems/longest-consecutive-sequence +class Solution: + def longestConsecutive(self, xs: list[int]): + """ + SOLUTION: + + The problem is phrased in a very confusing way. The sequence doesn't ACTUALLY need to be consecutive within the + array; it only needs to be consecutive if you plucked the sequence out of the array and sorted it. + + For example, [1, 500, 2, 3, 4] has consecutive elements [1, 2, 3, 4]. The fact that 500 appears in the middle + is okay; the number 500 begins its own consecutive sequence of length 1. The other elements [1, 2, 3, 4] create + a sequence of length 4. + + Conceptually, we'll do this by throwing all the array elements into a set. Then, for each element `x`, we can + figure out if it's part of a sequence by repeatedly checking its predecessor `x - 1` in the set. + + COMPLEXITY: + + Runs in O(n) time. It may appear that the inner loop runs multiple times, but each element in the array is only + processed once; the inner loop will skip `x - 1` if it was already part of some other sequence from a previous + iteration. + """ + longest = 0 + + # Use a set to keep track of all elements in the array; we'll reference it to find out if a predecessor to an + # element exists as we iterate through the array. + uniques: set[int] = set(xs) + + for x in uniques: + # Let us consider if element x is part of some sequence. We can do this by considering its predecessor, + # x - 1. + # + # If x - 1 is not in the set, x must begin some new sequence. Let's find out how long it is by incrementing + # x until we can't find any more consecutive elements. + if x - 1 not in uniques: + # We know that x is in the array, so let's start checking x + 1 and onwards for the length of a + # consecutive sequence. + x = x + 1 + length = 1 + + while x in uniques: + x += 1 + length += 1 + + # Once we run out of elements, we have determined the length of the sequence between at x. We'll + # compare it to the longest sequence we've found so far. + longest = max(longest, length) + else: + # If x - 1 is in the set, we know it is part of some sequence. However, this sequence must've already + # been found by the inner loop above, so we can skip this element. + pass + + return longest diff --git a/src/leetcode/array/max_chunks_to_make_sorted.py b/src/leetcode/array/max_chunks_to_make_sorted.py new file mode 100644 index 0000000..a2f8733 --- /dev/null +++ b/src/leetcode/array/max_chunks_to_make_sorted.py @@ -0,0 +1,39 @@ +# DIFFICULTY: MEDIUM +# +# You are given an integer array arr of length n that represents a permutation of the integers in the range [0, n - 1]. +# +# We split arr into some number of chunks (i.e., partitions), and individually sort each chunk. After concatenating +# them, the result should equal the sorted array. +# +# Return the largest number of chunks we can make to sort the array. +# +# See https://leetcode.com/problems/max-chunks-to-make-sorted +class Solution: + def maxChunksToSorted(self, xs: list[int]) -> int: + """ + SOLUTION: + + Because we know the elements in the array are a permutation of numbers less than n, we can use a greedy approach + by keeping track of the max element seen so far. + + Unlike the other problem, this does not require using a stack. + + COMPLEXITY: + + Runs in O(n). + """ + chunks = 0 + max_chunks = 0 + + for i, x in enumerate(xs): + max_chunks = max(x, max_chunks) + + # Because all elements are a permutation of the numbers from 0 to xs.length - 1, if we encounter i === max, + # this means that for sure all elements < i can be sorted to form a chunk. + # + # Again, because all elements to the right of i are going to be greater than max (and i), then we can be + # sure that the next element begins a new chunk. + if i == max_chunks: + chunks += 1 + + return chunks diff --git a/src/leetcode/array/maximum_swap.py b/src/leetcode/array/maximum_swap.py new file mode 100644 index 0000000..a9bf23c --- /dev/null +++ b/src/leetcode/array/maximum_swap.py @@ -0,0 +1,66 @@ +# DIFFICULTY: MEDIUM +# +# You are given an integer num. You can swap two digits at most once to get the maximum valued number. +# +# Return the maximum valued number you can get. +# +# See https://leetcode.com/problems/maximum-swap +class Solution: + def maximumSwap(self, num: int) -> int: + """ + SOLUTION: + + The simple idea of swapping the largest number with the first digit doesn't always work. It works a lot of the time, + but a couple of cases mess up this algorithm: + + - 98368 would stay 98368; we need to swap the 3 and 8 to get 98863. + - 1993 could become 9193, but we need to swap the 1 and other 9 to get 9913. + + The way to think about this is: + + 1. Find the FIRST digit that can be made bigger (larger digits appear later). + 2. Swap it with the LAST occurrence of the largest digit. + + COMPLEXITY: + + The time complexity is O(n) where n is the number of digits. We iterate through the digits once to create our array + and map. We iterate through the digits a second time to find the swap point. There is a nested loop, but the inner + loop is bounded by the 10 digits, so it doesn't cause O(n^2) time complexity. + + The space complexity is O(n) because we store an array and map of the digits. + """ + # First convert the number to an array of digits. + digits = list(str(num)) + + # Create a map of digit -> index where it was last seen. Use this to find out the last occurrence of any digit. + last_seen: dict[int, int] = {} + for i, digit in enumerate(digits): + last_seen[int(digit)] = i + + # Find the first digit that can be made bigger by iterating through the digits list. + for i, digit in enumerate(digits): + smaller = int(digit) + + # Find a larger digit to swap the smaller digit with. To do so, start at 9 and iterate downwards until we + # find a digit that is larger, and appears later in the number. + for larger in range(9, smaller, -1): + # Skip the digit if it's not larger. + if larger <= smaller: + continue + + # Skip the digit if it doesn't appear at all. + if larger not in last_seen: + continue + + # Skip the digit if it appears before the smaller digit. + j = last_seen[larger] + if j <= i: + continue + + # Success! We have found the largest digit that occurs as late as possible! Swap the digits and return + # the number. + digits[i], digits[j] = digits[j], digits[i] + return int("".join(digits)) + + # We made no swaps, so just return the number as is. + return num diff --git a/src/leetcode/array/merge_sorted_array.py b/src/leetcode/array/merge_sorted_array.py new file mode 100644 index 0000000..402312d --- /dev/null +++ b/src/leetcode/array/merge_sorted_array.py @@ -0,0 +1,68 @@ +# DIFFICULTY: EASY +# +# You are given two integer arrays nums1 and nums2, sorted in non-decreasing order, and two integers m and n, +# representing the number of elements in nums1 and nums2 respectively. +# +# Merge nums1 and nums2 into a single array sorted in non-decreasing order. +# +# The final sorted array should not be returned by the function, but instead be stored inside the array nums1. To +# accommodate this, nums1 has a length of m + n, where the first m elements denote the elements that should be merged, +# and the last n elements are set to 0 and should be ignored. nums2 has a length of n. +# +# See https://leetcode.com/problems/merge-sorted-array +class Solution: + def merge(self, xs: list[int], m: int, ys: list[int], n: int) -> None: + """ + SOLUTION: + + TLDR: Work backwards. Send the largest elements from the smaller array to the end of the bigger array. + + This would be VERY easy, if you could use extra memory. To merge without using extra memory, we do need to merge + into the bigger array, nums1. + + You could go about this in two different ways: + + 1. Start from the logical beginning of both arrays and swap elements as needed to maintain the correct order. + 2. Start from the logical end of both arrays and add largest elements to the end of nums1. + + Going with option 1 is more natural, but it's more error prone and more work. There may be elements at the end of + nums1 that need to be shifted to the right to make room. + + Going with option 2 is less natural, but it ensures you have enough space. Since nums1 has size m + n, and nums2 has + size n, you can reliably fit all of nums2 into nums1 without any collisions or shifting. Additionally, if you use + up all the elements in nums2, you don't need to do anything with the remaining elements of nums1 since they are + already sorted. + + Pro tip: when asked to do something in place, and you have extra space, consider doing backwards iteration instead of + forwards to avoid collisions and overwriting elements. Secondly, if you started with forwards iteration and realize + you might have to shift elements, consider a backwards iteration approach to see if it might work better. + + The problem doesn't state this, but we assume using no extra memory either. + """ + i = m - 1 + j = n - 1 + last = m + n - 1 + + # Send the largest elements from both arrays to the end of the array xs. + while i >= 0 and j >= 0: + a = xs[i] + b = ys[j] + + if a < b: + xs[last] = ys[j] + j -= 1 + last -= 1 + else: + xs[last] = xs[i] + i -= 1 + last -= 1 + + # Now we have consumed all elements in one of the arrays. If there are remaining elements in ys, we should add + # them to the array xs. + # + # On the other hand, if there are remaining elements in xs, there's nothing to be done because that slice of the + # array is already sorted. + while j >= 0: + xs[last] = ys[j] + j -= 1 + last -= 1 diff --git a/src/leetcode/array/pascals_triangle.py b/src/leetcode/array/pascals_triangle.py new file mode 100644 index 0000000..e41fea7 --- /dev/null +++ b/src/leetcode/array/pascals_triangle.py @@ -0,0 +1,33 @@ +# DIFFICULTY: MEDIUM +# +# Given an integer numRows, return the first numRows of Pascal's triangle. +# +# In Pascal's triangle, each number is the sum of the two numbers directly above it as shown: +# +# See https://leetcode.com/problems/pascals-triangle +class Solution: + def generate(self, numRows: int) -> list[list[int]]: + triangle: list[list[int]] = [] + + for i in range(numRows): + # Special case the first row + if i == 0: + triangle.append([1]) + continue + + # The first and last elements of this row have value 1. + current = [1] + + # The middle elements gotten by summing pairs of the previous row. + previous = triangle[i - 1] + if len(previous) > 1: + for j in range(1, len(previous)): + current.append(previous[j] + previous[j - 1]) + + # The first and last elements of this row have value 1. + current.append(1) + + # Push the constructed row onto the triangle. + triangle.append(current) + + return triangle diff --git a/src/leetcode/array/top_k_frequent_elements.py b/src/leetcode/array/top_k_frequent_elements.py new file mode 100644 index 0000000..22181ce --- /dev/null +++ b/src/leetcode/array/top_k_frequent_elements.py @@ -0,0 +1,37 @@ +# DIFFICULTY: MEDIUM +# +# Given an integer array nums and an integer k, return the k most frequent elements. You may return the answer in any +# order. +# +# See https://leetcode.com/problems/top-k-frequent-elements +from collections import defaultdict + + +class Solution: + def topKFrequent(self, xs: list[int], k: int) -> list[int]: + """ + SOLUTION: + + Just map each number to its frequency then sort by frequency. Return the first k elements. + + COMPLEXITY: + + Time complexity dominated by the sort, which is O(n log n). Space complexity is O(n) because we are using a map + to store frequency. + """ + # Create a map of number -> frequency. + map: dict[int, int] = defaultdict(int) + + # Now populate the map by mapping each number to its frequency. + for x in xs: + map[x] += 1 + + # Now get all the unique elements from the list. + uniques = list(map.keys()) + + # Sort the unique values by their frequency. Since we want the most frequent elements, we sort in decreasing + # order (aka reverse order). + uniques = sorted(map.keys(), key=lambda x: map[x], reverse=True) + + # Return the first k elements + return uniques[:k] diff --git a/src/leetcode/array/two_sum.py b/src/leetcode/array/two_sum.py new file mode 100644 index 0000000..5e0b8f5 --- /dev/null +++ b/src/leetcode/array/two_sum.py @@ -0,0 +1,42 @@ +# DIFFICULTY: EASY +# +# Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to +# target. +# +# You may assume that each input would have exactly one solution, and you may not use the same element twice. +# +# You can return the answer in any order. +# +# See https://leetcode.com/problems/two-sum +class Solution: + def twoSum(self, xs: list[int], target: int) -> list[int]: + """ + SOLUTION: + + Use a hashmap to keep track of values we've already seen. This lets us avoid the O(n^2) solution of checking every + possible pair of values. + + When iterating, you can check if the complement nums[j] = target - nums[i] already exists in the hashmap. If it + does then we've found the solution and can return the indices immediately. + + COMPLEXITY: + + Time complexity is O(n). + + Space complexity is O(n). + """ + # Map number complement -> index where it appears. + m: dict[int, int] = {} + + for i, x in enumerate(xs): + complement = target - x + + # If our map has the complementary value that would make up the target, we can return it immediately. + if complement in m: + return [i, m[complement]] + + # Otherwise, store the number and its index in the map. If we find the complement later, then this index + # will be the complement's complement and we can return the indices as normal. + m[x] = i + + return [] diff --git a/src/binary-search/README.md b/src/leetcode/binary_search/README.md similarity index 100% rename from src/binary-search/README.md rename to src/leetcode/binary_search/README.md diff --git a/src/binary-search/find-first-and-last-position-of-element-in-sorted-array.ts b/src/leetcode/binary_search/find-first-and-last-position-of-element-in-sorted-array.ts similarity index 100% rename from src/binary-search/find-first-and-last-position-of-element-in-sorted-array.ts rename to src/leetcode/binary_search/find-first-and-last-position-of-element-in-sorted-array.ts diff --git a/src/binary-search/find-peak-element.ts b/src/leetcode/binary_search/find-peak-element.ts similarity index 100% rename from src/binary-search/find-peak-element.ts rename to src/leetcode/binary_search/find-peak-element.ts diff --git a/src/binary-search/find-the-smallest-divisor-given-a-threshold.ts b/src/leetcode/binary_search/find-the-smallest-divisor-given-a-threshold.ts similarity index 100% rename from src/binary-search/find-the-smallest-divisor-given-a-threshold.ts rename to src/leetcode/binary_search/find-the-smallest-divisor-given-a-threshold.ts diff --git a/src/binary-search/k-th-missing-positive-number.ts b/src/leetcode/binary_search/k-th-missing-positive-number.ts similarity index 100% rename from src/binary-search/k-th-missing-positive-number.ts rename to src/leetcode/binary_search/k-th-missing-positive-number.ts diff --git a/src/binary-search/k-th-smallest-element-in-a-sorted-matrix.ts b/src/leetcode/binary_search/k-th-smallest-element-in-a-sorted-matrix.ts similarity index 100% rename from src/binary-search/k-th-smallest-element-in-a-sorted-matrix.ts rename to src/leetcode/binary_search/k-th-smallest-element-in-a-sorted-matrix.ts diff --git a/src/binary-search/longest-increasing-subsequence.ts b/src/leetcode/binary_search/longest-increasing-subsequence.ts similarity index 100% rename from src/binary-search/longest-increasing-subsequence.ts rename to src/leetcode/binary_search/longest-increasing-subsequence.ts diff --git a/src/binary-search/median-of-two-sorted-arrays.ts b/src/leetcode/binary_search/median-of-two-sorted-arrays.ts similarity index 100% rename from src/binary-search/median-of-two-sorted-arrays.ts rename to src/leetcode/binary_search/median-of-two-sorted-arrays.ts diff --git a/src/binary-search/search-in-rotated-sorted-array.ts b/src/leetcode/binary_search/search-in-rotated-sorted-array.ts similarity index 100% rename from src/binary-search/search-in-rotated-sorted-array.ts rename to src/leetcode/binary_search/search-in-rotated-sorted-array.ts diff --git a/src/dynamic-programming/README.md b/src/leetcode/dynamic_programming/README.md similarity index 100% rename from src/dynamic-programming/README.md rename to src/leetcode/dynamic_programming/README.md diff --git a/src/dynamic-programming/arithmetic-slices-ii-subsequences.ts b/src/leetcode/dynamic_programming/arithmetic-slices-ii-subsequences.ts similarity index 100% rename from src/dynamic-programming/arithmetic-slices-ii-subsequences.ts rename to src/leetcode/dynamic_programming/arithmetic-slices-ii-subsequences.ts diff --git a/src/dynamic-programming/max-subarray.ts b/src/leetcode/dynamic_programming/max-subarray.ts similarity index 100% rename from src/dynamic-programming/max-subarray.ts rename to src/leetcode/dynamic_programming/max-subarray.ts diff --git a/src/dynamic-programming/word-break-i.ts b/src/leetcode/dynamic_programming/word-break-i.ts similarity index 100% rename from src/dynamic-programming/word-break-i.ts rename to src/leetcode/dynamic_programming/word-break-i.ts diff --git a/src/graph/add-edges-to-make-degrees-even.ts b/src/leetcode/graph/add-edges-to-make-degrees-even.ts similarity index 100% rename from src/graph/add-edges-to-make-degrees-even.ts rename to src/leetcode/graph/add-edges-to-make-degrees-even.ts diff --git a/src/graph/bus-routes.ts b/src/leetcode/graph/bus-routes.ts similarity index 100% rename from src/graph/bus-routes.ts rename to src/leetcode/graph/bus-routes.ts diff --git a/src/graph/nested-list-weighted-sum-ii.ts b/src/leetcode/graph/nested-list-weighted-sum-ii.ts similarity index 100% rename from src/graph/nested-list-weighted-sum-ii.ts rename to src/leetcode/graph/nested-list-weighted-sum-ii.ts diff --git a/src/graph/nested-list-weighted-sum.ts b/src/leetcode/graph/nested-list-weighted-sum.ts similarity index 100% rename from src/graph/nested-list-weighted-sum.ts rename to src/leetcode/graph/nested-list-weighted-sum.ts diff --git a/src/graph/number-of-islands.ts b/src/leetcode/graph/number-of-islands.ts similarity index 100% rename from src/graph/number-of-islands.ts rename to src/leetcode/graph/number-of-islands.ts diff --git a/src/graph/parallel-job-scheduling.ts b/src/leetcode/graph/parallel-job-scheduling.ts similarity index 100% rename from src/graph/parallel-job-scheduling.ts rename to src/leetcode/graph/parallel-job-scheduling.ts diff --git a/src/graph/shortest-path-in-binary-matrix.ts b/src/leetcode/graph/shortest-path-in-binary-matrix.ts similarity index 100% rename from src/graph/shortest-path-in-binary-matrix.ts rename to src/leetcode/graph/shortest-path-in-binary-matrix.ts diff --git a/src/graph/smallest-greater-multiple-made-of-two-digits.ts b/src/leetcode/graph/smallest-greater-multiple-made-of-two-digits.ts similarity index 100% rename from src/graph/smallest-greater-multiple-made-of-two-digits.ts rename to src/leetcode/graph/smallest-greater-multiple-made-of-two-digits.ts diff --git a/src/graph/word-search-i.ts b/src/leetcode/graph/word-search-i.ts similarity index 100% rename from src/graph/word-search-i.ts rename to src/leetcode/graph/word-search-i.ts diff --git a/src/graph/word-search-ii.ts b/src/leetcode/graph/word-search-ii.ts similarity index 100% rename from src/graph/word-search-ii.ts rename to src/leetcode/graph/word-search-ii.ts diff --git a/src/leetcode/heap/README.md b/src/leetcode/heap/README.md new file mode 100644 index 0000000..cfa8f46 --- /dev/null +++ b/src/leetcode/heap/README.md @@ -0,0 +1,11 @@ +# Heaps + +## Common Phrases + +These phrases indicate a heap might be useful: + +- k closest, k largest, k smallest +- top k elements +- max k elements, min k elements +- priority order +- sorted order with constraints diff --git a/src/leetcode/heap/furthest_building_you_can_reach.py b/src/leetcode/heap/furthest_building_you_can_reach.py new file mode 100644 index 0000000..173d91e --- /dev/null +++ b/src/leetcode/heap/furthest_building_you_can_reach.py @@ -0,0 +1,70 @@ +# DIFFICULTY: Medium +# +# You are given an integer array heights representing the heights of buildings, some bricks, and some ladders. +# +# You start your journey from building 0 and move to the next building by possibly using bricks or ladders. +# +# While moving from building i to building i+1 (0-indexed), +# +# - If the current building's height is greater than or equal to the next building's height, you do not need a ladder +# or bricks. +# - If the current building's height is less than the next building's height, you can either use one ladder or +# (h[i+1] - h[i]) bricks. +# +# Return the furthest building index (0-indexed) you can reach if you use the given ladders and bricks optimally. +# +# See https://leetcode.com/problems/furthest-building-you-can-reach +from heapq import heappush, heappop + + +class Solution: + def furthestBuilding(self, heights: list[int], bricks: int, ladders: int) -> int: + """ + SOLUTION: + + You can't do this problem using sliding window or two pointer technique easily. + + Sliding window requires finding some value in a fixed size window. Here we don't know what the window size will + be. + + Two pointer technique can solve this problem but not optimally. We need to dynamically adjust our resources of + bricks or ladder at each step. + + A greedy algorithm would be better suited to solve this problem. + + COMPLEXITY: + + Time complexity is O(n log n) time where n is number of buildings. + + Space complexity is O(n) in worst case. + """ + heap: list[int] = [] + + for i in range(len(heights) - 1): + # Calculate the delta that we need to "jump" across. + delta = heights[i + 1] - heights[i] + + # A negative or zero height jump can be traversed by jumping across or down. + if delta <= 0: + continue + + # A non-negative delta means we must use either bricks or a ladder to jump across. + heappush(heap, delta) + + # Because the ladders can traverse any height, we'll "save" the ladders here to use on the the biggest + # deltas. As long as we have ladders remaining we can maintain a heap size of deltas that we'll + # eventually use ladders on. + if len(heap) <= ladders: + continue + + # If we have more elements on the heap than we do ladders, then SOME of these deltas need to be traversed + # using bricks instead of ladders. Let's pop the smallest delta out and use bricks on it. + smallest = heappop(heap) + bricks -= smallest + + # If we've run out of bricks, that's it. We can can only reach the building at position i. + if bricks < 0: + return i + + # If we got here, that means we can get to the very last building! + return len(heights) - 1 diff --git a/src/heap/k-closest-points-to-origin.ts b/src/leetcode/heap/k-closest-points-to-origin.ts similarity index 100% rename from src/heap/k-closest-points-to-origin.ts rename to src/leetcode/heap/k-closest-points-to-origin.ts diff --git a/src/leetcode/heap/kth_largest_element.py b/src/leetcode/heap/kth_largest_element.py new file mode 100644 index 0000000..16bd45f --- /dev/null +++ b/src/leetcode/heap/kth_largest_element.py @@ -0,0 +1,55 @@ +# DIFFICULTY: Medium +# +# Given an integer array nums and an integer k, return the kth largest element in the array. +# +# Note that it is the kth largest element in the sorted order, not the kth distinct element. +# +# Can you solve it without sorting? +# +# See https://leetcode.com/problems/kth-largest-element-in-an-array +from heapq import heappop, heappush + + +class Solution: + def findKthLargest(self, nums: list[int], k: int) -> int: + """ + SOLUTION: + + You can do this with quick select, similar to quick sort. The idea is to eliminate half of the search space at + each step. You pick a pivot element (using heuristics, or even randomly), then partition the array into two + halves: left half has elements smaller than the pivot, right half has elements larger than the pivot. + + After the partition, you can check if the pivot is the (n - k)th element. If it is, you're done. If not, + recurse into the left or right partitions depending on the pivot's position. + + That said, the leetcode questions seem to be crafted to ruin poor pivot index choices. + + The other way to do this is to use a heap. You can do this an efficient way or an inefficient way. The + inefficient way involves jamming all the elements onto a max heap, then popping off k elements to get the kth + largest element. + + The efficient way is to maintain a min heap and only ever allow k elements on the heap. This way, each time you + pop an element off the heap, you're left with ever larger elements. Since the heap size is k, the kth largest + element will be at the top of the heap. + + COMPLEXITY: + + Time complexity is O(n log k) where n is the length of the array. + + Space complexity is O(k). + """ + min_heap: list[int] = [] + + for n in nums: + heappush(min_heap, n) + + # Now we have the k + 1 smallest elements we've seen so far. If we get a new element, pop from the + # heap, which will leave us with larger elements in the heap. + # + # At the end, we'll pop off all the smallest elements, leaving us with the top k largest elements. + if len(min_heap) > k: + heappop(min_heap) + + # This is a min heap, so the smallest element is at the front. However, there are (k - 1) larger elements in + # elsewhere in the heap. That makes the front element the kth largest element. + return min_heap[0] diff --git a/src/heap/max-stack.ts b/src/leetcode/heap/max-stack.ts similarity index 100% rename from src/heap/max-stack.ts rename to src/leetcode/heap/max-stack.ts diff --git a/src/heap/merge-k-sorted-lists.ts b/src/leetcode/heap/merge-k-sorted-lists.ts similarity index 100% rename from src/heap/merge-k-sorted-lists.ts rename to src/leetcode/heap/merge-k-sorted-lists.ts diff --git a/src/leetcode/heap/minimize_deviation_in_array.py b/src/leetcode/heap/minimize_deviation_in_array.py new file mode 100644 index 0000000..fe0d8f3 --- /dev/null +++ b/src/leetcode/heap/minimize_deviation_in_array.py @@ -0,0 +1,78 @@ +# DIFFICULTY: Hard +# +# You are given an array nums of n positive integers. +# +# You can perform two types of operations on any element of the array any number of times: +# +# If the element is even, divide it by 2. +# +# For example, if the array is [1,2,3,4], then you can do this operation on the last element, and the array will be +# [1,2,3,2]. +# +# If the element is odd, multiply it by 2. +# +# For example, if the array is [1,2,3,4], then you can do this operation on the first element, and the array will be +# [2,2,3,4]. +# +# The deviation of the array is the maximum difference between any two elements in the array. +# +# Return the minimum deviation the array can have after performing some number of operations. +# +# See https://leetcode.com/problems/minimize-deviation-in-array +from heapq import heappop, heappush +from sys import maxsize + + +class Solution: + def minimumDeviation(self, nums: list[int]): + """ + SOLUTION: + + To solve this problem, we have to multiply elements to make them bigger, or divide elements to make them + smaller. We continue to do this until the minimum and maximum elements are as close as possible. + + To make this problem easier, we try to minimize the deviation by making bigger numbers smaller, instead of + simultaneously trying to make numbers bigger and smaller. To do this, we multiply all odd numbers by 2, so that + they all become even. Afterwards, we can choose to perform a division or not to make it smaller. + + COMPLEXITY: + + Time complexity is O(n log m) where n is the number of elements in nums, and m is the number of times we have to + halve a value. + + Space complexity is O(n). + """ + heap: list[int] = [] + + # Normalize all the numbers so that they are even. Now we can consider only division as a way to make numbers + # smaller and closer to each other. If a previously odd number was too big, we will eventually resize it smaller + # by division is necessary. + for i, value in enumerate(nums): + if value % 2 == 1: + value *= 2 + nums[i] = value + + heappush(heap, -value) + + minimum = min(nums) + deviation = maxsize + + # Calculate the current deviation using the max element of the array, then half the max element and return it to + # theheap. Then repeat to keep bringing the deviation down. + while True: + maximum = -heappop(heap) + deviation = min(deviation, maximum - minimum) + + # Oh no! If the max value was odd, we can't halve it and re-insert into the heap. This means that whatever + # the deviation is now, we are stuck. Halving smaller even values will not change the deviation. + if maximum % 2 == 1: + break + + # Halve the max value and return it to the heap for re-processing. + value = int(maximum / 2) + heappush(heap, -value) + + # Update the minimum value in case we've changed the minimum value by manipulating the maximum value. + minimum = min(value, minimum) + + return deviation diff --git a/src/leetcode/heap/number_of_orders_in_the_backlog.py b/src/leetcode/heap/number_of_orders_in_the_backlog.py new file mode 100644 index 0000000..f95b90c --- /dev/null +++ b/src/leetcode/heap/number_of_orders_in_the_backlog.py @@ -0,0 +1,132 @@ +# DIFFICULTY: Medium +# +# You are given a 2D integer array orders, where each orders[i] = [pricei, amounti, orderTypei] denotes that amounti +# orders have been placed of type orderTypei at the price pricei. The orderTypei is: +# +# 0 if it is a batch of buy orders, or +# 1 if it is a batch of sell orders. +# +# Note that orders[i] represents a batch of amounti independent orders with the same price and order type. All orders +# represented by orders[i] will be placed before all orders represented by orders[i+1] for all valid i. +# +# There is a backlog that consists of orders that have not been executed. The backlog is initially empty. When an order +# is placed, the following happens: +# +# If the order is a buy order, you look at the sell order with the smallest price in the backlog. If that sell order's +# price is smaller than or equal to the current buy order's price, they will match and be executed, and that sell order +# will be removed from the backlog. Else, the buy order is added to the backlog. +# +# Vice versa, if the order is a sell order, you look at the buy order with the largest price in the backlog. If that +# buy order's price is larger than or equal to the current sell order's price, they will match and be executed, and +# that buy order will be removed from the backlog. Else, the sell order is added to the backlog. +# +# Return the total amount of orders in the backlog after placing all the orders from the input. Since this number can +# be large, return it modulo 10^9 + 7. +# +# See https://leetcode.com/problems/number-of-orders-in-the-backlog +from heapq import heappop, heappush + + +class Solution: + def getNumberOfBacklogOrders(self, orders: list[list[int]]) -> int: + """ + SOLUTION: + + This problem looks like it can be solved by maintaining a sorted list of buy orders and sell orders (aka heaps). + For a sell order, you want buy orders in largest to smallest (max heap). For a buy order, you want sell orders + from smallest to largest (min heap). + + COMPLEXITY: + + Time complexity is O(n log n) because we are using heaps to maintain the order of the orders. + + Space complexity is O(n). + """ + # We want to match sellers with the largest price in the buy orders, so we want a max heap. Pro-tip: Negate the + # values here. + buys: list[tuple[int, int]] = [] + # We want to match buyers with the lowest price in the sell orders, so we want a min heap. + sells: list[tuple[int, int]] = [] + MOD = 10**9 + 7 + + def handle_buy(buy_price: int, buy_amount: int): + while buy_amount > 0: + # If we don't have any sellers, we can't match up a sale. So we should push the order into the buys + # backlog. + # + # Note that the buys backlog is a max heap, so we have to negate the price. + if not sells: + heappush(buys, (-buy_price, buy_amount)) + return + + # If we do have a seller, we can check the lowest sale price. If the lowest sale price is still too + # high, we can't match up a sale. So we should push the order into the buys backlog anyways. + # + # Note that the buys backlog is a max heap, so we have to negate the price. + sell_price, sell_amount = sells[0] + if buy_price < sell_price: + heappush(buys, (-buy_price, buy_amount)) + return + + # Here the buyer and seller have agreed to a sale, so let's execute the order. We can buy only as many + # shares as the seller is selling at that price. + delta = min(buy_amount, sell_amount) + buy_amount -= delta + sell_amount -= delta + sells[0] = (sell_price, sell_amount) + + # If the seller isn't offering any more shares, then pop their order from the sells backlog. + if sell_amount == 0: + heappop(sells) + + # If the buyer doesn't want any more shares, we can stop processing the loop. + if buy_amount == 0: + return + + def handle_sell(sell_price: int, sell_amount: int): + while sell_amount > 0: + # If we don't have any buyers, we can't match up a sale. So we should push the order into the sells backlog. + if not buys: + heappush(sells, (sell_price, sell_amount)) + return + + # If we do have a buyer, we can check the highest buy price. If the highest buy price is still too low, + # we can't match up a sale. So we should push the order into the buys backlog anyways. + # + # Note that this is a max heap so we need to negate the value. + buy_price, buy_amount = buys[0] + buy_price *= -1 + if sell_price > buy_price: + heappush(sells, (sell_price, sell_amount)) + return + + # Here the buyer nad seller have agreed to a sale, so let's execute the order. We can only buy as many shares + # as the seller is willing to sell. + delta = min(buy_amount, sell_amount) + buy_amount -= delta + sell_amount -= delta + buys[0] = (-buy_price, buy_amount) + + # If the buyer isn't willing to buy any more shares, then pop their order from the buys backlog. + if buy_amount == 0: + heappop(buys) + + # If the seller doesn't want any more shares, stop processing the loop. + if sell_amount == 0: + return + + for price, amount, order_type in orders: + if amount == 0: + continue + + if order_type == 0: + handle_buy(price, amount) + else: + handle_sell(price, amount) + + total = 0 + for _, amount in buys: + total = (total + amount) % MOD + for _, amount in sells: + total = (total + amount) % MOD + return total diff --git a/src/heap/task-scheduler.ts b/src/leetcode/heap/task-scheduler.ts similarity index 100% rename from src/heap/task-scheduler.ts rename to src/leetcode/heap/task-scheduler.ts diff --git a/src/interval/interval-list-intersections.ts b/src/leetcode/interval/interval-list-intersections.ts similarity index 100% rename from src/interval/interval-list-intersections.ts rename to src/leetcode/interval/interval-list-intersections.ts diff --git a/src/interval/meeting-rooms.ts b/src/leetcode/interval/meeting-rooms.ts similarity index 100% rename from src/interval/meeting-rooms.ts rename to src/leetcode/interval/meeting-rooms.ts diff --git a/src/interval/meeting-scheduler.ts b/src/leetcode/interval/meeting-scheduler.ts similarity index 100% rename from src/interval/meeting-scheduler.ts rename to src/leetcode/interval/meeting-scheduler.ts diff --git a/src/interval/merge-intervals.ts b/src/leetcode/interval/merge-intervals.ts similarity index 100% rename from src/interval/merge-intervals.ts rename to src/leetcode/interval/merge-intervals.ts diff --git a/src/linked-list/README.md b/src/leetcode/linked_list/README.md similarity index 100% rename from src/linked-list/README.md rename to src/leetcode/linked_list/README.md diff --git a/src/linked-list/add-two-numbers.ts b/src/leetcode/linked_list/add-two-numbers.ts similarity index 100% rename from src/linked-list/add-two-numbers.ts rename to src/leetcode/linked_list/add-two-numbers.ts diff --git a/src/linked-list/all-one.ts b/src/leetcode/linked_list/all-one.ts similarity index 100% rename from src/linked-list/all-one.ts rename to src/leetcode/linked_list/all-one.ts diff --git a/src/linked-list/common/list-node.ts b/src/leetcode/linked_list/common/list-node.ts similarity index 100% rename from src/linked-list/common/list-node.ts rename to src/leetcode/linked_list/common/list-node.ts diff --git a/src/linked-list/common/nested-integer.ts b/src/leetcode/linked_list/common/nested-integer.ts similarity index 100% rename from src/linked-list/common/nested-integer.ts rename to src/leetcode/linked_list/common/nested-integer.ts diff --git a/src/linked-list/common/random-node.ts b/src/leetcode/linked_list/common/random-node.ts similarity index 100% rename from src/linked-list/common/random-node.ts rename to src/leetcode/linked_list/common/random-node.ts diff --git a/src/linked-list/copy-list-with-random-pointer.ts b/src/leetcode/linked_list/copy-list-with-random-pointer.ts similarity index 100% rename from src/linked-list/copy-list-with-random-pointer.ts rename to src/leetcode/linked_list/copy-list-with-random-pointer.ts diff --git a/src/linked-list/lru-cache.ts b/src/leetcode/linked_list/lru-cache.ts similarity index 100% rename from src/linked-list/lru-cache.ts rename to src/leetcode/linked_list/lru-cache.ts diff --git a/src/linked-list/merge-two-sorted-lists.ts b/src/leetcode/linked_list/merge-two-sorted-lists.ts similarity index 100% rename from src/linked-list/merge-two-sorted-lists.ts rename to src/leetcode/linked_list/merge-two-sorted-lists.ts diff --git a/src/linked-list/remove-nth-node-from-end-of-list.ts b/src/leetcode/linked_list/remove-nth-node-from-end-of-list.ts similarity index 100% rename from src/linked-list/remove-nth-node-from-end-of-list.ts rename to src/leetcode/linked_list/remove-nth-node-from-end-of-list.ts diff --git a/src/math/number-of-one-bits.ts b/src/leetcode/math/number-of-one-bits.ts similarity index 100% rename from src/math/number-of-one-bits.ts rename to src/leetcode/math/number-of-one-bits.ts diff --git a/src/math/pow-x-n.ts b/src/leetcode/math/pow-x-n.ts similarity index 100% rename from src/math/pow-x-n.ts rename to src/leetcode/math/pow-x-n.ts diff --git a/src/math/reverse-integer.ts b/src/leetcode/math/reverse-integer.ts similarity index 100% rename from src/math/reverse-integer.ts rename to src/leetcode/math/reverse-integer.ts diff --git a/src/math/rotate-image.ts b/src/leetcode/math/rotate-image.ts similarity index 100% rename from src/math/rotate-image.ts rename to src/leetcode/math/rotate-image.ts diff --git a/src/math/sequential-digits.ts b/src/leetcode/math/sequential-digits.ts similarity index 100% rename from src/math/sequential-digits.ts rename to src/leetcode/math/sequential-digits.ts diff --git a/src/matrix/robot-bounded-in-circle.ts b/src/leetcode/matrix/robot-bounded-in-circle.ts similarity index 100% rename from src/matrix/robot-bounded-in-circle.ts rename to src/leetcode/matrix/robot-bounded-in-circle.ts diff --git a/src/matrix/valid-word-square.ts b/src/leetcode/matrix/valid-word-square.ts similarity index 100% rename from src/matrix/valid-word-square.ts rename to src/leetcode/matrix/valid-word-square.ts diff --git a/src/prefix-sum/README.md b/src/leetcode/prefix_sum/README.md similarity index 100% rename from src/prefix-sum/README.md rename to src/leetcode/prefix_sum/README.md diff --git a/src/prefix-sum/continuous-subarray-sum.ts b/src/leetcode/prefix_sum/continuous-subarray-sum.ts similarity index 100% rename from src/prefix-sum/continuous-subarray-sum.ts rename to src/leetcode/prefix_sum/continuous-subarray-sum.ts diff --git a/src/prefix-sum/max-sum-obtained-of-any-permutation.ts b/src/leetcode/prefix_sum/max-sum-obtained-of-any-permutation.ts similarity index 100% rename from src/prefix-sum/max-sum-obtained-of-any-permutation.ts rename to src/leetcode/prefix_sum/max-sum-obtained-of-any-permutation.ts diff --git a/src/prefix-sum/product-of-array-except-self.ts b/src/leetcode/prefix_sum/product-of-array-except-self.ts similarity index 100% rename from src/prefix-sum/product-of-array-except-self.ts rename to src/leetcode/prefix_sum/product-of-array-except-self.ts diff --git a/src/prefix-sum/random-pick-with-weight.ts b/src/leetcode/prefix_sum/random-pick-with-weight.ts similarity index 100% rename from src/prefix-sum/random-pick-with-weight.ts rename to src/leetcode/prefix_sum/random-pick-with-weight.ts diff --git a/src/prefix-sum/range-sum-query-immutable.ts b/src/leetcode/prefix_sum/range-sum-query-immutable.ts similarity index 100% rename from src/prefix-sum/range-sum-query-immutable.ts rename to src/leetcode/prefix_sum/range-sum-query-immutable.ts diff --git a/src/prefix-sum/running-sum-of-1d-array.ts b/src/leetcode/prefix_sum/running-sum-of-1d-array.ts similarity index 100% rename from src/prefix-sum/running-sum-of-1d-array.ts rename to src/leetcode/prefix_sum/running-sum-of-1d-array.ts diff --git a/src/prefix-sum/subarray-sum-equals-k.ts b/src/leetcode/prefix_sum/subarray-sum-equals-k.ts similarity index 100% rename from src/prefix-sum/subarray-sum-equals-k.ts rename to src/leetcode/prefix_sum/subarray-sum-equals-k.ts diff --git a/src/leetcode/recursion/generate_parentheses.py b/src/leetcode/recursion/generate_parentheses.py new file mode 100644 index 0000000..6080cad --- /dev/null +++ b/src/leetcode/recursion/generate_parentheses.py @@ -0,0 +1,49 @@ +# DIFFICULTY: MEDIUM +# +# Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses. +# +# See https://leetcode.com/problems/generate-parentheses +class Solution: + def generateParenthesis(self, n: int) -> list[int]: + """ + SOLUTION: + + A naive way to do this would be to do something like generate all the parentheses for n-1, then add more + parentheses to each of the elements generated. For example, by transforming each element of the n-1 solution to + `${e}()`, `()${e}`, and `($e)`. + + This doesn't work for values like n=4, because you will miss solutions like `(())(())`. Instead, we'll + construct the string as we go along, keeping track of how many open and close parentheses we have used. In + total, the resulting strings will each have length n*2. + + This is because well formed strings have additional constraints; when generating a power set using the naive + method, for example, you don't need to worry about balancing the elements within the sub set. In contrast to + generating a power set, you would have far fewer resulting elements. + + COMPLEXITY: + + Time complexity is uh... we'll just go with O(4^n) since we can generate all possible 2 * n length strings using + characters '(' and ')'. With 2 characters, it's 2^(2 * n) = 4^n. + + Space complexity is O(4^n). + """ + if n == 0: + return [] + + result = [] + + def generate(text, opens: int, closes: int): + # The max length of the string is n * 2 because each open paren requires a close paren. We can't get fewer + # than n * 2 characters in a string either, because we have to generate all possible combinations. + if len(text) == n * 2: + result.append(text) + return + + if opens < n: + generate(text + "(", opens + 1, closes) + + if closes < opens: + generate(text + ")", opens, closes + 1) + + generate("", 0, 0) + return result diff --git a/src/recursion/maximum-number-of-operations-with-the-same-score.ts b/src/leetcode/recursion/maximum-number-of-operations-with-the-same-score.ts similarity index 100% rename from src/recursion/maximum-number-of-operations-with-the-same-score.ts rename to src/leetcode/recursion/maximum-number-of-operations-with-the-same-score.ts diff --git a/src/recursion/optimal-account-balancing.ts b/src/leetcode/recursion/optimal-account-balancing.ts similarity index 100% rename from src/recursion/optimal-account-balancing.ts rename to src/leetcode/recursion/optimal-account-balancing.ts diff --git a/src/recursion/permutations.ts b/src/leetcode/recursion/permutations.ts similarity index 100% rename from src/recursion/permutations.ts rename to src/leetcode/recursion/permutations.ts diff --git a/src/recursion/subsets.ts b/src/leetcode/recursion/subsets.ts similarity index 100% rename from src/recursion/subsets.ts rename to src/leetcode/recursion/subsets.ts diff --git a/src/recursion/word-break-ii.ts b/src/leetcode/recursion/word-break-ii.ts similarity index 100% rename from src/recursion/word-break-ii.ts rename to src/leetcode/recursion/word-break-ii.ts diff --git a/src/sliding-window/README.md b/src/leetcode/sliding_window/README.md similarity index 100% rename from src/sliding-window/README.md rename to src/leetcode/sliding_window/README.md diff --git a/src/sliding-window/best-time-to-buy-and-sell-stock-i.ts b/src/leetcode/sliding_window/best-time-to-buy-and-sell-stock-i.ts similarity index 100% rename from src/sliding-window/best-time-to-buy-and-sell-stock-i.ts rename to src/leetcode/sliding_window/best-time-to-buy-and-sell-stock-i.ts diff --git a/src/sliding-window/count-subarrays-where-max-element-appears.ts b/src/leetcode/sliding_window/count-subarrays-where-max-element-appears.ts similarity index 100% rename from src/sliding-window/count-subarrays-where-max-element-appears.ts rename to src/leetcode/sliding_window/count-subarrays-where-max-element-appears.ts diff --git a/src/sliding-window/length-of-longest-substring.ts b/src/leetcode/sliding_window/length-of-longest-substring.ts similarity index 100% rename from src/sliding-window/length-of-longest-substring.ts rename to src/leetcode/sliding_window/length-of-longest-substring.ts diff --git a/src/sliding-window/longest-continuous-subarray.ts b/src/leetcode/sliding_window/longest-continuous-subarray.ts similarity index 100% rename from src/sliding-window/longest-continuous-subarray.ts rename to src/leetcode/sliding_window/longest-continuous-subarray.ts diff --git a/src/sliding-window/max-sum-distinct-subarray-of-size-k.ts b/src/leetcode/sliding_window/max-sum-distinct-subarray-of-size-k.ts similarity index 100% rename from src/sliding-window/max-sum-distinct-subarray-of-size-k.ts rename to src/leetcode/sliding_window/max-sum-distinct-subarray-of-size-k.ts diff --git a/src/sliding-window/minimum-window-substring.ts b/src/leetcode/sliding_window/minimum-window-substring.ts similarity index 100% rename from src/sliding-window/minimum-window-substring.ts rename to src/leetcode/sliding_window/minimum-window-substring.ts diff --git a/src/sliding-window/moving-average-from-data-stream.ts b/src/leetcode/sliding_window/moving-average-from-data-stream.ts similarity index 100% rename from src/sliding-window/moving-average-from-data-stream.ts rename to src/leetcode/sliding_window/moving-average-from-data-stream.ts diff --git a/src/sliding-window/repeated-dna-sequences.ts b/src/leetcode/sliding_window/repeated-dna-sequences.ts similarity index 100% rename from src/sliding-window/repeated-dna-sequences.ts rename to src/leetcode/sliding_window/repeated-dna-sequences.ts diff --git a/src/stack/README.md b/src/leetcode/stack/README.md similarity index 100% rename from src/stack/README.md rename to src/leetcode/stack/README.md diff --git a/src/stack/basic-calculator-ii.ts b/src/leetcode/stack/basic-calculator-ii.ts similarity index 100% rename from src/stack/basic-calculator-ii.ts rename to src/leetcode/stack/basic-calculator-ii.ts diff --git a/src/stack/basic-calculator.ts b/src/leetcode/stack/basic-calculator.ts similarity index 100% rename from src/stack/basic-calculator.ts rename to src/leetcode/stack/basic-calculator.ts diff --git a/src/stack/key-value-store-nested-transactions.ts b/src/leetcode/stack/key-value-store-nested-transactions.ts similarity index 100% rename from src/stack/key-value-store-nested-transactions.ts rename to src/leetcode/stack/key-value-store-nested-transactions.ts diff --git a/src/stack/longest-absolute-file-path.ts b/src/leetcode/stack/longest-absolute-file-path.ts similarity index 100% rename from src/stack/longest-absolute-file-path.ts rename to src/leetcode/stack/longest-absolute-file-path.ts diff --git a/src/stack/max-chunks-to-make-sorted-ii.ts b/src/leetcode/stack/max-chunks-to-make-sorted-ii.ts similarity index 100% rename from src/stack/max-chunks-to-make-sorted-ii.ts rename to src/leetcode/stack/max-chunks-to-make-sorted-ii.ts diff --git a/src/stack/min-stack.ts b/src/leetcode/stack/min-stack.ts similarity index 100% rename from src/stack/min-stack.ts rename to src/leetcode/stack/min-stack.ts diff --git a/src/stack/minimum-add-to-make-valid-parentheses.ts b/src/leetcode/stack/minimum-add-to-make-valid-parentheses.ts similarity index 100% rename from src/stack/minimum-add-to-make-valid-parentheses.ts rename to src/leetcode/stack/minimum-add-to-make-valid-parentheses.ts diff --git a/src/stack/minimum-remove-to-make-valid-parentheses.ts b/src/leetcode/stack/minimum-remove-to-make-valid-parentheses.ts similarity index 100% rename from src/stack/minimum-remove-to-make-valid-parentheses.ts rename to src/leetcode/stack/minimum-remove-to-make-valid-parentheses.ts diff --git a/src/stack/simplify-path.ts b/src/leetcode/stack/simplify-path.ts similarity index 100% rename from src/stack/simplify-path.ts rename to src/leetcode/stack/simplify-path.ts diff --git a/src/stack/valid-number.ts b/src/leetcode/stack/valid-number.ts similarity index 100% rename from src/stack/valid-number.ts rename to src/leetcode/stack/valid-number.ts diff --git a/src/leetcode/stack/valid_parentheses.py b/src/leetcode/stack/valid_parentheses.py new file mode 100644 index 0000000..a45ee18 --- /dev/null +++ b/src/leetcode/stack/valid_parentheses.py @@ -0,0 +1,40 @@ +# DIFFICULTY: EASY +# +# Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid. +# +# An input string is valid if: +# +# Open brackets must be closed by the same type of brackets. +# Open brackets must be closed in the correct order. +# Every close bracket has a corresponding open bracket of the same type. +# +# See https://leetcode.com/problems/valid-parentheses +class Solution: + def isValid(self, text: str) -> bool: + """ + SOLUTION: + + Simple stack based solution. + + COMPLEXITY: + + Time complexity is O(n) where n is the number of characters in the string. + + Space complexity is O(n). + """ + map = {"(": ")", "{": "}", "[": "]"} + stack = [] + + for c in text: + if c in map: + stack.append(c) + continue + + if c in map.values(): + if not stack: + return False + open_c = stack.pop() + if map[open_c] != c: + return False + + return not stack diff --git a/src/string/find-the-closest-palindrome.ts b/src/leetcode/string/find-the-closest-palindrome.ts similarity index 100% rename from src/string/find-the-closest-palindrome.ts rename to src/leetcode/string/find-the-closest-palindrome.ts diff --git a/src/string/int-to-roman.ts b/src/leetcode/string/int-to-roman.ts similarity index 100% rename from src/string/int-to-roman.ts rename to src/leetcode/string/int-to-roman.ts diff --git a/src/string/integer-to-english-words.ts b/src/leetcode/string/integer-to-english-words.ts similarity index 100% rename from src/string/integer-to-english-words.ts rename to src/leetcode/string/integer-to-english-words.ts diff --git a/src/string/letter-combinations-phone-number.ts b/src/leetcode/string/letter-combinations-phone-number.ts similarity index 100% rename from src/string/letter-combinations-phone-number.ts rename to src/leetcode/string/letter-combinations-phone-number.ts diff --git a/src/string/longest-palindromic-substring.ts b/src/leetcode/string/longest-palindromic-substring.ts similarity index 100% rename from src/string/longest-palindromic-substring.ts rename to src/leetcode/string/longest-palindromic-substring.ts diff --git a/src/string/palindrome-number.ts b/src/leetcode/string/palindrome-number.ts similarity index 100% rename from src/string/palindrome-number.ts rename to src/leetcode/string/palindrome-number.ts diff --git a/src/string/remove-all-adjacent-duplicates-i.ts b/src/leetcode/string/remove-all-adjacent-duplicates-i.ts similarity index 100% rename from src/string/remove-all-adjacent-duplicates-i.ts rename to src/leetcode/string/remove-all-adjacent-duplicates-i.ts diff --git a/src/string/remove-all-adjacent-duplicates-ii.ts b/src/leetcode/string/remove-all-adjacent-duplicates-ii.ts similarity index 100% rename from src/string/remove-all-adjacent-duplicates-ii.ts rename to src/leetcode/string/remove-all-adjacent-duplicates-ii.ts diff --git a/src/string/reverse-words-in-a-string.ts b/src/leetcode/string/reverse-words-in-a-string.ts similarity index 100% rename from src/string/reverse-words-in-a-string.ts rename to src/leetcode/string/reverse-words-in-a-string.ts diff --git a/src/string/roman-to-int.ts b/src/leetcode/string/roman-to-int.ts similarity index 100% rename from src/string/roman-to-int.ts rename to src/leetcode/string/roman-to-int.ts diff --git a/src/string/time-needed-to-rearrange-binary-string.ts b/src/leetcode/string/time-needed-to-rearrange-binary-string.ts similarity index 100% rename from src/string/time-needed-to-rearrange-binary-string.ts rename to src/leetcode/string/time-needed-to-rearrange-binary-string.ts diff --git a/src/leetcode/string/valid_word_abbreviation.py b/src/leetcode/string/valid_word_abbreviation.py new file mode 100644 index 0000000..135776e --- /dev/null +++ b/src/leetcode/string/valid_word_abbreviation.py @@ -0,0 +1,67 @@ +# DIFFICULTY: EASY +# +# A string can be abbreviated by replacing any number of non-adjacent, non-empty substrings with their lengths. The +# lengths should not have leading zeros. +# +# For example, a string such as "substitution" could be abbreviated as (but not limited to): +# +# "s10n" ("s ubstitutio n") +# "sub4u4" ("sub stit u tion") +# "12" ("substitution") +# "su3i1u2on" ("su bst i t u ti on") +# "substitution" (no substrings replaced) +# The following are not valid abbreviations: +# +# "s55n" ("s ubsti tutio n", the replaced substrings are adjacent) +# "s010n" (has leading zeros) +# "s0ubstitution" (replaces an empty substring) +# Given a string word and an abbreviation abbr, return whether the string matches the given abbreviation. +# +# A substring is a contiguous non-empty sequence of characters within a string. +# +# See https://leetcode.com/problems/valid-word-abbreviation +class Solution: + def validWordAbbreviation(self, word: str, abbr: str) -> bool: + """ + SOLUTION: + + To do this, we'll use two pointers, but not to iterate through a single string. Instead, each pointer iterates + through its own string. If we see an numerical abbreviation, we'll advance through the other string a the + corresponding number of characters. Because of that, this problem isn't categorized as a two-pointer problem. + + At each stage of advancement we'll check if the characters match. + + COMPLEXITY: + + Time complexity is O(max(m, n)) where m is the length of word, and n is the length of abbr. + + Space complexity is O(1). + """ + i, j = 0, 0 + + while i < len(word) and j < len(abbr): + # If we have a digit in abbr, figure out the entire number, then skip over that many characters from the + # word string. + if abbr[j].isdigit(): + # If we have leading zeroes, like "s010n", then the abbreviation can never be valid. + if abbr[j] == "0": + return False + + # Read in the number from the abbr string. + num = 0 + while j < len(abbr) and abbr[j].isdigit(): + num = num * 10 + int(abbr[j]) + j += 1 + + # Skip that many numbers from the word string. + i += num + + # If we don't have a digit in abbr, we just need to compare that the characters in each string match. + else: + if i >= len(word) or word[i] != abbr[j]: + return False + + i += 1 + j += 1 + + return i == len(word) and j == len(abbr) diff --git a/src/string/vowel-spellchecker.ts b/src/leetcode/string/vowel-spellchecker.ts similarity index 100% rename from src/string/vowel-spellchecker.ts rename to src/leetcode/string/vowel-spellchecker.ts diff --git a/src/tree/binary-tree-level-order-traversal.ts b/src/leetcode/tree/binary-tree-level-order-traversal.ts similarity index 100% rename from src/tree/binary-tree-level-order-traversal.ts rename to src/leetcode/tree/binary-tree-level-order-traversal.ts diff --git a/src/tree/binary-tree-right-side-view.ts b/src/leetcode/tree/binary-tree-right-side-view.ts similarity index 100% rename from src/tree/binary-tree-right-side-view.ts rename to src/leetcode/tree/binary-tree-right-side-view.ts diff --git a/src/tree/binary-tree-vertical-order-traversal.ts b/src/leetcode/tree/binary-tree-vertical-order-traversal.ts similarity index 100% rename from src/tree/binary-tree-vertical-order-traversal.ts rename to src/leetcode/tree/binary-tree-vertical-order-traversal.ts diff --git a/src/tree/common/parent-node.ts b/src/leetcode/tree/common/parent-node.ts similarity index 100% rename from src/tree/common/parent-node.ts rename to src/leetcode/tree/common/parent-node.ts diff --git a/src/tree/common/quad-tree.ts b/src/leetcode/tree/common/quad-tree.ts similarity index 100% rename from src/tree/common/quad-tree.ts rename to src/leetcode/tree/common/quad-tree.ts diff --git a/src/tree/common/tree-node.ts b/src/leetcode/tree/common/tree-node.ts similarity index 100% rename from src/tree/common/tree-node.ts rename to src/leetcode/tree/common/tree-node.ts diff --git a/src/tree/construct-quad-tree.ts b/src/leetcode/tree/construct-quad-tree.ts similarity index 100% rename from src/tree/construct-quad-tree.ts rename to src/leetcode/tree/construct-quad-tree.ts diff --git a/src/tree/design-in-memory-file-system.ts b/src/leetcode/tree/design-in-memory-file-system.ts similarity index 100% rename from src/tree/design-in-memory-file-system.ts rename to src/leetcode/tree/design-in-memory-file-system.ts diff --git a/src/tree/diameter-of-binary-tree.ts b/src/leetcode/tree/diameter-of-binary-tree.ts similarity index 100% rename from src/tree/diameter-of-binary-tree.ts rename to src/leetcode/tree/diameter-of-binary-tree.ts diff --git a/src/tree/lowest-common-ancestor-of-a-binary-tree-iii.ts b/src/leetcode/tree/lowest-common-ancestor-of-a-binary-tree-iii.ts similarity index 100% rename from src/tree/lowest-common-ancestor-of-a-binary-tree-iii.ts rename to src/leetcode/tree/lowest-common-ancestor-of-a-binary-tree-iii.ts diff --git a/src/tree/lowest-common-ancestor-of-a-binary-tree.ts b/src/leetcode/tree/lowest-common-ancestor-of-a-binary-tree.ts similarity index 100% rename from src/tree/lowest-common-ancestor-of-a-binary-tree.ts rename to src/leetcode/tree/lowest-common-ancestor-of-a-binary-tree.ts diff --git a/src/tree/range-sum-of-bst.ts b/src/leetcode/tree/range-sum-of-bst.ts similarity index 100% rename from src/tree/range-sum-of-bst.ts rename to src/leetcode/tree/range-sum-of-bst.ts diff --git a/src/tree/same-tree.ts b/src/leetcode/tree/same-tree.ts similarity index 100% rename from src/tree/same-tree.ts rename to src/leetcode/tree/same-tree.ts diff --git a/src/tree/sum-root-to-leaf-numbers.ts b/src/leetcode/tree/sum-root-to-leaf-numbers.ts similarity index 100% rename from src/tree/sum-root-to-leaf-numbers.ts rename to src/leetcode/tree/sum-root-to-leaf-numbers.ts diff --git a/src/two-pointer/README.md b/src/leetcode/two_pointers/README.md similarity index 100% rename from src/two-pointer/README.md rename to src/leetcode/two_pointers/README.md diff --git a/src/leetcode/two_pointers/bag_of_tokens.py b/src/leetcode/two_pointers/bag_of_tokens.py new file mode 100644 index 0000000..8147a6d --- /dev/null +++ b/src/leetcode/two_pointers/bag_of_tokens.py @@ -0,0 +1,69 @@ +# DIFFICULTY: Medium +# +# You start with an initial power of `power`, an initial score of 0, and a bag of tokens given as an integer array +# tokens, where each tokens[i] denotes the value of tokeni. +# +# Your goal is to maximize the total score by strategically playing these tokens. In one move, you can play an unplayed +# token in one of the two ways (but not both for the same token): +# +# Face-up: If your current power is at least tokens[i], you may play tokeni, losing tokens[i] power and gaining 1 +# score. +# Face-down: If your current score is at least 1, you may play tokeni, gaining tokens[i] power and losing 1 score. +# +# Return the maximum possible score you can achieve after playing any number of tokens. +# +# See https://leetcode.com/problems/bag-of-tokens +class Solution: + def bagOfTokensScore(self, tokens: list[int], power: int) -> int: + """ + SOLUTION: + + You basically are allowed to gain power at the cost of score, or gain score at the cost of power. You want to + maximize the score. The problem does not tell you this, but: + + 1. You may view all the tokens in the bag before making any plays. + 1. You may play tokens in the bag in any order. + 2. You may opt to not play any tokens at all. + + Because score is always gained one point at a time, but power can be be any value, we'll want to spend the least + amount of power for score. That is, sacrifice smaller power tokens face up for score, play higher power tokens + for their raw (power) value. + + COMPLEXITY: + + Time complexity is dominated by sorting, making it O(n log n). + + Space complexity is O(1). + """ + # Sort the tokens so we can take power from the right end (higher power), and sacrifice tokens for score from + # the left end. + tokens.sort() + + # This problem is well suited for using the two pointers technique, iterating through the array from both + # directions. + left = 0 + right = len(tokens) - 1 + score = 0 + max_score = 0 + + while left <= right: + # If we can, try to eat the smaller power tokens to build score until we run out of power. + if power >= tokens[left]: + power -= tokens[left] + score += 1 + left += 1 + # It can be the case that while running through the bag of tokens, we've achieved maximum score possible. + # However, we should continue going through the bag just to check. + max_score = max(max_score, score) + + # If we can't eat a smaller token, let's eat a bigger token to build power so we can eat more later. + elif score > 0: + power += tokens[right] + score -= 1 + right -= 1 + + # If we cannot eat any tokens, then simply quit. + else: + break + + return max_score diff --git a/src/two-pointer/container-with-most-water.ts b/src/leetcode/two_pointers/container-with-most-water.ts similarity index 100% rename from src/two-pointer/container-with-most-water.ts rename to src/leetcode/two_pointers/container-with-most-water.ts diff --git a/src/two-pointer/longest-common-prefix.ts b/src/leetcode/two_pointers/longest-common-prefix.ts similarity index 100% rename from src/two-pointer/longest-common-prefix.ts rename to src/leetcode/two_pointers/longest-common-prefix.ts diff --git a/src/two-pointer/longest-substring-without-repeating-chars.ts b/src/leetcode/two_pointers/longest-substring-without-repeating-chars.ts similarity index 100% rename from src/two-pointer/longest-substring-without-repeating-chars.ts rename to src/leetcode/two_pointers/longest-substring-without-repeating-chars.ts diff --git a/src/two-pointer/remove-duplicates-from-sorted-array.ts b/src/leetcode/two_pointers/remove-duplicates-from-sorted-array.ts similarity index 100% rename from src/two-pointer/remove-duplicates-from-sorted-array.ts rename to src/leetcode/two_pointers/remove-duplicates-from-sorted-array.ts diff --git a/src/two-pointer/remove-element.ts b/src/leetcode/two_pointers/remove-element.ts similarity index 100% rename from src/two-pointer/remove-element.ts rename to src/leetcode/two_pointers/remove-element.ts diff --git a/src/two-pointer/string-compression.ts b/src/leetcode/two_pointers/string-compression.ts similarity index 100% rename from src/two-pointer/string-compression.ts rename to src/leetcode/two_pointers/string-compression.ts diff --git a/src/two-pointer/three-sum-closest.ts b/src/leetcode/two_pointers/three-sum-closest.ts similarity index 100% rename from src/two-pointer/three-sum-closest.ts rename to src/leetcode/two_pointers/three-sum-closest.ts diff --git a/src/two-pointer/three-sum.ts b/src/leetcode/two_pointers/three-sum.ts similarity index 100% rename from src/two-pointer/three-sum.ts rename to src/leetcode/two_pointers/three-sum.ts diff --git a/src/two-pointer/trapping-rain-water.ts b/src/leetcode/two_pointers/trapping-rain-water.ts similarity index 100% rename from src/two-pointer/trapping-rain-water.ts rename to src/leetcode/two_pointers/trapping-rain-water.ts diff --git a/src/two-pointer/two-sum-input-array-sorted.ts b/src/leetcode/two_pointers/two-sum-input-array-sorted.ts similarity index 100% rename from src/two-pointer/two-sum-input-array-sorted.ts rename to src/leetcode/two_pointers/two-sum-input-array-sorted.ts diff --git a/src/two-pointer/valid-palindrome-ii.ts b/src/leetcode/two_pointers/valid-palindrome-ii.ts similarity index 100% rename from src/two-pointer/valid-palindrome-ii.ts rename to src/leetcode/two_pointers/valid-palindrome-ii.ts diff --git a/src/two-pointer/valid-palindrome.ts b/src/leetcode/two_pointers/valid-palindrome.ts similarity index 100% rename from src/two-pointer/valid-palindrome.ts rename to src/leetcode/two_pointers/valid-palindrome.ts diff --git a/src/two-pointer/valid-triangle-number.ts b/src/leetcode/two_pointers/valid-triangle-number.ts similarity index 100% rename from src/two-pointer/valid-triangle-number.ts rename to src/leetcode/two_pointers/valid-triangle-number.ts diff --git a/src/recursion/generate-parentheses.ts b/src/recursion/generate-parentheses.ts deleted file mode 100644 index 689300d..0000000 --- a/src/recursion/generate-parentheses.ts +++ /dev/null @@ -1,54 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses. -// -// See {@link https://leetcode.com/problems/generate-parentheses/} -export { generateParenthesis }; - -// SOLUTION: -// -// A naive way to do this would be to do something like generate all the parentheses for n-1, then add more -// parentheses to each of the elements generated. For example, by transforming each element of the n-1 solution to -// `${e}()`, `()${e}`, and `($e)`. -// -// This doesn't work for values like n=4, because you will miss solutions like `(())(())`. Instead, we'll construct -// the string as we go along, keeping track of how many open and close parentheses we have used. In total, the -// resulting strings will each have length n*2. -// -// This is because well formed strings have additional constraints; when generating a power set using the naive -// method, for example, you don't need to worry about balancing the elements within the sub set. In contrast to -// generating a power set, you would have far fewer resulting elements. -function generateParenthesis(n: number): string[] { - if (n === 0) { - return []; - } - - const result: string[] = []; - const max = n * 2; - - function generate(text: string, opens: number, closes: number) { - // The max length of the string is n * 2 because each open paren requires a close paren. We can't get fewer than - // n * 2 characters in a string either, because we have to generate all possible combinations. - if (text.length === max) { - result.push(text); - return; - } - - // The total string length is n * 2, but we can only open a max of n parens; the rest have to be closed. If we - // still have opens remaining, we can open more, or we can close. - if (opens < n) { - generate(`${text}(`, opens + 1, closes); - // Additionally, we can open to close some more parens if there are any open. - if (opens > closes) { - generate(`${text})`, opens, closes + 1); - } - } - // Here we have exceeded our budget of opening parens, so we have to only close. - else { - generate(`${text})`, opens, closes + 1); - } - } - - generate('', 0, 0); - return result; -} diff --git a/src/stack/valid-parentheses.ts b/src/stack/valid-parentheses.ts deleted file mode 100644 index 7a5d7b7..0000000 --- a/src/stack/valid-parentheses.ts +++ /dev/null @@ -1,44 +0,0 @@ -// DIFFICULTY: EASY -// -// Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid. -// -// An input string is valid if: -// -// Open brackets must be closed by the same type of brackets. -// Open brackets must be closed in the correct order. -// Every close bracket has a corresponding open bracket of the same type. -// -// See {@link https://leetcode.com/problems/valid-parentheses/} -export { isValid }; - -// SOLUTION: -function isValid(text: string) { - const map = new Map(); - map.set('(', ')'); - map.set('{', '}'); - map.set('[', ']'); - - const stack = new Array(); - for (let i = 0; i < text.length; i++) { - const c = text[i]; - - if (c === '(' || c === '{' || c === '[') { - stack.push(c); - continue; - } - - if (c === ')' || c === '}' || c === ']') { - if (stack.length === 0) { - return false; - } - - const open = stack.pop(); - const close = map.get(open || ''); - if (close !== c) { - return false; - } - } - } - - return stack.length === 0; -} diff --git a/src/string/valid-word-abbreviation.ts b/src/string/valid-word-abbreviation.ts deleted file mode 100644 index ef80968..0000000 --- a/src/string/valid-word-abbreviation.ts +++ /dev/null @@ -1,78 +0,0 @@ -// DIFFICULTY: EASY -// -// A string can be abbreviated by replacing any number of non-adjacent, non-empty substrings with their lengths. The -// lengths should not have leading zeros. -// -// For example, a string such as "substitution" could be abbreviated as (but not limited to): -// -// "s10n" ("s ubstitutio n") -// "sub4u4" ("sub stit u tion") -// "12" ("substitution") -// "su3i1u2on" ("su bst i t u ti on") -// "substitution" (no substrings replaced) -// The following are not valid abbreviations: -// -// "s55n" ("s ubsti tutio n", the replaced substrings are adjacent) -// "s010n" (has leading zeros) -// "s0ubstitution" (replaces an empty substring) -// Given a string word and an abbreviation abbr, return whether the string matches the given abbreviation. -// -// A substring is a contiguous non-empty sequence of characters within a string. -// -// See {@link https://leetcode.com/problems/valid-word-abbreviation/} -export { validWordAbbreviation }; - -// SOLUTION: -// -// To do this, we'll use two pointers, but not to iterate through a single string. Instead, each pointer iterates -// through its own string. If we see an numerical abbreviation, we'll advance through the other string a the -// corresponding number of characters. Because of that, this problem isn't categorized as a two-pointer problem. -// -// At each stage of advancement we'll check if the characters match. -function validWordAbbreviation(word: string, abbr: string): boolean { - function isDigit(c: string) { - // Number(x) will stop on invalid characters anywhere in the string, but Number.parseInt(x) will keep going until - // it finds a non-digit. We expect the entire input to be a number. - return !Number.isNaN(Number(c)); - } - - let i = 0; - let j = 0; - while (i < word.length && j < abbr.length) { - // Substitutions with leading zeroes (or replace an empty substring) are not valid. The word string can only - // contain non-numeric characters, so this can never match. - if (abbr[j] === '0') { - return false; - } - - // If it is a digit, perform the substitution by advancing the word pointer by the number of digits we read. - if (isDigit(abbr[j])) { - let num = 0; - - // We should collect digits into a number, and then do the advancement. - // - // To append to a number, we multiply by 10 and add the new digit. We also could've kept it as a string and then - // converted it afterwards. - while (j < abbr.length && isDigit(abbr[j])) { - num = num * 10 + Number(abbr[j]); - j++; - } - - i += num; - continue; - } - - // If it's not a digit, we check if the characters match. If they don't, it's not a valid substitution. - if (word[i] !== abbr[j]) { - return false; - } - - i++; - j++; - } - - // We need to have advanced PAST the last character in each string for it to be valid. The last character is - // checked on line 55, and the pointers advance one more time afterwards. Hence we should not subtract 1 from - // word.length or abbr.length. - return i === word.length && j === abbr.length; -} diff --git a/src/two-pointer/bag-of-tokens.ts b/src/two-pointer/bag-of-tokens.ts deleted file mode 100644 index 6fdca75..0000000 --- a/src/two-pointer/bag-of-tokens.ts +++ /dev/null @@ -1,76 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You start with an initial power of `power`, an initial score of 0, and a bag of tokens given as an integer array -// tokens, where each tokens[i] denotes the value of tokeni. -// -// Your goal is to maximize the total score by strategically playing these tokens. In one move, you can play an unplayed -// token in one of the two ways (but not both for the same token): -// -// Face-up: If your current power is at least tokens[i], you may play tokeni, losing tokens[i] power and gaining 1 -// score. -// Face-down: If your current score is at least 1, you may play tokeni, gaining tokens[i] power and losing 1 score. -// -// Return the maximum possible score you can achieve after playing any number of tokens. -// -// See {@link https://leetcode.com/problems/bag-of-tokens/} -export { bagOfTokensScore }; - -// SOLUTION: -// -// You basically are allowed to gain power at the cost of score, or gain score at the cost of power. You want to -// maximize the score. The problem does not tell you this, but: -// -// 1. The tokens are given to you as an array, but you may play them in any order. -// 2. You may opt to not play any tokens at all. -// -// Because score is always gained one point at a time, but power can be unlimited, we'll want to spend the least -// amount of power for score (that is make the smallest power tokens face up), while also sacrificing score for the -// highest power tokens. -// -// To start with, we'll try to play the highest power tokens face down first, then use that power to score as much as -// we can with the lower power tokens. This problem is suited for the two pointer technique since we are going to -// keep track of the high/low power tokens simultaneously. -// -// COMPLEXITY: -// -// Runs in O(n log n) time due to the sort. -function bagOfTokensScore(tokens: number[], power: number): number { - // First sort the tokens, so we can consume tokens from the ends to maximize score. - tokens.sort((a, b) => a - b); - - // Use the two pointers technique to figure out if we should consume tokens from the left to build power, or - // consume tokens from the right to build score. - let left = 0; - let right = tokens.length - 1; - let score = 0; - let max = 0; - - // Use left <= right here because you must process each token. - while (left <= right) { - // Since we are attempting to maximize score, let's try to eat the smaller tokens first to build score. - if (power >= tokens[left]) { - power -= tokens[left]; - score++; - left++; - - // Because we are running through every possible combination of token placing, we may have achieved max score - // already, but continue to play tokens. The problem asks for the max possible score, so record it here. - max = Math.max(score, max); - continue; - } - - // Otherwise, let's try to eat a big token to build power if we can. - if (score > 0) { - power += tokens[right]; - score--; - right--; - continue; - } - - // If we've reached this point, we don't have the score to build power. We also can't eat any tokens to build - // score, so we have to stop. - break; - } - - return max; -} diff --git a/test/array/__snapshots__/group-anagrams.test.ts.snap b/test/array/__snapshots__/group-anagrams.test.ts.snap deleted file mode 100644 index 8ad83de..0000000 --- a/test/array/__snapshots__/group-anagrams.test.ts.snap +++ /dev/null @@ -1,18 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`group anagrams group anagrams - test case 1 1`] = ` -[ - [ - "eat", - "tea", - "ate", - ], - [ - "tan", - "nat", - ], - [ - "bat", - ], -] -`; diff --git a/test/array/best-time-to-buy-and-sell-stock-ii.test.ts b/test/array/best-time-to-buy-and-sell-stock-ii.test.ts deleted file mode 100644 index 07f927c..0000000 --- a/test/array/best-time-to-buy-and-sell-stock-ii.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { maxProfit } from './../../src/array/best-time-to-buy-and-sell-stock-ii'; - -describe('best time to buy and sell stock ii', () => { - test('max profit ii - test case 1', async () => { - expect(maxProfit([7, 1, 5, 3, 6, 4])).toBe(7); - }); - - test('max profit ii - test case 2', async () => { - expect(maxProfit([1, 2, 3, 4, 5])).toBe(4); - }); -}); diff --git a/test/array/buildings-with-an-ocean-view.test.ts b/test/array/buildings-with-an-ocean-view.test.ts deleted file mode 100644 index f89fb22..0000000 --- a/test/array/buildings-with-an-ocean-view.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { findBuildings } from '../../src/array/buildings-with-an-ocean-view'; - -describe('buildings with an ocean view', () => { - test('findBuildings - test case 1', async () => { - expect(findBuildings([4, 2, 3, 1])).toEqual([0, 2, 3]); - }); -}); diff --git a/test/array/design-hit-counter.test.ts b/test/array/design-hit-counter.test.ts deleted file mode 100644 index 610e4ca..0000000 --- a/test/array/design-hit-counter.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { HitCounter } from '../../src/array/design-hit-counter'; - -describe('design hit counter', () => { - test('design hit counter - test case 1', async () => { - const counter = new HitCounter(); - - counter.hit(1); - counter.hit(2); - counter.hit(3); - - expect(counter.getHits(4)).toBe(3); - - counter.hit(300); - - expect(counter.getHits(300)).toBe(4); - expect(counter.getHits(301)).toBe(3); - }); -}); diff --git a/test/array/dot-product-of-two-sparse-vectors.test.ts b/test/array/dot-product-of-two-sparse-vectors.test.ts deleted file mode 100644 index f6e47c7..0000000 --- a/test/array/dot-product-of-two-sparse-vectors.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { SparseVector } from '../../src/array/dot-product-of-two-sparse-vectors'; - -describe('dot product of two sparse vectors', () => { - test('dot product of two sparse vectors', async () => { - const a = new SparseVector([1, 0, 0, 2, 3]); - const b = new SparseVector([0, 3, 0, 4, 0]); - - expect(a.dotProduct(b)).toStrictEqual(8); - }); -}); diff --git a/test/array/group-anagrams.test.ts b/test/array/group-anagrams.test.ts deleted file mode 100644 index d3c5217..0000000 --- a/test/array/group-anagrams.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { groupAnagrams } from '../../src/array/group-anagrams'; - -describe('group anagrams', () => { - test('group anagrams - test case 1', async () => { - expect(groupAnagrams(['eat', 'tea', 'tan', 'ate', 'nat', 'bat'])).toMatchSnapshot(); - }); -}); diff --git a/test/array/longest-consecutive-sequence.test.ts b/test/array/longest-consecutive-sequence.test.ts deleted file mode 100644 index 07b760c..0000000 --- a/test/array/longest-consecutive-sequence.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { longestConsecutive } from '../../src/array/longest-consecutive-sequence'; - -describe('longest consecutive sequence', () => { - test('longest consecutive - test case 1', async () => { - expect(longestConsecutive([100, 4, 200, 1, 3, 2])).toBe(4); - }); -}); diff --git a/test/array/max-chunks-to-make-sorted.test.ts b/test/array/max-chunks-to-make-sorted.test.ts deleted file mode 100644 index dabadcc..0000000 --- a/test/array/max-chunks-to-make-sorted.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { maxChunksToSorted } from '../../src/array/max-chunks-to-make-sorted'; - -describe('max chunks to make sorted', () => { - test('max chunks to make sorted - test case 1', async () => { - expect(maxChunksToSorted([4, 3, 2, 1, 0])).toBe(1); - }); -}); diff --git a/test/array/maximum-swap.test.ts b/test/array/maximum-swap.test.ts deleted file mode 100644 index 6f846b4..0000000 --- a/test/array/maximum-swap.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { maximumSwap } from '../../src/array/maximum-swap'; - -describe('maximum swap', () => { - test('maximumSwap - test case 1', () => { - expect(maximumSwap(2736)).toEqual(7236); - }); - - test('maximumSwap - test case 2', () => { - expect(maximumSwap(1993)).toEqual(9913); - }); - - test('maximumSwap - test case 1', () => { - expect(maximumSwap(98368)).toEqual(98863); - }); -}); diff --git a/test/array/merge-sorted-array.test.ts b/test/array/merge-sorted-array.test.ts deleted file mode 100644 index 23f0569..0000000 --- a/test/array/merge-sorted-array.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { merge } from '../../src/array/merge-sorted-array'; - -describe('merge sorted array', () => { - test('merge sorted array - test case 1', async () => { - const nums1 = [1, 2, 3, 0, 0, 0]; - const m = 3; - const nums2 = [2, 5, 6]; - const n = 3; - - merge(nums1, m, nums2, n); - - expect(nums1).toStrictEqual([1, 2, 2, 3, 5, 6]); - }); - - test('merge sorted array - test case 2', async () => { - const nums1 = [4, 5, 6, 0, 0, 0]; - const m = 3; - const nums2 = [1, 2, 3]; - const n = 3; - - merge(nums1, m, nums2, n); - - expect(nums1).toStrictEqual([1, 2, 3, 4, 5, 6]); - }); -}); diff --git a/test/array/pascals-triangle.test.ts b/test/array/pascals-triangle.test.ts deleted file mode 100644 index 7d2cecc..0000000 --- a/test/array/pascals-triangle.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { generate } from '../../src/array/pascals-triangle'; - -describe('pascals triangle', () => { - test('pascals triangle - test case 1', async () => { - expect(generate(5)).toStrictEqual([[1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1]]); - }); -}); diff --git a/test/array/product-of-array-except-self.test.ts b/test/array/product-of-array-except-self.test.ts deleted file mode 100644 index 31113cc..0000000 --- a/test/array/product-of-array-except-self.test.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { productExceptSelf } from '../../src/prefix-sum/product-of-array-except-self'; - -describe('product of array except self', () => { - test('product except self - test case 1', async () => { - expect(productExceptSelf([0, 2, 3, 4])).toStrictEqual([24, 0, 0, 0]); - expect(productExceptSelf([1, 2, 3, 4])).toStrictEqual([24, 12, 8, 6]); - }); -}); diff --git a/test/array/top-k-frequent-elements.test.ts b/test/array/top-k-frequent-elements.test.ts deleted file mode 100644 index a42587d..0000000 --- a/test/array/top-k-frequent-elements.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { topKFrequent } from '../../src/array/top-k-frequent-elements'; - -describe('top k frequent elements', () => { - test('top k frequent - test case 1', async () => { - expect(topKFrequent([1, 1, 1, 2, 2, 3], 2)).toStrictEqual([1, 2]); - }); -}); diff --git a/test/array/two-sum.test.ts b/test/array/two-sum.test.ts deleted file mode 100644 index 4d24646..0000000 --- a/test/array/two-sum.test.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { twoSum } from '../../src/array/two-sum'; - -describe('two sum', () => { - test('two sum - test case 2', async () => { - expect(twoSum([2, 7, 11, 15], 9)).toStrictEqual([1, 0]); - expect(twoSum([3, 2, 4], 6)).toStrictEqual([2, 1]); - expect(twoSum([3, 3], 6)).toStrictEqual([1, 0]); - }); -}); diff --git a/test/heap/furthest-building-you-can-reach.test.ts b/test/heap/furthest-building-you-can-reach.test.ts deleted file mode 100644 index fd47144..0000000 --- a/test/heap/furthest-building-you-can-reach.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { furthestBuilding } from '../../src/heap/furthest-building-you-can-reach'; - -describe('furthest building you can reach', () => { - test('furthest building - test case 1', async () => { - const heights = [4, 2, 7, 6, 9, 14, 12]; - expect(furthestBuilding(heights, 5, 1)).toBe(4); - }); - - test('furthest building - test case 2', async () => { - const heights = [4, 12, 2, 7, 3, 18, 20, 3, 19]; - expect(furthestBuilding(heights, 10, 2)).toBe(7); - }); - - test('furthest building - test case 3', async () => { - const heights = [14, 3, 19, 3]; - expect(furthestBuilding(heights, 17, 0)).toBe(3); - }); -}); diff --git a/test/heap/kth-largest-element.test.ts b/test/heap/kth-largest-element.test.ts deleted file mode 100644 index 25ed297..0000000 --- a/test/heap/kth-largest-element.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import { findKthLargest } from '../../src/heap/kth-largest-element'; - -describe('kth largest element in an array', () => { - test('find kth largest - test case 1', async () => { - expect(findKthLargest([3, 2, 1, 5, 6, 4], 2)).toBe(5); - }); - - test('find kth largest - test case 2', async () => { - expect(findKthLargest([3, 2, 3, 1, 2, 4, 5, 5, 6], 4)).toBe(4); - }); - - test.skip('find kth largest - test case 3', async () => { - const data = fs.readFileSync(path.join(__dirname, '__data__', 'kth-largest-element.test.json')).toString(); - const array = JSON.parse(data); - - expect(findKthLargest(array, 50000)).toBe(1); - }); -}); diff --git a/test/heap/minimize-deviation-in-array.test.ts b/test/heap/minimize-deviation-in-array.test.ts deleted file mode 100644 index 34c6733..0000000 --- a/test/heap/minimize-deviation-in-array.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { minimumDeviation } from '../../src/heap/minimize-deviation-in-array'; - -describe('minimize deviation in array', () => { - test('minimum deviation - test case 1', async () => { - const xs = [1, 2, 3, 4]; - - expect(minimumDeviation(xs)).toBe(1); - }); - - test('minimum deviation - test case 2', async () => { - const xs = [4, 1, 5, 20, 3]; - - expect(minimumDeviation(xs)).toBe(3); - }); - - test('minimum deviation - test case 3', async () => { - const xs = [2, 10, 8]; - - expect(minimumDeviation(xs)).toBe(3); - }); -}); diff --git a/test/heap/numbers-of-orders-in-the-backlog.test.ts b/test/heap/numbers-of-orders-in-the-backlog.test.ts deleted file mode 100644 index ced2874..0000000 --- a/test/heap/numbers-of-orders-in-the-backlog.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { getNumberOfBacklogOrders } from '../../src/heap/numbers-of-orders-in-the-backlog'; - -describe('number of orders in the backlog', () => { - test('number of backlog orders - test case 1', async () => { - const orders = [ - [10, 5, 0], - [15, 2, 1], - [25, 1, 1], - [30, 4, 0] - ]; - - expect(getNumberOfBacklogOrders(orders)).toBe(6); - }); - - test('number of backlog orders - test case 2', async () => { - const orders = [ - [7, 1000000000, 1], - [15, 3, 0], - [5, 999999995, 0], - [5, 1, 1] - ]; - - expect(getNumberOfBacklogOrders(orders)).toBe(999999984); - }); - - test('number of backlog orders - test case 3', async () => { - const orders = [ - [19, 28, 0], - [9, 4, 1], - [25, 15, 1] - ]; - - expect(getNumberOfBacklogOrders(orders)).toBe(39); - }); - - test('number of backlog orders - test case 4', async () => { - const orders = [ - [26, 7, 0], - [16, 1, 1], - [14, 20, 0], - [23, 15, 1], - [24, 26, 0], - [19, 4, 1], - [1, 1, 0] - ]; - - expect(getNumberOfBacklogOrders(orders)).toBe(34); - }); - - test('number of backlog orders - test case 5', async () => { - const orders = [ - [1, 29, 1], - [22, 7, 1], - [24, 1, 0], - [25, 15, 1], - [18, 8, 1], - [8, 22, 0], - [25, 15, 1], - [30, 1, 1], - [27, 30, 0] - ]; - - expect(getNumberOfBacklogOrders(orders)).toBe(22); - }); -}); diff --git a/test/jest.config.mjs b/test/jest.config.mjs deleted file mode 100644 index a5c5568..0000000 --- a/test/jest.config.mjs +++ /dev/null @@ -1,12 +0,0 @@ -export default { - clearMocks: true, - moduleFileExtensions: ['js', 'ts'], - modulePaths: [''], - testEnvironment: 'node', - testMatch: ['**/*.test.ts'], - testTimeout: 1000, - transform: { - '^.+\\.ts$': 'ts-jest' - }, - verbose: true -}; diff --git a/test/leetcode/array/best_time_to_buy_and_sell_stock_ii_test.py b/test/leetcode/array/best_time_to_buy_and_sell_stock_ii_test.py new file mode 100644 index 0000000..583dcbe --- /dev/null +++ b/test/leetcode/array/best_time_to_buy_and_sell_stock_ii_test.py @@ -0,0 +1,16 @@ +from leetcode.heap.furthest_building_you_can_reach import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.furthestBuilding([4, 2, 7, 6, 9, 14, 12], 5, 1) == 4 + + +def test_case_2(): + assert soln.furthestBuilding([4, 12, 2, 7, 3, 18, 20, 3, 19], 10, 2) == 7 + + +def test_case_3(): + assert soln.furthestBuilding([14, 3, 19, 3], 17, 0) == 3 diff --git a/test/leetcode/array/buildings_with_an_ocean_view_test.py b/test/leetcode/array/buildings_with_an_ocean_view_test.py new file mode 100644 index 0000000..731e918 --- /dev/null +++ b/test/leetcode/array/buildings_with_an_ocean_view_test.py @@ -0,0 +1,7 @@ +from leetcode.array.buildings_with_an_ocean_view import Solution + +soln = Solution() + + +def test_case_1(): + assert soln.findBuildings([4, 2, 3, 1]) == [0, 2, 3] diff --git a/test/leetcode/array/design_hit_counter_test.py b/test/leetcode/array/design_hit_counter_test.py new file mode 100644 index 0000000..7a5aa5a --- /dev/null +++ b/test/leetcode/array/design_hit_counter_test.py @@ -0,0 +1,17 @@ +from leetcode.array.design_hit_counter import HitCounter + + +hc = HitCounter() + + +def test_case_1(): + hc.hit(1) + hc.hit(2) + hc.hit(3) + + assert hc.getHits(4) == 3 + + hc.hit(300) + + assert hc.getHits(300) == 4 + assert hc.getHits(301) == 3 diff --git a/test/leetcode/array/dot_product_of_two_sparse_vectors_test.py b/test/leetcode/array/dot_product_of_two_sparse_vectors_test.py new file mode 100644 index 0000000..83f8303 --- /dev/null +++ b/test/leetcode/array/dot_product_of_two_sparse_vectors_test.py @@ -0,0 +1,8 @@ +from leetcode.array.dot_product_of_two_sparse_vectors import SparseVector + + +def test_case_1(): + a = SparseVector([1, 0, 0, 2, 3]) + b = SparseVector([0, 3, 0, 4, 0]) + + assert a.dotProduct(b) == 8 diff --git a/test/leetcode/array/group_anagrams_test.py b/test/leetcode/array/group_anagrams_test.py new file mode 100644 index 0000000..09b620f --- /dev/null +++ b/test/leetcode/array/group_anagrams_test.py @@ -0,0 +1,8 @@ +from leetcode.array.group_anagrams import Solution + + +soln = Solution() + + +def test_case_1(snapshot): + snapshot.assert_match(soln.groupAnagrams(["eat", "tea", "tan", "ate", "nat", "bat"])) diff --git a/test/leetcode/array/longest_consecutive_sequence_test.py b/test/leetcode/array/longest_consecutive_sequence_test.py new file mode 100644 index 0000000..3ff4114 --- /dev/null +++ b/test/leetcode/array/longest_consecutive_sequence_test.py @@ -0,0 +1,8 @@ +from leetcode.array.longest_consecutive_sequence import Solution + + +soln = Solution() + + +def test_case_1(): + soln.longestConsecutive([100, 4, 200, 1, 3, 2]) == 4 diff --git a/test/leetcode/array/max_chunks_to_make_sorted_test.py b/test/leetcode/array/max_chunks_to_make_sorted_test.py new file mode 100644 index 0000000..250e4a9 --- /dev/null +++ b/test/leetcode/array/max_chunks_to_make_sorted_test.py @@ -0,0 +1,8 @@ +from leetcode.array.max_chunks_to_make_sorted import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.maxChunksToSorted([4, 3, 2, 1, 0]) == 1 diff --git a/test/leetcode/array/maximum_swap_test.py b/test/leetcode/array/maximum_swap_test.py new file mode 100644 index 0000000..c0702bb --- /dev/null +++ b/test/leetcode/array/maximum_swap_test.py @@ -0,0 +1,16 @@ +from leetcode.array.maximum_swap import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.maximumSwap(2736) == 7236 + + +def test_case_2(): + assert soln.maximumSwap(1993) == 9913 + + +def test_case_3(): + assert soln.maximumSwap(98368) == 98863 diff --git a/test/leetcode/array/merge_sorted_array_test.py b/test/leetcode/array/merge_sorted_array_test.py new file mode 100644 index 0000000..7b3ec07 --- /dev/null +++ b/test/leetcode/array/merge_sorted_array_test.py @@ -0,0 +1,26 @@ +from leetcode.array.merge_sorted_array import Solution + + +soln = Solution() + + +def test_case_1(): + xs = [1, 2, 3, 0, 0, 0] + m = 3 + ys = [2, 5, 6] + n = 3 + + soln.merge(xs, m, ys, n) + + assert xs == [1, 2, 2, 3, 5, 6] + + +def test_case_2(): + xs = [4, 5, 6, 0, 0, 0] + m = 3 + ys = [1, 2, 3] + n = 3 + + soln.merge(xs, m, ys, n) + + assert xs == [1, 2, 3, 4, 5, 6] diff --git a/test/leetcode/array/pascals_triangle_test.py b/test/leetcode/array/pascals_triangle_test.py new file mode 100644 index 0000000..c02e794 --- /dev/null +++ b/test/leetcode/array/pascals_triangle_test.py @@ -0,0 +1,8 @@ +from leetcode.array.pascals_triangle import Solution + + +soln = Solution() + + +def test_case_1(snapshot): + snapshot.assert_match(soln.generate(5)) diff --git a/test/leetcode/array/snapshots/snap_group_anagrams_test.py b/test/leetcode/array/snapshots/snap_group_anagrams_test.py new file mode 100644 index 0000000..fc448f3 --- /dev/null +++ b/test/leetcode/array/snapshots/snap_group_anagrams_test.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# snapshottest: v1 - https://goo.gl/zC4yUc +from __future__ import unicode_literals + +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots["test_case_1 1"] = [["eat", "tea", "ate"], ["tan", "nat"], ["bat"]] diff --git a/test/leetcode/array/snapshots/snap_pascals_triangle_test.py b/test/leetcode/array/snapshots/snap_pascals_triangle_test.py new file mode 100644 index 0000000..6b06dfa --- /dev/null +++ b/test/leetcode/array/snapshots/snap_pascals_triangle_test.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# snapshottest: v1 - https://goo.gl/zC4yUc +from __future__ import unicode_literals + +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots["test_case_1 1"] = [[1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1]] diff --git a/test/leetcode/array/top_k_frequent_elements_test.py b/test/leetcode/array/top_k_frequent_elements_test.py new file mode 100644 index 0000000..b6833a6 --- /dev/null +++ b/test/leetcode/array/top_k_frequent_elements_test.py @@ -0,0 +1,8 @@ +from leetcode.array.top_k_frequent_elements import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.topKFrequent([1, 1, 1, 2, 2, 3], 2) == [1, 2] diff --git a/test/leetcode/array/two_sum_test.py b/test/leetcode/array/two_sum_test.py new file mode 100644 index 0000000..5f08564 --- /dev/null +++ b/test/leetcode/array/two_sum_test.py @@ -0,0 +1,16 @@ +from leetcode.array.two_sum import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.twoSum([2, 7, 11, 15], 9) == [1, 0] + + +def test_case_2(): + assert soln.twoSum([3, 2, 4], 6) == [2, 1] + + +def test_case_3(): + assert soln.twoSum([3, 3], 6) == [1, 0] diff --git a/test/binary-search/find-first-and-last-position-of-element-in-sorted-array.test.ts b/test/leetcode/binary_search/find-first-and-last-position-of-element-in-sorted-array.test.ts similarity index 100% rename from test/binary-search/find-first-and-last-position-of-element-in-sorted-array.test.ts rename to test/leetcode/binary_search/find-first-and-last-position-of-element-in-sorted-array.test.ts diff --git a/test/binary-search/find-peak-element.test.ts b/test/leetcode/binary_search/find-peak-element.test.ts similarity index 100% rename from test/binary-search/find-peak-element.test.ts rename to test/leetcode/binary_search/find-peak-element.test.ts diff --git a/test/binary-search/find-the-smallest-divisor-given-a-threshold.test.ts b/test/leetcode/binary_search/find-the-smallest-divisor-given-a-threshold.test.ts similarity index 100% rename from test/binary-search/find-the-smallest-divisor-given-a-threshold.test.ts rename to test/leetcode/binary_search/find-the-smallest-divisor-given-a-threshold.test.ts diff --git a/test/binary-search/k-th-missing-positive-number.test.ts b/test/leetcode/binary_search/k-th-missing-positive-number.test.ts similarity index 100% rename from test/binary-search/k-th-missing-positive-number.test.ts rename to test/leetcode/binary_search/k-th-missing-positive-number.test.ts diff --git a/test/binary-search/k-th-smallest-element-in-a-sorted-matrix.test.ts b/test/leetcode/binary_search/k-th-smallest-element-in-a-sorted-matrix.test.ts similarity index 100% rename from test/binary-search/k-th-smallest-element-in-a-sorted-matrix.test.ts rename to test/leetcode/binary_search/k-th-smallest-element-in-a-sorted-matrix.test.ts diff --git a/test/binary-search/longest-increasing-subsequence.test.ts b/test/leetcode/binary_search/longest-increasing-subsequence.test.ts similarity index 100% rename from test/binary-search/longest-increasing-subsequence.test.ts rename to test/leetcode/binary_search/longest-increasing-subsequence.test.ts diff --git a/test/binary-search/median-of-two-sorted-arrays.test.ts b/test/leetcode/binary_search/median-of-two-sorted-arrays.test.ts similarity index 100% rename from test/binary-search/median-of-two-sorted-arrays.test.ts rename to test/leetcode/binary_search/median-of-two-sorted-arrays.test.ts diff --git a/test/binary-search/search-in-rotated-sorted-array.test.ts b/test/leetcode/binary_search/search-in-rotated-sorted-array.test.ts similarity index 100% rename from test/binary-search/search-in-rotated-sorted-array.test.ts rename to test/leetcode/binary_search/search-in-rotated-sorted-array.test.ts diff --git a/test/dynamic-programming/arithmetic-slices-ii-subsequences.test.ts b/test/leetcode/dynamic_programming/arithmetic-slices-ii-subsequences.test.ts similarity index 100% rename from test/dynamic-programming/arithmetic-slices-ii-subsequences.test.ts rename to test/leetcode/dynamic_programming/arithmetic-slices-ii-subsequences.test.ts diff --git a/test/dynamic-programming/max-subarray.test.ts b/test/leetcode/dynamic_programming/max-subarray.test.ts similarity index 100% rename from test/dynamic-programming/max-subarray.test.ts rename to test/leetcode/dynamic_programming/max-subarray.test.ts diff --git a/test/dynamic-programming/word-break-i.test.ts b/test/leetcode/dynamic_programming/word-break-i.test.ts similarity index 100% rename from test/dynamic-programming/word-break-i.test.ts rename to test/leetcode/dynamic_programming/word-break-i.test.ts diff --git a/test/graph/__data__/bus-routes.test.json b/test/leetcode/graph/__data__/bus-routes.test.json similarity index 100% rename from test/graph/__data__/bus-routes.test.json rename to test/leetcode/graph/__data__/bus-routes.test.json diff --git a/test/graph/__snapshots__/parallel-job-scheduling.test.ts.snap b/test/leetcode/graph/__snapshots__/parallel-job-scheduling.test.ts.snap similarity index 100% rename from test/graph/__snapshots__/parallel-job-scheduling.test.ts.snap rename to test/leetcode/graph/__snapshots__/parallel-job-scheduling.test.ts.snap diff --git a/test/graph/__snapshots__/word-search-ii.test.ts.snap b/test/leetcode/graph/__snapshots__/word-search-ii.test.ts.snap similarity index 100% rename from test/graph/__snapshots__/word-search-ii.test.ts.snap rename to test/leetcode/graph/__snapshots__/word-search-ii.test.ts.snap diff --git a/test/graph/add-edges-to-make-degrees-even.test.ts b/test/leetcode/graph/add-edges-to-make-degrees-even.test.ts similarity index 100% rename from test/graph/add-edges-to-make-degrees-even.test.ts rename to test/leetcode/graph/add-edges-to-make-degrees-even.test.ts diff --git a/test/graph/bus-routes.test.ts b/test/leetcode/graph/bus-routes.test.ts similarity index 100% rename from test/graph/bus-routes.test.ts rename to test/leetcode/graph/bus-routes.test.ts diff --git a/test/graph/nested-list-weighted-sum-ii.test.ts b/test/leetcode/graph/nested-list-weighted-sum-ii.test.ts similarity index 100% rename from test/graph/nested-list-weighted-sum-ii.test.ts rename to test/leetcode/graph/nested-list-weighted-sum-ii.test.ts diff --git a/test/graph/nested-list-weighted-sum.test.ts b/test/leetcode/graph/nested-list-weighted-sum.test.ts similarity index 100% rename from test/graph/nested-list-weighted-sum.test.ts rename to test/leetcode/graph/nested-list-weighted-sum.test.ts diff --git a/test/graph/number-of-islands.test.ts b/test/leetcode/graph/number-of-islands.test.ts similarity index 100% rename from test/graph/number-of-islands.test.ts rename to test/leetcode/graph/number-of-islands.test.ts diff --git a/test/graph/parallel-job-scheduling.test.ts b/test/leetcode/graph/parallel-job-scheduling.test.ts similarity index 100% rename from test/graph/parallel-job-scheduling.test.ts rename to test/leetcode/graph/parallel-job-scheduling.test.ts diff --git a/test/graph/shortest-path-in-binary-matrix.test.ts b/test/leetcode/graph/shortest-path-in-binary-matrix.test.ts similarity index 100% rename from test/graph/shortest-path-in-binary-matrix.test.ts rename to test/leetcode/graph/shortest-path-in-binary-matrix.test.ts diff --git a/test/graph/smallest-greater-multiple-made-of-two-digits.test.ts b/test/leetcode/graph/smallest-greater-multiple-made-of-two-digits.test.ts similarity index 100% rename from test/graph/smallest-greater-multiple-made-of-two-digits.test.ts rename to test/leetcode/graph/smallest-greater-multiple-made-of-two-digits.test.ts diff --git a/test/graph/word-search-i.test.ts b/test/leetcode/graph/word-search-i.test.ts similarity index 100% rename from test/graph/word-search-i.test.ts rename to test/leetcode/graph/word-search-i.test.ts diff --git a/test/graph/word-search-ii.test.ts b/test/leetcode/graph/word-search-ii.test.ts similarity index 100% rename from test/graph/word-search-ii.test.ts rename to test/leetcode/graph/word-search-ii.test.ts diff --git a/test/heap/__data__/kth-largest-element.test.json b/test/leetcode/heap/__data__/kth-largest-element.test.json similarity index 100% rename from test/heap/__data__/kth-largest-element.test.json rename to test/leetcode/heap/__data__/kth-largest-element.test.json diff --git a/test/leetcode/heap/furthest_building_you_can_reach_test.py b/test/leetcode/heap/furthest_building_you_can_reach_test.py new file mode 100644 index 0000000..583dcbe --- /dev/null +++ b/test/leetcode/heap/furthest_building_you_can_reach_test.py @@ -0,0 +1,16 @@ +from leetcode.heap.furthest_building_you_can_reach import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.furthestBuilding([4, 2, 7, 6, 9, 14, 12], 5, 1) == 4 + + +def test_case_2(): + assert soln.furthestBuilding([4, 12, 2, 7, 3, 18, 20, 3, 19], 10, 2) == 7 + + +def test_case_3(): + assert soln.furthestBuilding([14, 3, 19, 3], 17, 0) == 3 diff --git a/test/heap/k-closest-points-to-origin.test.ts b/test/leetcode/heap/k-closest-points-to-origin.test.ts similarity index 100% rename from test/heap/k-closest-points-to-origin.test.ts rename to test/leetcode/heap/k-closest-points-to-origin.test.ts diff --git a/test/leetcode/heap/kth_largest_element_test.py b/test/leetcode/heap/kth_largest_element_test.py new file mode 100644 index 0000000..b199e7a --- /dev/null +++ b/test/leetcode/heap/kth_largest_element_test.py @@ -0,0 +1,12 @@ +from leetcode.heap.kth_largest_element import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.findKthLargest([3, 2, 1, 5, 6, 4], 2) == 5 + + +def test_case_2(): + assert soln.findKthLargest([3, 2, 3, 1, 2, 4, 5, 5, 6], 4) == 4 diff --git a/test/leetcode/heap/minimize_deviation_in_array_test.py b/test/leetcode/heap/minimize_deviation_in_array_test.py new file mode 100644 index 0000000..773aa81 --- /dev/null +++ b/test/leetcode/heap/minimize_deviation_in_array_test.py @@ -0,0 +1,16 @@ +from leetcode.heap.minimize_deviation_in_array import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.minimumDeviation([1, 2, 3, 4]) == 1 + + +def test_case_2(): + assert soln.minimumDeviation([4, 1, 5, 20, 3]) == 3 + + +def test_case_3(): + assert soln.minimumDeviation([2, 10, 8]) == 3 diff --git a/test/leetcode/heap/number_of_orders_in_the_backlog_test.py b/test/leetcode/heap/number_of_orders_in_the_backlog_test.py new file mode 100644 index 0000000..1200c2a --- /dev/null +++ b/test/leetcode/heap/number_of_orders_in_the_backlog_test.py @@ -0,0 +1,44 @@ +from leetcode.heap.number_of_orders_in_the_backlog import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.getNumberOfBacklogOrders([[10, 5, 0], [15, 2, 1], [25, 1, 1], [30, 4, 0]]) == 6 + + +def test_case_2(): + assert soln.getNumberOfBacklogOrders([[7, 1000000000, 1], [15, 3, 0], [5, 999999995, 0], [5, 1, 1]]) == 999999984 + + +def test_case_3(): + assert soln.getNumberOfBacklogOrders([[19, 28, 0], [9, 4, 1], [25, 15, 1]]) == 39 + + +def test_case_4(): + assert ( + soln.getNumberOfBacklogOrders( + [[26, 7, 0], [16, 1, 1], [14, 20, 0], [23, 15, 1], [24, 26, 0], [19, 4, 1], [1, 1, 0]] + ) + == 34 + ) + + +def test_case_5(): + assert ( + soln.getNumberOfBacklogOrders( + [ + [1, 29, 1], + [22, 7, 1], + [24, 1, 0], + [25, 15, 1], + [18, 8, 1], + [8, 22, 0], + [25, 15, 1], + [30, 1, 1], + [27, 30, 0], + ] + ) + == 22 + ) diff --git a/test/heap/task-scheduler.test.ts b/test/leetcode/heap/task-scheduler.test.ts similarity index 100% rename from test/heap/task-scheduler.test.ts rename to test/leetcode/heap/task-scheduler.test.ts diff --git a/test/interval/interval-list-intersection.test.ts b/test/leetcode/interval/interval-list-intersection.test.ts similarity index 100% rename from test/interval/interval-list-intersection.test.ts rename to test/leetcode/interval/interval-list-intersection.test.ts diff --git a/test/interval/meeting-rooms.test.ts b/test/leetcode/interval/meeting-rooms.test.ts similarity index 100% rename from test/interval/meeting-rooms.test.ts rename to test/leetcode/interval/meeting-rooms.test.ts diff --git a/test/interval/meeting-scheduler.test.ts b/test/leetcode/interval/meeting-scheduler.test.ts similarity index 100% rename from test/interval/meeting-scheduler.test.ts rename to test/leetcode/interval/meeting-scheduler.test.ts diff --git a/test/interval/merge-intervals.test.ts b/test/leetcode/interval/merge-intervals.test.ts similarity index 100% rename from test/interval/merge-intervals.test.ts rename to test/leetcode/interval/merge-intervals.test.ts diff --git a/test/linked-list/add-two-numbers.test.ts b/test/leetcode/linked_list/add-two-numbers.test.ts similarity index 100% rename from test/linked-list/add-two-numbers.test.ts rename to test/leetcode/linked_list/add-two-numbers.test.ts diff --git a/test/linked-list/all-one.test.ts b/test/leetcode/linked_list/all-one.test.ts similarity index 100% rename from test/linked-list/all-one.test.ts rename to test/leetcode/linked_list/all-one.test.ts diff --git a/test/linked-list/lru-cache.test.ts b/test/leetcode/linked_list/lru-cache.test.ts similarity index 100% rename from test/linked-list/lru-cache.test.ts rename to test/leetcode/linked_list/lru-cache.test.ts diff --git a/test/linked-list/merge-k-sorted-lists.test.ts b/test/leetcode/linked_list/merge-k-sorted-lists.test.ts similarity index 100% rename from test/linked-list/merge-k-sorted-lists.test.ts rename to test/leetcode/linked_list/merge-k-sorted-lists.test.ts diff --git a/test/linked-list/merge-two-sorted-lists.test.ts b/test/leetcode/linked_list/merge-two-sorted-lists.test.ts similarity index 100% rename from test/linked-list/merge-two-sorted-lists.test.ts rename to test/leetcode/linked_list/merge-two-sorted-lists.test.ts diff --git a/test/math/__data__/task-scheduler.test.json b/test/leetcode/math/__data__/task-scheduler.test.json similarity index 100% rename from test/math/__data__/task-scheduler.test.json rename to test/leetcode/math/__data__/task-scheduler.test.json diff --git a/test/math/__snapshots__/sequential-digits.test.ts.snap b/test/leetcode/math/__snapshots__/sequential-digits.test.ts.snap similarity index 100% rename from test/math/__snapshots__/sequential-digits.test.ts.snap rename to test/leetcode/math/__snapshots__/sequential-digits.test.ts.snap diff --git a/test/math/number-of-one-bits.test.ts b/test/leetcode/math/number-of-one-bits.test.ts similarity index 100% rename from test/math/number-of-one-bits.test.ts rename to test/leetcode/math/number-of-one-bits.test.ts diff --git a/test/math/pow-x-n.test.ts b/test/leetcode/math/pow-x-n.test.ts similarity index 100% rename from test/math/pow-x-n.test.ts rename to test/leetcode/math/pow-x-n.test.ts diff --git a/test/math/reverse-integer.test.ts b/test/leetcode/math/reverse-integer.test.ts similarity index 100% rename from test/math/reverse-integer.test.ts rename to test/leetcode/math/reverse-integer.test.ts diff --git a/test/math/rotate-image.test.ts b/test/leetcode/math/rotate-image.test.ts similarity index 100% rename from test/math/rotate-image.test.ts rename to test/leetcode/math/rotate-image.test.ts diff --git a/test/math/sequential-digits.test.ts b/test/leetcode/math/sequential-digits.test.ts similarity index 100% rename from test/math/sequential-digits.test.ts rename to test/leetcode/math/sequential-digits.test.ts diff --git a/test/matrix/robot-bounded-in-circle.test.ts b/test/leetcode/matrix/robot-bounded-in-circle.test.ts similarity index 100% rename from test/matrix/robot-bounded-in-circle.test.ts rename to test/leetcode/matrix/robot-bounded-in-circle.test.ts diff --git a/test/matrix/valid-word-square.test.ts b/test/leetcode/matrix/valid-word-square.test.ts similarity index 100% rename from test/matrix/valid-word-square.test.ts rename to test/leetcode/matrix/valid-word-square.test.ts diff --git a/test/prefix-sum/continuous-subarray-sum.test.ts b/test/leetcode/prefix_sum/continuous-subarray-sum.test.ts similarity index 100% rename from test/prefix-sum/continuous-subarray-sum.test.ts rename to test/leetcode/prefix_sum/continuous-subarray-sum.test.ts diff --git a/test/prefix-sum/max-sum-obtained-of-any-permutation.test.ts b/test/leetcode/prefix_sum/max-sum-obtained-of-any-permutation.test.ts similarity index 100% rename from test/prefix-sum/max-sum-obtained-of-any-permutation.test.ts rename to test/leetcode/prefix_sum/max-sum-obtained-of-any-permutation.test.ts diff --git a/test/prefix-sum/range-sum-query-immutable.test.ts b/test/leetcode/prefix_sum/range-sum-query-immutable.test.ts similarity index 100% rename from test/prefix-sum/range-sum-query-immutable.test.ts rename to test/leetcode/prefix_sum/range-sum-query-immutable.test.ts diff --git a/test/prefix-sum/running-sum-of-1d-array.test.ts b/test/leetcode/prefix_sum/running-sum-of-1d-array.test.ts similarity index 100% rename from test/prefix-sum/running-sum-of-1d-array.test.ts rename to test/leetcode/prefix_sum/running-sum-of-1d-array.test.ts diff --git a/test/prefix-sum/subarray-sum-equals-k.test.ts b/test/leetcode/prefix_sum/subarray-sum-equals-k.test.ts similarity index 100% rename from test/prefix-sum/subarray-sum-equals-k.test.ts rename to test/leetcode/prefix_sum/subarray-sum-equals-k.test.ts diff --git a/test/recursion/generate-parentheses.test.ts b/test/leetcode/recursion/generate-parentheses.test.ts similarity index 100% rename from test/recursion/generate-parentheses.test.ts rename to test/leetcode/recursion/generate-parentheses.test.ts diff --git a/test/leetcode/recursion/generate_parentheses_test.py b/test/leetcode/recursion/generate_parentheses_test.py new file mode 100644 index 0000000..f646b85 --- /dev/null +++ b/test/leetcode/recursion/generate_parentheses_test.py @@ -0,0 +1,16 @@ +from leetcode.recursion.generate_parentheses import Solution + + +soln = Solution() + + +def test_case_1(): + assert set(soln.generateParenthesis(2)) == set(["()()", "(())"]) + + +def test_case_2(snapshot): + snapshot.assert_match(soln.generateParenthesis(3)) + + +def test_case_3(snapshot): + snapshot.assert_match(soln.generateParenthesis(4)) diff --git a/test/recursion/maximum-number-of-operations-with-the-same-score.test.ts b/test/leetcode/recursion/maximum-number-of-operations-with-the-same-score.test.ts similarity index 100% rename from test/recursion/maximum-number-of-operations-with-the-same-score.test.ts rename to test/leetcode/recursion/maximum-number-of-operations-with-the-same-score.test.ts diff --git a/test/recursion/optimal-account-balancing.test.ts b/test/leetcode/recursion/optimal-account-balancing.test.ts similarity index 100% rename from test/recursion/optimal-account-balancing.test.ts rename to test/leetcode/recursion/optimal-account-balancing.test.ts diff --git a/test/recursion/permutations.test.ts b/test/leetcode/recursion/permutations.test.ts similarity index 100% rename from test/recursion/permutations.test.ts rename to test/leetcode/recursion/permutations.test.ts diff --git a/test/leetcode/recursion/snapshots/snap_generate_parentheses_test.py b/test/leetcode/recursion/snapshots/snap_generate_parentheses_test.py new file mode 100644 index 0000000..9e27885 --- /dev/null +++ b/test/leetcode/recursion/snapshots/snap_generate_parentheses_test.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# snapshottest: v1 - https://goo.gl/zC4yUc +from __future__ import unicode_literals + +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots["test_case_2 1"] = ["((()))", "(()())", "(())()", "()(())", "()()()"] + +snapshots["test_case_3 1"] = [ + "(((())))", + "((()()))", + "((())())", + "((()))()", + "(()(()))", + "(()()())", + "(()())()", + "(())(())", + "(())()()", + "()((()))", + "()(()())", + "()(())()", + "()()(())", + "()()()()", +] diff --git a/test/recursion/subsets.test.ts b/test/leetcode/recursion/subsets.test.ts similarity index 100% rename from test/recursion/subsets.test.ts rename to test/leetcode/recursion/subsets.test.ts diff --git a/test/recursion/word-break-ii.test.ts b/test/leetcode/recursion/word-break-ii.test.ts similarity index 100% rename from test/recursion/word-break-ii.test.ts rename to test/leetcode/recursion/word-break-ii.test.ts diff --git a/test/sliding-window/__data__/count-subarrays-where-max-element-appears.test.json b/test/leetcode/sliding_window/__data__/count-subarrays-where-max-element-appears.test.json similarity index 100% rename from test/sliding-window/__data__/count-subarrays-where-max-element-appears.test.json rename to test/leetcode/sliding_window/__data__/count-subarrays-where-max-element-appears.test.json diff --git a/test/sliding-window/best-time-to-buy-and-sell-stock-i.test.ts b/test/leetcode/sliding_window/best-time-to-buy-and-sell-stock-i.test.ts similarity index 100% rename from test/sliding-window/best-time-to-buy-and-sell-stock-i.test.ts rename to test/leetcode/sliding_window/best-time-to-buy-and-sell-stock-i.test.ts diff --git a/test/sliding-window/count-subarrays-where-max-element-appears.test.ts b/test/leetcode/sliding_window/count-subarrays-where-max-element-appears.test.ts similarity index 100% rename from test/sliding-window/count-subarrays-where-max-element-appears.test.ts rename to test/leetcode/sliding_window/count-subarrays-where-max-element-appears.test.ts diff --git a/test/sliding-window/length-of-longest-substring.test.ts b/test/leetcode/sliding_window/length-of-longest-substring.test.ts similarity index 100% rename from test/sliding-window/length-of-longest-substring.test.ts rename to test/leetcode/sliding_window/length-of-longest-substring.test.ts diff --git a/test/sliding-window/longest-continuous-subarray.test.ts b/test/leetcode/sliding_window/longest-continuous-subarray.test.ts similarity index 100% rename from test/sliding-window/longest-continuous-subarray.test.ts rename to test/leetcode/sliding_window/longest-continuous-subarray.test.ts diff --git a/test/sliding-window/max-sum-distinct-subarray-of-size-k.test.ts b/test/leetcode/sliding_window/max-sum-distinct-subarray-of-size-k.test.ts similarity index 100% rename from test/sliding-window/max-sum-distinct-subarray-of-size-k.test.ts rename to test/leetcode/sliding_window/max-sum-distinct-subarray-of-size-k.test.ts diff --git a/test/sliding-window/minimum-window-substring.test.ts b/test/leetcode/sliding_window/minimum-window-substring.test.ts similarity index 100% rename from test/sliding-window/minimum-window-substring.test.ts rename to test/leetcode/sliding_window/minimum-window-substring.test.ts diff --git a/test/sliding-window/repeated-dna-sequences.test.ts b/test/leetcode/sliding_window/repeated-dna-sequences.test.ts similarity index 100% rename from test/sliding-window/repeated-dna-sequences.test.ts rename to test/leetcode/sliding_window/repeated-dna-sequences.test.ts diff --git a/test/stack/basic-calculator-ii.test.ts b/test/leetcode/stack/basic-calculator-ii.test.ts similarity index 100% rename from test/stack/basic-calculator-ii.test.ts rename to test/leetcode/stack/basic-calculator-ii.test.ts diff --git a/test/stack/basic-calculator.test.ts b/test/leetcode/stack/basic-calculator.test.ts similarity index 100% rename from test/stack/basic-calculator.test.ts rename to test/leetcode/stack/basic-calculator.test.ts diff --git a/test/stack/key-value-store-nested-transactions.test.ts b/test/leetcode/stack/key-value-store-nested-transactions.test.ts similarity index 100% rename from test/stack/key-value-store-nested-transactions.test.ts rename to test/leetcode/stack/key-value-store-nested-transactions.test.ts diff --git a/test/stack/longest-absolute-file-path.test.ts b/test/leetcode/stack/longest-absolute-file-path.test.ts similarity index 100% rename from test/stack/longest-absolute-file-path.test.ts rename to test/leetcode/stack/longest-absolute-file-path.test.ts diff --git a/test/stack/max-chunks-to-make-sorted-ii.test.ts b/test/leetcode/stack/max-chunks-to-make-sorted-ii.test.ts similarity index 100% rename from test/stack/max-chunks-to-make-sorted-ii.test.ts rename to test/leetcode/stack/max-chunks-to-make-sorted-ii.test.ts diff --git a/test/stack/max-stack.test.ts b/test/leetcode/stack/max-stack.test.ts similarity index 100% rename from test/stack/max-stack.test.ts rename to test/leetcode/stack/max-stack.test.ts diff --git a/test/stack/min-stack.test.ts b/test/leetcode/stack/min-stack.test.ts similarity index 100% rename from test/stack/min-stack.test.ts rename to test/leetcode/stack/min-stack.test.ts diff --git a/test/stack/minimum-add-to-make-valid-parentheses.test.ts b/test/leetcode/stack/minimum-add-to-make-valid-parentheses.test.ts similarity index 100% rename from test/stack/minimum-add-to-make-valid-parentheses.test.ts rename to test/leetcode/stack/minimum-add-to-make-valid-parentheses.test.ts diff --git a/test/stack/minimum-remove-to-make-valid-parentheses.test.ts b/test/leetcode/stack/minimum-remove-to-make-valid-parentheses.test.ts similarity index 100% rename from test/stack/minimum-remove-to-make-valid-parentheses.test.ts rename to test/leetcode/stack/minimum-remove-to-make-valid-parentheses.test.ts diff --git a/test/stack/simplify-path.test.ts b/test/leetcode/stack/simplify-path.test.ts similarity index 100% rename from test/stack/simplify-path.test.ts rename to test/leetcode/stack/simplify-path.test.ts diff --git a/test/stack/valid-number.test.ts b/test/leetcode/stack/valid-number.test.ts similarity index 100% rename from test/stack/valid-number.test.ts rename to test/leetcode/stack/valid-number.test.ts diff --git a/test/stack/valid-parentheses.test.ts b/test/leetcode/stack/valid-parentheses.test.ts similarity index 100% rename from test/stack/valid-parentheses.test.ts rename to test/leetcode/stack/valid-parentheses.test.ts diff --git a/test/string/find-the-closest-palindrome.test.ts b/test/leetcode/string/find-the-closest-palindrome.test.ts similarity index 100% rename from test/string/find-the-closest-palindrome.test.ts rename to test/leetcode/string/find-the-closest-palindrome.test.ts diff --git a/test/string/int-to-roman.test.ts b/test/leetcode/string/int-to-roman.test.ts similarity index 100% rename from test/string/int-to-roman.test.ts rename to test/leetcode/string/int-to-roman.test.ts diff --git a/test/string/integer-to-english-words.test.ts b/test/leetcode/string/integer-to-english-words.test.ts similarity index 100% rename from test/string/integer-to-english-words.test.ts rename to test/leetcode/string/integer-to-english-words.test.ts diff --git a/test/string/letter-combinations-phone-number.test.ts b/test/leetcode/string/letter-combinations-phone-number.test.ts similarity index 100% rename from test/string/letter-combinations-phone-number.test.ts rename to test/leetcode/string/letter-combinations-phone-number.test.ts diff --git a/test/string/palindrome-number.test.ts b/test/leetcode/string/palindrome-number.test.ts similarity index 100% rename from test/string/palindrome-number.test.ts rename to test/leetcode/string/palindrome-number.test.ts diff --git a/test/string/remove-all-adjacent-duplicates-i.test.ts b/test/leetcode/string/remove-all-adjacent-duplicates-i.test.ts similarity index 100% rename from test/string/remove-all-adjacent-duplicates-i.test.ts rename to test/leetcode/string/remove-all-adjacent-duplicates-i.test.ts diff --git a/test/string/remove-all-adjacent-duplicates-ii.test.ts b/test/leetcode/string/remove-all-adjacent-duplicates-ii.test.ts similarity index 100% rename from test/string/remove-all-adjacent-duplicates-ii.test.ts rename to test/leetcode/string/remove-all-adjacent-duplicates-ii.test.ts diff --git a/test/string/reverse-words-in-a-string.test.ts b/test/leetcode/string/reverse-words-in-a-string.test.ts similarity index 100% rename from test/string/reverse-words-in-a-string.test.ts rename to test/leetcode/string/reverse-words-in-a-string.test.ts diff --git a/test/string/roman-to-int.test.ts b/test/leetcode/string/roman-to-int.test.ts similarity index 100% rename from test/string/roman-to-int.test.ts rename to test/leetcode/string/roman-to-int.test.ts diff --git a/test/string/time-needed-to-rearrange-binary-string.test.ts b/test/leetcode/string/time-needed-to-rearrange-binary-string.test.ts similarity index 100% rename from test/string/time-needed-to-rearrange-binary-string.test.ts rename to test/leetcode/string/time-needed-to-rearrange-binary-string.test.ts diff --git a/test/string/valid-word-abbreviation.test.ts b/test/leetcode/string/valid-word-abbreviation.test.ts similarity index 100% rename from test/string/valid-word-abbreviation.test.ts rename to test/leetcode/string/valid-word-abbreviation.test.ts diff --git a/test/leetcode/string/valid_parentheses_test.py b/test/leetcode/string/valid_parentheses_test.py new file mode 100644 index 0000000..50b9ecd --- /dev/null +++ b/test/leetcode/string/valid_parentheses_test.py @@ -0,0 +1,16 @@ +from leetcode.stack.valid_parentheses import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.isValid("()") is True + + +def test_case_2(): + assert soln.isValid("()[]{}") is True + + +def test_case_3(): + assert not soln.isValid("(}") diff --git a/test/leetcode/string/valid_word_abbreviation_test.py b/test/leetcode/string/valid_word_abbreviation_test.py new file mode 100644 index 0000000..9705506 --- /dev/null +++ b/test/leetcode/string/valid_word_abbreviation_test.py @@ -0,0 +1,12 @@ +from leetcode.string.valid_word_abbreviation import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.validWordAbbreviation("internationalization", "i12iz4n") is True + + +def test_case_2(): + assert soln.validWordAbbreviation("apple", "a2e") is False diff --git a/test/string/vowel-spellchecker.test.ts b/test/leetcode/string/vowel-spellchecker.test.ts similarity index 100% rename from test/string/vowel-spellchecker.test.ts rename to test/leetcode/string/vowel-spellchecker.test.ts diff --git a/test/tree/__snapshots__/construct-quad-tree.test.ts.snap b/test/leetcode/tree/__snapshots__/construct-quad-tree.test.ts.snap similarity index 100% rename from test/tree/__snapshots__/construct-quad-tree.test.ts.snap rename to test/leetcode/tree/__snapshots__/construct-quad-tree.test.ts.snap diff --git a/test/tree/binary-tree-level-order-traversal.test.ts b/test/leetcode/tree/binary-tree-level-order-traversal.test.ts similarity index 100% rename from test/tree/binary-tree-level-order-traversal.test.ts rename to test/leetcode/tree/binary-tree-level-order-traversal.test.ts diff --git a/test/tree/binary-tree-right-side-view.test.ts b/test/leetcode/tree/binary-tree-right-side-view.test.ts similarity index 100% rename from test/tree/binary-tree-right-side-view.test.ts rename to test/leetcode/tree/binary-tree-right-side-view.test.ts diff --git a/test/tree/binary-tree-vertical-order-traversal.test.ts b/test/leetcode/tree/binary-tree-vertical-order-traversal.test.ts similarity index 100% rename from test/tree/binary-tree-vertical-order-traversal.test.ts rename to test/leetcode/tree/binary-tree-vertical-order-traversal.test.ts diff --git a/test/tree/construct-quad-tree.test.ts b/test/leetcode/tree/construct-quad-tree.test.ts similarity index 100% rename from test/tree/construct-quad-tree.test.ts rename to test/leetcode/tree/construct-quad-tree.test.ts diff --git a/test/tree/design-in-memory-file-system.test.ts b/test/leetcode/tree/design-in-memory-file-system.test.ts similarity index 100% rename from test/tree/design-in-memory-file-system.test.ts rename to test/leetcode/tree/design-in-memory-file-system.test.ts diff --git a/test/tree/diameter-of-binary-tree.test.ts b/test/leetcode/tree/diameter-of-binary-tree.test.ts similarity index 100% rename from test/tree/diameter-of-binary-tree.test.ts rename to test/leetcode/tree/diameter-of-binary-tree.test.ts diff --git a/test/tree/longest-palindromic-substring.test.ts b/test/leetcode/tree/longest-palindromic-substring.test.ts similarity index 100% rename from test/tree/longest-palindromic-substring.test.ts rename to test/leetcode/tree/longest-palindromic-substring.test.ts diff --git a/test/tree/lowest-common-ancestor-of-a-binary-tree-iii.test.ts b/test/leetcode/tree/lowest-common-ancestor-of-a-binary-tree-iii.test.ts similarity index 100% rename from test/tree/lowest-common-ancestor-of-a-binary-tree-iii.test.ts rename to test/leetcode/tree/lowest-common-ancestor-of-a-binary-tree-iii.test.ts diff --git a/test/tree/lowest-common-ancestor-of-a-binary-tree.test.ts b/test/leetcode/tree/lowest-common-ancestor-of-a-binary-tree.test.ts similarity index 100% rename from test/tree/lowest-common-ancestor-of-a-binary-tree.test.ts rename to test/leetcode/tree/lowest-common-ancestor-of-a-binary-tree.test.ts diff --git a/test/tree/range-sum-of-bst.test.ts b/test/leetcode/tree/range-sum-of-bst.test.ts similarity index 100% rename from test/tree/range-sum-of-bst.test.ts rename to test/leetcode/tree/range-sum-of-bst.test.ts diff --git a/test/tree/same-tree.test.ts b/test/leetcode/tree/same-tree.test.ts similarity index 100% rename from test/tree/same-tree.test.ts rename to test/leetcode/tree/same-tree.test.ts diff --git a/test/tree/sum-root-to-leaf-numbers.test.ts b/test/leetcode/tree/sum-root-to-leaf-numbers.test.ts similarity index 100% rename from test/tree/sum-root-to-leaf-numbers.test.ts rename to test/leetcode/tree/sum-root-to-leaf-numbers.test.ts diff --git a/test/two-pointer/__snapshots__/three-sum.test.ts.snap b/test/leetcode/two_pointers/__snapshots__/three-sum.test.ts.snap similarity index 100% rename from test/two-pointer/__snapshots__/three-sum.test.ts.snap rename to test/leetcode/two_pointers/__snapshots__/three-sum.test.ts.snap diff --git a/test/two-pointer/bag-of-tokens.test.ts b/test/leetcode/two_pointers/bag-of-tokens.test.ts similarity index 100% rename from test/two-pointer/bag-of-tokens.test.ts rename to test/leetcode/two_pointers/bag-of-tokens.test.ts diff --git a/test/leetcode/two_pointers/bag_of_tokens_test.py b/test/leetcode/two_pointers/bag_of_tokens_test.py new file mode 100644 index 0000000..a9026e0 --- /dev/null +++ b/test/leetcode/two_pointers/bag_of_tokens_test.py @@ -0,0 +1,16 @@ +from leetcode.two_pointers.bag_of_tokens import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.bagOfTokensScore([100], 50) == 0 + + +def test_case_2(): + assert soln.bagOfTokensScore([200, 100], 150) == 1 + + +def test_case_3(): + assert soln.bagOfTokensScore([100, 200, 300, 400], 200) == 2 diff --git a/test/two-pointer/container-with-most-water.test.ts b/test/leetcode/two_pointers/container-with-most-water.test.ts similarity index 100% rename from test/two-pointer/container-with-most-water.test.ts rename to test/leetcode/two_pointers/container-with-most-water.test.ts diff --git a/test/two-pointer/longest-common-prefix.test.ts b/test/leetcode/two_pointers/longest-common-prefix.test.ts similarity index 100% rename from test/two-pointer/longest-common-prefix.test.ts rename to test/leetcode/two_pointers/longest-common-prefix.test.ts diff --git a/test/two-pointer/longest-substring-without-repeating-chars.test.ts b/test/leetcode/two_pointers/longest-substring-without-repeating-chars.test.ts similarity index 100% rename from test/two-pointer/longest-substring-without-repeating-chars.test.ts rename to test/leetcode/two_pointers/longest-substring-without-repeating-chars.test.ts diff --git a/test/two-pointer/remove-duplicates-from-sorted-array.test.ts b/test/leetcode/two_pointers/remove-duplicates-from-sorted-array.test.ts similarity index 100% rename from test/two-pointer/remove-duplicates-from-sorted-array.test.ts rename to test/leetcode/two_pointers/remove-duplicates-from-sorted-array.test.ts diff --git a/test/two-pointer/remove-element.test.ts b/test/leetcode/two_pointers/remove-element.test.ts similarity index 100% rename from test/two-pointer/remove-element.test.ts rename to test/leetcode/two_pointers/remove-element.test.ts diff --git a/test/two-pointer/string-compression.test.ts b/test/leetcode/two_pointers/string-compression.test.ts similarity index 100% rename from test/two-pointer/string-compression.test.ts rename to test/leetcode/two_pointers/string-compression.test.ts diff --git a/test/two-pointer/three-sum-closest.test.ts b/test/leetcode/two_pointers/three-sum-closest.test.ts similarity index 100% rename from test/two-pointer/three-sum-closest.test.ts rename to test/leetcode/two_pointers/three-sum-closest.test.ts diff --git a/test/two-pointer/three-sum.test.ts b/test/leetcode/two_pointers/three-sum.test.ts similarity index 100% rename from test/two-pointer/three-sum.test.ts rename to test/leetcode/two_pointers/three-sum.test.ts diff --git a/test/two-pointer/trapping-rain-water.test.ts b/test/leetcode/two_pointers/trapping-rain-water.test.ts similarity index 100% rename from test/two-pointer/trapping-rain-water.test.ts rename to test/leetcode/two_pointers/trapping-rain-water.test.ts diff --git a/test/two-pointer/two-sum-input-array-sorted.test.ts b/test/leetcode/two_pointers/two-sum-input-array-sorted.test.ts similarity index 100% rename from test/two-pointer/two-sum-input-array-sorted.test.ts rename to test/leetcode/two_pointers/two-sum-input-array-sorted.test.ts diff --git a/test/two-pointer/valid-palindrome-ii.test.ts b/test/leetcode/two_pointers/valid-palindrome-ii.test.ts similarity index 100% rename from test/two-pointer/valid-palindrome-ii.test.ts rename to test/leetcode/two_pointers/valid-palindrome-ii.test.ts diff --git a/test/two-pointer/valid-palindrome.test.ts b/test/leetcode/two_pointers/valid-palindrome.test.ts similarity index 100% rename from test/two-pointer/valid-palindrome.test.ts rename to test/leetcode/two_pointers/valid-palindrome.test.ts diff --git a/test/two-pointer/valid-triangle-number.test.ts b/test/leetcode/two_pointers/valid-triangle-number.test.ts similarity index 100% rename from test/two-pointer/valid-triangle-number.test.ts rename to test/leetcode/two_pointers/valid-triangle-number.test.ts diff --git a/test/recursion/__snapshots__/generate-parentheses.test.ts.snap b/test/recursion/__snapshots__/generate-parentheses.test.ts.snap deleted file mode 100644 index 84edeb4..0000000 --- a/test/recursion/__snapshots__/generate-parentheses.test.ts.snap +++ /dev/null @@ -1,30 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`generate parentheses generate parenthesis - test case 4 1`] = ` -Set { - "((()))", - "(()())", - "(())()", - "()(())", - "()()()", -} -`; - -exports[`generate parentheses generate parenthesis - test case 5 1`] = ` -Set { - "(((())))", - "((()()))", - "((())())", - "((()))()", - "(()(()))", - "(()()())", - "(()())()", - "(())(())", - "(())()()", - "()((()))", - "()(()())", - "()(())()", - "()()(())", - "()()()()", -} -`; diff --git a/test/recursion/__snapshots__/permutations.test.ts.snap b/test/recursion/__snapshots__/permutations.test.ts.snap deleted file mode 100644 index 96ccf70..0000000 --- a/test/recursion/__snapshots__/permutations.test.ts.snap +++ /dev/null @@ -1,49 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`permute permute - test case 2 1`] = ` -[ - [ - 1, - 2, - 3, - ], - [ - 1, - 3, - 2, - ], - [ - 2, - 1, - 3, - ], - [ - 2, - 3, - 1, - ], - [ - 3, - 1, - 2, - ], - [ - 3, - 2, - 1, - ], -] -`; - -exports[`permute permute - test case 3 1`] = ` -[ - [ - 0, - 1, - ], - [ - 1, - 0, - ], -] -`; diff --git a/test/recursion/__snapshots__/subsets.test.ts.snap b/test/recursion/__snapshots__/subsets.test.ts.snap deleted file mode 100644 index eb64e59..0000000 --- a/test/recursion/__snapshots__/subsets.test.ts.snap +++ /dev/null @@ -1,42 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`subsets subsets - test case 2 1`] = ` -[ - [], - [ - 3, - ], - [ - 2, - ], - [ - 3, - 2, - ], - [ - 1, - ], - [ - 3, - 1, - ], - [ - 2, - 1, - ], - [ - 3, - 2, - 1, - ], -] -`; - -exports[`subsets subsets - test case 3 1`] = ` -[ - [], - [ - 0, - ], -] -`; diff --git a/test/recursion/__snapshots__/word-break-ii.test.ts.snap b/test/recursion/__snapshots__/word-break-ii.test.ts.snap deleted file mode 100644 index 342dee4..0000000 --- a/test/recursion/__snapshots__/word-break-ii.test.ts.snap +++ /dev/null @@ -1,16 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`word break ii word break ii - test case 1 1`] = ` -[ - "cat sand dog", - "cats and dog", -] -`; - -exports[`word break ii word break ii - test case 2 1`] = ` -[ - "pine apple pen apple", - "pine applepen apple", - "pineapple pen apple", -] -`; diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 2f75948..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - // See https://github.com/tsconfig/bases/blob/main/bases/recommended.json - "extends": "@tsconfig/recommended/tsconfig.json", - "compilerOptions": { - "declaration": true, - "declarationMap": true, - "esModuleInterop": true, - "module": "ESNext", - "moduleResolution": "node", - "outDir": "./build", - "sourceMap": true, - "strict": true, - "target": "ES2024", - "typeRoots": ["node_modules/@types"], - "types": ["jest", "node"] - }, - "include": ["script", "src", "test"], - "exclude": ["build", "dist", "node_modules"] -} From b8ab2ecd28d4bbc7d4c4b79175091ecc10869c7d Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 9 Mar 2025 23:06:43 -0700 Subject: [PATCH 002/158] Updates to poetry 2.1.1 --- .github/workflows/build-and-test.yml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 5bde350..801510b 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -34,7 +34,7 @@ jobs: python-version: '3.13' - name: Install poetry - run: python -m pip install poetry==1.8.5 + run: python -m pip install poetry==2.1.1 - name: Configure poetry run: poetry config virtualenvs.in-project true diff --git a/README.md b/README.md index 7c440c7..1009f22 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Problems and solutions for LeetCode. ## Development -1. Install poetry: `python -m pip install poetry==1.8.5` +1. Install poetry: `pip install poetry==2.1.1` 1. Configure poetry: `poetry config virtualenvs.in-project true` 1. Install dependencies: `poetry install` 1. Run tests: `poetry run test` From 8e3dab4446a2b053539f79567ebdb8f6f25daff5 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 9 Mar 2025 23:44:10 -0700 Subject: [PATCH 003/158] fix type issues --- src/leetcode/binary_search/README.md | 50 ++++++++-------- .../heap/k-closest-points-to-origin.ts | 56 ------------------ .../heap/k_closest_points_to_origin.py | 59 +++++++++++++++++++ .../__data__/kth-largest-element.test.json | 1 - .../heap/k-closest-points-to-origin.test.ts | 15 ----- .../heap/k_closest_points_to_origin_test.py | 8 +++ 6 files changed, 93 insertions(+), 96 deletions(-) delete mode 100644 src/leetcode/heap/k-closest-points-to-origin.ts create mode 100644 src/leetcode/heap/k_closest_points_to_origin.py delete mode 100644 test/leetcode/heap/__data__/kth-largest-element.test.json delete mode 100644 test/leetcode/heap/k-closest-points-to-origin.test.ts create mode 100644 test/leetcode/heap/k_closest_points_to_origin_test.py diff --git a/src/leetcode/binary_search/README.md b/src/leetcode/binary_search/README.md index 0030111..9ea77d8 100644 --- a/src/leetcode/binary_search/README.md +++ b/src/leetcode/binary_search/README.md @@ -12,18 +12,19 @@ Use this to check if an element exists in the array. The `left` and `right` poi Note that this can be used like left most duplicate to find a good insertion point as well. ``` -let left = 0, right = xs.length - 1; -while (left <= right) { - const m = Math.floor((left + right) / 2); - if (xs[m] < target) { - left = m + 1; - } else if (xs[m] > target) { - right = m - 1; - } else { - return m; - } -} -return -1; +left = 0 +right = len(xs) - 1 + +while left <= right: + m = (left + right) // 2 + if xs[m] < target: + left = m + 1 + elif xs[m] > target: + right = m - 1 + else: + return m + +return -1 ``` ### Left-most Duplicate @@ -31,24 +32,25 @@ return -1; Use this to check where an element can be inserted into the array. The `left` and `right` pointers are not always within bounds (because the element could be inserted at the end of the array). Exiting the while loop indicates the element was found or you've found a good place to insert. ``` -let left = 0, right = xs.length; -while (left < right) { - const m = Math.floor((left + right) / 2); - if (xs[m] < target) { - left = m + 1; - } else { - right = m; - } -} -return left < xs.length && xs[left] === target ? left : -1; +left = 0 +right = len(xs) + +while left < right: + m = (left + right) // 2 + if xs[m] < target: + left = m + 1 + else: + right = m + +return left < len(xs) && xs[left] === target and left or -1 ``` ## Common Phrases These phrases indicate binary search might be useful: -- 'sorted array' -- 'find the position or index' +- sorted array +- find the position or index ## Boundaries diff --git a/src/leetcode/heap/k-closest-points-to-origin.ts b/src/leetcode/heap/k-closest-points-to-origin.ts deleted file mode 100644 index eb591ac..0000000 --- a/src/leetcode/heap/k-closest-points-to-origin.ts +++ /dev/null @@ -1,56 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an array of points where points[i] = [xi, yi] represents a point on the X-Y plane and an integer k, return the -// k closest points to the origin (0, 0). -// -// The distance between two points on the X-Y plane is the Euclidean distance (i.e., √(x1 - x2)2 + (y1 - y2)2). -// -// You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in). -// -// See {@link https://leetcode.com/problems/k-closest-points-to-origin/} -import { MaxPriorityQueue } from '@datastructures-js/priority-queue'; -export { kClosest }; - -// SOLUTION: -// -// The request for "k-closest" indicates you'll want a heap. Using a either a min heap or max heap will work. -// -// With a min heap, just jam all the points on the heap and then slice the first k elements. This requires storing all -// the points on the heap. -// -// With a max heap, you can jam points on the heap until you have k elements. Then, when you have a new point, compare -// it to the max element on the heap. If it's closer, pop off the max element and jam it on the heap; otherwise, ignore -// it. -// -// The max heap approach can be more efficient, so we'll go that route. -// -// COMPLEXITY: -// -// With a min heap, it will be O(n * log(n)) to insert all the points on the heap. -// -// With a max heap, if k is much smaller than n, it will be O(n * log(k)) to insert all the points on the heap. -function kClosest(points: number[][], k: number): number[][] { - // Calculates the distance to the origin. Technically, since we're only using the distance to do comparison, we - // don't need to take the square root since they are all going to compare the same square root or not. I'm leaving - // it here for correctness. - function distance(p: number[]) { - const [x, y] = p; - return Math.sqrt(x ** 2 + y ** 2); - } - - // The datastructures-js/priority-queue behavior depends on the comparator. Make sure you do (a, b) => b - a to get - // max heap behavior, or else you'll end up with a min heap. - const heap = new MaxPriorityQueue({ compare: (a: number[], b: number[]) => distance(b) - distance(a) }); - for (const p of points) { - heap.enqueue(p); - - // If we have more than k elements, remove the farthest point. - if (heap.size() > k) { - heap.dequeue(); - } - } - - // Why do we need this cast? Because of LeetCode, TypeScript, datastructures-js, and the reason. - const array = heap.toArray() as unknown; - return array as number[][]; -} diff --git a/src/leetcode/heap/k_closest_points_to_origin.py b/src/leetcode/heap/k_closest_points_to_origin.py new file mode 100644 index 0000000..4f6df34 --- /dev/null +++ b/src/leetcode/heap/k_closest_points_to_origin.py @@ -0,0 +1,59 @@ +# DIFFICULTY: MEDIUM +# +# Given an array of points where points[i] = [xi, yi] represents a point on the X-Y plane and an integer k, return the +# k closest points to the origin (0, 0). +# +# The distance between two points on the X-Y plane is the Euclidean distance (i.e., √(x1 - x2)2 + (y1 - y2)2). +# +# You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in). +# +# See https://leetcode.com/problems/k-closest-points-to-origin +from heapq import heappush, heappop +from math import sqrt + + +class Solution: + def kClosest(self, points: list[list[int]], k: int) -> list[list[int]]: + """ + SOLUTION: + + The request for "k-closest" indicates you'll want a heap. Using a either a min heap or max heap will work. + + With a min heap, just jam all the points on the heap and then slice the first k elements. This requires storing all + the points on the heap. + + With a max heap, you can jam points on the heap until you have k elements. Then, when you have a new point, compare + it to the max element on the heap. If it's closer, pop off the max element and jam it on the heap; otherwise, ignore + it. + + The max heap approach can be more efficient, so we'll go that route. + + COMPLEXITY: + + With a min heap, it will be O(n * log(n)) to insert all the points on the heap. + + With a max heap, if k is much smaller than n, it will be O(n * log(k)) to insert all the points on the heap. + """ + + # Calculates the distance to the origin. Technically, since we're only using the distance to do comparison, we + # don't need to take the square root since they are all going to compare the same square root or not. I'm + # leaving it here for correctness. + def distance(p: list[int]) -> float: + [x, y] = p + return sqrt(x**2 + y**2) + + # Store max heap of (distance, point). LeetCode gives us the points as list of [x, y]. + max_heap: list[tuple[float, list[int]]] = [] + for p in points: + # Simulate a max heap by negating the distance. When storing a tuple in the heap, Python will sort by tuple + # ordering, so will first sort by the first element, then the second. Sorting by the first element + # (distance) is all we need. + d = distance(p) + heappush(max_heap, (-d, p)) + + # If we have more than k elements, remove the farthest point. + if len(max_heap) > k: + heappop(max_heap) + + # Return the k closest points, throwing away the distance. + return [p for _, p in max_heap] diff --git a/test/leetcode/heap/__data__/kth-largest-element.test.json b/test/leetcode/heap/__data__/kth-largest-element.test.json deleted file mode 100644 index d574998..0000000 --- a/test/leetcode/heap/__data__/kth-largest-element.test.json +++ /dev/null @@ -1 +0,0 @@ -[1,2,3,4,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,-5,-4,-3,-2,-1] \ No newline at end of file diff --git a/test/leetcode/heap/k-closest-points-to-origin.test.ts b/test/leetcode/heap/k-closest-points-to-origin.test.ts deleted file mode 100644 index cf02156..0000000 --- a/test/leetcode/heap/k-closest-points-to-origin.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { kClosest } from '../../src/heap/k-closest-points-to-origin'; - -describe('k closest points to origin', () => { - test('k closest - test case 1', () => { - expect( - kClosest( - [ - [1, 3], - [-2, 2] - ], - 1 - ) - ).toStrictEqual([[-2, 2]]); - }); -}); diff --git a/test/leetcode/heap/k_closest_points_to_origin_test.py b/test/leetcode/heap/k_closest_points_to_origin_test.py new file mode 100644 index 0000000..814811b --- /dev/null +++ b/test/leetcode/heap/k_closest_points_to_origin_test.py @@ -0,0 +1,8 @@ +from leetcode.heap.k_closest_points_to_origin import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.kClosest([[1, 3], [-2, 2]], 1) == [[-2, 2]] From 8b0e7dbe0fa7a25194113d5158e25a549c64c7d7 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 10 Mar 2025 00:56:17 -0700 Subject: [PATCH 004/158] implements max stack --- src/leetcode/array/design_hit_counter.py | 4 +- .../dot_product_of_two_sparse_vectors.py | 6 +- src/leetcode/array/group_anagrams.py | 6 +- .../array/longest_consecutive_sequence.py | 8 +- .../array/max_chunks_to_make_sorted.py | 4 +- src/leetcode/array/maximum_swap.py | 12 +- src/leetcode/array/merge_sorted_array.py | 31 ++-- src/leetcode/array/pascals_triangle.py | 11 ++ src/leetcode/array/top_k_frequent_elements.py | 5 +- .../heap/k_closest_points_to_origin.py | 15 +- src/leetcode/heap/max-stack.ts | 174 ------------------ src/leetcode/heap/max_stack.py | 133 +++++++++++++ test/leetcode/heap/max_stack_test.py | 17 ++ 13 files changed, 216 insertions(+), 210 deletions(-) delete mode 100644 src/leetcode/heap/max-stack.ts create mode 100644 src/leetcode/heap/max_stack.py create mode 100644 test/leetcode/heap/max_stack_test.py diff --git a/src/leetcode/array/design_hit_counter.py b/src/leetcode/array/design_hit_counter.py index 7f3d39a..89f00fb 100644 --- a/src/leetcode/array/design_hit_counter.py +++ b/src/leetcode/array/design_hit_counter.py @@ -17,7 +17,9 @@ def __init__(self): COMPLEXITY: - Both methods run in O(1) time since we fix the array size at 300. + Time complexity is O(1) for both methods since we fix the array size at 300. + + Space complexity is O(1) for both methods because we fix the array size at 300. """ # Each event is a [timestamp, hit_count]. Since our granularity is in seconds, we only need to track the last # 300 seconds. diff --git a/src/leetcode/array/dot_product_of_two_sparse_vectors.py b/src/leetcode/array/dot_product_of_two_sparse_vectors.py index 4a06ba9..6a12c5a 100644 --- a/src/leetcode/array/dot_product_of_two_sparse_vectors.py +++ b/src/leetcode/array/dot_product_of_two_sparse_vectors.py @@ -25,8 +25,10 @@ def __init__(self, nums: list[int]): COMPLEXITY: - Best case both vectors are sparse and we get O(k) where k is the number of non-zero elements. But in the worst - case scenario, it could still be O(n) if both vectors are dense. + Time complexity is O(k) in the best case, where k is the number of non-zero elements. In the worst case, + though, it could still be O(n) if both vectors are dense. + + Space complexity is O(k) in the best case, where k is the number of non-zero elements. """ # This compresses the vector so that non-zero values are mapped. self.map: dict[int, int] = {i: num for i, num in enumerate(nums) if num != 0} diff --git a/src/leetcode/array/group_anagrams.py b/src/leetcode/array/group_anagrams.py index 6faf14b..9fcd92b 100644 --- a/src/leetcode/array/group_anagrams.py +++ b/src/leetcode/array/group_anagrams.py @@ -19,8 +19,10 @@ def groupAnagrams(self, texts: list[str]) -> list[list[str]]: COMPLEXITY: - Runs in O(n * m * log m) time, where n is the number of strings and m is the length of the longest string. This - is because we have to sort each string's characters in O(m * log m), and there are n strings. + Time complexity is O(n * m * log m) where n is the number of strings, and m is the length of the longest string. + This is because we have to sort each string's characters in O(m * log m), and there are n strings. + + Space complexity is O(m * n). """ # Define a map of canonical representation -> list of anagrams. map: dict[str, list[str]] = defaultdict(list) diff --git a/src/leetcode/array/longest_consecutive_sequence.py b/src/leetcode/array/longest_consecutive_sequence.py index ee82062..4eecfb9 100644 --- a/src/leetcode/array/longest_consecutive_sequence.py +++ b/src/leetcode/array/longest_consecutive_sequence.py @@ -22,9 +22,11 @@ def longestConsecutive(self, xs: list[int]): COMPLEXITY: - Runs in O(n) time. It may appear that the inner loop runs multiple times, but each element in the array is only - processed once; the inner loop will skip `x - 1` if it was already part of some other sequence from a previous - iteration. + Time complexity is O(n). It may appear that the inner loop runs multiple times, but each element in the array + is only processed once; the inner loop will skip `x - 1` if it was already part of some other sequence from a + previous iteration. + + Space complexity is O(n). """ longest = 0 diff --git a/src/leetcode/array/max_chunks_to_make_sorted.py b/src/leetcode/array/max_chunks_to_make_sorted.py index a2f8733..a093bc3 100644 --- a/src/leetcode/array/max_chunks_to_make_sorted.py +++ b/src/leetcode/array/max_chunks_to_make_sorted.py @@ -20,7 +20,9 @@ def maxChunksToSorted(self, xs: list[int]) -> int: COMPLEXITY: - Runs in O(n). + Time complexity is O(n). + + Space complexity is O(1). """ chunks = 0 max_chunks = 0 diff --git a/src/leetcode/array/maximum_swap.py b/src/leetcode/array/maximum_swap.py index a9bf23c..254ec09 100644 --- a/src/leetcode/array/maximum_swap.py +++ b/src/leetcode/array/maximum_swap.py @@ -10,8 +10,8 @@ def maximumSwap(self, num: int) -> int: """ SOLUTION: - The simple idea of swapping the largest number with the first digit doesn't always work. It works a lot of the time, - but a couple of cases mess up this algorithm: + The simple idea of swapping the largest number with the first digit doesn't always work. It works a lot of the + time, but a couple of cases mess up this algorithm: - 98368 would stay 98368; we need to swap the 3 and 8 to get 98863. - 1993 could become 9193, but we need to swap the 1 and other 9 to get 9913. @@ -23,11 +23,11 @@ def maximumSwap(self, num: int) -> int: COMPLEXITY: - The time complexity is O(n) where n is the number of digits. We iterate through the digits once to create our array - and map. We iterate through the digits a second time to find the swap point. There is a nested loop, but the inner - loop is bounded by the 10 digits, so it doesn't cause O(n^2) time complexity. + Time complexity is O(n) where n is the number of digits. We iterate through the digits once to create our array + and map. We iterate through the digits a second time to find the swap point. There is a nested loop, but the + inner loop is bounded by the 10 digits, so it doesn't cause O(n^2) time complexity. - The space complexity is O(n) because we store an array and map of the digits. + Space complexity is O(n) because we store an array and map of the digits. """ # First convert the number to an array of digits. digits = list(str(num)) diff --git a/src/leetcode/array/merge_sorted_array.py b/src/leetcode/array/merge_sorted_array.py index 402312d..117935a 100644 --- a/src/leetcode/array/merge_sorted_array.py +++ b/src/leetcode/array/merge_sorted_array.py @@ -17,27 +17,34 @@ def merge(self, xs: list[int], m: int, ys: list[int], n: int) -> None: TLDR: Work backwards. Send the largest elements from the smaller array to the end of the bigger array. - This would be VERY easy, if you could use extra memory. To merge without using extra memory, we do need to merge - into the bigger array, nums1. + This would be VERY easy, if you could use extra memory. To merge without using extra memory, we do need to + merge into the bigger array, xs. You could go about this in two different ways: 1. Start from the logical beginning of both arrays and swap elements as needed to maintain the correct order. - 2. Start from the logical end of both arrays and add largest elements to the end of nums1. + 2. Start from the logical end of both arrays and add largest elements to the end of xs. - Going with option 1 is more natural, but it's more error prone and more work. There may be elements at the end of - nums1 that need to be shifted to the right to make room. + Going with option 1 is more natural, but it's more error prone and more work. There may be elements at the end + of xs that need to be shifted to the right to make room. - Going with option 2 is less natural, but it ensures you have enough space. Since nums1 has size m + n, and nums2 has - size n, you can reliably fit all of nums2 into nums1 without any collisions or shifting. Additionally, if you use - up all the elements in nums2, you don't need to do anything with the remaining elements of nums1 since they are - already sorted. + Going with option 2 is less natural, but it ensures you have enough space. Since nums1 has size m + n, and ys + has size n, you can reliably fit all of nums2 into nums1 without any collisions or shifting. Additionally, if + you use up all the elements in ys, you don't need to do anything with the remaining elements of ys since they + are already sorted. - Pro tip: when asked to do something in place, and you have extra space, consider doing backwards iteration instead of - forwards to avoid collisions and overwriting elements. Secondly, if you started with forwards iteration and realize - you might have to shift elements, consider a backwards iteration approach to see if it might work better. + Pro tip: when asked to do something in place, and you have extra space, consider doing backwards iteration + instead of forwards to avoid collisions and overwriting elements. Secondly, if you started with forwards + iteration and realize you might have to shift elements, consider a backwards iteration approach to see if it + might work better. The problem doesn't state this, but we assume using no extra memory either. + + COMPLEXITY: + + Time complexity is O(m + n). + + Space complexity is O(1) as per problem requirements. """ i = m - 1 j = n - 1 diff --git a/src/leetcode/array/pascals_triangle.py b/src/leetcode/array/pascals_triangle.py index e41fea7..885a70c 100644 --- a/src/leetcode/array/pascals_triangle.py +++ b/src/leetcode/array/pascals_triangle.py @@ -7,6 +7,17 @@ # See https://leetcode.com/problems/pascals-triangle class Solution: def generate(self, numRows: int) -> list[list[int]]: + """ + SOLUTION: + + A simple straightforward algorithm works. + + COMPLEXITY: + + Time complexity is O(n). + + Space complexity is O(n). + """ triangle: list[list[int]] = [] for i in range(numRows): diff --git a/src/leetcode/array/top_k_frequent_elements.py b/src/leetcode/array/top_k_frequent_elements.py index 22181ce..1e1f627 100644 --- a/src/leetcode/array/top_k_frequent_elements.py +++ b/src/leetcode/array/top_k_frequent_elements.py @@ -16,8 +16,9 @@ def topKFrequent(self, xs: list[int], k: int) -> list[int]: COMPLEXITY: - Time complexity dominated by the sort, which is O(n log n). Space complexity is O(n) because we are using a map - to store frequency. + Time complexity dominated by the sort, which is O(n log n). + + Space complexity is O(n) because we are using a map to store frequency. """ # Create a map of number -> frequency. map: dict[int, int] = defaultdict(int) diff --git a/src/leetcode/heap/k_closest_points_to_origin.py b/src/leetcode/heap/k_closest_points_to_origin.py index 4f6df34..2f6d247 100644 --- a/src/leetcode/heap/k_closest_points_to_origin.py +++ b/src/leetcode/heap/k_closest_points_to_origin.py @@ -19,20 +19,21 @@ def kClosest(self, points: list[list[int]], k: int) -> list[list[int]]: The request for "k-closest" indicates you'll want a heap. Using a either a min heap or max heap will work. - With a min heap, just jam all the points on the heap and then slice the first k elements. This requires storing all - the points on the heap. + With a min heap, just jam all the points on the heap and then slice the first k elements. This requires storing + all the points on the heap. - With a max heap, you can jam points on the heap until you have k elements. Then, when you have a new point, compare - it to the max element on the heap. If it's closer, pop off the max element and jam it on the heap; otherwise, ignore - it. + With a max heap, you can jam points on the heap until you have k elements. Then, when you have a new point, + compare it to the max element on the heap. If it's closer, pop off the max element and jam it on the heap; + otherwise, ignore it. The max heap approach can be more efficient, so we'll go that route. COMPLEXITY: - With a min heap, it will be O(n * log(n)) to insert all the points on the heap. + Time complexity is O(n log k) with a max heap, which will be faster when k is much smaller than n. If we go + with the min heap approach, it's O(n log n). - With a max heap, if k is much smaller than n, it will be O(n * log(k)) to insert all the points on the heap. + Space complexity is O(n). """ # Calculates the distance to the origin. Technically, since we're only using the distance to do comparison, we diff --git a/src/leetcode/heap/max-stack.ts b/src/leetcode/heap/max-stack.ts deleted file mode 100644 index 1caa1bd..0000000 --- a/src/leetcode/heap/max-stack.ts +++ /dev/null @@ -1,174 +0,0 @@ -// DIFFICULTY: HARD -// -// Design a max stack data structure that supports the stack operations and supports finding the stack's maximum element. -// -// Implement the MaxStack class: -// -// - MaxStack() Initializes the stack object. -// - void push(int x) Pushes element x onto the stack. -// - int pop() Removes the element on top of the stack and returns it. -// - int top() Gets the element on the top of the stack without removing it. -// - int peekMax() Retrieves the maximum element in the stack without removing it. -// - int popMax() Retrieves the maximum element in the stack and removes it. If there is more than one maximum element, -// only remove the top-most one. -// -// You must come up with a solution that supports O(1) for each top call and O(logn) for each other call. -// -// See {@link https://leetcode.com/problems/max-stack/} -const { MaxPriorityQueue } = require('@datastructures-js/priority-queue'); -export { MaxStack }; - -// SOLUTION: -// -// We want a max priority queue, but we need to determine the max by comparing both a key and a value. This can -// only be done by supplying a custom comparator, so instead of using a MaxPriorityQueue, we'll use a PriorityQueue -// that takes a custom comparator. -interface StackNode { - key: number; - value: number; - previous: StackNode; - next: StackNode; -} - -class MaxStack { - // MaxPriorityQueue is exported as a value and not a type in version 5.4.0. - private readonly heap: InstanceType; - - private readonly compare: (a: StackNode, b: StackNode) => number; - - private readonly deleted: Set; - - private keys: number; - - private head: StackNode; - - private tail: StackNode; - - constructor() { - this.keys = 0; - this.deleted = new Set(); - this.compare = (a: StackNode, b: StackNode): number => { - // Because we want to pop max the top most max element, we should compare their keys in the case of a tie; - // that is because the higher key will be on top of the stack. - if (a.value === b.value) { - return b.key - a.key; - } - - return b.value - a.value; - }; - this.heap = new MaxPriorityQueue({ - compare: this.compare - }); - - // Create sentinel values for head and tail so we don't have to deal with null pointers. - const head: Partial = { - key: Number.MIN_SAFE_INTEGER, - value: Number.MIN_SAFE_INTEGER - }; - const tail: Partial = { - key: Number.MIN_SAFE_INTEGER, - value: Number.MIN_SAFE_INTEGER - }; - - head.next = tail as StackNode; - tail.previous = head as StackNode; - this.head = head as StackNode; - this.tail = tail as StackNode; - } - - push(x: number): void { - const node: StackNode = { - key: this.keys++, - value: x, - previous: this.head, - next: this.tail - }; - this.heap.enqueue(node); - - // If we have no elements between head and tail, insert this node between them as the only element. - if (this.head.next === this.tail) { - node.previous = this.head; - node.next = this.tail; - this.head.next = node; - this.tail.previous = node; - return; - } - - // If we do have an element between head and tail, add this node after that one. - const previous = this.tail.previous; - - node.previous = previous; - node.next = this.tail; - - previous.next = node; - this.tail.previous = node; - } - - pop(): number { - if (this.head.next === this.tail) { - throw new Error('nothing to pop'); - } - - const node = this.tail.previous; - const previous = node.previous; - const next = node.next; - previous.next = next; - next.previous = previous; - - // In contrast with removing from a doubly linked list, it's going to be very hard to remove from the middle of - // the heap. - // - // Instead, we'll cheat and just save this deletion for later. - this.deleted.add(node.key); - return node.value; - } - - top(): number { - if (this.head.next === this.head) { - throw new Error('nothing on top'); - } - - return this.tail.previous.value; - } - - peekMax(): number { - if (this.heap.isEmpty()) { - throw new Error('nothing to peek max'); - } - - this.deleteMax(); - const node = this.heap.front(); - return node.value; - } - - popMax(): number { - if (this.heap.isEmpty()) { - throw new Error('nothing to pop max'); - } - - this.deleteMax(); - if (this.heap.isEmpty()) { - throw new Error('nothing to pop max'); - } - - const node = this.heap.dequeue(); - const previous = node.previous; - const next = node.next; - previous.next = next; - next.previous = previous; - - return node.value; - } - - private deleteMax() { - while (!this.heap.isEmpty()) { - const max = this.heap.front(); - if (!this.deleted.has(max.key)) { - return; - } - - this.heap.dequeue(); - this.deleted.delete(max.key); - } - } -} diff --git a/src/leetcode/heap/max_stack.py b/src/leetcode/heap/max_stack.py new file mode 100644 index 0000000..aa20dc7 --- /dev/null +++ b/src/leetcode/heap/max_stack.py @@ -0,0 +1,133 @@ +# DIFFICULTY: HARD +# +# Design a max stack data structure that supports the stack operations and supports finding the stack's maximum element. +# +# Implement the MaxStack class: +# +# - MaxStack() Initializes the stack object. +# - void push(int x) Pushes element x onto the stack. +# - int pop() Removes the element on top of the stack and returns it. +# - int top() Gets the element on the top of the stack without removing it. +# - int peekMax() Retrieves the maximum element in the stack without removing it. +# - int popMax() Retrieves the maximum element in the stack and removes it. If there is more than one maximum element, +# only remove the top-most one. +# +# You must come up with a solution that supports O(1) for each top call and O(logn) for each other call. +# +# See https://leetcode.com/problems/max-stack +from heapq import heappop, heappush + + +class StackNode: + def __init__(self, key: int, value: int): + self.key = key + self.value = value + self.previous: "StackNode" | None = None + self.next: "StackNode" | None = None + + +class MaxStack: + def __init__(self): + """ + SOLUTION: + + We want a max priority queue, but we need to determine the max by comparing both a key and a value. This can + only be done by supplying a custom comparator, so instead of using a MaxPriorityQueue, we'll use a PriorityQueue + that takes a custom comparator. + + COMPLEXITY: + + Time complexity is O(1) for top and O(log n) for each other call. + """ + # Because the stack could have multiple duplicate elements, use a unique ID for each node. + self.keys = 0 + # Because deleting from the middle of a heap is hard, keep track of a set of deletions while popping and defer + # them for later. + self.deleted = set() + # Use a max heap to keep track of max elements. The heap will have a tuple of (value, key, node) because we + # want to support dupes, so have the heap order by value first, then the key. + self.max_heap = [] + # Setup sentinel values for the head and tail so we don't have to check for nulls. + self.head = StackNode(float("-inf"), float("-inf")) + self.tail = StackNode(float("-inf"), float("-inf")) + self.head.next = self.tail + self.tail.previous = self.head + + def push(self, value: int) -> None: + # Add this new node to the linked list. + node = StackNode(self.keys, value) + self.keys += 1 + + # Push onto the heap, but because this is a max heap, negate the value. Because the stack might have dupes, + # we'll want the heap to be ordered by first the value, then the key. + heappush(self.max_heap, (-value, -node.key, node)) + + # Now add this node to the end of the linked list. + a = self.tail.previous + b = node + c = self.tail + self.__insert_node(a, b, c) + + def pop(self) -> int: + # Remove the last element of the linked list. + node = self.tail.previous + self.__delete_node(node) + + # We also need to remove this element from the heap, but this will be difficult as we can't remove arbitrary + # elements from the middle of the heap. Instead, we will note the deletion and defer it. + self.deleted.add(node.key) + return node.value + + def top(self) -> int: + return self.tail.previous.value + + def peekMax(self) -> int: + # Since peekMax is allowed to run in O(log n), perform the deferred heap deletions from any previous pops. + self.__delete_max() + + # Return the front of the of heap. + (_, _, node) = self.max_heap[0] + return node.value + + def popMax(self) -> int: + # Since popMax is allowed to run in O(log n), perform the deferred heap deletions from any previous pops. + self.__delete_max() + + # Pop off the front of the heap, which is efficient to do so here (we don't have to delete from the middle). + (_, _, node) = heappop(self.max_heap) + + # Delete from the linked list, which is also efficient to do here. + self.__delete_node(node) + return node.value + + def __delete_node(self, b: StackNode): + a = b.previous + c = b.next + + # This shouldn't be necessary, but mypy cannot tell if these values are not None. + if a and c: + a.next = c + c.previous = a + + def __insert_node(self, a: StackNode, b: StackNode, c: StackNode): + # Update the pointers around the node. + a.next = b + c.previous = b + + # Update the pointers for the node itself. + b.previous = a + b.next = c + + def __delete_max(self): + while self.max_heap: + (_, _, node) = self.max_heap[0] + + # If the front of the heap is actually something we have deleted, then any peekMax or popMax operation will + # return the wrong value. If that is the case, remove them from the heap in an efficient way right now. + if node.key in self.deleted: + heappop(self.max_heap) + self.deleted.remove(node.key) + # If the front of the heap hasn't been deleted, then we don't have to do anything as peekMax or popMax will + # still be valid. + else: + return diff --git a/test/leetcode/heap/max_stack_test.py b/test/leetcode/heap/max_stack_test.py new file mode 100644 index 0000000..66851ad --- /dev/null +++ b/test/leetcode/heap/max_stack_test.py @@ -0,0 +1,17 @@ +from leetcode.heap.max_stack import MaxStack + + +stack = MaxStack() + + +def test_case_1(): + stack.push(5) + stack.push(1) + stack.push(5) + + assert stack.top() == 5 + assert stack.popMax() == 5 + assert stack.top() == 1 + assert stack.peekMax() == 5 + assert stack.pop() == 1 + assert stack.top() == 5 From b65ff1a82ddac58255e7174f9b74c76bd559bbb4 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 10 Mar 2025 01:31:14 -0700 Subject: [PATCH 005/158] add task scheduler --- src/leetcode/heap/max_stack.py | 12 +++- src/leetcode/heap/task-scheduler.ts | 74 ----------------------- src/leetcode/heap/task_scheduler.py | 68 +++++++++++++++++++++ test/leetcode/heap/task-scheduler.test.ts | 28 --------- test/leetcode/heap/task_scheduler_test.py | 20 ++++++ 5 files changed, 97 insertions(+), 105 deletions(-) delete mode 100644 src/leetcode/heap/task-scheduler.ts create mode 100644 src/leetcode/heap/task_scheduler.py delete mode 100644 test/leetcode/heap/task-scheduler.test.ts create mode 100644 test/leetcode/heap/task_scheduler_test.py diff --git a/src/leetcode/heap/max_stack.py b/src/leetcode/heap/max_stack.py index aa20dc7..18d6ce2 100644 --- a/src/leetcode/heap/max_stack.py +++ b/src/leetcode/heap/max_stack.py @@ -31,9 +31,15 @@ def __init__(self): """ SOLUTION: - We want a max priority queue, but we need to determine the max by comparing both a key and a value. This can - only be done by supplying a custom comparator, so instead of using a MaxPriorityQueue, we'll use a PriorityQueue - that takes a custom comparator. + Use a heap and a doubly linked list to represent the max stack. Most operations can be performed in O(1) or + O(log n). The only challenge is that when popping from the stack, it's easy to remove from the end of the + linked list, but it's not easy to remove from the middle of the max heap. + + Instead of removing from the max heap immediately, defer the deletion until we perform a peekMax or popMax, and + check if processing the deferred deletions are required. If so, perform them there. + + For the purposes of this solution, we won't consider edge cases like popping from an empty stack. This keeps + the solution simple. COMPLEXITY: diff --git a/src/leetcode/heap/task-scheduler.ts b/src/leetcode/heap/task-scheduler.ts deleted file mode 100644 index 2e26e1e..0000000 --- a/src/leetcode/heap/task-scheduler.ts +++ /dev/null @@ -1,74 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given an array of CPU tasks, each represented by letters A to Z, and a cooling time, n. Each cycle or -// interval allows the completion of one task. Tasks can be completed in any order, but there's a constraint: identical -// tasks must be separated by at least n intervals due to cooling time. -// -// ​Return the minimum number of intervals required to complete all tasks. -// -// See {@link https://leetcode.com/problems/task-scheduler/} -const { MaxPriorityQueue } = require('@datastructures-js/priority-queue'); -export { leastInterval }; - -// SOLUTION: -// -// It's not necessary to return an execution order, only the minimum number of cycles/intervals required to complete -// all of the tasks. -// -// Note that either the most frequent task or the number of tasks will dictate the number of cycles required. First -// let's examine the case where we have a very frequent task that requires multiple cooling off periods. -// -// Let's suppose that task A appears 10 times, and task B appears 9 times, and some other tasks appear with lower -// frequency. No matter what, the 10 executions of task A will require n cycles of gaps between them, except for the -// last execution of task A. This means that there will be n cycles of idle slots between the executions of -// task A. But because we have 9 executions that need to spaced out (not the last), there are (10 - 1) * n empty -// cycles between A, for the other tasks to execute. -// -// Here, even though task B appears 9 times, we can fit task B comfortably in between the 9 cycles of task A. This, -// however, doesn't quite work if there are a huge number of tasks; if there are 10 task A's and 9 task B's, but 50 -// other uniquely named tasks, then we will still take 50 cycles if (10 - 1) * n is a smaller number. -function leastInterval(tasks: string[], n: number): number { - type Task = string; - type Frequency = number; - const map = new Map(); - for (const task of tasks) { - const freq = map.get(task) ?? 0; - map.set(task, freq + 1); - } - - const heap = new MaxPriorityQueue(); - for (const [_, freq] of map) { - heap.enqueue(freq); - } - - type Item = { - freq: number; - cycle: number; - }; - let cycle = 0; - const queue: Item[] = []; - while (!heap.isEmpty() || queue.length !== 0) { - if (queue.length !== 0) { - if (queue[0].cycle === cycle) { - const item = queue.shift()!; - heap.enqueue(item.freq); - } - } - - if (!heap.isEmpty()) { - let freq = heap.dequeue().element; - freq--; - - if (freq !== 0) { - queue.push({ - freq, - cycle: cycle + n + 1 - }); - } - } - - cycle++; - } - - return cycle; -} diff --git a/src/leetcode/heap/task_scheduler.py b/src/leetcode/heap/task_scheduler.py new file mode 100644 index 0000000..d1a6d95 --- /dev/null +++ b/src/leetcode/heap/task_scheduler.py @@ -0,0 +1,68 @@ +# DIFFICULTY: MEDIUM +# +# You are given an array of CPU tasks, each represented by letters A to Z, and a cooling time, n. Each cycle or +# interval allows the completion of one task. Tasks can be completed in any order, but there's a constraint: identical +# tasks must be separated by at least n intervals due to cooling time. +# +# ​Return the minimum number of intervals required to complete all tasks. +# +# See https://leetcode.com/problems/task-scheduler +from collections import defaultdict, deque +from heapq import heappop, heappush + + +class Solution: + def leastInterval(self, tasks: list[str], n: int) -> int: + """ + SOLUTION: + + It's not necessary to return an execution order, only the minimum number of cycles/intervals required to + complete all of the tasks. + + Note that either the most frequent task or the number of tasks will dictate the number of cycles required. + First let's examine the case where we have a very frequent task that requires multiple cooling off periods. + + Let's suppose that task A appears 10 times, and task B appears 9 times, and some other tasks appear with lower + frequency. No matter what, the 10 executions of task A will require n cycles of gaps between them, except for + the last execution of task A. This means that there will be n cycles of idle slots between the executions of + task A. But because we have 9 executions that need to spaced out (not the last), there are (10 - 1) * n empty + cycles between A, for the other tasks to execute. + + Here, even though task B appears 9 times, we can fit task B comfortably in between the 9 cycles of task A. + This, however, doesn't quite work if there are a huge number of tasks; if there are 10 task A's and 9 task B's, + but 50 other uniquely named tasks, then we will still take 50 cycles if (10 - 1) * n is a smaller number. + """ + # Create a map of task to frequency. + map: dict[str, int] = defaultdict(int) + for task in tasks: + map[task] += 1 + + # Create a max heap of frequency. To simulate max heap, negate the value. + max_heap: list[int] = [] + for freq in map.values(): + heappush(max_heap, -freq) + + # Create a queue of [freq, cycle] for cooldown tasks. + queue: deque[tuple[int, int]] = deque() + cycle = 0 + + # Process tasks. + while max_heap or queue: + if queue: + (f, c) = queue[0] + if c == cycle: + queue.popleft() + # Negate frequency because it's a max heap. + heappush(max_heap, -f) + + if max_heap: + # Negate frequency because it's a max heap. + f = -heappop(max_heap) + f -= 1 + + if f != 0: + queue.append((f, cycle + n + 1)) + + cycle += 1 + + return cycle diff --git a/test/leetcode/heap/task-scheduler.test.ts b/test/leetcode/heap/task-scheduler.test.ts deleted file mode 100644 index fb6bcdd..0000000 --- a/test/leetcode/heap/task-scheduler.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import { leastInterval } from '../../src/heap/task-scheduler'; - -describe('task scheduler', () => { - test('task scheduler - test case 1', () => { - // [A, B, _, A, B, _, A, B] - expect(leastInterval(['A', 'A', 'A', 'B', 'B', 'B'], 2)).toBe(8); - }); - - test('task scheduler - test case 2', () => { - // [A, B, A, B, C, D] - expect(leastInterval(['A', 'C', 'A', 'B', 'D', 'B'], 1)).toBe(6); - }); - - test('task scheduler - test case 3', () => { - // [A, B, _, _, A, B, _, _, A, B] - // [A, _, _, _, A, _, _, _] - expect(leastInterval(['A', 'A', 'A', 'B', 'B', 'B'], 3)).toBe(10); - }); - - test.skip('task scheduler - test case 4', async () => { - const data = fs.readFileSync(path.join(__dirname, '__data__', 'task-scheduler.test.json')).toString(); - const tasks = JSON.parse(data); - - expect(leastInterval(tasks, 1)).toBe(1000); - }); -}); diff --git a/test/leetcode/heap/task_scheduler_test.py b/test/leetcode/heap/task_scheduler_test.py new file mode 100644 index 0000000..b84cdd7 --- /dev/null +++ b/test/leetcode/heap/task_scheduler_test.py @@ -0,0 +1,20 @@ +from leetcode.heap.task_scheduler import Solution + + +soln = Solution() + + +def test_case_1(): + # [A, B, _, A, B, _, A, B] + assert soln.leastInterval(["A", "A", "A", "B", "B", "B"], 2) == 8 + + +def test_case_2(): + # [A, B, A, B, C, D] + assert soln.leastInterval(["A", "C", "A", "B", "D", "B"], 1) == 6 + + +def test_case_3(): + # [A, B, _, _, A, B, _, _, A, B] + # [A, _, _, _, A, _, _, _] + assert soln.leastInterval(["A", "A", "A", "B", "B", "B"], 3) == 10 From 6aae54f5d4b88b7436ba0d8c9cae22199d733219 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 10 Mar 2025 02:10:14 -0700 Subject: [PATCH 006/158] merge k sorte dlists --- src/leetcode/heap/common/list_node.py | 4 ++ src/leetcode/heap/merge-k-sorted-lists.ts | 58 --------------------- src/leetcode/heap/merge_k_sorted_lists.py | 63 +++++++++++++++++++++++ 3 files changed, 67 insertions(+), 58 deletions(-) create mode 100644 src/leetcode/heap/common/list_node.py delete mode 100644 src/leetcode/heap/merge-k-sorted-lists.ts create mode 100644 src/leetcode/heap/merge_k_sorted_lists.py diff --git a/src/leetcode/heap/common/list_node.py b/src/leetcode/heap/common/list_node.py new file mode 100644 index 0000000..9efd65c --- /dev/null +++ b/src/leetcode/heap/common/list_node.py @@ -0,0 +1,4 @@ +class ListNode: + def __init__(self, val=0, next=None): + self.val = val + self.next = next diff --git a/src/leetcode/heap/merge-k-sorted-lists.ts b/src/leetcode/heap/merge-k-sorted-lists.ts deleted file mode 100644 index 1c27fe9..0000000 --- a/src/leetcode/heap/merge-k-sorted-lists.ts +++ /dev/null @@ -1,58 +0,0 @@ -// DIFFICULTY: HARD -// -// You are given an array of k linked-lists lists, each linked-list is sorted in ascending order. -// -// Merge all the linked-lists into one sorted linked-list and return it. -// -// See {@link https://leetcode.com/problems/merge-k-sorted-lists/} -import { MinPriorityQueue } from '@datastructures-js/priority-queue'; -import { ListNode } from '../linked-list/common/list-node'; -export { mergeKLists }; - -// SOLUTION: -// -// Instead of only 2 lists, we have to merge k lists. The naive way is to do a linear scan of the head of every list -// to find the smallest element then create a new list node with that element and append it to the result list. -// -// However, we can do better by using a heap to store the head of each list so we can always find the minimum value -// without a linear scan. Additionally, the problem does not say we cannot reuse the input lists, so we do not have to -// create new list nodes. -// -// COMPLEXITY: -// -// There are k lists, so each enqueuing operation will take O(log k) time complexity. However, we are going to do this -// for every node in the list, so the total time complexity is O(n * log k) where n is the total number of nodes in all -// the lists. The naive approach with a linear scan would be O(n * k). -// -// Space complexity is O(k) because we are storing the head of each list in the heap, and we do not create new list -// nodes. -function mergeKLists(lists: Array): ListNode | null { - // Use a heap to store the head of each list so we can always find the minimum value to add to the result list - // without resorting to a linear scan. - const heap = new MinPriorityQueue({ priority: (node: ListNode) => node.val }); - for (const head of lists) { - if (head !== null) { - heap.enqueue(head); - } - } - - // As we keep advancing the current node, we'll lose track of the head. Let's have a sentinel node so that we can - // always keep track of where the list begins. - const sentinel = new ListNode(-1); - let current = sentinel; - while (heap.size() > 0) { - const smallest = heap.dequeue().element; - - // Advance the current node and push the smallest element in. - current.next = smallest; - current = current.next; - - // Now, advance the smallest node to the next value. - if (smallest.next !== null) { - heap.enqueue(smallest.next); - } - } - - // The sentinel node will point to the head of the list, so we did not lose it. - return sentinel.next; -} diff --git a/src/leetcode/heap/merge_k_sorted_lists.py b/src/leetcode/heap/merge_k_sorted_lists.py new file mode 100644 index 0000000..8fb975b --- /dev/null +++ b/src/leetcode/heap/merge_k_sorted_lists.py @@ -0,0 +1,63 @@ +# DIFFICULTY: HARD +# +# You are given an array of k linked-lists lists, each linked-list is sorted in ascending order. +# +# Merge all the linked-lists into one sorted linked-list and return it. +# +# See https://leetcode.com/problems/merge-k-sorted-lists +from leetcode.heap.common.list_node import ListNode +from heapq import heappop, heappush + + +class Solution: + def mergeKLists(self, lists: list[ListNode | None]) -> ListNode | None: + """ + SOLUTION: + + Instead of only 2 lists, we have to merge k lists. The naive way is to do a linear scan of the head of every list + to find the smallest element then create a new list node with that element and append it to the result list. + + However, we can do better by using a heap to store the head of each list so we can always find the minimum value + without a linear scan. Additionally, the problem does not say we cannot reuse the input lists, so we do not have to + create new list nodes. + + COMPLEXITY: + + Time complexity is O(n log k), where n is the number of nodes and there are k lists, so each enqueuing operation + will take O(log k) time. However, we are going to do this for every node in the list, so the total time + complexity is O(n log k). The naive approach with a linear scan would be O(n * k). + + Space complexity is O(k) because we are storing the head of each list in the heap, and we do not create new list + nodes. + """ + # Use a heap to store the head of each list so we can always find the minimum value to add to the result list + # without resorting to a linear scan. + # + # Store (value, id, ListNode) tuples in the heap because we need to handle dupes. In case of dupes, Python will + # try to compare the second value of the tuple. If the second value is a ListNode, it won't know how to compare + # it. To fix this, store some unique ID for the ListNode as the second value so the third value will never be + # compared. + min_heap: list[tuple[int, int, ListNode]] = [] + for head in lists: + if head: + heappush(min_heap, (head.val, id(head), head)) + + # As we keep advancing the current node, we'll lose track of the head. Let's have a sentinel node so that we + # can always keep track of where the list begins. + sentinel = ListNode(-1) + current = sentinel + + while min_heap: + (_, _, smallest) = heappop(min_heap) + + # Advance the current node and push the smallest element in. + current.next = smallest + current = current.next + + # Now, advance the smallest node to the next value. + if smallest.next: + next = smallest.next + heappush(min_heap, (next.val, id(next), next)) + + # The sentinel node will point to the head of the list, so we did not lose it. + return sentinel.next From 74e7b280839106a644d336d93b64e9f752530abd Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 10 Mar 2025 13:27:06 -0700 Subject: [PATCH 007/158] updates binary search --- src/leetcode/array/two_sum.py | 8 +- ...ast-position-of-element-in-sorted-array.ts | 86 ------------------- ...ast_position_of_element_in_sorted_array.py | 80 +++++++++++++++++ ...osition-of-element-in-sorted-array.test.ts | 7 -- ...osition_of_element_in_sorted_array_test.py | 8 ++ 5 files changed, 92 insertions(+), 97 deletions(-) delete mode 100644 src/leetcode/binary_search/find-first-and-last-position-of-element-in-sorted-array.ts create mode 100644 src/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array.py delete mode 100644 test/leetcode/binary_search/find-first-and-last-position-of-element-in-sorted-array.test.ts create mode 100644 test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py diff --git a/src/leetcode/array/two_sum.py b/src/leetcode/array/two_sum.py index 5e0b8f5..3e1d7cd 100644 --- a/src/leetcode/array/two_sum.py +++ b/src/leetcode/array/two_sum.py @@ -26,17 +26,17 @@ def twoSum(self, xs: list[int], target: int) -> list[int]: Space complexity is O(n). """ # Map number complement -> index where it appears. - m: dict[int, int] = {} + map: dict[int, int] = {} for i, x in enumerate(xs): complement = target - x # If our map has the complementary value that would make up the target, we can return it immediately. - if complement in m: - return [i, m[complement]] + if complement in map: + return [i, map[complement]] # Otherwise, store the number and its index in the map. If we find the complement later, then this index # will be the complement's complement and we can return the indices as normal. - m[x] = i + map[x] = i return [] diff --git a/src/leetcode/binary_search/find-first-and-last-position-of-element-in-sorted-array.ts b/src/leetcode/binary_search/find-first-and-last-position-of-element-in-sorted-array.ts deleted file mode 100644 index 756805c..0000000 --- a/src/leetcode/binary_search/find-first-and-last-position-of-element-in-sorted-array.ts +++ /dev/null @@ -1,86 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an array of integers nums sorted in non-decreasing order, find the starting and ending position of a given target value. -// -// If target is not found in the array, return [-1, -1]. -// -// You must write an algorithm with O(log n) runtime complexity. -// -// @see {@link https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/} -export { searchRange }; - -// SOLUTION: -// -// The problem stipulates it must run in O(log n) time complexity, so that means we can't just do a standard binary -// search and then a linear scan for the start and end ranges. -// -// The most braindead simple way to do this is to do binary search twice. Once using the left-most duplicate technique -// and once using the right-most duplicate technique. -// -// Technically we could combine both the left and right searches into one binary search, using a flag to denote the -// direction we are searching in. But that would be a bit more complex and harder to understand. -// -// COMPLEXITY: -// -// Runs in O(log n) time complexity because of max two binary searches. Uses O(1) space complexity. -function searchRange(nums: number[], target: number): number[] { - // Run binary search using the left-most duplicate technique. Here if we find that the the middle element is less - // than the target, we move to the right. - // - // Otherwise if the middle element is greater than or equal to the target, we move to the left (guaranteeing that if - // we find a duplicate target, we will keep moving left). - function binarySearchLeft() { - let left = 0; - let right = nums.length; - - while (left < right) { - const m = Math.floor((left + right) / 2); - if (nums[m] < target) { - left = m + 1; - } else { - right = m; - } - } - - // If we have found the target, return the index, otherwise return -1. - if (left < nums.length && nums[left] === target) { - return left; - } else { - return -1; - } - } - - // Run binary search using the right-most duplicate technique. Here if we find that the the middle element is greater - // than the target, we move to the left. - // - // Otherwise if the middle element is less than or equal to the target, we move to the right (guaranteeing that if - // we find a duplicate target, we will keep moving right). - function binarySearchRight() { - let left = 0; - let right = nums.length; - - while (left < right) { - const m = Math.floor((left + right) / 2); - if (nums[m] > target) { - right = m; - } else { - left = m + 1; - } - } - - if (right > 0 && nums[right - 1] === target) { - return right - 1; - } else { - return -1; - } - } - - // Now we just run both binary searches and return the range. - const left = binarySearchLeft(); - if (left === -1) { - return [-1, -1]; - } - - const right = binarySearchRight(); - return [left, right]; -} diff --git a/src/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array.py b/src/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array.py new file mode 100644 index 0000000..9a26e6b --- /dev/null +++ b/src/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array.py @@ -0,0 +1,80 @@ +# DIFFICULTY: MEDIUM +# +# Given an array of integers nums sorted in non-decreasing order, find the starting and ending position of a given target value. +# +# If target is not found in the array, return [-1, -1]. +# +# You must write an algorithm with O(log n) runtime complexity. +# +# See https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array +class Solution: + def searchRange(self, xs: list[int], target: int) -> list[int]: + """ + SOLUTION: + + The problem stipulates it must run in O(log n) time complexity, so that means we can't just do a standard binary + search and then a linear scan for the start and end ranges. + + The most braindead simple way to do this is to do binary search twice. Once using the left-most duplicate + technique and once using the right-most duplicate technique. + + Technically we could combine both the left and right searches into one binary search, using a flag to denote the + direction we are searching in. But that would be a bit more complex and harder to understand. + + COMPLEXITY: + + Time complexity is O(log n) time complexity because of max two binary searches. + + Space complexity is O(1) space complexity. + """ + + # Run binary search using the left-most duplicate technique. Here if we find that the the middle element is + # less than the target, we move to the right. + # + # Otherwise if the middle element is greater than or equal to the target, we move to the left (guaranteeing that + # if we find a duplicate target, we will keep moving left). + def binarySearchLeft(): + left = 0 + right = len(xs) + + while left < right: + mid = (left + right) // 2 + if xs[mid] < target: + left = mid + 1 + else: + right = mid + + # If we have found the target, return the index, otherwise return -1. + if left < len(xs) and xs[left] == target: + return left + else: + return -1 + + # Run binary search using the right-most duplicate technique. Here if we find that the the middle element is + # greater than the target, we move to the left. + # + # Otherwise if the middle element is less than or equal to the target, we move to the right (guaranteeing that + # if we find a duplicate target, we will keep moving right). + def binarySearchRight(): + left = 0 + right = len(xs) + + while left < right: + mid = (left + right) // 2 + if xs[mid] > target: + right = mid + else: + left = mid + 1 + + if right > 0 and xs[right - 1] == target: + return right - 1 + else: + return -1 + + # Now we just run both binary searches and return the range. + left = binarySearchLeft() + if left == -1: + return [-1, -1] + + right = binarySearchRight() + return [left, right] diff --git a/test/leetcode/binary_search/find-first-and-last-position-of-element-in-sorted-array.test.ts b/test/leetcode/binary_search/find-first-and-last-position-of-element-in-sorted-array.test.ts deleted file mode 100644 index c659b27..0000000 --- a/test/leetcode/binary_search/find-first-and-last-position-of-element-in-sorted-array.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { searchRange } from '../../src/binary-search/find-first-and-last-position-of-element-in-sorted-array'; - -describe('find first and last position of element in sorted array', () => { - test('searchRange - test case i', async () => { - expect(searchRange([5, 7, 7, 8, 8, 10], 8)).toStrictEqual([3, 4]); - }); -}); diff --git a/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py b/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py new file mode 100644 index 0000000..5981bca --- /dev/null +++ b/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py @@ -0,0 +1,8 @@ +from leetcode.binary_search.find_first_and_last_position_of_element_in_sorted_array import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.searchRange([5, 7, 7, 8, 8, 10], 8) == [3, 4] From 834163195ce6614a8a3f2c1fc6a4e98829c470a8 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 10 Mar 2025 13:51:39 -0700 Subject: [PATCH 008/158] adds find peak element --- .../binary_search/find_peak_element.py | 74 +++++++++++++++++++ .../binary_search/find_peak_element_test.py | 28 +++++++ 2 files changed, 102 insertions(+) create mode 100644 src/leetcode/binary_search/find_peak_element.py create mode 100644 test/leetcode/binary_search/find_peak_element_test.py diff --git a/src/leetcode/binary_search/find_peak_element.py b/src/leetcode/binary_search/find_peak_element.py new file mode 100644 index 0000000..3185ba4 --- /dev/null +++ b/src/leetcode/binary_search/find_peak_element.py @@ -0,0 +1,74 @@ +# DIFFICULTY: MEDIUM +# +# A peak element is an element that is strictly greater than its neighbors. +# +# Given a 0-indexed integer array nums, find a peak element, and return its index. If the array contains multiple +# peaks, return the index to any of the peaks. +# +# You may imagine that nums[-1] = nums[n] = -∞. In other words, an element is always considered to be strictly greater +# than a neighbor that is outside the array. +# +# You must write an algorithm that runs in O(log(n)) time. +# +# See https://leetcode.com/problems/find-peak-element +class Solution: + def findPeakElement(self, xs: list[int]) -> int: + """ + SOLUTION: + + This problem is not hard, but the wording is incredibly tricky. First off, let's talk about linear scan versus + binary search. There are only 1000 elements max in the array, so either would work. However, the problem says + that the algorithm must run in O(log(n)) time. This means we have to use binary search. + + Yes, binary search will still work even if the array is not sorted. That's because at each step, we are chasing + a gradient uphill. For example, if we see [..., 10, 5, 5, ...] then we know that the elements are RISING in the + left direction (and then eventually fall, because the left side element is -Infinity). So, we should slice the + array in half and look for a peak in the left half; the opposite logic applies if the array is rising to the + right. + + Secondly, the problem doesn't explicitly say what happens if there are no peaks. The solution I give throws an + error if none are found (e.g. [1, 1, 1], or [1, 10, 10, 1]). Nowhere does LeetCode say what should happen if + the inputs are invalid (e.g. have no peaks), or that all inputs are valid. However, it does apply to pass if we + assume peaks always exist! + + COMPLEXITY: + """ + if len(xs) == 1: + return 0 + + # Use insertion point binary search to find the peak. + left = 0 + right = len(xs) + + while left < right: + mid = (left + right) // 2 + prev = float("-inf") if mid == 0 else xs[mid - 1] + next = float("-inf") if mid == len(xs) - 1 else xs[mid + 1] + + # If we've found a peak, we can simply return it. + if xs[mid] > prev and xs[mid] > next: + return mid + + # Otherwise, if the right side number is larger, then a peak must exist somewhere on the right, so update + # the left boundary. + if next > xs[mid]: + left = mid + 1 + # Likewise, if the left side number is larger, then a peak must exist somewhere on the left, so update the + # right boundary. + elif prev > xs[mid]: + right = mid + # Oh, wait, what if we have a plateau? Like [..., 10, 10, 10, ...]. We don't know which direction to go, + # and in fact, binary search would NOT even work here. + # + # In fact, the inputs given will never cause this situation to occur. So we will simply ignore this edge + # case. + else: + pass + + # If we've reached this point, we have possibly found a peak. In theory, we could have landed on a plateau + # after running through the binary search, and the problem doesn't say what to do, but it appears you never + # get any inputs that cause this to happen. + # + # Again, the inputs given will never cause this situation to occur. Therefore, we simply assume that the binary + # search ran successfully and return the midpoint. + return mid diff --git a/test/leetcode/binary_search/find_peak_element_test.py b/test/leetcode/binary_search/find_peak_element_test.py new file mode 100644 index 0000000..42f5995 --- /dev/null +++ b/test/leetcode/binary_search/find_peak_element_test.py @@ -0,0 +1,28 @@ +from leetcode.binary_search.find_peak_element import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.findPeakElement([1, 2, 3, 1]) == 2 + + +def test_case_2(): + assert soln.findPeakElement([1, 2, 1, 3, 5, 6, 4]) == 5 + + +def test_case_3(): + assert soln.findPeakElement([1]) == 0 + + +def test_case_4(): + assert soln.findPeakElement([2, 1]) == 0 + + +def test_case_5(): + assert soln.findPeakElement([-2147483647, -2147483648]) == 0 + + +def test_case_6(): + assert soln.findPeakElement([1, 2]) == 1 From 3560d99b3df35a5dd8dc3d738bccc833e0c81107 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 10 Mar 2025 13:52:44 -0700 Subject: [PATCH 009/158] removes the find peak element --- .../binary_search/find-peak-element.ts | 79 ------------------- .../binary_search/find-peak-element.test.ts | 27 ------- 2 files changed, 106 deletions(-) delete mode 100644 src/leetcode/binary_search/find-peak-element.ts delete mode 100644 test/leetcode/binary_search/find-peak-element.test.ts diff --git a/src/leetcode/binary_search/find-peak-element.ts b/src/leetcode/binary_search/find-peak-element.ts deleted file mode 100644 index e41eff9..0000000 --- a/src/leetcode/binary_search/find-peak-element.ts +++ /dev/null @@ -1,79 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// A peak element is an element that is strictly greater than its neighbors. -// -// Given a 0-indexed integer array nums, find a peak element, and return its index. If the array contains multiple -// peaks, return the index to any of the peaks. -// -// You may imagine that nums[-1] = nums[n] = -∞. In other words, an element is always considered to be strictly greater -// than a neighbor that is outside the array. -// -// You must write an algorithm that runs in O(log(n)) time. -// -// See {@link https://leetcode.com/problems/find-peak-element/} -export { findPeakElement }; - -// SOLUTION: -// -// This problem is not hard, but the wording is incredibly tricky. First off, let's talk about linear scan versus -// binary search. There are only 1000 elements max in the array, so either would work. However, the problem says that -// the algorithm must run in O(log(n)) time. This means we have to use binary search. -// -// Yes, binary search will still work even if the array is not sorted. That's because at each step, we are chasing -// a gradient uphill. For example, if we see [..., 10, 5, 5, ...] then we know that the elements are RISING in the left -// direction (and then eventually fall, because the left side element is -Infinity). So, we should slice the array in -// half and look for a peak in the left half; the opposite logic applies if the array is rising to the right. -// -// Secondly, the problem doesn't explicitly say what happens if there are no peaks. The solution I give throws an error -// if none are found (e.g. [1, 1, 1], or [1, 10, 10, 1]). Nowhere does LeetCode say what should happen if the inputs -// are invalid (e.g. have no peaks), or that all inputs are valid. However, it does apply to pass if we assume peaks -// always exist! -function findPeakElement(nums: number[]): number { - if (nums.length === 1) { - return 0; - } - - // Use insertion point binary search to find the peak. - let left = 0; - let right = nums.length; - while (left < right) { - const mid = Math.floor((left + right) / 2); - const prev = mid === 0 ? -Infinity : nums[mid - 1]; - const next = mid === nums.length - 1 ? -Infinity : nums[mid + 1]; - - // If we've found a peak, we can simply return it. - if (nums[mid] > prev && nums[mid] > next) { - return mid; - } - - // Otherwise, if the right side number is larger, then a peak must exist somewhere on the right, so update the left - // boundary. - if (next > nums[mid]) { - left = mid + 1; - } - // Likewise, if the left side number is larger, then a peak must exist somewhere on the left, so update the right - // boundary. - else if (prev > nums[mid]) { - right = mid; - } - // Oh, wait, what if we have a plateau? Like [..., 10, 10, 10, ...]. We don't know which direction to go, and in - // fact, binary search would NOT even work here. Well, the problem doesn't tell you this, but this can never - // happen with the inputs you are given, haha! - else { - throw new Error('there is a plateau in the middle of the array'); - } - } - - // If we've reached this point, we have possibly found a peak. In theory, we could have landed on a plateau after - // running through the binary search, and the problem doesn't say what to do, but it appears you never get any inputs - // that cause this to happen. - const mid = left; - const prev = mid === 0 ? -Infinity : nums[mid - 1]; - const next = mid === nums.length - 1 ? -Infinity : nums[mid + 1]; - if (nums[mid] > prev && nums[mid] > next) { - return mid; - } - - // This never happens. As far as I can tell, you never even get an input with a plateau. - throw new Error('we landed on a plateau after performing binary search'); -} diff --git a/test/leetcode/binary_search/find-peak-element.test.ts b/test/leetcode/binary_search/find-peak-element.test.ts deleted file mode 100644 index 49ec19b..0000000 --- a/test/leetcode/binary_search/find-peak-element.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { findPeakElement } from '../../src/binary-search/find-peak-element'; - -describe('find peak element', () => { - test('find peak element - test case 1', async () => { - expect(findPeakElement([1, 2, 3, 1])).toBe(2); - }); - - test('find peak element - test case 2', async () => { - expect(findPeakElement([1, 2, 1, 3, 5, 6, 4])).toBe(5); - }); - - test('find peak element - test case 3', async () => { - expect(findPeakElement([1])).toBe(0); - }); - - test('find peak element - test case 4', async () => { - expect(findPeakElement([2, 1])).toBe(0); - }); - - test('find peak element - test case 5', async () => { - expect(findPeakElement([-2147483647, -2147483648])).toBe(0); - }); - - test('find peak element - test case 6', async () => { - expect(findPeakElement([1, 2])).toBe(1); - }); -}); From 2f196951ba4de6ae3b82f6a00c28cd3bf0786fa9 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 10 Mar 2025 14:12:57 -0700 Subject: [PATCH 010/158] find smallest divisor --- ...-the-smallest-divisor-given-a-threshold.ts | 48 ----------------- .../binary_search/find_peak_element.py | 4 ++ ..._the_smallest_divisor_given_a_threshold.py | 51 +++++++++++++++++++ ...smallest-divisor-given-a-threshold.test.ts | 7 --- ...smallest_divisor_given_a_threshold_test.py | 8 +++ 5 files changed, 63 insertions(+), 55 deletions(-) delete mode 100644 src/leetcode/binary_search/find-the-smallest-divisor-given-a-threshold.ts create mode 100644 src/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold.py delete mode 100644 test/leetcode/binary_search/find-the-smallest-divisor-given-a-threshold.test.ts create mode 100644 test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py diff --git a/src/leetcode/binary_search/find-the-smallest-divisor-given-a-threshold.ts b/src/leetcode/binary_search/find-the-smallest-divisor-given-a-threshold.ts deleted file mode 100644 index 26df12a..0000000 --- a/src/leetcode/binary_search/find-the-smallest-divisor-given-a-threshold.ts +++ /dev/null @@ -1,48 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an array of integers nums and an integer threshold, we will choose a positive integer divisor, divide all the -// array by it, and sum the division's result. Find the smallest divisor such that the result mentioned above is less -// than or equal to threshold. -// -// Each result of the division is rounded to the nearest integer greater than or equal to that element. -// (For example: 7/3 = 3 and 10/2 = 5). -// -// The test cases are generated so that there will be an answer. -// -// See {@link https://leetcode.com/problems/find-the-smallest-divisor-given-a-threshold/} -export { smallestDivisor }; - -// SOLUTION: -// -// The smallest divisor is 1. This would maximize the sum. -// -// The largest divisor is Math.max(...nums). This would minimize the sum. -// -// We want a sum that is exactly the threshold or just belong. This is a good candidate to use binary search. -function smallestDivisor(nums: number[], threshold: number): number { - function sum(xs: number[], divisor: number) { - // We do Math.ceil because the problem asks us to round up. - return xs.map(x => Math.ceil(x / divisor)).reduce((a, b) => a + b); - } - - // Use the insert point binary search approach to find the divisor we want. - let left = 1; - let right = Math.max(...nums); - while (left < right) { - const mid = Math.floor((left + right) / 2); - const divisor = mid; - const value = sum(nums, divisor); - - // If the value is too large, our divisor was too small, so we should shift our left value to be mid + 1. - if (value > threshold) { - left = mid + 1; - } - // Otherwise, we should shift our right value to mid. - else { - right = mid; - } - } - - // Found the divisor at the "insertion point". - return left; -} diff --git a/src/leetcode/binary_search/find_peak_element.py b/src/leetcode/binary_search/find_peak_element.py index 3185ba4..3866e9f 100644 --- a/src/leetcode/binary_search/find_peak_element.py +++ b/src/leetcode/binary_search/find_peak_element.py @@ -32,6 +32,10 @@ def findPeakElement(self, xs: list[int]) -> int: assume peaks always exist! COMPLEXITY: + + Time complexity is O(log n). + + Space complexity is O(1). """ if len(xs) == 1: return 0 diff --git a/src/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold.py b/src/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold.py new file mode 100644 index 0000000..30a934c --- /dev/null +++ b/src/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold.py @@ -0,0 +1,51 @@ +# DIFFICULTY: MEDIUM +# +# Given an array of integers nums and an integer threshold, we will choose a positive integer divisor, divide all the +# array by it, and sum the division's result. Find the smallest divisor such that the result mentioned above is less +# than or equal to threshold. +# +# Each result of the division is rounded to the nearest integer greater than or equal to that element. +# (For example: 7/3 = 3 and 10/2 = 5). +# +# The test cases are generated so that there will be an answer. +# +# See https://leetcode.com/problems/find-the-smallest-divisor-given-a-threshold +from math import ceil + + +class Solution: + def smallestDivisor(self, xs: list[int], threshold: int) -> int: + """ + SOLUTION: + + The smallest divisor is 1. This would maximize the sum. + + The largest divisor is Math.max(...nums). This would minimize the sum. + + We want a sum that is exactly the threshold or just belong. This is a good candidate to use binary search. + """ + + def compute(ys: list[int], divisor: int): + # We do math.ceil because the problem asks us to round up. + # + # Note: there's no way to chain map -> reduce. Mind boggling. + return sum(ceil(x / divisor) for x in xs) + + # Use the insert point binary search approach to find the divisor we want. + left = 1 + right = max(xs) + + while left < right: + mid = (left + right) // 2 + divisor = mid + value = compute(xs, divisor) + + # If the value is too large, our divisor was too small, so we should shift our left value to be mid + 1. + if value > threshold: + left = mid + 1 + # Otherwise, we should shift our right value to mid. + else: + right = mid + + # Found the divisor at the "insertion point". + return left diff --git a/test/leetcode/binary_search/find-the-smallest-divisor-given-a-threshold.test.ts b/test/leetcode/binary_search/find-the-smallest-divisor-given-a-threshold.test.ts deleted file mode 100644 index 62e84b3..0000000 --- a/test/leetcode/binary_search/find-the-smallest-divisor-given-a-threshold.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { smallestDivisor } from '../../src/binary-search/find-the-smallest-divisor-given-a-threshold'; - -describe('find the smallest divisor given a threshold', () => { - test('find the smallest divisor given a threshold', async () => { - expect(smallestDivisor([1, 2, 5, 9], 6)).toBe(5); - }); -}); diff --git a/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py b/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py new file mode 100644 index 0000000..6c757bc --- /dev/null +++ b/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py @@ -0,0 +1,8 @@ +from leetcode.binary_search.find_the_smallest_divisor_given_a_threshold import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.smallestDivisor([1, 2, 5, 9], 6) == 5 From 996b5fac0be31cc8fa21655060005d40ebb6da08 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 10 Mar 2025 14:23:25 -0700 Subject: [PATCH 011/158] kth missing positive number --- .../k-th-missing-positive-number.ts | 55 ----------------- .../kth_missing_positive_number.py | 60 +++++++++++++++++++ .../k-th-missing-positive-number.test.ts | 17 ------ .../kth_missing_positive_number_test.py | 12 ++++ 4 files changed, 72 insertions(+), 72 deletions(-) delete mode 100644 src/leetcode/binary_search/k-th-missing-positive-number.ts create mode 100644 src/leetcode/binary_search/kth_missing_positive_number.py delete mode 100644 test/leetcode/binary_search/k-th-missing-positive-number.test.ts create mode 100644 test/leetcode/binary_search/kth_missing_positive_number_test.py diff --git a/src/leetcode/binary_search/k-th-missing-positive-number.ts b/src/leetcode/binary_search/k-th-missing-positive-number.ts deleted file mode 100644 index a33a4f8..0000000 --- a/src/leetcode/binary_search/k-th-missing-positive-number.ts +++ /dev/null @@ -1,55 +0,0 @@ -// DIFFICULTY: EASY -// -// Given an array arr of positive integers sorted in a strictly increasing order, and an integer k. -// -// Return the kth positive integer that is missing from this array. -// -// See {@link https://leetcode.com/problems/kth-missing-positive-number/} -export { findKthPositive }; - -// SOLUTION: -// -// There are multiple naive ways to do this. One way is to throw all elements into a set, then iterate from 1 to the -// max set element, and check if the element is in the set. Do this k times to find the k-th missing element. -// -// Another naive way to do this is to do a linear scan, and keep count of the missing numbers. You'd also keep a count -// of what the current element *should* be versus the current index. If the current index matches current element, then -// continue on. Otherwise increment the count of missing numbers. If the count of missing numbers is equal to k, then -// you've found it. -// -// Finally, you can use binary search to find the k-th missing element. Since the array is sorted, there's no need to -// scan the entire array. We can calculate how many missing numbers exist at each index, and use that information to -// determine if we should go left or right. -function findKthPositive(arr: number[], k: number): number { - // Use insertion point binary search to find the k-th missing element. - let left = 0; - let right = arr.length; - while (left < right) { - const mid = Math.floor((left + right) / 2); - - // So at the mid index, if there were no missing elements, then arr[mid] should be mid + 1. For example, if we have - // - // [1, 2, 3, 4, 5] - // - // ...then mid === 2, and arr[2] === 3. And 3 === mid + 1. Therefore, there are arr[2] - (2 + 1) missing elements, - // and here we have 0 missing elements. - const missing = arr[mid] - (mid + 1); - - // If there aren't enough missing numbers, then the k-th missing number must be to the right, so update the left - // pointer to be mid + 1. - if (missing < k) { - left = mid + 1; - } - // If there are too many missing numbers, then the k-th missing number must be to the left, so update the right - // pointer to be mid - 1; - else { - right = mid; - } - } - - // At this point, left === right, and left represents the index where missing >= k. Recall that if no numbers were - // missing, then arr[left] + 1 === left. If we assume that arr[left] + 1 > left by a COUNT of k elements, then we - // can find the VALUE of the k-th missing element, we add k to left. This also works if there are no "missing" - // elements (e.g. [1, 2, 3, 4]). - return left + k; -} diff --git a/src/leetcode/binary_search/kth_missing_positive_number.py b/src/leetcode/binary_search/kth_missing_positive_number.py new file mode 100644 index 0000000..86e2c09 --- /dev/null +++ b/src/leetcode/binary_search/kth_missing_positive_number.py @@ -0,0 +1,60 @@ +# DIFFICULTY: EASY +# +# Given an array arr of positive integers sorted in a strictly increasing order, and an integer k. +# +# Return the kth positive integer that is missing from this array. +# +# See https://leetcode.com/problems/kth-missing-positive-number +class Solution: + def findKthPositive(self, xs: list[int], k: int) -> int: + """ + SOLUTION: + + There are multiple naive ways to do this. One way is to throw all elements into a set, then iterate from 1 to + the max set element, and check if the element is in the set. Do this k times to find the k-th missing element. + + Another naive way to do this is to do a linear scan, and keep count of the missing numbers. You'd also keep a + count of what the current element *should* be versus the current index. If the current index matches current + element, then continue on. Otherwise increment the count of missing numbers. If the count of missing numbers + is equal to k, then you've found it. + + Finally, you can use binary search to find the k-th missing element. Since the array is sorted, there's no need + to scan the entire array. We can calculate how many missing numbers exist at each index, and use that + information to determine if we should go left or right. + + COMPLEXITY: + + Time complexity is O(log n). + + Space complexity is O(1). + """ + # Use insertion point binary search to find the k-th missing element. + left = 0 + right = len(xs) + + while left < right: + mid = (left + right) // 2 + + # So at the mid index, if there were no missing elements, then xs[mid] should be mid + 1. For example, if + # we have: + # + # [1, 2, 3, 4, 5] + # + # ...then mid === 2, and xs[2] === 3. And 3 === mid + 1. Therefore, there are xs[2] - (2 + 1) missing + # elements, and here we have 0 missing elements. + missing = xs[mid] - (mid + 1) + + # If there aren't enough missing numbers, then the k-th missing number must be to the right, so update the + # left pointer to be mid + 1. + if missing < k: + left = mid + 1 + # If there are too many missing numbers, then the k-th missing number must be to the left, so update the + # right pointer to be mid - 1; + else: + right = mid + + # At this point, left === right, and left represents the index where missing >= k. Recall that if no numbers + # were missing, then arr[left] + 1 === left. If we assume that arr[left] + 1 > left by a COUNT of k elements, + # then we can find the VALUE of the k-th missing element, we add k to left. This also works if there are no + # "missing" elements (e.g. [1, 2, 3, 4]). + return left + k diff --git a/test/leetcode/binary_search/k-th-missing-positive-number.test.ts b/test/leetcode/binary_search/k-th-missing-positive-number.test.ts deleted file mode 100644 index 049362b..0000000 --- a/test/leetcode/binary_search/k-th-missing-positive-number.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { findKthPositive } from '../../src/binary-search/k-th-missing-positive-number'; - -describe('k-th-missing-positive-number', () => { - test('find kth positive - test case 1', () => { - const arr = [2, 3, 4, 7, 11]; - const k = 5; - - expect(findKthPositive(arr, k)).toEqual(9); - }); - - test('find kth positive - test case 2', () => { - const arr = [1, 2, 3, 4]; - const k = 2; - - expect(findKthPositive(arr, k)).toEqual(6); - }); -}); diff --git a/test/leetcode/binary_search/kth_missing_positive_number_test.py b/test/leetcode/binary_search/kth_missing_positive_number_test.py new file mode 100644 index 0000000..22a4a63 --- /dev/null +++ b/test/leetcode/binary_search/kth_missing_positive_number_test.py @@ -0,0 +1,12 @@ +from leetcode.binary_search.kth_missing_positive_number import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.findKthPositive([2, 3, 4, 7, 11], 5) == 9 + + +def test_case_2(): + assert soln.findKthPositive([1, 2, 3, 4], 2) == 6 From cb5400f232ed82ecaf4fe1341b429260ca166e89 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 10 Mar 2025 14:37:34 -0700 Subject: [PATCH 012/158] kth smallest in sorted matrix --- ...-th-smallest-element-in-a-sorted-matrix.ts | 99 ------------------- ...kth_smallest_element_in_a_sorted_matrix.py | 95 ++++++++++++++++++ ...mallest-element-in-a-sorted-matrix.test.ts | 14 --- ...mallest_element_in_a_sorted_matrix_test.py | 11 +++ 4 files changed, 106 insertions(+), 113 deletions(-) delete mode 100644 src/leetcode/binary_search/k-th-smallest-element-in-a-sorted-matrix.ts create mode 100644 src/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix.py delete mode 100644 test/leetcode/binary_search/k-th-smallest-element-in-a-sorted-matrix.test.ts create mode 100644 test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py diff --git a/src/leetcode/binary_search/k-th-smallest-element-in-a-sorted-matrix.ts b/src/leetcode/binary_search/k-th-smallest-element-in-a-sorted-matrix.ts deleted file mode 100644 index b0001cb..0000000 --- a/src/leetcode/binary_search/k-th-smallest-element-in-a-sorted-matrix.ts +++ /dev/null @@ -1,99 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an n x n matrix where each of the rows and columns is sorted in ascending order, return the kth smallest -// element in the matrix. -// -// Note that it is the kth smallest element in the sorted order, not the kth distinct element. -// -// You must find a solution with a memory complexity better than O(n^2). -// -// See {@link https://leetcode.com/problems/kth-smallest-element-in-a-sorted-matrix/} -export { kthSmallest }; - -// SOLUTION: -// -// We can't flatten the list because that would take O(n^2) memory. -// -// Use binary search to narrow down where the kth smallest element is. Here, the left value is the smallest element, -// and the right value is the largest element. -// -// We can use the mid element to tell us how close or far we are from the kth smallest element. The matrix is sorted, -// so for any mid === matrix[i][j] we can figure out how many elements are less than or equal to mid. -// -// COMPLEXITY: -// -// We are using binary search on a range between the smallest and largest elements in the matrix, call it k. We will -// do O(log k) iterations, but in each iteration, we do have to count how many elements are less than or equal to the -// mid target. -// -// The countLessThanOrEqualTo() function takes at most n steps (where n is the number of rows/columns in the matrix). -// Since this is done at every single iteration, the total time is O(n log k). -// -// Space complexity is O(1). -function kthSmallest(matrix: number[][], k: number): number { - // Use insertion sort binary search to find exactly where the kth smallest element is; don't use the standard binary - // search algorithm because we're not looking for an exact value. - let left = matrix[0][0]; - let right = matrix[matrix.length - 1][matrix.length - 1]; - while (left < right) { - const mid = Math.floor((left + right) / 2); - const count = countLessThanOrEqualTo(matrix, mid); - - if (count < k) { - left = mid + 1; - } else { - right = mid; - } - } - - return left; -} - -function countLessThanOrEqualTo(matrix: number[][], target: number) { - // We can leverage the properties of the matrix to count how many elements are less than or equal to the target. - // Use a modified two pointer technique to count up elements. - let count = 0; - - // Start at the bottom left corner of the matrix: - // - // - - - - - // - - - - - // - - - - - // x - - - - // - // If the value is less than or equal to the target, then all elements from all previous rows are also less than our - // target (since the matrix is sorted). To count up that specific column, add (row + 1) to the count (the rows are - // 0 indexed, but the count is not, so we add 1). - let row = matrix.length - 1; - let column = 0; - while (row >= 0 && column < matrix.length) { - // Add up all the elements in the column. That is: - // - // x - - - - // x - - - - // x - - - - // x - - - - // - // All x's get added to the count, which means all elements in the column get added to the count. Because the - // matrix is sorted, all elements in the above column MUST be less than or equal to the target. - // - // Stop when we reach column === matrix.length - 1. - if (matrix[row][column] <= target) { - count += row + 1; - column++; - } - // If the element is greater than the target, we can move up a row and try again: - // - // x - - - - // x - - - - // x - - - - // - - - - - // - // Stop when we reach row === 0. - else { - row--; - } - } - - return count; -} diff --git a/src/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix.py b/src/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix.py new file mode 100644 index 0000000..9a4bf2a --- /dev/null +++ b/src/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix.py @@ -0,0 +1,95 @@ +# DIFFICULTY: MEDIUM +# +# Given an n x n matrix where each of the rows and columns is sorted in ascending order, return the kth smallest +# element in the matrix. +# +# Note that it is the kth smallest element in the sorted order, not the kth distinct element. +# +# You must find a solution with a memory complexity better than O(n^2). +# +# See https://leetcode.com/problems/kth-smallest-element-in-a-sorted-matrix +class Solution: + def kthSmallest(self, matrix: list[list[int]], k: int) -> int: + """ + SOLUTION: + + We can't flatten the list because that would take O(n^2) memory. + + Use binary search to narrow down where the kth smallest element is. Here, the left value is the smallest + element, and the right value is the largest element. + + We can use the mid element to tell us how close or far we are from the kth smallest element. The matrix is + sorted, so for any mid === matrix[i][j] we can figure out how many elements are less than or equal to mid. + + COMPLEXITY: + + Time complexity is O(n log m). We are using binary search on a range between the smallest and largest elements + in the matrix, call it m. We will do O(log m) iterations, but in each iteration, we do have to count how many + elements are less than or equal to the mid target. + + The countLessThanOrEqualTo() function takes at most n steps (where n is the number of rows/columns in the + matrix). Since this is done at every single iteration, the total time is O(n log m). + + Space complexity is O(1). + """ + + def countLessThanOrEqualTo(matrix: list[list[int]], target: int): + # We can leverage the properties of the matrix to count how many elements are less than or equal to the + # target. Use a modified two pointer technique to count up elements. + count = 0 + + # Start at the bottom left corner of the matrix: + # + # - - - - + # - - - - + # - - - - + # x - - - + # + # If the value is less than or equal to the target, then all elements from all previous rows are also less + # than our target (since the matrix is sorted). To count up that specific column, add (row + 1) to the + # count (the rows are 0 indexed, but the count is not, so we add 1). + row = len(matrix) - 1 + column = 0 + while row >= 0 and column < len(matrix): + # Add up all the elements in the column. That is: + # + # x - - - + # x - - - + # x - - - + # x - - - + # + # All x's get added to the count, which means all elements in the column get added to the count. + # Because the matrix is sorted, all elements in the above column MUST be less than or equal to the + # target. + # + # Stop when we reach column === matrix.length - 1. + if matrix[row][column] <= target: + count += row + 1 + column += 1 + # If the element is greater than the target, we can move up a row and try again: + # + # x - - - + # x - - - + # x - - - + # - - - - + # + # Stop when we reach row === 0. + else: + row -= 1 + + return count + + # Use insertion sort binary search to find exactly where the kth smallest element is; don't use the standard + # binary search algorithm because we're not looking for an exact value. + left = matrix[0][0] + right = matrix[len(matrix) - 1][len(matrix) - 1] + while left < right: + mid = (left + right) // 2 + count = countLessThanOrEqualTo(matrix, mid) + + if count < k: + left = mid + 1 + else: + right = mid + + return left diff --git a/test/leetcode/binary_search/k-th-smallest-element-in-a-sorted-matrix.test.ts b/test/leetcode/binary_search/k-th-smallest-element-in-a-sorted-matrix.test.ts deleted file mode 100644 index 75f4a63..0000000 --- a/test/leetcode/binary_search/k-th-smallest-element-in-a-sorted-matrix.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { kthSmallest } from '../../src/binary-search/k-th-smallest-element-in-a-sorted-matrix'; - -describe('k-th smallest element in a sorted matrix', () => { - test('kthSmallest - test case 1', () => { - const matrix = [ - [1, 5, 9], - [10, 11, 13], - [12, 13, 15] - ]; - const k = 8; - - expect(kthSmallest(matrix, k)).toEqual(13); - }); -}); diff --git a/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py b/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py new file mode 100644 index 0000000..bce2baf --- /dev/null +++ b/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py @@ -0,0 +1,11 @@ +from leetcode.binary_search.kth_smallest_element_in_a_sorted_matrix import Solution + + +soln = Solution() + + +def test_case_1(): + matrix = [[1, 5, 9], [10, 11, 13], [12, 13, 15]] + k = 8 + + assert soln.kthSmallest(matrix, k) == 13 From 254f31dd96d372e3aaa88ada985f05eafdab0e83 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 10 Mar 2025 14:46:56 -0700 Subject: [PATCH 013/158] lis --- .../longest-increasing-subsequence.ts | 58 ------------------- .../longest_increasing_subsequence.py | 55 ++++++++++++++++++ .../longest-increasing-subsequence.test.ts | 7 --- .../longest_increasing_subsequence_test.py | 8 +++ 4 files changed, 63 insertions(+), 65 deletions(-) delete mode 100644 src/leetcode/binary_search/longest-increasing-subsequence.ts create mode 100644 src/leetcode/binary_search/longest_increasing_subsequence.py delete mode 100644 test/leetcode/binary_search/longest-increasing-subsequence.test.ts create mode 100644 test/leetcode/binary_search/longest_increasing_subsequence_test.py diff --git a/src/leetcode/binary_search/longest-increasing-subsequence.ts b/src/leetcode/binary_search/longest-increasing-subsequence.ts deleted file mode 100644 index e8d2df1..0000000 --- a/src/leetcode/binary_search/longest-increasing-subsequence.ts +++ /dev/null @@ -1,58 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an integer array nums, return the length of the longest strictly increasing subsequence. -// -// @see {@link https://leetcode.com/problems/longest-increasing-subsequence/} -export { lengthOfLIS }; - -// SOLUTION: -// -// The naive way is to do this with dynamic programming and two nested loops. -// -// The more efficient way is to do binary search after constructing a "tails" array. The tails array represents the -// smallest possible ending element for a subsequence of length i + 1. For example, if tails is [2, 3, 7], that means: -// -// - There is a length 1 subsequence ending in 2. -// - There is a length 2 subsequence ending in 3. -// - There is a length 3 subsequence ending in 7. -// -// As we iterate through the array, we will keep the tails array updated. At the end of iteration, the length of the -// tails array will tell us the longest increasing subsequence. -// -// COMPLEXITY: -// -// Time complexity is O(n log n) because we are doing a binary search for each element in the array. Space complexity -// is O(n) because we are using an array to store the tails. -function lengthOfLIS(nums: number[]): number { - const tails: number[] = []; - - for (const num of nums) { - // Now we do a binary search to figure out where to insert num into tails. - let left = 0; - let right = tails.length; - while (left < right) { - const mid = Math.floor((left + right) / 2); - if (tails[mid] < num) { - // Move to the right if num is larger. - left = mid + 1; - } else { - // Move to the left if num is smaller. - right = mid; - } - } - - // We found an index in tails; this number is the smallest possible ending element for a subsequence of length left. - // So update that value in tails. - if (left < tails.length) { - tails[left] = num; - } - // Otherwise left === tails.length, which means we are adding a new element to the end of tails. This number is the - // smallest possible ending element for a subsequence of length left + 1. - else { - tails.push(num); - } - } - - // The length of tails is the length of the longest increasing subsequence. - return tails.length; -} diff --git a/src/leetcode/binary_search/longest_increasing_subsequence.py b/src/leetcode/binary_search/longest_increasing_subsequence.py new file mode 100644 index 0000000..e7826d3 --- /dev/null +++ b/src/leetcode/binary_search/longest_increasing_subsequence.py @@ -0,0 +1,55 @@ +# DIFFICULTY: MEDIUM +# +# Given an integer array nums, return the length of the longest strictly increasing subsequence. +# +# See https://leetcode.com/problems/longest-increasing-subsequence +class Solution: + def lengthOfLIS(self, xs: list[int]) -> int: + """ + SOLUTION: + + The naive way is to do this with dynamic programming and two nested loops. + + The more efficient way is to do binary search after constructing a "tails" array. The tails array represents + the smallest possible ending element for a subsequence of length i + 1. For example, if tails is [2, 3, 7], + that means: + + - There is a length 1 subsequence ending in 2. + - There is a length 2 subsequence ending in 3. + - There is a length 3 subsequence ending in 7. + + As we iterate through the array, we will keep the tails array updated. At the end of iteration, the length of + the tails array will tell us the longest increasing subsequence. + + COMPLEXITY: + + Time complexity is O(n log n) because we are doing a binary search for each element in the array. + + Space complexity is O(n) because we are using an array to store the tails. + """ + tails: list[int] = [] + + for x in xs: + left = 0 + right = len(tails) + while left < right: + mid = (left + right) // 2 + + if tails[mid] < x: + # Move to the right if num is larger. + left = mid + 1 + else: + # Move to the left if num is smaller. + right = mid + + # We found an index in tails; this number is the smallest possible ending element for a subsequence of + # length left. So update that value in tails. + if left < len(tails): + tails[left] = x + # Otherwise left == len(tails), which means we are adding a new element to the end of tails. This number is + # the smallest possible ending element for a subsequence of length left + 1. + else: + tails.append(x) + + # The length of tails is the length of the longest increasing subsequence. + return len(tails) diff --git a/test/leetcode/binary_search/longest-increasing-subsequence.test.ts b/test/leetcode/binary_search/longest-increasing-subsequence.test.ts deleted file mode 100644 index 39c77e4..0000000 --- a/test/leetcode/binary_search/longest-increasing-subsequence.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { lengthOfLIS } from '../../src/binary-search/longest-increasing-subsequence'; - -describe('longest increasing subsequence', () => { - test('lengthOfLIS - test case i', () => { - expect(lengthOfLIS([10, 9, 2, 5, 3, 7, 101, 18])).toEqual(4); - }); -}); diff --git a/test/leetcode/binary_search/longest_increasing_subsequence_test.py b/test/leetcode/binary_search/longest_increasing_subsequence_test.py new file mode 100644 index 0000000..d73e756 --- /dev/null +++ b/test/leetcode/binary_search/longest_increasing_subsequence_test.py @@ -0,0 +1,8 @@ +from leetcode.binary_search.longest_increasing_subsequence import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.lengthOfLIS([10, 9, 2, 5, 3, 7, 101, 18]) == 4 From 03ac22dbc5c99a3848413ef725152b3b35af6a8c Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 10 Mar 2025 15:16:14 -0700 Subject: [PATCH 014/158] median of 2 sorted arrays --- src/leetcode/array/design_hit_counter.py | 2 +- .../dot_product_of_two_sparse_vectors.py | 2 +- .../array/longest_consecutive_sequence.py | 2 +- ...ast_position_of_element_in_sorted_array.py | 4 +- ...kth_smallest_element_in_a_sorted_matrix.py | 2 +- .../median-of-two-sorted-arrays.ts | 120 ----------------- .../median_of_two_sorted_arrays.py | 122 ++++++++++++++++++ .../median-of-two-sorted-arrays.test.ts | 11 -- .../median_of_two_sorted_arrays_test.py | 12 ++ 9 files changed, 140 insertions(+), 137 deletions(-) delete mode 100644 src/leetcode/binary_search/median-of-two-sorted-arrays.ts create mode 100644 src/leetcode/binary_search/median_of_two_sorted_arrays.py delete mode 100644 test/leetcode/binary_search/median-of-two-sorted-arrays.test.ts create mode 100644 test/leetcode/binary_search/median_of_two_sorted_arrays_test.py diff --git a/src/leetcode/array/design_hit_counter.py b/src/leetcode/array/design_hit_counter.py index 89f00fb..a2c20a2 100644 --- a/src/leetcode/array/design_hit_counter.py +++ b/src/leetcode/array/design_hit_counter.py @@ -8,7 +8,7 @@ # # See https://leetcode.com/problems/design-hit-counter class HitCounter: - def __init__(self): + def __init__(self) -> None: """ SOLUTION: diff --git a/src/leetcode/array/dot_product_of_two_sparse_vectors.py b/src/leetcode/array/dot_product_of_two_sparse_vectors.py index 6a12c5a..e90fafc 100644 --- a/src/leetcode/array/dot_product_of_two_sparse_vectors.py +++ b/src/leetcode/array/dot_product_of_two_sparse_vectors.py @@ -12,7 +12,7 @@ # # See https://leetcode.com/problems/dot-product-of-two-sparse-vectors class SparseVector: - def __init__(self, nums: list[int]): + def __init__(self, nums: list[int]) -> None: """ SOLUTION: diff --git a/src/leetcode/array/longest_consecutive_sequence.py b/src/leetcode/array/longest_consecutive_sequence.py index 4eecfb9..6c49637 100644 --- a/src/leetcode/array/longest_consecutive_sequence.py +++ b/src/leetcode/array/longest_consecutive_sequence.py @@ -6,7 +6,7 @@ # # See https://leetcode.com/problems/longest-consecutive-sequence class Solution: - def longestConsecutive(self, xs: list[int]): + def longestConsecutive(self, xs: list[int]) -> int: """ SOLUTION: diff --git a/src/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array.py b/src/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array.py index 9a26e6b..3f3b04b 100644 --- a/src/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array.py +++ b/src/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array.py @@ -33,7 +33,7 @@ def searchRange(self, xs: list[int], target: int) -> list[int]: # # Otherwise if the middle element is greater than or equal to the target, we move to the left (guaranteeing that # if we find a duplicate target, we will keep moving left). - def binarySearchLeft(): + def binarySearchLeft() -> int: left = 0 right = len(xs) @@ -55,7 +55,7 @@ def binarySearchLeft(): # # Otherwise if the middle element is less than or equal to the target, we move to the right (guaranteeing that # if we find a duplicate target, we will keep moving right). - def binarySearchRight(): + def binarySearchRight() -> int: left = 0 right = len(xs) diff --git a/src/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix.py b/src/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix.py index 9a4bf2a..8cda580 100644 --- a/src/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix.py +++ b/src/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix.py @@ -33,7 +33,7 @@ def kthSmallest(self, matrix: list[list[int]], k: int) -> int: Space complexity is O(1). """ - def countLessThanOrEqualTo(matrix: list[list[int]], target: int): + def countLessThanOrEqualTo(matrix: list[list[int]], target: int) -> int: # We can leverage the properties of the matrix to count how many elements are less than or equal to the # target. Use a modified two pointer technique to count up elements. count = 0 diff --git a/src/leetcode/binary_search/median-of-two-sorted-arrays.ts b/src/leetcode/binary_search/median-of-two-sorted-arrays.ts deleted file mode 100644 index 9de2355..0000000 --- a/src/leetcode/binary_search/median-of-two-sorted-arrays.ts +++ /dev/null @@ -1,120 +0,0 @@ -// DIFFICULTY: HARD -// -// Given two sorted arrays nums1 and nums2 of size m and n respectively, return the median of the two sorted arrays. -// -// The overall run time complexity should be O(log (m+n)). -// -// See {@link https://leetcode.com/problems/median-of-two-sorted-arrays/} -export { findMedianSortedArrays }; - -// SOLUTION: -// -// This is not a reasonable question to ask in an interview. It's quite algorithmically complex. -// -// The naive way to solve this problem is to simply merge the two arrays and then take the midpoint. This is not what -// the problem asks for. -// -// To more efficiently find the median, imagine that the merged array's median partitions the merged array into two -// parts; a left part and a right part. The left part's partition will have elements that are all less than the -// median, and the right part's partition will have elements that are all larger than the median. -// -// Our goal now is to find four partitions; two (possibly different sized) left partitions from each array such that -// the values in those partitions are all less than the median. These two left partitions, if merged, would be the -// same as the left partition in the fully merged array. Likewise, the two (possibly different sized) right -// partitions, when merged, would be the right partition of the merged array. -// -// Note that we know that the left partition in the merged array is going to have h=(m+n)/2 elements, where m and n -// are the lengths of the two arrays (h representing half of the array elements in the merged array). We need to take -// a guess at where to partition on the smaller array, so let's say that the size of that partition is s=m/2. Then -// the size of the left partition for the other array must be h-s. -// -// Using the values of s and h-s, we can check if we've hit upon properly sized partitions that give us the median. -// If so, we can simply return it. If we didn't hit upon the properly sized partitions, then we'll have to update our -// guess for s and try again. -// -// How do we try again? Well, we can simulate concatenating the left partitions and right partitions to find out -// where we goofed for finding size s. For the smaller array, if we found s by setting the left pointer to 0, and -// the right pointer to m/2, then we'll have to use a modified version of binary search to update the pointers: -// -// - If the value at m=(left+right)/2 was too small, set left=m+1 and set right=m. -// - If the value at m=(left+right)/2 was too big, set left=0 and set right=m/2. -// -// Continue updating the left and right pointers until we have found the median. -function findMedianSortedArrays(xs: number[], ys: number[]) { - // Because we want to operate on the smaller array, let's find out which one is smaller and set xs equal to it. - if (xs.length > ys.length) { - [xs, ys] = [ys, xs]; - } - - // Again, we're using m and n as the array sizes, and m represents our smaller array size. - const m = xs.length; - const n = ys.length; - - // Note that this is a variation of the standard binary search algorithm. - let left = 0; - let right = m; - while (left <= right) { - // Compute the partition midpoints for both arrays. - // - // Imagine we have m=3 and n=3. Then we want p1 to be at (m+n)/2=1. The size of the "merged" array would be - // m+n=6. The midpoint of the other array, including the median, would be at (m+n)/2-3=3. - // - // However, for a "merged" array of odd size, say m=3, and n=4, then m+n=7. And if we use (m+n)/2-3=3, that - // would not include the median at index 4. So we should use (m+n+1)/2 instead. - // - // Using (m+n+1)/2 does not affect the calculation for an even sized "merged" array because the floor function - // removes the decimal. - const p1 = Math.floor((left + right) / 2); // Note that p1 needs to be calculated using left/right. - const p2 = Math.floor((m + n + 1) / 2) - p1; // Note that p2 needs to be calculated based on total size. - - // Now we imagine that the two left partitions are merged, and the two right partitions are merged. If we've - // chosen the partition points correctly, then the last value of both left partitions should be less than the - // head values of both right partitons. - // - // Here's an example partition of two arrays, where p1=2 and p2=3. - // - // xs = [1,2 | 3,3] with lmax1=2 and rmin1=3 (lmax1 is at p1-1 due to zero indexing). - // ys = [1,2,3 | 4,5,5] with lmax2=3 and rmin2=4 (lmax2 is at p2-1 due to zero indexing). - // - // In this case, we want to compare the rightmost values of xs with the opposite leftmost value of ys, and vice - // versa. - // - // Note that if p1 or p2 are zero, that means that the partitioning leaves no elements in the partition, so - // those min/max values should be replaced by -Infinity or +Infinity. - // - // Consider the following cases: - // - // xs = [] with lmax1=-Infinity because there is no value, and everything is >lmax1. - // xs = [1,2,3|] with rmax1=+Infinity because there is no value, and everything is rmin2) { - right = p1 - 1; - } - - // Here the xs side left partition's last value is too small, so we have to change the left pointer. At the next - // iteration we will do binary search where the left pointer has been set to the element after p1. - else { - left = p1 + 1; - } - } - - throw new Error('arrays are not sorted'); -} diff --git a/src/leetcode/binary_search/median_of_two_sorted_arrays.py b/src/leetcode/binary_search/median_of_two_sorted_arrays.py new file mode 100644 index 0000000..c99f374 --- /dev/null +++ b/src/leetcode/binary_search/median_of_two_sorted_arrays.py @@ -0,0 +1,122 @@ +# DIFFICULTY: HARD +# +# Given two sorted arrays nums1 and nums2 of size m and n respectively, return the median of the two sorted arrays. +# +# The overall run time complexity should be O(log (m+n)). +# +# See https://leetcode.com/problems/median-of-two-sorted-arrays +class Solution: + def findMedianSortedArrays(self, xs: list[int], ys: list[int]) -> float: + """ + SOLUTION: + + This is not a reasonable question to ask in an interview. It's quite algorithmically complex. + + The naive way to solve this problem is to simply merge the two arrays and then take the midpoint. This is not + what the problem asks for. + + To more efficiently find the median, imagine that the merged array's median partitions the merged array into two + parts; a left part and a right part. The left part's partition will have elements that are all less than the + median, and the right part's partition will have elements that are all larger than the median. + + Our goal now is to find four partitions; two (possibly different sized) left partitions from each array such + that the values in those partitions are all less than the median. These two left partitions, if merged, would + be the same as the left partition in the fully merged array. Likewise, the two (possibly different sized) right + partitions, when merged, would be the right partition of the merged array. + + Note that we know that the left partition in the merged array is going to have h = (m + n) / 2 elements, where m + and n are the lengths of the two arrays (h representing half of the array elements in the merged array). We + need to take a guess at where to partition on the smaller array, so let's say that the size of that partition is + s = m / 2. Then the size of the left partition for the other array must be h - s. + + Using the values of s and h - s, we can check if we've hit upon properly sized partitions that give us the + median. If so, we can simply return it. If we didn't hit upon the properly sized partitions, then we'll have + to update our guess for s and try again. + + How do we try again? Well, we can simulate concatenating the left partitions and right partitions to find out + where we goofed for finding size s. For the smaller array, if we found s by setting the left pointer to 0, and + the right pointer to m / 2, then we'll have to use a modified version of binary search to update the pointers: + + - If the value at m = (left + right) / 2 was too small, set left = m + 1 and set right = m. + - If the value at m = (left + right) / 2 was too big, set left = 0 and set right = m / 2. + + Continue updating the left and right pointers until we have found the median. + + COMPLEXITY: + + Time complexity is O(log (m + n)). + + Space complexity is O(1). + """ + # Because we want to operate on the smaller array, let's find out which one is smaller and set xs equal to it. + if len(xs) > len(ys): + xs, ys = ys, xs + + # Again, we're using m and n as the array sizes, and m represents our smaller array size. + m = len(xs) + n = len(ys) + + # Note that this is a variation of the standard binary search algorithm. + left = 0 + right = m + while left <= right: + # Compute the partition midpoints for both arrays. + # + # Imagine we have m = 3 and n = 3. Then we want p1 to be at (m + n) / 2 = 1. The size of the "merged" + # array would be m + n = 6. The midpoint of the other array, including the median, would be at + # (m + n) / 2 - 3 = 3. + # + # However, for a "merged" array of odd size, say m = 3, and n = 4, then m + n = 7. And if we use + # (m + n) /2 - 3 = 3, that would not include the median at index 4. So we should use (m + n + 1) / 2 + # instead. + # + # Using (m + n + 1) / 2 does not affect the calculation for an even sized "merged" array because the floor + # function removes the decimal. + p1 = (left + right) // 2 # Note that p1 needs to be calculated using left / right. + p2 = ((m + n + 1) // 2) - p1 # Note that p2 needs to be calculated based on total size. + + # Now we imagine that the two left partitions are merged, and the two right partitions are merged. If we've + # chosen the partition points correctly, then the last value of both left partitions should be less than the + # head values of both right partitons. + # + # Here's an example partition of two arrays, where p1 = 2 and p2 = 3. + # + # xs = [1,2 | 3,3] with lmax1 = 2 and rmin1 = 3 (lmax1 is at p1 - 1 due to zero indexing). + # ys = [1,2,3 | 4,5,5] with lmax2 = 3 and rmin2 = 4 (lmax2 is at p2 - 1 due to zero indexing). + # + # In this case, we want to compare the rightmost values of xs with the opposite leftmost value of ys, and + # vice versa. + # + # Note that if p1 or p2 are zero, that means that the partitioning leaves no elements in the partition, so + # those min/max values should be replaced by -Infinity or +Infinity. + # + # Consider the following cases: + # + # xs = [] with lmax1 = -Infinity because there is no value, and everything is > lmax1. + # xs = [1,2,3|] with rmax1 = +Infinity because there is no value, and everything is < rmin1. + lmax1 = float("-inf") if p1 == 0 else xs[p1 - 1] # If xs partition is empty, lmax1 value is unused. + rmin1 = float("inf") if p1 == m else xs[p1] # If xs partition is full, rmin1 value is unused. + lmax2 = float("-inf") if p2 == 0 else ys[p2 - 1] # If ys partition is empty, lmax2 value is unused. + rmin2 = float("inf") if p2 == n else ys[p2] # If ys partition is full, rmin2 value is unused. + + # If our inequalities hold, we have found the median. The value differs depending on if we have an odd + # sized "merged" array or even. + if lmax1 <= rmin2 and lmax2 <= rmin1: + # Even case + if (m + n) % 2 == 0: + return (max(lmax1, lmax2) + min(rmin1, rmin2)) / 2 + + # Odd case. + return max(lmax1, lmax2) + + # Here the xs side left partition's last value is too large, so we have to update the right pointer. At the + # next iteration we will do a binary search where the right pointer has been set to the element before p1. + if lmax1 > rmin2: + right = p1 - 1 + # Here the xs side left partition's last value is too small, so we have to change the left pointer. At the + # next iteration we will do binary search where the left pointer has been set to the element after p1. + else: + left = p1 + 1 + + # This is never supposed to happen. + raise Exception("arrays are not sorted") diff --git a/test/leetcode/binary_search/median-of-two-sorted-arrays.test.ts b/test/leetcode/binary_search/median-of-two-sorted-arrays.test.ts deleted file mode 100644 index 30af627..0000000 --- a/test/leetcode/binary_search/median-of-two-sorted-arrays.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { findMedianSortedArrays } from '../../src/binary-search/median-of-two-sorted-arrays'; - -describe('median of two sorted arrays', () => { - test('median of two sorted arrays - test case 4', async () => { - expect(findMedianSortedArrays([1, 3], [2])).toBe(2); - }); - - test('median of two sorted arrays - test case 5', async () => { - expect(findMedianSortedArrays([3, 2, 3, 1, 2, 4, 5, 5, 6], [4])).toBe(3); - }); -}); diff --git a/test/leetcode/binary_search/median_of_two_sorted_arrays_test.py b/test/leetcode/binary_search/median_of_two_sorted_arrays_test.py new file mode 100644 index 0000000..3b87ffc --- /dev/null +++ b/test/leetcode/binary_search/median_of_two_sorted_arrays_test.py @@ -0,0 +1,12 @@ +from leetcode.binary_search.median_of_two_sorted_arrays import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.findMedianSortedArrays([1, 3], [2]) == 2 + + +def test_case_2(): + assert soln.findMedianSortedArrays([3, 2, 3, 1, 2, 4, 5, 5, 6], [4]) == 3 From 788876935f37e945d68307639e2f7908bc180eae Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 10 Mar 2025 15:34:27 -0700 Subject: [PATCH 015/158] searching in rotated array --- .../search_in_rotated_sorted_array.py | 65 +++++++++++++++++++ .../search_in_rotated_sorted_array_test.py | 20 ++++++ 2 files changed, 85 insertions(+) create mode 100644 src/leetcode/binary_search/search_in_rotated_sorted_array.py create mode 100644 test/leetcode/binary_search/search_in_rotated_sorted_array_test.py diff --git a/src/leetcode/binary_search/search_in_rotated_sorted_array.py b/src/leetcode/binary_search/search_in_rotated_sorted_array.py new file mode 100644 index 0000000..f04102b --- /dev/null +++ b/src/leetcode/binary_search/search_in_rotated_sorted_array.py @@ -0,0 +1,65 @@ +# DIFFICULTY: MEDIUM +# +# There is an integer array nums sorted in ascending order (with distinct values). +# +# Prior to being passed to your function, nums is possibly rotated at an unknown pivot index k (1 <= k < nums.length) +# such that the resulting array is [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]] (0-indexed). +# For example, [0,1,2,4,5,6,7] might be rotated at pivot index 3 and become [4,5,6,7,0,1,2]. +# +# Given the array nums after the possible rotation and an integer target, return the index of target if it is in nums, +# or -1 if it is not in nums. +# +# You must write an algorithm with O(log n) runtime complexity. +# +# See https://leetcode.com/problems/search-in-rotated-sorted-array +class Solution: + def search(self, xs: list[int], target: int) -> int: + """ + SOLUTION: + + You can still use binary search for this; you just have to recognize when you have looped around in the array. + This uses the standard binary search algorithm, but with recursion. + + The standard approach is used because we are looking for an exact match for an element in the array, and return + -1 if the element is not found. + """ + def searchInternal(ys: list[int], start: int, end: int, t: int) -> int: + mid = (start + end) // 2 + if ys[mid] == t: + return mid + + # If we've looped back around ourselves, that means the number wasn't found, and we can stop. + if start > end: + return -1 + + # If the left portion is sorted, and our target is within that portion, update the start/end indexes to be + # strictly within the left side of the pivot, and perform binary search there. + # + # Otherwise, it is in the right portion of the array, which contains a rotation. Call this function again, + # with an updated restriction on the sub array to search. + isLeftSorted = ys[start] < ys[mid] + isWithinLeftPortion = t >= ys[start] and t <= ys[mid - 1] + + if isLeftSorted and isWithinLeftPortion: + return searchInternal(ys, start, mid - 1, t) + + if isLeftSorted and not isWithinLeftPortion: + return searchInternal(ys, mid + 1, end, t) + + # If the right portion is sorted, and our target is within that portion, update the start/end indexes to be + # strictly within the right side of the pivot, and perform binary search there. + # + # Otherwise, it is in the left portion of the array, which contains a rotation. Call this function again, + # with an updated restriction on the sub array to search. + isRightSorted = not isLeftSorted + isWithinRightPortion = t >= ys[mid + 1] and t <= ys[end] + + if isRightSorted and isWithinRightPortion: + return searchInternal(ys, mid + 1, end, t) + + if isRightSorted and not isWithinRightPortion: + return searchInternal(ys, start, mid - 1, t) + + return -1 + + return searchInternal(xs, 0, len(xs) - 1, target) diff --git a/test/leetcode/binary_search/search_in_rotated_sorted_array_test.py b/test/leetcode/binary_search/search_in_rotated_sorted_array_test.py new file mode 100644 index 0000000..957acac --- /dev/null +++ b/test/leetcode/binary_search/search_in_rotated_sorted_array_test.py @@ -0,0 +1,20 @@ +from leetcode.binary_search.search_in_rotated_sorted_array import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.search([4, 5, 6, 7, 0, 1, 2], 0) == 4 + + +def test_case_2(): + assert soln.search([4, 5, 6, 7, 0, 1, 2], 3) == -1 + + +def test_case_3(): + assert soln.search([1], 0) == -1 + + +def test_case_4(): + assert soln.search([3, 1], 1) == 1 From 1e509fe1f5d3e44d15ce8fbaad3dd15ed4d4e896 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 10 Mar 2025 15:42:36 -0700 Subject: [PATCH 016/158] search in rotated --- .../search-in-rotated-sorted-array.ts | 70 ------------------- .../search_in_rotated_sorted_array.py | 53 +++++++------- .../search-in-rotated-sorted-array.test.ts | 19 ----- 3 files changed, 24 insertions(+), 118 deletions(-) delete mode 100644 src/leetcode/binary_search/search-in-rotated-sorted-array.ts delete mode 100644 test/leetcode/binary_search/search-in-rotated-sorted-array.test.ts diff --git a/src/leetcode/binary_search/search-in-rotated-sorted-array.ts b/src/leetcode/binary_search/search-in-rotated-sorted-array.ts deleted file mode 100644 index 95c8039..0000000 --- a/src/leetcode/binary_search/search-in-rotated-sorted-array.ts +++ /dev/null @@ -1,70 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// There is an integer array nums sorted in ascending order (with distinct values). -// -// Prior to being passed to your function, nums is possibly rotated at an unknown pivot index k (1 <= k < nums.length) -// such that the resulting array is [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]] (0-indexed). -// For example, [0,1,2,4,5,6,7] might be rotated at pivot index 3 and become [4,5,6,7,0,1,2]. -// -// Given the array nums after the possible rotation and an integer target, return the index of target if it is in nums, -// or -1 if it is not in nums. -// -// You must write an algorithm with O(log n) runtime complexity. -// -// See {@link https://leetcode.com/problems/search-in-rotated-sorted-array/} -export { search }; - -// SOLUTION: -// -// You can still use binary search for this; you just have to recognize when you have looped around in the array. -// This uses the standard binary search algorithm, but with recursion. -// -// The standard approach is used because we are looking for an exact match for an element in the array, and return -1 -// if the element is not found. -function search(xs: number[], target: number): number { - function searchInternal(ys: number[], start: number, end: number, t: number) { - const mid = Math.floor((end + start) / 2); - if (ys[mid] === t) { - return mid; - } - - // If we've looped back around ourselves, that means the number wasn't found, and we can stop. - if (start > end) { - return -1; - } - - // If the left portion is sorted, and our target is within that portion, update the start/end indexes to be - // strictly within the left side of the pivot, and perform binary search there. - // - // Otherwise, it is in the right portion of the array, which contains a rotation. Call this function again, - // with an updated restriction on the sub array to search. - const isLeftSorted = ys[start] < ys[mid]; - const isWithinLeftPortion = t >= ys[start] && t <= ys[mid - 1]; - if (isLeftSorted && isWithinLeftPortion) { - return searchInternal(ys, start, mid - 1, t); - } - - if (isLeftSorted && !isWithinLeftPortion) { - return searchInternal(ys, mid + 1, end, t); - } - - // If the right portion is sorted, and our target is within that portion, update the start/end indexes to be - // strictly within the right side of the pivot, and perform binary search there. - // - // Otherwise, it is in the left portion of the array, which contains a rotation. Call this function again, - // with an updated restriction on the sub array to search. - const isRightSorted = !isLeftSorted; - const isWithinRightPortion = t >= ys[mid + 1] && t <= ys[end]; - if (isRightSorted && isWithinRightPortion) { - return searchInternal(ys, mid + 1, end, t); - } - - if (isRightSorted && !isWithinRightPortion) { - return searchInternal(ys, start, mid - 1, t); - } - - return -1; - } - - return searchInternal(xs, 0, xs.length - 1, target); -} diff --git a/src/leetcode/binary_search/search_in_rotated_sorted_array.py b/src/leetcode/binary_search/search_in_rotated_sorted_array.py index f04102b..c70ac58 100644 --- a/src/leetcode/binary_search/search_in_rotated_sorted_array.py +++ b/src/leetcode/binary_search/search_in_rotated_sorted_array.py @@ -24,42 +24,37 @@ def search(self, xs: list[int], target: int) -> int: -1 if the element is not found. """ def searchInternal(ys: list[int], start: int, end: int, t: int) -> int: - mid = (start + end) // 2 - if ys[mid] == t: - return mid - # If we've looped back around ourselves, that means the number wasn't found, and we can stop. if start > end: return -1 - # If the left portion is sorted, and our target is within that portion, update the start/end indexes to be - # strictly within the left side of the pivot, and perform binary search there. - # - # Otherwise, it is in the right portion of the array, which contains a rotation. Call this function again, - # with an updated restriction on the sub array to search. - isLeftSorted = ys[start] < ys[mid] - isWithinLeftPortion = t >= ys[start] and t <= ys[mid - 1] - - if isLeftSorted and isWithinLeftPortion: - return searchInternal(ys, start, mid - 1, t) + mid = (start + end) // 2 + if ys[mid] == t: + return mid - if isLeftSorted and not isWithinLeftPortion: - return searchInternal(ys, mid + 1, end, t) + isLeftSorted = ys[start] <= ys[mid] + if isLeftSorted: + # If the left portion is sorted, and our target is within that portion, update the start/end indexes to + # be strictly within the left side of the pivot, and perform binary search there. + if ys[start] <= t <= ys[mid]: + return searchInternal(ys, start, mid - 1, t) + # Otherwise, it is in the right portion of the array, which contains a rotation. Call this function + # again, with an updated restriction on the sub array to search. + else: + return searchInternal(ys, mid + 1, end, t) - # If the right portion is sorted, and our target is within that portion, update the start/end indexes to be - # strictly within the right side of the pivot, and perform binary search there. - # - # Otherwise, it is in the left portion of the array, which contains a rotation. Call this function again, - # with an updated restriction on the sub array to search. isRightSorted = not isLeftSorted - isWithinRightPortion = t >= ys[mid + 1] and t <= ys[end] - - if isRightSorted and isWithinRightPortion: - return searchInternal(ys, mid + 1, end, t) - - if isRightSorted and not isWithinRightPortion: - return searchInternal(ys, start, mid - 1, t) - + if isRightSorted: + # If the right portion is sorted, and our target is within that portion, update the start/end indexes to + # be strictly within the right side of the pivot, and perform binary search there. + if ys[mid + 1] <= t <= ys[end]: + return searchInternal(ys, mid + 1, end, t) + # Otherwise, it is in the left portion of the array, which contains a rotation. Call this function + # again, with an updated restriction on the sub array to search. + else: + return searchInternal(ys, start, mid - 1, t) + + # Otherwise, the value wasn't found at all return -1 return searchInternal(xs, 0, len(xs) - 1, target) diff --git a/test/leetcode/binary_search/search-in-rotated-sorted-array.test.ts b/test/leetcode/binary_search/search-in-rotated-sorted-array.test.ts deleted file mode 100644 index 85c3903..0000000 --- a/test/leetcode/binary_search/search-in-rotated-sorted-array.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { search } from '../../src/binary-search/search-in-rotated-sorted-array'; - -describe('search in rotated sorted array', () => { - test('search in rotated sorted array - test case 1', async () => { - expect(search([4, 5, 6, 7, 0, 1, 2], 0)).toBe(4); - }); - - test('search in rotated sorted array - test case 2', async () => { - expect(search([4, 5, 6, 7, 0, 1, 2], 3)).toBe(-1); - }); - - test('search in rotated sorted array - test case 3', async () => { - expect(search([1], 0)).toBe(-1); - }); - - test('search in rotated sorted array - test case 4', async () => { - expect(search([3, 1], 1)).toBe(1); - }); -}); From 11e4af07855a66a486a3578791db935801cd4682 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 10 Mar 2025 15:51:05 -0700 Subject: [PATCH 017/158] make more pythonistic --- src/leetcode/array/dot_product_of_two_sparse_vectors.py | 8 +------- .../binary_search/search_in_rotated_sorted_array.py | 1 + 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/leetcode/array/dot_product_of_two_sparse_vectors.py b/src/leetcode/array/dot_product_of_two_sparse_vectors.py index e90fafc..ae2f059 100644 --- a/src/leetcode/array/dot_product_of_two_sparse_vectors.py +++ b/src/leetcode/array/dot_product_of_two_sparse_vectors.py @@ -35,7 +35,7 @@ def __init__(self, nums: list[int]) -> None: def dotProduct(self, vec: "SparseVector") -> int: # Get the vectors in order of size; we'll iterate over the smaller vector. - smaller, larger = self.__ordered(self.map, vec.map) + (smaller, larger) = (self.map, vec.map) if len(self.map) < len(vec.map) else (vec.map, self.map) result = 0 for i, value in smaller.items(): @@ -43,9 +43,3 @@ def dotProduct(self, vec: "SparseVector") -> int: result += value * larger[i] return result - - def __ordered(self, a: dict[int, int], b: dict[int, int]) -> tuple[dict[int, int], dict[int, int]]: - if len(a) < len(b): - return (a, b) - else: - return (b, a) diff --git a/src/leetcode/binary_search/search_in_rotated_sorted_array.py b/src/leetcode/binary_search/search_in_rotated_sorted_array.py index c70ac58..cab36d7 100644 --- a/src/leetcode/binary_search/search_in_rotated_sorted_array.py +++ b/src/leetcode/binary_search/search_in_rotated_sorted_array.py @@ -23,6 +23,7 @@ def search(self, xs: list[int], target: int) -> int: The standard approach is used because we are looking for an exact match for an element in the array, and return -1 if the element is not found. """ + def searchInternal(ys: list[int], start: int, end: int, t: int) -> int: # If we've looped back around ourselves, that means the number wasn't found, and we can stop. if start > end: From 28536afd927f75c307a352acbc66d6418e47d1b2 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 10 Mar 2025 20:54:47 -0700 Subject: [PATCH 018/158] arithmetic slices ii --- src/leetcode/dynamic_programming/README.md | 8 +- .../arithmetic-slices-ii-subsequences.ts | 78 ------------------- .../arithmetic_slices_ii_subsequences.py | 78 +++++++++++++++++++ .../arithmetic-slices-ii-subsequences.test.ts | 15 ---- .../arithmetic_slices_ii_subsequences_test.py | 12 +++ 5 files changed, 94 insertions(+), 97 deletions(-) delete mode 100644 src/leetcode/dynamic_programming/arithmetic-slices-ii-subsequences.ts create mode 100644 src/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences.py delete mode 100644 test/leetcode/dynamic_programming/arithmetic-slices-ii-subsequences.test.ts create mode 100644 test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py diff --git a/src/leetcode/dynamic_programming/README.md b/src/leetcode/dynamic_programming/README.md index 1658486..5573b57 100644 --- a/src/leetcode/dynamic_programming/README.md +++ b/src/leetcode/dynamic_programming/README.md @@ -4,7 +4,7 @@ These phrases indicate dynamic programming might be useful: -- 'count the number of ways' -- 'optimal strategy' -- 'maximize sum, path, value, or profit' -- 'knapsack style constraints' +- count the number of ways +- optimal strategy +- maximize sum, path, value, or profit +- knapsack style constraints diff --git a/src/leetcode/dynamic_programming/arithmetic-slices-ii-subsequences.ts b/src/leetcode/dynamic_programming/arithmetic-slices-ii-subsequences.ts deleted file mode 100644 index a086b44..0000000 --- a/src/leetcode/dynamic_programming/arithmetic-slices-ii-subsequences.ts +++ /dev/null @@ -1,78 +0,0 @@ -// DIFFICULTY: HARD -// -// Given an integer array nums, return the number of all the arithmetic subsequences of nums. -// -// A sequence of numbers is called arithmetic if it consists of at least three elements and if the difference between -// any two consecutive elements is the same. -// -// - For example, [1, 3, 5, 7, 9], [7, 7, 7, 7], and [3, -1, -5, -9] are arithmetic sequences. -// - For example, [1, 1, 2, 5, 7] is not an arithmetic sequence. -// -// A subsequence of an array is a sequence that can be formed by removing some elements (possibly none) of the array. -// -// - For example, [2,5,10] is a subsequence of [1,2,1,2,4,1,5,10]. -// -// The test cases are generated so that the answer fits in 32-bit integer. -// -// See {@link https://leetcode.com/problems/arithmetic-slices-ii-subsequence/} -export { numberOfArithmeticSlices }; - -// SOLUTION: -// -// To solve this we can use a dynamic programming approach to build up the number of subsequences at each index, for -// each subsequence difference. -// -// Note that we can have [2, 4, 6, 8], and there are subsequences [2, 4, 6, 8] (the entire array) and [2, 4, 6], -// which is part of the array. Also note that [7, 7, 7, 7] has a total of 16 subsequences because every subsequence -// is arithmetic (they can overlap). -function numberOfArithmeticSlices(nums: number[]): number { - // An arithmetic subsequence must have at least length 3, so if we have fewer, then there are no subsequences. - if (nums.length < 3) { - return 0; - } - - let result = 0; - - // A list of dictionaries keeping track of subsequences ending at index i. The keys in the dictionary represent - // the differences in the arithmetic subsequence, and the values represent the count with that difference. - type Difference = number; - type Count = number; - const subsequences: Array> = []; - for (let i = 0; i < nums.length; i++) { - subsequences.push(new Map()); - } - - // Since we are considering pairs of elements, start at 1 to ensure that there is at least another element before - // it. - for (let i = 1; i < nums.length; i++) { - // At each index j, every number at index i (prior to j) could be participating in a subsequence where the - // diference of the arithmetic subsequence is nums[j] - nums[i]. So for each j, examine every previous element - // at i to see if it contributes to some subsequence. - for (let j = 0; j < i; j++) { - // Calculate the difference between the two numbers, and note that the difference can be negative (and the - // numbers in the array can be negative as well). This means the subsequence can be increasing or decreasing; - // it does not matter. - // - // That is, we want to find and extend any arithmetic subsequences that nums[j] might be a part of with the - // difference of of nums[i] - nums[j]. - const difference = nums[i] - nums[j]; - - // Get count of existing subsequences at j with difference (nums[j - 1] === nums[j] - difference is true). - // We can extend these subsequences by adding nums[i]. - const cj = subsequences[j].get(difference) || 0; - - // Get count of existing subsequences at i with difference (nums[i - 1] === nums[i] - difference is true). - const ci = subsequences[i].get(difference) || 0; - - // Update the count of subsequences at j, which is the previous count at i, plus the previous count at j, and - // then 1 more to account for adding j to the subsequence (by going from nums[i] to nums[j]). - subsequences[i].set(difference, ci + cj + 1); - - // Add to the running total, excluding the pair (nums[i], nums[j]). If we just have this pair, it will NOT - // contribute to a 3 element arithmetic subsequence (because there are only 2 elements). - result += cj; - } - } - - return result; -} diff --git a/src/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences.py b/src/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences.py new file mode 100644 index 0000000..218d72b --- /dev/null +++ b/src/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences.py @@ -0,0 +1,78 @@ +# DIFFICULTY: HARD +# +# Given an integer array nums, return the number of all the arithmetic subsequences of nums. +# +# A sequence of numbers is called arithmetic if it consists of at least three elements and if the difference between +# any two consecutive elements is the same. +# +# - For example, [1, 3, 5, 7, 9], [7, 7, 7, 7], and [3, -1, -5, -9] are arithmetic sequences. +# - For example, [1, 1, 2, 5, 7] is not an arithmetic sequence. +# +# A subsequence of an array is a sequence that can be formed by removing some elements (possibly none) of the array. +# +# - For example, [2,5,10] is a subsequence of [1,2,1,2,4,1,5,10]. +# +# The test cases are generated so that the answer fits in 32-bit integer. +# +# See https://leetcode.com/problems/arithmetic-slices-ii-subsequence +from collections import defaultdict + + +class Solution: + def numberOfArithmeticSlices(self, xs: list[int]) -> int: + """ + SOLUTION: + + To solve this we can use a dynamic programming approach to build up the number of subsequences at each index, + for each subsequence difference. + + Note that we can have [2, 4, 6, 8], and there are subsequences [2, 4, 6, 8] (the entire array) and [2, 4, 6], + which is part of the array. Also note that [7, 7, 7, 7] has a total of 16 subsequences because every + subsequence is arithmetic (they can overlap). + """ + if len(xs) < 3: + # An arithmetic subsequence must have at least length 3, so if we have fewer, then there are no + # subsequences. + return 0 + + result = 0 + + # A list of dictionaries keeping track of subsequences ending at index i. The keys in the dictionary represent + # the differences in the arithmetic subsequence, and the values represent the count with that difference. + # + # Each dict is a map of difference -> count. + subsequences: list[dict[int, int]] = [] + for i in range(len(xs)): + subsequences.append(defaultdict(int)) + + # Since we are considering pairs of elements, start at 1 to ensure that there is at least another element before + # it. + for i in range(len(xs)): + # At each index j, every number at index i (prior to j) could be participating in a subsequence where the + # difference of the arithmetic subsequence is xs[j] - xs[i]. So for each j, examine every previous element + # at i to see if it contributes to some subsequence. + for j in range(i): + # Calculate the difference between the two numbers, and note that the difference can be negative (and + # the numbers in the array can be negative as well). This means the subsequence can be increasing or + # decreasing; it does not matter. + # + # That is, we want to find and extend any arithmetic subsequences that xs[j] might be a part of with the + # difference of of xs[i] - xs[j]. + difference = xs[i] - xs[j] + + # Get count of existing subsequences at j with difference (xs[j - 1] === xs[j] - difference is true). + # We can extend these subsequences by adding xs[i]. + cj = subsequences[j][difference] + + # Get count of existing subsequences at i with difference (xs[i - 1] === xs[i] - difference is true). + ci = subsequences[i][difference] + + # Update the count of subsequences at j, which is the previous count at i, plus the previous count at j, + # and then 1 more to account for adding j to the subsequence (by going from xs[i] to xs[j]). + subsequences[i][difference] = ci + cj + 1 + + # Add to the running total, excluding the pair (xs[i], xs[j]). If we just have this pair, it will NOT + # contribute to a 3 element arithmetic subsequence (because there are only 2 elements). + result += cj + + return result diff --git a/test/leetcode/dynamic_programming/arithmetic-slices-ii-subsequences.test.ts b/test/leetcode/dynamic_programming/arithmetic-slices-ii-subsequences.test.ts deleted file mode 100644 index 9ee58f6..0000000 --- a/test/leetcode/dynamic_programming/arithmetic-slices-ii-subsequences.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { numberOfArithmeticSlices } from '../../src/dynamic-programming/arithmetic-slices-ii-subsequences'; - -describe('arithmetic slices ii - subsequence', () => { - test('arithmetic slices ii - subsequence - test case 1', async () => { - const nums = [2, 4, 6, 8, 10]; - - expect(numberOfArithmeticSlices(nums)).toBe(7); - }); - - test('arithmetic slices ii - subsequence - test case 2', async () => { - const nums = [7, 7, 7, 7, 7]; - - expect(numberOfArithmeticSlices(nums)).toBe(16); - }); -}); diff --git a/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py b/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py new file mode 100644 index 0000000..5b148f9 --- /dev/null +++ b/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py @@ -0,0 +1,12 @@ +from leetcode.dynamic_programming.arithmetic_slices_ii_subsequences import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.numberOfArithmeticSlices([2, 4, 6, 8, 10]) == 7 + + +def test_case_2(): + assert soln.numberOfArithmeticSlices([7, 7, 7, 7, 7]) == 16 From 9d34291d57fed28b0078d4ec8926a7f0c977169d Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 10 Mar 2025 20:56:24 -0700 Subject: [PATCH 019/158] update docs --- .../arithmetic_slices_ii_subsequences.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences.py b/src/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences.py index 218d72b..ba75fa4 100644 --- a/src/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences.py +++ b/src/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences.py @@ -29,6 +29,13 @@ def numberOfArithmeticSlices(self, xs: list[int]) -> int: Note that we can have [2, 4, 6, 8], and there are subsequences [2, 4, 6, 8] (the entire array) and [2, 4, 6], which is part of the array. Also note that [7, 7, 7, 7] has a total of 16 subsequences because every subsequence is arithmetic (they can overlap). + + COMPLEXITY: + + Time complexity is O(n^2) due to iterating over all pairs (i, j). + + Space complexity is O(n^2) due to storing a dictionary for each index (and each dictionary having at most O(n) + entries). """ if len(xs) < 3: # An arithmetic subsequence must have at least length 3, so if we have fewer, then there are no From bcbbf32bd5cff57cb9004f7322548e91d1a50af8 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 10 Mar 2025 21:03:59 -0700 Subject: [PATCH 020/158] max subarray sum --- .../dynamic_programming/max-subarray.ts | 51 ------------------- .../dynamic_programming/max_subarray.py | 51 +++++++++++++++++++ .../dynamic_programming/max-subarray.test.ts | 7 --- .../dynamic_programming/max_subarray_test.py | 8 +++ 4 files changed, 59 insertions(+), 58 deletions(-) delete mode 100644 src/leetcode/dynamic_programming/max-subarray.ts create mode 100644 src/leetcode/dynamic_programming/max_subarray.py delete mode 100644 test/leetcode/dynamic_programming/max-subarray.test.ts create mode 100644 test/leetcode/dynamic_programming/max_subarray_test.py diff --git a/src/leetcode/dynamic_programming/max-subarray.ts b/src/leetcode/dynamic_programming/max-subarray.ts deleted file mode 100644 index 374347d..0000000 --- a/src/leetcode/dynamic_programming/max-subarray.ts +++ /dev/null @@ -1,51 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an integer array nums, find the subarray with the largest sum, and return its sum. -// -// See {@link https://leetcode.com/problems/maximum-subarray/} -export { maxSubArray }; - -// SOLUTION: -// -// Kadane's algorithm finds a solution in O(n) with O(1) extra space. -// -// The idea is to consider subarrays starting from the 0th, 1st, 2nd, etc element. However, it is not necessary to -// consider the subarrays starting from 0, then 1, then 2, etc. -// -// It is enough to note that if all elements are positive, you want the entire array. But if they are not all -// positive, then you want to only take elements until it's "not worth it". To determine what is "not worth it", -// consider that if you start from the 0th index and continue to take elements, but the subarray sum becomes negative -// when it was positive before. Then we know to abandon the entire subarray from 0 to that element. -// -// Generalizing, suppose we have a current subarray sum, and we add the next element. If adding that next element -// causes the new current sum to be *less* than just that element by itself, we should abandon that subarray and -// start over. -function maxSubArray(xs: number[]): number { - if (xs.length === 0) { - return 0; - } - - if (xs.length === 1) { - return xs[0]; - } - - // Start calculating the sum of the current subarray at i = 0. - let current = xs[0]; - let max = xs[0]; - for (let i = 1; i < xs.length; i++) { - const x = xs[i]; - - if (current + x > x) { - // If adding x makes our current sum larger, we'll take x and add it to our potential max subarray sum. - current += x; - } else { - // However, if adding x makes our current sum smaller, abandon the current subarray that was from where we - // started to i. We'll start counting the sum from a new subarray, starting at i instead. - current = x; - } - - max = Math.max(current, max); - } - - return max; -} diff --git a/src/leetcode/dynamic_programming/max_subarray.py b/src/leetcode/dynamic_programming/max_subarray.py new file mode 100644 index 0000000..f1db977 --- /dev/null +++ b/src/leetcode/dynamic_programming/max_subarray.py @@ -0,0 +1,51 @@ +# DIFFICULTY: MEDIUM +# +# Given an integer array nums, find the subarray with the largest sum, and return its sum. +# +# See https://leetcode.com/problems/maximum-subarray +class Solution: + def maxSubArray(self, xs: list[int]) -> int: + """ + SOLUTION: + + Kadane's algorithm finds a solution in O(n) with O(1) extra space. + + The idea is to consider subarrays starting from the 0th, 1st, 2nd, etc element. However, it is not necessary to + consider the subarrays starting from 0, then 1, then 2, etc. + + It is enough to note that if all elements are positive, you want the entire array. But if they are not all + positive, then you want to only take elements until it's "not worth it". To determine what is "not worth it", + consider that if you start from the 0th index and continue to take elements, but the subarray sum becomes + negative when it was positive before. Then we know to abandon the entire subarray from 0 to that element. + + Generalizing, suppose we have a current subarray sum, and we add the next element. If adding that next element + causes the new current sum to be LESS than just that element by itself, we should abandon that subarray and + start over. + + COMPLEXITY: + + Time complexity is O(n). + + Space complexity is O(1). + """ + if len(xs) == 0: + return 0 + + if len(xs) == 1: + return xs[0] + + # Start calculating the sum of the current subarray at i = 0. + current = xs[0] + max_value = xs[0] + for x in xs[1:]: + # If adding x makes our current sum larger, we'll take x and add it to our potential max subarray sum. + if current + x > x: + current += x + # However, if adding x makes our current sum smaller, abandon the current subarray that was from where we + # started to i. We'll start counting the sum from a new subarray, starting at i instead. + else: + current = x + + max_value = max(current, max_value) + + return max_value diff --git a/test/leetcode/dynamic_programming/max-subarray.test.ts b/test/leetcode/dynamic_programming/max-subarray.test.ts deleted file mode 100644 index aaef8b2..0000000 --- a/test/leetcode/dynamic_programming/max-subarray.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { maxSubArray } from '../../src/dynamic-programming/max-subarray'; - -describe('maximum subarray', () => { - test('maximum subarray - test case 1', async () => { - expect(maxSubArray([-2, 1, -3, 4, -1, 2, 1, -5, 4])).toBe(6); - }); -}); diff --git a/test/leetcode/dynamic_programming/max_subarray_test.py b/test/leetcode/dynamic_programming/max_subarray_test.py new file mode 100644 index 0000000..802a692 --- /dev/null +++ b/test/leetcode/dynamic_programming/max_subarray_test.py @@ -0,0 +1,8 @@ +from leetcode.dynamic_programming.max_subarray import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.maxSubArray([-2, 1, -3, 4, -1, 2, 1, -5, 4]) == 6 From ea0f1411b288581908d9e8c20ef182ea284274de Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 10 Mar 2025 21:23:27 -0700 Subject: [PATCH 021/158] adds word break i --- .../dynamic_programming/word-break-i.ts | 56 ------------------- .../dynamic_programming/word_break_i.py | 51 +++++++++++++++++ .../dynamic_programming/word-break-i.test.ts | 10 ---- .../dynamic_programming/word_break_i_test.py | 8 +++ 4 files changed, 59 insertions(+), 66 deletions(-) delete mode 100644 src/leetcode/dynamic_programming/word-break-i.ts create mode 100644 src/leetcode/dynamic_programming/word_break_i.py delete mode 100644 test/leetcode/dynamic_programming/word-break-i.test.ts create mode 100644 test/leetcode/dynamic_programming/word_break_i_test.py diff --git a/src/leetcode/dynamic_programming/word-break-i.ts b/src/leetcode/dynamic_programming/word-break-i.ts deleted file mode 100644 index 34643fc..0000000 --- a/src/leetcode/dynamic_programming/word-break-i.ts +++ /dev/null @@ -1,56 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given a string s and a dictionary of strings wordDict, return true if s can be segmented into a space-separated -// sequence of one or more dictionary words. -// -// Note that the same word in the dictionary may be reused multiple times in the segmentation. -// -// See {@link https://leetcode.com/problems/word-break/} -export { wordBreak }; - -// SOLUTION: -// -// Note that this problem is significantly easier than word break ii because we don't need to do any backtracking to -// generate all of the possible sentences. -// -// Here we can just incrementally build up a solution using dynamic programming. -// -// We loop through the string and seeing if we can build a word out of (0, i). If we can, we'd like to continue on -// by seeing if we can build out a word from (0, j) where j > i. We'll keep going until we've exhausted the string -// or if we've run out of words to try. -// -// However, we'll have to try every possible starting word. Then every possible secondary word, and so on. That's -// why dynamic programming will be useful here. -function wordBreak(s: string, wordDict: string[]): boolean { - const set = new Set(wordDict); - - // We'll use a map to indicate if a slice (0,i) can make a sentence. This can just be an array, because the first - // index is always going to be 0. However, we make a map here because it's a little clearer what we are doing. - const map = new Map(); - - // The slice (0,0) is always going to form a sentence; the base sentence. - map.set('[0,0)', true); - - // We want to see if we can successfully parse a sentence from [0,s.length), but to do so, we'll incrementally build - // up whether or not we can parse a sentence from [0,j) for all j <= s.length. - // - // Yes we want j <= s.length because slice is exclusive on the last index. - for (let i = 0; i < s.length; i++) { - // We want j <= s.length here because slice is exclusive on the last index [i,j). - for (let j = i + 1; j <= s.length; j++) { - const word = s.slice(i, j); - if (!set.has(word)) { - continue; - } - - // If the slice (0,i) formed a sentence, then (0,j) must form a sentence because (i,j) is a word. For example, - // if (0,i) was 'hello' and (i,j) was 'there', then (0,j) slice of 'hellothere' is a sentence - if (map.has(`[0,${i})`)) { - map.set(`[0,${j})`, true); - } - } - } - - // If we've formed a sentence using all the characters, we've got a winner here. - return map.has(`[0,${s.length})`); -} diff --git a/src/leetcode/dynamic_programming/word_break_i.py b/src/leetcode/dynamic_programming/word_break_i.py new file mode 100644 index 0000000..88c5299 --- /dev/null +++ b/src/leetcode/dynamic_programming/word_break_i.py @@ -0,0 +1,51 @@ +# DIFFICULTY: MEDIUM +# +# Given a string s and a dictionary of strings wordDict, return true if s can be segmented into a space-separated +# sequence of one or more dictionary words. +# +# Note that the same word in the dictionary may be reused multiple times in the segmentation. +# +# See https://leetcode.com/problems/word-break +class Solution: + def wordBreak(self, s: str, wordDict: list[str]) -> bool: + """ + SOLUTION: + + Note that this problem is significantly easier than word break ii because we don't need to do any backtracking + to generate all of the possible sentences. + + Here we can just incrementally build up a solution using dynamic programming. + + We loop through the string and seeing if we can build a word out of (0, i). If we can, we'd like to continue on + by seeing if we can build out a word from (0, j) where j > i. We'll keep going until we've exhausted the string + or if we've run out of words to try. + + However, we'll have to try every possible starting word. Then every possible secondary word, and so on. That's + why dynamic programming will be useful here. + """ + uniques = set(wordDict) + + # We'll use a map to indicate if a slice (0,i) can make a sentence. This can just be an array, because the + # first index is always going to be 0. However, we make a map here because it's a little clearer what we are + # doing. + map: dict[tuple[int, int], bool] = {} + + # The slice [0,0) is always going to form a sentence; the base sentence. + map[(0, 0)] = True + + # We want to see if we can successfully parse a sentence from [0,s.length), but to do so, we'll incrementally + # build up whether or not we can parse a sentence from [0,j) for all j <= len(s). + for i in range(len(s)): + # Yes we want j <= len(s) because slice is exclusive on the last index [i, j). + for j in range(i + 1, len(s) + 1): + word = s[i:j] + if word not in uniques: + continue + + # If the slice (0,i) formed a sentence, then (0,j) must form a sentence because (i,j) is a word. For + # example, if (0,i) was 'hello' and (i,j) was 'there', then (0,j) slice of 'hellothere' is a sentence. + if (0, i) in map: + map[(0, j)] = True + + # If we've formed a sentence using all the characters, we've got a winner here. + return (0, len(s)) in map diff --git a/test/leetcode/dynamic_programming/word-break-i.test.ts b/test/leetcode/dynamic_programming/word-break-i.test.ts deleted file mode 100644 index c5898b3..0000000 --- a/test/leetcode/dynamic_programming/word-break-i.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { wordBreak } from '../../src/dynamic-programming/word-break-i'; - -describe('word break i', () => { - test('word break i - test case 1', () => { - const s = 'leetcode'; - const wordDict = ['leet', 'code']; - - expect(wordBreak(s, wordDict)).toBe(true); - }); -}); diff --git a/test/leetcode/dynamic_programming/word_break_i_test.py b/test/leetcode/dynamic_programming/word_break_i_test.py new file mode 100644 index 0000000..dc88c05 --- /dev/null +++ b/test/leetcode/dynamic_programming/word_break_i_test.py @@ -0,0 +1,8 @@ +from leetcode.dynamic_programming.word_break_i import Solution + + +soln = Solution() + + +def test_case_i(): + assert soln.wordBreak("leetcode", ["leet", "code"]) From dc2e3e26f8fcfbc30e0191d0b1fa0e021219c79f Mon Sep 17 00:00:00 2001 From: Min Huang Date: Tue, 11 Mar 2025 18:31:53 -0700 Subject: [PATCH 022/158] add to make degrees even --- .../graph/add-edges-to-make-degrees-even.ts | 131 ------------------ .../graph/add_edges_to_make_degrees_even.py | 113 +++++++++++++++ .../add-edges-to-make-degrees-even.test.ts | 50 ------- .../add_edges_to_make_degrees_even_test.py | 32 +++++ 4 files changed, 145 insertions(+), 181 deletions(-) delete mode 100644 src/leetcode/graph/add-edges-to-make-degrees-even.ts create mode 100644 src/leetcode/graph/add_edges_to_make_degrees_even.py delete mode 100644 test/leetcode/graph/add-edges-to-make-degrees-even.test.ts create mode 100644 test/leetcode/graph/add_edges_to_make_degrees_even_test.py diff --git a/src/leetcode/graph/add-edges-to-make-degrees-even.ts b/src/leetcode/graph/add-edges-to-make-degrees-even.ts deleted file mode 100644 index 5e58715..0000000 --- a/src/leetcode/graph/add-edges-to-make-degrees-even.ts +++ /dev/null @@ -1,131 +0,0 @@ -// DIFFICULTY: HARD -// -// There is an undirected graph consisting of n nodes numbered from 1 to n. You are given the integer n and a 2D array -// edges where edges[i] = [ai, bi] indicates that there is an edge between nodes ai and bi. The graph can be disconnected. -// -// You can add at most two additional edges (possibly none) to this graph so that there are no repeated edges and no self-loops. -// -// Return true if it is possible to make the degree of each node in the graph even, otherwise return false. -// -// The degree of a node is the number of edges connected to it. -// -// See {@link https://leetcode.com/problems/add-edges-to-make-degrees-of-all-nodes-even/} -export { isPossible }; - -// SOLUTION: -// -// When you add an edge between two nodes, you change the degree of those two nodes. We don't need to modify edges -// for any even degree nodes; they are already good. Instead we want to find nodes with an odd degree. -// -// If we join any two odd degree nodes with an edge, both of them will become even degree nodes. Since we can only -// add at most two edges, this can only be possible if we have 0, 2, or 4 nodes with an odd degree. -function isPossible(n: number, edges: number[][]): boolean { - type Node = number; - type Degree = number; - - // Let's define an easy way to keep track of edges, because later, we may want to add edges to our graph without - // adding duplicate edges. Typescript sets can store arrays, [1, 2] is the same as [2, 1] in an undirected graph - // so it's easier to just have a canonical string representation of an edge stored instead. - const set = new Set(); - function hasEdge(edge: number[]) { - const [a, b] = edge; - const value = a < b ? `${a},${b}` : `${b},${a}`; - return set.has(value); - } - - function addEdge(edge: number[]) { - const [a, b] = edge; - const value = a < b ? `${a},${b}` : `${b},${a}`; - set.add(value); - } - - // Find all the nodes with an odd degree. - const degreeMap = new Map(); - for (const edge of edges) { - const [u, v] = edge; - if (!degreeMap.has(u)) { - degreeMap.set(u, 0); - } - if (!degreeMap.has(v)) { - degreeMap.set(v, 0); - } - - degreeMap.set(u, degreeMap.get(u)! + 1); - degreeMap.set(v, degreeMap.get(v)! + 1); - addEdge([u, v]); - } - - // Note that the nodes are numbered 1 through n inclusive. - const oddNodes = []; - for (let i = 1; i <= n; i++) { - const degrees = degreeMap.get(i) ?? 0; - if (degrees % 2 === 1) { - oddNodes.push(i); - } - } - - // Only 0, 2, or 4 odd degree node graphs can be have edges added to create a valid all even degree graph. - if (oddNodes.length === 0) { - return true; - } - - // If we have 2 odd degree nodes, we have to check if we can create an all even degree graph by doing one of the - // following: - // - // 1. Add an edge between the two nodes, as long as it's not a duplicate. - // 2. Add two edges connecting the nodes, with another node in between. - if (oddNodes.length === 2) { - const [a, b] = oddNodes; - // Check if we can create an all even degree graph by connecting the nodes directly. - if (!hasEdge([a, b])) { - return true; - } - - // Check if we can create an all even degree graph by connecting the nodes through a different node. Note that - // the nodes are numbered 1 through n inclusive. - for (let c = 1; c <= n; c++) { - if (c === a || c === b) { - continue; - } - - if (!hasEdge([a, c]) && !hasEdge([c, b])) { - return true; - } - } - - return false; - } - - // If we have 4 odd degree nodes, we can check if we can create an all even degree graph by connecting the nodes - // together. The nodes must be connected directly though; we can't connect them through some unrelated node as that - // requires two edges already, leaving the other two odd degree nodes stranded. - if (oddNodes.length === 4) { - const [a, b, c, d] = oddNodes; - - // With 4 nodes, we can only connect them in 3 distinct ways. - const pairs = [ - [ - [a, b], - [c, d] - ], - [ - [a, c], - [b, d] - ], - [ - [a, d], - [c, b] - ] - ]; - for (const pair of pairs) { - const [first, second] = pair; - if (!hasEdge(first) && !hasEdge(second)) { - return true; - } - } - - return false; - } - - return false; -} diff --git a/src/leetcode/graph/add_edges_to_make_degrees_even.py b/src/leetcode/graph/add_edges_to_make_degrees_even.py new file mode 100644 index 0000000..afd044c --- /dev/null +++ b/src/leetcode/graph/add_edges_to_make_degrees_even.py @@ -0,0 +1,113 @@ +# DIFFICULTY: HARD +# +# There is an undirected graph consisting of n nodes numbered from 1 to n. You are given the integer n and a 2D array +# edges where edges[i] = [ai, bi] indicates that there is an edge between nodes ai and bi. The graph can be disconnected. +# +# You can add at most two additional edges (possibly none) to this graph so that there are no repeated edges and no self-loops. +# +# Return true if it is possible to make the degree of each node in the graph even, otherwise return false. +# +# The degree of a node is the number of edges connected to it. +# +# See https://leetcode.com/problems/add-edges-to-make-degrees-of-all-nodes-even +from collections import defaultdict + + +class Solution: + def isPossible(self, n: int, edges: list[list[int]]) -> bool: + """ + SOLUTION: + + When you add an edge between two nodes, you change the degree of those two nodes. We don't need to modify edges + for any even degree nodes; they are already good. Instead we want to find nodes with an odd degree. + + If we join any two odd degree nodes with an edge, both of them will become even degree nodes. Since we can only + add at most two edges, this can only be possible if we have 0, 2, or 4 nodes with an odd degree. + + COMPLEXITY: + + Time complexity is O(m + n). Each node and edge must be examined. + + Space complexity is O(m + n). We are storing a set of edges and a map of each node to degree. + """ + edgeSet: set[tuple[int, int]] = set() + + def normalizeEdge(edge: tuple[int, int]) -> tuple[int, int]: + (a, b) = edge + return (a, b) if a < b else (b, a) + + def hasEdge(edge: tuple[int, int]) -> bool: + e = normalizeEdge(edge) + return e in edgeSet + + def addEdge(edge: tuple[int, int]) -> None: + e = normalizeEdge(edge) + edgeSet.add(e) + + # Create a map of node -> degrees. + degreeMap: dict[int, int] = defaultdict(int) + for edge in edges: + [a, b] = edge + if a not in degreeMap: + degreeMap[a] = 0 + if b not in degreeMap: + degreeMap[b] = 0 + + degreeMap[a] += 1 + degreeMap[b] += 1 + addEdge((a, b)) + + # Find all odd degree nodes. Note that nodes are number 1 through n inclusive. + oddNodes: list[int] = [] + for i in range(1, n + 1): + degrees = degreeMap[i] + if degrees % 2 == 1: + oddNodes.append(i) + + # Only 0, 2, or 4 odd degree node graphs can have edges added to create a valid all even degree path. So if we + # have 0 odd degree nodes, we are golden. + if len(oddNodes) == 0: + return True + + # If we have 2 odd degree nodes, we have to check if we can create an all even degree graph by doing one of the + # following: + # + # 1. Add an edge between the two nodes, as long as it's not a duplicate. + # 2. Add two edges connecting the nodes, with another node in between. + if len(oddNodes) == 2: + [a, b] = oddNodes + # Check if we can create an all even degree graph by connecting the nodes directly. + if not hasEdge((a, b)): + return True + + # Check if we can create an all even degree graph by connecting the nodes through a different node. Note + # that the nodes are numbered 1 through n inclusive. + for c in range(1, n + 1): + if c == a or c == b: + continue + + if not hasEdge((a, c)) and not hasEdge((b, c)): + return True + + # Otherwise, it can't be done. + return False + + # If we have 4 odd degree nodes, we can check if we can create an all even degree graph by connecting the nodes + # together. The nodes must be connected directly though; we can't connect them through some unrelated node as + # that requires two edges already, leaving the other two odd degree nodes stranded. + if len(oddNodes) == 4: + [a, b, c, d] = oddNodes + + # With 4 nodes, we can only connect them in 3 distinct ways: + pairs = [((a, b), (c, d)), ((a, c), (b, d)), ((a, d), (c, b))] + for pair in pairs: + (e1, e2) = pair + if not hasEdge(e1) and not hasEdge(e2): + return True + + # Otherwise, it can't be done. + return False + + # If we do NOT have 0, 2, or 4 odd degree nodes, there are no combination of 2 edges we can add to make this + # possible. + return False diff --git a/test/leetcode/graph/add-edges-to-make-degrees-even.test.ts b/test/leetcode/graph/add-edges-to-make-degrees-even.test.ts deleted file mode 100644 index 661a0f4..0000000 --- a/test/leetcode/graph/add-edges-to-make-degrees-even.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { isPossible } from '../../src/graph/add-edges-to-make-degrees-even'; - -describe('add edges to make degrees of all nodes even', () => { - test('add edges to make degrees even - test case 1', async () => { - const n = 5; - const edges = [ - [1, 2], - [2, 3], - [3, 4], - [4, 2], - [1, 4], - [2, 5] - ]; - - expect(isPossible(n, edges)).toBe(true); - }); - - test('add edges to make degrees even - test case 2', async () => { - const n = 4; - const edges = [ - [1, 2], - [3, 4] - ]; - - expect(isPossible(n, edges)).toBe(true); - }); - - test('add edges to make degrees even - test case 3', async () => { - const n = 4; - const edges = [ - [1, 2], - [1, 3], - [1, 4] - ]; - - expect(isPossible(n, edges)).toBe(false); - }); - - test('add edges to make degrees even - test case 4', async () => { - const n = 4; - const edges = [ - [1, 2], - [2, 3], - [2, 4], - [3, 4] - ]; - - expect(isPossible(n, edges)).toBe(false); - }); -}); diff --git a/test/leetcode/graph/add_edges_to_make_degrees_even_test.py b/test/leetcode/graph/add_edges_to_make_degrees_even_test.py new file mode 100644 index 0000000..35e7754 --- /dev/null +++ b/test/leetcode/graph/add_edges_to_make_degrees_even_test.py @@ -0,0 +1,32 @@ +from leetcode.graph.add_edges_to_make_degrees_even import Solution + + +soln = Solution() + + +def test_case_1(): + n = 5 + edges = [[1, 2], [2, 3], [3, 4], [4, 2], [1, 4], [2, 5]] + + assert soln.isPossible(n, edges) + + +def test_case_2(): + n = 4 + edges = [[1, 2], [3, 4]] + + assert soln.isPossible(n, edges) + + +def test_case_3(): + n = 4 + edges = [[1, 2], [1, 3], [1, 4]] + + assert not soln.isPossible(n, edges) + + +def test_case_4(): + n = 4 + edges = [[1, 2], [2, 3], [2, 4], [3, 4]] + + assert not soln.isPossible(n, edges) From b3a1e309face0e0285cdcdbf7b0dcb3431a889b1 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 12 Mar 2025 14:16:42 -0700 Subject: [PATCH 023/158] switch linters --- poetry.lock | 221 +++++++++--------- pyproject.toml | 10 +- scripts.py | 7 +- .../arithmetic_slices_ii_subsequences.py | 2 +- src/leetcode/graph/bus_routes.py | 81 +++++++ .../heap/furthest_building_you_can_reach.py | 2 +- .../heap/k_closest_points_to_origin.py | 2 +- src/leetcode/heap/merge_k_sorted_lists.py | 3 +- ...best_time_to_buy_and_sell_stock_ii_test.py | 1 - .../leetcode/array/design_hit_counter_test.py | 1 - test/leetcode/array/group_anagrams_test.py | 1 - .../longest_consecutive_sequence_test.py | 3 +- .../array/max_chunks_to_make_sorted_test.py | 1 - test/leetcode/array/maximum_swap_test.py | 1 - .../leetcode/array/merge_sorted_array_test.py | 1 - test/leetcode/array/pascals_triangle_test.py | 1 - .../snapshots/snap_group_anagrams_test.py | 1 - .../snapshots/snap_pascals_triangle_test.py | 1 - .../array/top_k_frequent_elements_test.py | 1 - test/leetcode/array/two_sum_test.py | 1 - ...osition_of_element_in_sorted_array_test.py | 4 +- .../binary_search/find_peak_element_test.py | 1 - ...smallest_divisor_given_a_threshold_test.py | 4 +- .../kth_missing_positive_number_test.py | 1 - ...mallest_element_in_a_sorted_matrix_test.py | 4 +- .../longest_increasing_subsequence_test.py | 1 - .../median_of_two_sorted_arrays_test.py | 1 - .../search_in_rotated_sorted_array_test.py | 1 - .../arithmetic_slices_ii_subsequences_test.py | 4 +- .../dynamic_programming/max_subarray_test.py | 1 - .../dynamic_programming/word_break_i_test.py | 1 - .../add_edges_to_make_degrees_even_test.py | 1 - test/leetcode/graph/bus-routes.test.ts | 15 +- test/leetcode/graph/bus_routes_test.py | 14 ++ .../bus_routes.json} | 0 .../furthest_building_you_can_reach_test.py | 1 - .../heap/k_closest_points_to_origin_test.py | 1 - .../leetcode/heap/kth_largest_element_test.py | 1 - test/leetcode/heap/max_stack_test.py | 1 - .../heap/minimize_deviation_in_array_test.py | 1 - .../number_of_orders_in_the_backlog_test.py | 1 - test/leetcode/heap/task_scheduler_test.py | 1 - .../recursion/generate_parentheses_test.py | 1 - .../snap_generate_parentheses_test.py | 1 - .../leetcode/string/valid_parentheses_test.py | 1 - .../string/valid_word_abbreviation_test.py | 1 - .../two_pointers/bag_of_tokens_test.py | 1 - 47 files changed, 229 insertions(+), 178 deletions(-) create mode 100644 src/leetcode/graph/bus_routes.py create mode 100644 test/leetcode/graph/bus_routes_test.py rename test/leetcode/graph/{__data__/bus-routes.test.json => data/bus_routes.json} (100%) diff --git a/poetry.lock b/poetry.lock index d8ab112..d531723 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,34 +1,35 @@ -# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. [[package]] name = "black" -version = "24.10.0" +version = "25.1.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ - {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, - {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, - {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, - {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, - {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, - {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, - {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, - {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, - {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, - {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, - {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, - {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, - {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, - {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, - {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, - {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, - {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, - {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, - {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, - {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, - {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, - {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, + {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"}, + {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"}, + {file = "black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7"}, + {file = "black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9"}, + {file = "black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0"}, + {file = "black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299"}, + {file = "black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096"}, + {file = "black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2"}, + {file = "black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b"}, + {file = "black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc"}, + {file = "black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f"}, + {file = "black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba"}, + {file = "black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f"}, + {file = "black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3"}, + {file = "black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171"}, + {file = "black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18"}, + {file = "black-25.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1ee0a0c330f7b5130ce0caed9936a904793576ef4d2b98c40835d6a65afa6a0"}, + {file = "black-25.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3df5f1bf91d36002b0a75389ca8663510cf0531cca8aa5c1ef695b46d98655f"}, + {file = "black-25.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9e6827d563a2c820772b32ce8a42828dc6790f095f441beef18f96aa6f8294e"}, + {file = "black-25.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:bacabb307dca5ebaf9c118d2d2f6903da0d62c9faa82bd21a33eecc319559355"}, + {file = "black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717"}, + {file = "black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666"}, ] [package.dependencies] @@ -50,6 +51,7 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -64,6 +66,8 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +markers = "platform_system == \"Windows\" or sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -75,6 +79,7 @@ version = "0.3.0" description = "A fast native implementation of diff algorithm with a pure python fallback" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "fastdiff-0.3.0-py2.py3-none-any.whl", hash = "sha256:ca5f61f6ddf5a1564ddfd98132ad28e7abe4a88a638a8b014a2214f71e5918ec"}, {file = "fastdiff-0.3.0.tar.gz", hash = "sha256:4dfa09c47832a8c040acda3f1f55fc0ab4d666f0e14e6951e6da78d59acd945a"}, @@ -84,95 +89,33 @@ files = [ wasmer = ">=1.0.0" wasmer-compiler-cranelift = ">=1.0.0" -[[package]] -name = "flake8" -version = "7.1.2" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.8.1" -files = [ - {file = "flake8-7.1.2-py2.py3-none-any.whl", hash = "sha256:1cbc62e65536f65e6d754dfe6f1bada7f5cf392d6f5db3c2b85892466c3e7c1a"}, - {file = "flake8-7.1.2.tar.gz", hash = "sha256:c586ffd0b41540951ae41af572e6790dbd49fc12b3aa2541685d253d9bd504bd"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.12.0,<2.13.0" -pyflakes = ">=3.2.0,<3.3.0" - [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] [[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" +name = "isort" +version = "6.0.1" +description = "A Python utility / library to sort Python imports." optional = false -python-versions = ">=3.6" +python-versions = ">=3.9.0" +groups = ["dev"] files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, + {file = "isort-6.0.1-py3-none-any.whl", hash = "sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615"}, + {file = "isort-6.0.1.tar.gz", hash = "sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450"}, ] -[[package]] -name = "mypy" -version = "1.15.0" -description = "Optional static typing for Python" -optional = false -python-versions = ">=3.9" -files = [ - {file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"}, - {file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3"}, - {file = "mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b"}, - {file = "mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c"}, - {file = "mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f"}, - {file = "mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee"}, - {file = "mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e"}, - {file = "mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036"}, - {file = "mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357"}, - {file = "mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b"}, - {file = "mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2"}, - {file = "mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980"}, - {file = "mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e"}, - {file = "mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43"}, -] - -[package.dependencies] -mypy_extensions = ">=1.0.0" -typing_extensions = ">=4.6.0" - [package.extras] -dmypy = ["psutil (>=4.0)"] -faster-cache = ["orjson"] -install-types = ["pip"] -mypyc = ["setuptools (>=50)"] -reports = ["lxml"] +colors = ["colorama"] +plugins = ["setuptools"] [[package]] name = "mypy-extensions" @@ -180,17 +123,31 @@ version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" +groups = ["dev"] files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "nodeenv" +version = "1.9.1" +description = "Node.js virtual environment builder" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +files = [ + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, +] + [[package]] name = "packaging" version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -202,6 +159,7 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -213,6 +171,7 @@ version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, @@ -229,6 +188,7 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -239,26 +199,25 @@ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] [[package]] -name = "pycodestyle" -version = "2.12.1" -description = "Python style guide checker" +name = "pyright" +version = "1.1.396" +description = "Command line wrapper for pyright" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" +groups = ["dev"] files = [ - {file = "pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3"}, - {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"}, + {file = "pyright-1.1.396-py3-none-any.whl", hash = "sha256:c635e473095b9138c471abccca22b9fedbe63858e0b40d4fc4b67da041891844"}, + {file = "pyright-1.1.396.tar.gz", hash = "sha256:142901f5908f5a0895be3d3befcc18bedcdb8cc1798deecaec86ef7233a29b03"}, ] -[[package]] -name = "pyflakes" -version = "3.2.0" -description = "passive checker of Python programs" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, - {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, -] +[package.dependencies] +nodeenv = ">=1.6.0" +typing-extensions = ">=4.1" + +[package.extras] +all = ["nodejs-wheel-binaries", "twine (>=3.4.1)"] +dev = ["twine (>=3.4.1)"] +nodejs = ["nodejs-wheel-binaries"] [[package]] name = "pytest" @@ -266,6 +225,7 @@ version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, @@ -280,12 +240,41 @@ pluggy = ">=1.5,<2" [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +[[package]] +name = "ruff" +version = "0.9.10" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "ruff-0.9.10-py3-none-linux_armv6l.whl", hash = "sha256:eb4d25532cfd9fe461acc83498361ec2e2252795b4f40b17e80692814329e42d"}, + {file = "ruff-0.9.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:188a6638dab1aa9bb6228a7302387b2c9954e455fb25d6b4470cb0641d16759d"}, + {file = "ruff-0.9.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5284dcac6b9dbc2fcb71fdfc26a217b2ca4ede6ccd57476f52a587451ebe450d"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47678f39fa2a3da62724851107f438c8229a3470f533894b5568a39b40029c0c"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99713a6e2766b7a17147b309e8c915b32b07a25c9efd12ada79f217c9c778b3e"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524ee184d92f7c7304aa568e2db20f50c32d1d0caa235d8ddf10497566ea1a12"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:df92aeac30af821f9acf819fc01b4afc3dfb829d2782884f8739fb52a8119a16"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de42e4edc296f520bb84954eb992a07a0ec5a02fecb834498415908469854a52"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d257f95b65806104b6b1ffca0ea53f4ef98454036df65b1eda3693534813ecd1"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60dec7201c0b10d6d11be00e8f2dbb6f40ef1828ee75ed739923799513db24c"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d838b60007da7a39c046fcdd317293d10b845001f38bcb55ba766c3875b01e43"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ccaf903108b899beb8e09a63ffae5869057ab649c1e9231c05ae354ebc62066c"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f9567d135265d46e59d62dc60c0bfad10e9a6822e231f5b24032dba5a55be6b5"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5f202f0d93738c28a89f8ed9eaba01b7be339e5d8d642c994347eaa81c6d75b8"}, + {file = "ruff-0.9.10-py3-none-win32.whl", hash = "sha256:bfb834e87c916521ce46b1788fbb8484966e5113c02df216680102e9eb960029"}, + {file = "ruff-0.9.10-py3-none-win_amd64.whl", hash = "sha256:f2160eeef3031bf4b17df74e307d4c5fb689a6f3a26a2de3f7ef4044e3c484f1"}, + {file = "ruff-0.9.10-py3-none-win_arm64.whl", hash = "sha256:5fd804c0327a5e5ea26615550e706942f348b197d5475ff34c19733aee4b2e69"}, + {file = "ruff-0.9.10.tar.gz", hash = "sha256:9bacb735d7bada9cfb0f2c227d3658fc443d90a727b47f206fb33f52f3c0eac7"}, +] + [[package]] name = "snapshottest" version = "1.0.0a1" description = "Snapshot testing for pytest, unittest, Django, and Nose" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "snapshottest-1.0.0a1-py3-none-any.whl", hash = "sha256:fff0e1da3825c32d001018777c3b56d1eae1c850fc5b3418618da3d7f2cd152f"}, {file = "snapshottest-1.0.0a1.tar.gz", hash = "sha256:6ef848ee4d6621baff79df6a36bb1da4d7eddf5013dc6b9ca9c361bc42c605b9"}, @@ -306,6 +295,7 @@ version = "2.5.0" description = "ANSI color formatting for output in terminal" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "termcolor-2.5.0-py3-none-any.whl", hash = "sha256:37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8"}, {file = "termcolor-2.5.0.tar.gz", hash = "sha256:998d8d27da6d48442e8e1f016119076b690d962507531df4890fcd2db2ef8a6f"}, @@ -320,6 +310,7 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -331,6 +322,7 @@ version = "1.1.0" description = "Python extension to run WebAssembly binaries" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "wasmer-1.1.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:c2af4b907ae2dabcac41e316e811d5937c93adf1f8b05c5d49427f8ce0f37630"}, {file = "wasmer-1.1.0-cp310-cp310-manylinux_2_24_x86_64.whl", hash = "sha256:ab1ae980021e5ec0bf0c6cdd3b979b1d15a5f3eb2b8a32da8dcb1156e4a1e484"}, @@ -354,6 +346,7 @@ version = "1.1.0" description = "The Cranelift compiler for the `wasmer` package (to compile WebAssembly module)" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "wasmer_compiler_cranelift-1.1.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:9869910179f39696a020edc5689f7759257ac1cce569a7a0fcf340c59788baad"}, {file = "wasmer_compiler_cranelift-1.1.0-cp310-cp310-manylinux_2_24_x86_64.whl", hash = "sha256:405546ee864ac158a4107f374dfbb1c8d6cfb189829bdcd13050143a4bd98f28"}, @@ -372,6 +365,6 @@ files = [ ] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = "^3.12" -content-hash = "7fb2fde931b96232aad1885131c2deb21feb64ab9edd93a1d3baf1895bf9c472" +content-hash = "6d78596a07070c6661815dfd55e31e1689c462bed43c1d16278c757e01b6e346" diff --git a/pyproject.toml b/pyproject.toml index 86f5acb..76dc0e3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,18 +16,16 @@ test = "scripts:run_tests" python = "^3.12" [tool.poetry.group.dev.dependencies] -black = "^24.10.0" -flake8 = "^7.1.1" -mypy = "^1.13.0" +black = "^25.1.0" +isort = "^6.0.1" +pyright = "^1.1.396" pytest = "^8.3.4" +ruff = "^0.9.10" snapshottest = "^1.0.0a0" [tool.black] line-length = 120 -[tool.mypy] -ignore_missing_imports = true - [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" diff --git a/scripts.py b/scripts.py index 0965ad6..44dda45 100644 --- a/scripts.py +++ b/scripts.py @@ -12,12 +12,13 @@ def run_all(): def run_lint(): - run_cmd("poetry run flake8 src test") - run_cmd("poetry run mypy src test") + run_cmd("poetry run ruff check . --fix") + run_cmd("poetry run pyright .") def run_format(): - run_cmd("poetry run black src test") + run_cmd("poetry run black .") + run_cmd("poetry run isort .") def run_tests(): diff --git a/src/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences.py b/src/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences.py index ba75fa4..81c3c74 100644 --- a/src/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences.py +++ b/src/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences.py @@ -49,7 +49,7 @@ def numberOfArithmeticSlices(self, xs: list[int]) -> int: # # Each dict is a map of difference -> count. subsequences: list[dict[int, int]] = [] - for i in range(len(xs)): + for _ in range(len(xs)): subsequences.append(defaultdict(int)) # Since we are considering pairs of elements, start at 1 to ensure that there is at least another element before diff --git a/src/leetcode/graph/bus_routes.py b/src/leetcode/graph/bus_routes.py new file mode 100644 index 0000000..06c0bef --- /dev/null +++ b/src/leetcode/graph/bus_routes.py @@ -0,0 +1,81 @@ +# DIFFICULTY: HARD +# +# You are given an array routes representing bus routes where routes[i] is a bus route that the ith bus repeats +# forever. +# +# For example, if routes[0] = [1, 5, 7], this means that the 0th bus travels in the sequence: +# +# 1 -> 5 -> 7 -> 1 -> 5 -> 7 -> 1 -> ... +# +# ...forever. +# +# You will start at the bus stop source (You are not on any bus initially), and you want to go to the bus stop target. +# You can travel between bus stops by buses only. +# +# Return the least number of buses you must take to travel from source to target. Return -1 if it is not possible. +# +# See https://leetcode.com/problems/bus-routes +class Solution: + def numBusesToDestination(self, routes: list[list[int]], source: int, target: int) -> int: + """ + SOLUTION: + + This seems to be just a BFS problem. The fact that buses repeat forever doesn't seem to be relevant because + there is no cost associated with waiting at a bus stop until the bus arrives; the only thing we are minimizing + is the number of buses we want to take. + + Building a graph and then running BFS on it naively seems to exceed execution time so we'll have to do something + more clever and dirty. + + COMPLEXITY: + + Time complexity is O(n * m) where n is the number of bus routes, and m is the number of stops per route. + + Space complexity is O(n * m). + """ + if source == target: + return 0 + + # Once no more stops are added, break out of the loop. + added = True + + # The number of buses we've taken so far. + result = 0 + + # Run a BFS from the source stop. We'll iterate through the routes and add stops to the visited set until we + # can no longer do so. If we haven't found our target stop by then we'll return -1. + visited: set[int] = set() + visited.add(source) + + while added: + # These are all routes that are reachable from currently visited stops, represented as individual stops in + # the route. + stops: list[int] = [] + added = False + result += 1 + + for i, route in enumerate(routes): + for stop in route: + if stop in visited: + # Collect all stops in the route associated with the current stop. + stops.extend(route) + + # We just added all the stops in the route, and we are about to mark them all as visited. Clear + # the routes array at i because we do not have to visit them again, so there's no need to + # iterate through them anymore. + routes[i] = [] + + # We've found new stops to visit; break out of the (inner) loop and move onto the next route. + # We'll note that stops have been visited, so we should continue the outer while loop. + added = True + break + + # Mark all the stops we've seen in this iteration as visited. + for stop in stops: + visited.add(stop) + + # Immediately return if we have ended up visiting our target destination. + if target in visited: + return result + + return -1 diff --git a/src/leetcode/heap/furthest_building_you_can_reach.py b/src/leetcode/heap/furthest_building_you_can_reach.py index 173d91e..9afd1a7 100644 --- a/src/leetcode/heap/furthest_building_you_can_reach.py +++ b/src/leetcode/heap/furthest_building_you_can_reach.py @@ -14,7 +14,7 @@ # Return the furthest building index (0-indexed) you can reach if you use the given ladders and bricks optimally. # # See https://leetcode.com/problems/furthest-building-you-can-reach -from heapq import heappush, heappop +from heapq import heappop, heappush class Solution: diff --git a/src/leetcode/heap/k_closest_points_to_origin.py b/src/leetcode/heap/k_closest_points_to_origin.py index 2f6d247..53b9c45 100644 --- a/src/leetcode/heap/k_closest_points_to_origin.py +++ b/src/leetcode/heap/k_closest_points_to_origin.py @@ -8,7 +8,7 @@ # You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in). # # See https://leetcode.com/problems/k-closest-points-to-origin -from heapq import heappush, heappop +from heapq import heappop, heappush from math import sqrt diff --git a/src/leetcode/heap/merge_k_sorted_lists.py b/src/leetcode/heap/merge_k_sorted_lists.py index 8fb975b..b804c15 100644 --- a/src/leetcode/heap/merge_k_sorted_lists.py +++ b/src/leetcode/heap/merge_k_sorted_lists.py @@ -5,9 +5,10 @@ # Merge all the linked-lists into one sorted linked-list and return it. # # See https://leetcode.com/problems/merge-k-sorted-lists -from leetcode.heap.common.list_node import ListNode from heapq import heappop, heappush +from leetcode.heap.common.list_node import ListNode + class Solution: def mergeKLists(self, lists: list[ListNode | None]) -> ListNode | None: diff --git a/test/leetcode/array/best_time_to_buy_and_sell_stock_ii_test.py b/test/leetcode/array/best_time_to_buy_and_sell_stock_ii_test.py index 583dcbe..efe553f 100644 --- a/test/leetcode/array/best_time_to_buy_and_sell_stock_ii_test.py +++ b/test/leetcode/array/best_time_to_buy_and_sell_stock_ii_test.py @@ -1,6 +1,5 @@ from leetcode.heap.furthest_building_you_can_reach import Solution - soln = Solution() diff --git a/test/leetcode/array/design_hit_counter_test.py b/test/leetcode/array/design_hit_counter_test.py index 7a5aa5a..d33b870 100644 --- a/test/leetcode/array/design_hit_counter_test.py +++ b/test/leetcode/array/design_hit_counter_test.py @@ -1,6 +1,5 @@ from leetcode.array.design_hit_counter import HitCounter - hc = HitCounter() diff --git a/test/leetcode/array/group_anagrams_test.py b/test/leetcode/array/group_anagrams_test.py index 09b620f..864d213 100644 --- a/test/leetcode/array/group_anagrams_test.py +++ b/test/leetcode/array/group_anagrams_test.py @@ -1,6 +1,5 @@ from leetcode.array.group_anagrams import Solution - soln = Solution() diff --git a/test/leetcode/array/longest_consecutive_sequence_test.py b/test/leetcode/array/longest_consecutive_sequence_test.py index 3ff4114..9e9ea56 100644 --- a/test/leetcode/array/longest_consecutive_sequence_test.py +++ b/test/leetcode/array/longest_consecutive_sequence_test.py @@ -1,8 +1,7 @@ from leetcode.array.longest_consecutive_sequence import Solution - soln = Solution() def test_case_1(): - soln.longestConsecutive([100, 4, 200, 1, 3, 2]) == 4 + assert soln.longestConsecutive([100, 4, 200, 1, 3, 2]) == 4 diff --git a/test/leetcode/array/max_chunks_to_make_sorted_test.py b/test/leetcode/array/max_chunks_to_make_sorted_test.py index 250e4a9..c9946f8 100644 --- a/test/leetcode/array/max_chunks_to_make_sorted_test.py +++ b/test/leetcode/array/max_chunks_to_make_sorted_test.py @@ -1,6 +1,5 @@ from leetcode.array.max_chunks_to_make_sorted import Solution - soln = Solution() diff --git a/test/leetcode/array/maximum_swap_test.py b/test/leetcode/array/maximum_swap_test.py index c0702bb..fbf21c4 100644 --- a/test/leetcode/array/maximum_swap_test.py +++ b/test/leetcode/array/maximum_swap_test.py @@ -1,6 +1,5 @@ from leetcode.array.maximum_swap import Solution - soln = Solution() diff --git a/test/leetcode/array/merge_sorted_array_test.py b/test/leetcode/array/merge_sorted_array_test.py index 7b3ec07..101a3bb 100644 --- a/test/leetcode/array/merge_sorted_array_test.py +++ b/test/leetcode/array/merge_sorted_array_test.py @@ -1,6 +1,5 @@ from leetcode.array.merge_sorted_array import Solution - soln = Solution() diff --git a/test/leetcode/array/pascals_triangle_test.py b/test/leetcode/array/pascals_triangle_test.py index c02e794..5ffc5f0 100644 --- a/test/leetcode/array/pascals_triangle_test.py +++ b/test/leetcode/array/pascals_triangle_test.py @@ -1,6 +1,5 @@ from leetcode.array.pascals_triangle import Solution - soln = Solution() diff --git a/test/leetcode/array/snapshots/snap_group_anagrams_test.py b/test/leetcode/array/snapshots/snap_group_anagrams_test.py index fc448f3..c4bd240 100644 --- a/test/leetcode/array/snapshots/snap_group_anagrams_test.py +++ b/test/leetcode/array/snapshots/snap_group_anagrams_test.py @@ -4,7 +4,6 @@ from snapshottest import Snapshot - snapshots = Snapshot() snapshots["test_case_1 1"] = [["eat", "tea", "ate"], ["tan", "nat"], ["bat"]] diff --git a/test/leetcode/array/snapshots/snap_pascals_triangle_test.py b/test/leetcode/array/snapshots/snap_pascals_triangle_test.py index 6b06dfa..6005bc4 100644 --- a/test/leetcode/array/snapshots/snap_pascals_triangle_test.py +++ b/test/leetcode/array/snapshots/snap_pascals_triangle_test.py @@ -4,7 +4,6 @@ from snapshottest import Snapshot - snapshots = Snapshot() snapshots["test_case_1 1"] = [[1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1]] diff --git a/test/leetcode/array/top_k_frequent_elements_test.py b/test/leetcode/array/top_k_frequent_elements_test.py index b6833a6..0432845 100644 --- a/test/leetcode/array/top_k_frequent_elements_test.py +++ b/test/leetcode/array/top_k_frequent_elements_test.py @@ -1,6 +1,5 @@ from leetcode.array.top_k_frequent_elements import Solution - soln = Solution() diff --git a/test/leetcode/array/two_sum_test.py b/test/leetcode/array/two_sum_test.py index 5f08564..8484510 100644 --- a/test/leetcode/array/two_sum_test.py +++ b/test/leetcode/array/two_sum_test.py @@ -1,6 +1,5 @@ from leetcode.array.two_sum import Solution - soln = Solution() diff --git a/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py b/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py index 5981bca..a277c55 100644 --- a/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py +++ b/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py @@ -1,5 +1,5 @@ -from leetcode.binary_search.find_first_and_last_position_of_element_in_sorted_array import Solution - +from leetcode.binary_search.find_first_and_last_position_of_element_in_sorted_array import \ + Solution soln = Solution() diff --git a/test/leetcode/binary_search/find_peak_element_test.py b/test/leetcode/binary_search/find_peak_element_test.py index 42f5995..d26a1c2 100644 --- a/test/leetcode/binary_search/find_peak_element_test.py +++ b/test/leetcode/binary_search/find_peak_element_test.py @@ -1,6 +1,5 @@ from leetcode.binary_search.find_peak_element import Solution - soln = Solution() diff --git a/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py b/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py index 6c757bc..21b9be9 100644 --- a/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py +++ b/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py @@ -1,5 +1,5 @@ -from leetcode.binary_search.find_the_smallest_divisor_given_a_threshold import Solution - +from leetcode.binary_search.find_the_smallest_divisor_given_a_threshold import \ + Solution soln = Solution() diff --git a/test/leetcode/binary_search/kth_missing_positive_number_test.py b/test/leetcode/binary_search/kth_missing_positive_number_test.py index 22a4a63..0ad1f30 100644 --- a/test/leetcode/binary_search/kth_missing_positive_number_test.py +++ b/test/leetcode/binary_search/kth_missing_positive_number_test.py @@ -1,6 +1,5 @@ from leetcode.binary_search.kth_missing_positive_number import Solution - soln = Solution() diff --git a/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py b/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py index bce2baf..d9c3d74 100644 --- a/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py +++ b/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py @@ -1,5 +1,5 @@ -from leetcode.binary_search.kth_smallest_element_in_a_sorted_matrix import Solution - +from leetcode.binary_search.kth_smallest_element_in_a_sorted_matrix import \ + Solution soln = Solution() diff --git a/test/leetcode/binary_search/longest_increasing_subsequence_test.py b/test/leetcode/binary_search/longest_increasing_subsequence_test.py index d73e756..29d22b7 100644 --- a/test/leetcode/binary_search/longest_increasing_subsequence_test.py +++ b/test/leetcode/binary_search/longest_increasing_subsequence_test.py @@ -1,6 +1,5 @@ from leetcode.binary_search.longest_increasing_subsequence import Solution - soln = Solution() diff --git a/test/leetcode/binary_search/median_of_two_sorted_arrays_test.py b/test/leetcode/binary_search/median_of_two_sorted_arrays_test.py index 3b87ffc..c18640b 100644 --- a/test/leetcode/binary_search/median_of_two_sorted_arrays_test.py +++ b/test/leetcode/binary_search/median_of_two_sorted_arrays_test.py @@ -1,6 +1,5 @@ from leetcode.binary_search.median_of_two_sorted_arrays import Solution - soln = Solution() diff --git a/test/leetcode/binary_search/search_in_rotated_sorted_array_test.py b/test/leetcode/binary_search/search_in_rotated_sorted_array_test.py index 957acac..64a938d 100644 --- a/test/leetcode/binary_search/search_in_rotated_sorted_array_test.py +++ b/test/leetcode/binary_search/search_in_rotated_sorted_array_test.py @@ -1,6 +1,5 @@ from leetcode.binary_search.search_in_rotated_sorted_array import Solution - soln = Solution() diff --git a/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py b/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py index 5b148f9..95891d9 100644 --- a/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py +++ b/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py @@ -1,5 +1,5 @@ -from leetcode.dynamic_programming.arithmetic_slices_ii_subsequences import Solution - +from leetcode.dynamic_programming.arithmetic_slices_ii_subsequences import \ + Solution soln = Solution() diff --git a/test/leetcode/dynamic_programming/max_subarray_test.py b/test/leetcode/dynamic_programming/max_subarray_test.py index 802a692..cda2670 100644 --- a/test/leetcode/dynamic_programming/max_subarray_test.py +++ b/test/leetcode/dynamic_programming/max_subarray_test.py @@ -1,6 +1,5 @@ from leetcode.dynamic_programming.max_subarray import Solution - soln = Solution() diff --git a/test/leetcode/dynamic_programming/word_break_i_test.py b/test/leetcode/dynamic_programming/word_break_i_test.py index dc88c05..96898d8 100644 --- a/test/leetcode/dynamic_programming/word_break_i_test.py +++ b/test/leetcode/dynamic_programming/word_break_i_test.py @@ -1,6 +1,5 @@ from leetcode.dynamic_programming.word_break_i import Solution - soln = Solution() diff --git a/test/leetcode/graph/add_edges_to_make_degrees_even_test.py b/test/leetcode/graph/add_edges_to_make_degrees_even_test.py index 35e7754..6b13f7f 100644 --- a/test/leetcode/graph/add_edges_to_make_degrees_even_test.py +++ b/test/leetcode/graph/add_edges_to_make_degrees_even_test.py @@ -1,6 +1,5 @@ from leetcode.graph.add_edges_to_make_degrees_even import Solution - soln = Solution() diff --git a/test/leetcode/graph/bus-routes.test.ts b/test/leetcode/graph/bus-routes.test.ts index 38e5323..c7cd695 100644 --- a/test/leetcode/graph/bus-routes.test.ts +++ b/test/leetcode/graph/bus-routes.test.ts @@ -1,18 +1,13 @@ -import fs from 'fs'; -import path from 'path'; -import { numBusesToDestination } from '../../src/graph/bus-routes'; +from + + +soln = Solution() -describe('bus routes', () => { - test('bus routes - test case 2', async () => { const routes = [[7, 12], [4, 5, 15], [6], [15, 19], [9, 12, 13]]; expect(numBusesToDestination(routes, 15, 12)).toBe(-1); - }); - test('bus routes - test case 3', async () => { const data = fs.readFileSync(path.join(__dirname, '__data__', 'bus-routes.test.json')).toString(); const routes = JSON.parse(data); - expect(numBusesToDestination(routes, 0, 100000)).toBe(-1); - }); -}); + expect(numBusesToDestination(routes, 0, 100000)).toBe(-1); \ No newline at end of file diff --git a/test/leetcode/graph/bus_routes_test.py b/test/leetcode/graph/bus_routes_test.py new file mode 100644 index 0000000..fca8b9f --- /dev/null +++ b/test/leetcode/graph/bus_routes_test.py @@ -0,0 +1,14 @@ +# from pathlib import Path +from leetcode.graph.bus_routes import Solution + +soln = Solution() + + +def test_case_1(): + assert soln.numBusesToDestination([[7, 12], [4, 5, 15], [6], [15, 19], [9, 12, 13]], 15, 12) == -1 + + +# def test_case_2(): +# with Path("leetcode/data/bus_routes.json").open("r") as file: +# routes = json.loads(file) +# assert soln.numBusesToDestination(routes, 0, 100000) == -1 diff --git a/test/leetcode/graph/__data__/bus-routes.test.json b/test/leetcode/graph/data/bus_routes.json similarity index 100% rename from test/leetcode/graph/__data__/bus-routes.test.json rename to test/leetcode/graph/data/bus_routes.json diff --git a/test/leetcode/heap/furthest_building_you_can_reach_test.py b/test/leetcode/heap/furthest_building_you_can_reach_test.py index 583dcbe..efe553f 100644 --- a/test/leetcode/heap/furthest_building_you_can_reach_test.py +++ b/test/leetcode/heap/furthest_building_you_can_reach_test.py @@ -1,6 +1,5 @@ from leetcode.heap.furthest_building_you_can_reach import Solution - soln = Solution() diff --git a/test/leetcode/heap/k_closest_points_to_origin_test.py b/test/leetcode/heap/k_closest_points_to_origin_test.py index 814811b..942e393 100644 --- a/test/leetcode/heap/k_closest_points_to_origin_test.py +++ b/test/leetcode/heap/k_closest_points_to_origin_test.py @@ -1,6 +1,5 @@ from leetcode.heap.k_closest_points_to_origin import Solution - soln = Solution() diff --git a/test/leetcode/heap/kth_largest_element_test.py b/test/leetcode/heap/kth_largest_element_test.py index b199e7a..65ed2b0 100644 --- a/test/leetcode/heap/kth_largest_element_test.py +++ b/test/leetcode/heap/kth_largest_element_test.py @@ -1,6 +1,5 @@ from leetcode.heap.kth_largest_element import Solution - soln = Solution() diff --git a/test/leetcode/heap/max_stack_test.py b/test/leetcode/heap/max_stack_test.py index 66851ad..c05bae6 100644 --- a/test/leetcode/heap/max_stack_test.py +++ b/test/leetcode/heap/max_stack_test.py @@ -1,6 +1,5 @@ from leetcode.heap.max_stack import MaxStack - stack = MaxStack() diff --git a/test/leetcode/heap/minimize_deviation_in_array_test.py b/test/leetcode/heap/minimize_deviation_in_array_test.py index 773aa81..e081def 100644 --- a/test/leetcode/heap/minimize_deviation_in_array_test.py +++ b/test/leetcode/heap/minimize_deviation_in_array_test.py @@ -1,6 +1,5 @@ from leetcode.heap.minimize_deviation_in_array import Solution - soln = Solution() diff --git a/test/leetcode/heap/number_of_orders_in_the_backlog_test.py b/test/leetcode/heap/number_of_orders_in_the_backlog_test.py index 1200c2a..4ae1acf 100644 --- a/test/leetcode/heap/number_of_orders_in_the_backlog_test.py +++ b/test/leetcode/heap/number_of_orders_in_the_backlog_test.py @@ -1,6 +1,5 @@ from leetcode.heap.number_of_orders_in_the_backlog import Solution - soln = Solution() diff --git a/test/leetcode/heap/task_scheduler_test.py b/test/leetcode/heap/task_scheduler_test.py index b84cdd7..df349a8 100644 --- a/test/leetcode/heap/task_scheduler_test.py +++ b/test/leetcode/heap/task_scheduler_test.py @@ -1,6 +1,5 @@ from leetcode.heap.task_scheduler import Solution - soln = Solution() diff --git a/test/leetcode/recursion/generate_parentheses_test.py b/test/leetcode/recursion/generate_parentheses_test.py index f646b85..b299c9d 100644 --- a/test/leetcode/recursion/generate_parentheses_test.py +++ b/test/leetcode/recursion/generate_parentheses_test.py @@ -1,6 +1,5 @@ from leetcode.recursion.generate_parentheses import Solution - soln = Solution() diff --git a/test/leetcode/recursion/snapshots/snap_generate_parentheses_test.py b/test/leetcode/recursion/snapshots/snap_generate_parentheses_test.py index 9e27885..0c30c61 100644 --- a/test/leetcode/recursion/snapshots/snap_generate_parentheses_test.py +++ b/test/leetcode/recursion/snapshots/snap_generate_parentheses_test.py @@ -4,7 +4,6 @@ from snapshottest import Snapshot - snapshots = Snapshot() snapshots["test_case_2 1"] = ["((()))", "(()())", "(())()", "()(())", "()()()"] diff --git a/test/leetcode/string/valid_parentheses_test.py b/test/leetcode/string/valid_parentheses_test.py index 50b9ecd..643e75e 100644 --- a/test/leetcode/string/valid_parentheses_test.py +++ b/test/leetcode/string/valid_parentheses_test.py @@ -1,6 +1,5 @@ from leetcode.stack.valid_parentheses import Solution - soln = Solution() diff --git a/test/leetcode/string/valid_word_abbreviation_test.py b/test/leetcode/string/valid_word_abbreviation_test.py index 9705506..844c816 100644 --- a/test/leetcode/string/valid_word_abbreviation_test.py +++ b/test/leetcode/string/valid_word_abbreviation_test.py @@ -1,6 +1,5 @@ from leetcode.string.valid_word_abbreviation import Solution - soln = Solution() diff --git a/test/leetcode/two_pointers/bag_of_tokens_test.py b/test/leetcode/two_pointers/bag_of_tokens_test.py index a9026e0..1c8d861 100644 --- a/test/leetcode/two_pointers/bag_of_tokens_test.py +++ b/test/leetcode/two_pointers/bag_of_tokens_test.py @@ -1,6 +1,5 @@ from leetcode.two_pointers.bag_of_tokens import Solution - soln = Solution() From bd3364a8cb796de195f2667b8c210e7c2b558fab Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 12 Mar 2025 14:44:05 -0700 Subject: [PATCH 024/158] update linting --- poetry.lock | 4 +-- pyproject.toml | 5 +++- .../binary_search/find_peak_element.py | 1 + src/leetcode/heap/max_stack.py | 27 ++++++++++++++----- ...osition_of_element_in_sorted_array_test.py | 5 ++-- ...smallest_divisor_given_a_threshold_test.py | 3 +-- ...mallest_element_in_a_sorted_matrix_test.py | 3 +-- .../arithmetic_slices_ii_subsequences_test.py | 3 +-- 8 files changed, 33 insertions(+), 18 deletions(-) diff --git a/poetry.lock b/poetry.lock index d531723..3bf3bc1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -366,5 +366,5 @@ files = [ [metadata] lock-version = "2.1" -python-versions = "^3.12" -content-hash = "6d78596a07070c6661815dfd55e31e1689c462bed43c1d16278c757e01b6e346" +python-versions = "^3.13" +content-hash = "a23e446554f21e15c723222e3bc375121ed1ce083e24ccbe625f13333687b9e7" diff --git a/pyproject.toml b/pyproject.toml index 76dc0e3..594de25 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ lint = "scripts:run_lint" test = "scripts:run_tests" [tool.poetry.dependencies] -python = "^3.12" +python = "^3.13" [tool.poetry.group.dev.dependencies] black = "^25.1.0" @@ -26,6 +26,9 @@ snapshottest = "^1.0.0a0" [tool.black] line-length = 120 +[tool.ruff] +lint.select = ["F401", "I"] + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" diff --git a/src/leetcode/binary_search/find_peak_element.py b/src/leetcode/binary_search/find_peak_element.py index 3866e9f..19f8a2a 100644 --- a/src/leetcode/binary_search/find_peak_element.py +++ b/src/leetcode/binary_search/find_peak_element.py @@ -43,6 +43,7 @@ def findPeakElement(self, xs: list[int]) -> int: # Use insertion point binary search to find the peak. left = 0 right = len(xs) + mid = -1 while left < right: mid = (left + right) // 2 diff --git a/src/leetcode/heap/max_stack.py b/src/leetcode/heap/max_stack.py index 18d6ce2..efaaa60 100644 --- a/src/leetcode/heap/max_stack.py +++ b/src/leetcode/heap/max_stack.py @@ -19,11 +19,15 @@ class StackNode: + # The problem itself states that the smallest value we can get is -10^7. This definition avoids having to declare + # the type of a node key/value to be float for float("-inf"). + MIN_INT_VALUE = -(10**7) - 1 + def __init__(self, key: int, value: int): self.key = key self.value = value - self.previous: "StackNode" | None = None - self.next: "StackNode" | None = None + self.previous: "StackNode | None" = None + self.next: "StackNode | None" = None class MaxStack: @@ -54,8 +58,8 @@ def __init__(self): # want to support dupes, so have the heap order by value first, then the key. self.max_heap = [] # Setup sentinel values for the head and tail so we don't have to check for nulls. - self.head = StackNode(float("-inf"), float("-inf")) - self.tail = StackNode(float("-inf"), float("-inf")) + self.head = StackNode(StackNode.MIN_INT_VALUE, StackNode.MIN_INT_VALUE) + self.tail = StackNode(StackNode.MIN_INT_VALUE, StackNode.MIN_INT_VALUE) self.head.next = self.tail self.tail.previous = self.head @@ -75,8 +79,10 @@ def push(self, value: int) -> None: self.__insert_node(a, b, c) def pop(self) -> int: - # Remove the last element of the linked list. node = self.tail.previous + assert node is not None + + # Remove the last element of the linked list. self.__delete_node(node) # We also need to remove this element from the heap, but this will be difficult as we can't remove arbitrary @@ -85,6 +91,7 @@ def pop(self) -> int: return node.value def top(self) -> int: + assert self.tail.previous is not None return self.tail.previous.value def peekMax(self) -> int: @@ -106,7 +113,9 @@ def popMax(self) -> int: self.__delete_node(node) return node.value - def __delete_node(self, b: StackNode): + def __delete_node(self, b: StackNode | None): + assert b is not None + a = b.previous c = b.next @@ -115,7 +124,11 @@ def __delete_node(self, b: StackNode): a.next = c c.previous = a - def __insert_node(self, a: StackNode, b: StackNode, c: StackNode): + def __insert_node(self, a: StackNode | None, b: StackNode, c: StackNode | None): + assert a is not None + assert b is not None + assert c is not None + # Update the pointers around the node. a.next = b c.previous = b diff --git a/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py b/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py index a277c55..f0867f0 100644 --- a/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py +++ b/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py @@ -1,5 +1,6 @@ -from leetcode.binary_search.find_first_and_last_position_of_element_in_sorted_array import \ - Solution +from leetcode.binary_search.find_first_and_last_position_of_element_in_sorted_array import ( + Solution, +) soln = Solution() diff --git a/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py b/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py index 21b9be9..c587ff1 100644 --- a/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py +++ b/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py @@ -1,5 +1,4 @@ -from leetcode.binary_search.find_the_smallest_divisor_given_a_threshold import \ - Solution +from leetcode.binary_search.find_the_smallest_divisor_given_a_threshold import Solution soln = Solution() diff --git a/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py b/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py index d9c3d74..dcadba0 100644 --- a/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py +++ b/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py @@ -1,5 +1,4 @@ -from leetcode.binary_search.kth_smallest_element_in_a_sorted_matrix import \ - Solution +from leetcode.binary_search.kth_smallest_element_in_a_sorted_matrix import Solution soln = Solution() diff --git a/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py b/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py index 95891d9..36fce50 100644 --- a/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py +++ b/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py @@ -1,5 +1,4 @@ -from leetcode.dynamic_programming.arithmetic_slices_ii_subsequences import \ - Solution +from leetcode.dynamic_programming.arithmetic_slices_ii_subsequences import Solution soln = Solution() From dbc3f8fa83549952169d3e1c806d917a1e3d1fdf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 12 Mar 2025 21:44:32 +0000 Subject: [PATCH 025/158] Automatic commit via GitHub Actions --- ...irst_and_last_position_of_element_in_sorted_array_test.py | 5 ++--- .../find_the_smallest_divisor_given_a_threshold_test.py | 3 ++- .../kth_smallest_element_in_a_sorted_matrix_test.py | 3 ++- .../arithmetic_slices_ii_subsequences_test.py | 3 ++- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py b/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py index f0867f0..a277c55 100644 --- a/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py +++ b/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py @@ -1,6 +1,5 @@ -from leetcode.binary_search.find_first_and_last_position_of_element_in_sorted_array import ( - Solution, -) +from leetcode.binary_search.find_first_and_last_position_of_element_in_sorted_array import \ + Solution soln = Solution() diff --git a/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py b/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py index c587ff1..21b9be9 100644 --- a/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py +++ b/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py @@ -1,4 +1,5 @@ -from leetcode.binary_search.find_the_smallest_divisor_given_a_threshold import Solution +from leetcode.binary_search.find_the_smallest_divisor_given_a_threshold import \ + Solution soln = Solution() diff --git a/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py b/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py index dcadba0..d9c3d74 100644 --- a/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py +++ b/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py @@ -1,4 +1,5 @@ -from leetcode.binary_search.kth_smallest_element_in_a_sorted_matrix import Solution +from leetcode.binary_search.kth_smallest_element_in_a_sorted_matrix import \ + Solution soln = Solution() diff --git a/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py b/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py index 36fce50..95891d9 100644 --- a/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py +++ b/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py @@ -1,4 +1,5 @@ -from leetcode.dynamic_programming.arithmetic_slices_ii_subsequences import Solution +from leetcode.dynamic_programming.arithmetic_slices_ii_subsequences import \ + Solution soln = Solution() From a3b2de146be5347065dcd10d468f94613f61dad8 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 12 Mar 2025 15:21:32 -0700 Subject: [PATCH 026/158] only use import --- .flake8 | 2 - pyproject.toml | 8 +- src/leetcode/graph/bus-routes.ts | 85 ------------------- src/leetcode/heap/merge_k_sorted_lists.py | 1 - ...best_time_to_buy_and_sell_stock_ii_test.py | 5 +- .../buildings_with_an_ocean_view_test.py | 5 +- .../leetcode/array/design_hit_counter_test.py | 5 +- .../dot_product_of_two_sparse_vectors_test.py | 6 +- test/leetcode/array/group_anagrams_test.py | 5 +- .../longest_consecutive_sequence_test.py | 5 +- .../array/max_chunks_to_make_sorted_test.py | 5 +- test/leetcode/array/maximum_swap_test.py | 5 +- .../leetcode/array/merge_sorted_array_test.py | 5 +- test/leetcode/array/pascals_triangle_test.py | 5 +- .../snapshots/snap_group_anagrams_test.py | 2 +- .../snapshots/snap_pascals_triangle_test.py | 2 +- .../array/top_k_frequent_elements_test.py | 5 +- test/leetcode/array/two_sum_test.py | 5 +- ...osition_of_element_in_sorted_array_test.py | 7 +- .../binary_search/find_peak_element_test.py | 5 +- ...smallest_divisor_given_a_threshold_test.py | 5 +- .../kth_missing_positive_number_test.py | 5 +- ...mallest_element_in_a_sorted_matrix_test.py | 5 +- .../longest_increasing_subsequence_test.py | 5 +- .../median_of_two_sorted_arrays_test.py | 5 +- .../search_in_rotated_sorted_array_test.py | 5 +- .../arithmetic_slices_ii_subsequences_test.py | 5 +- .../dynamic_programming/max_subarray_test.py | 5 +- .../dynamic_programming/word_break_i_test.py | 5 +- .../add_edges_to_make_degrees_even_test.py | 5 +- test/leetcode/graph/bus-routes.test.ts | 13 --- test/leetcode/graph/bus_routes_test.py | 18 ++-- .../furthest_building_you_can_reach_test.py | 5 +- .../heap/k_closest_points_to_origin_test.py | 5 +- .../leetcode/heap/kth_largest_element_test.py | 5 +- test/leetcode/heap/max_stack_test.py | 5 +- .../heap/minimize_deviation_in_array_test.py | 5 +- .../number_of_orders_in_the_backlog_test.py | 5 +- test/leetcode/heap/task_scheduler_test.py | 5 +- .../recursion/generate-parentheses.test.ts | 21 ----- .../recursion/generate_parentheses_test.py | 5 +- .../snap_generate_parentheses_test.py | 2 +- .../string/valid-word-abbreviation.test.ts | 7 -- .../leetcode/string/valid_parentheses_test.py | 5 +- .../string/valid_word_abbreviation_test.py | 5 +- .../two_pointers/bag-of-tokens.test.ts | 15 ---- .../two_pointers/bag_of_tokens_test.py | 5 +- 47 files changed, 126 insertions(+), 228 deletions(-) delete mode 100644 .flake8 delete mode 100644 src/leetcode/graph/bus-routes.ts delete mode 100644 test/leetcode/graph/bus-routes.test.ts delete mode 100644 test/leetcode/recursion/generate-parentheses.test.ts delete mode 100644 test/leetcode/string/valid-word-abbreviation.test.ts delete mode 100644 test/leetcode/two_pointers/bag-of-tokens.test.ts diff --git a/.flake8 b/.flake8 deleted file mode 100644 index a6f727b..0000000 --- a/.flake8 +++ /dev/null @@ -1,2 +0,0 @@ -[flake8] -ignore = E501, W503 diff --git a/pyproject.toml b/pyproject.toml index 594de25..bcec259 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,8 +26,14 @@ snapshottest = "^1.0.0a0" [tool.black] line-length = 120 +[tool.isort] +profile = "black" +force_sort_within_sections = true +lines_between_sections = 0 +lines_after_imports = 2 + [tool.ruff] -lint.select = ["F401", "I"] +line-length = 120 [build-system] requires = ["poetry-core"] diff --git a/src/leetcode/graph/bus-routes.ts b/src/leetcode/graph/bus-routes.ts deleted file mode 100644 index c4da6c5..0000000 --- a/src/leetcode/graph/bus-routes.ts +++ /dev/null @@ -1,85 +0,0 @@ -// DIFFICULTY: HARD -// -// You are given an array routes representing bus routes where routes[i] is a bus route that the ith bus repeats -// forever. -// -// For example, if routes[0] = [1, 5, 7], this means that the 0th bus travels in the sequence: -// -// 1 -> 5 -> 7 -> 1 -> 5 -> 7 -> 1 -> ... -// -// ...forever. -// -// You will start at the bus stop source (You are not on any bus initially), and you want to go to the bus stop target. -// You can travel between bus stops by buses only. -// -// Return the least number of buses you must take to travel from source to target. Return -1 if it is not possible. -// -// See {@link https://leetcode.com/problems/bus-routes/} -export { numBusesToDestination }; - -// SOLUTION: -// -// This seems to be just a BFS problem. The fact that buses repeat forever doesn't seem to be relevant because there -// is no cost associated with waiting at a bus stop until the bus arrives; the only thing we are minimizing is the -// number of buses we want to take. -// -// Building a graph and then running BFS on it naively seems to exceed execution time so we'll have to do something -// more clever and dirty. -function numBusesToDestination(routes: number[][], source: number, target: number): number { - type Stop = number; - - if (source === target) { - return 0; - } - - // Once no more stops are added, break out of the loop. - let added = true; - - // The number of buses we've taken so far. - let result = 0; - - // Run a BFS from the source stop. We'll iterate through the routes and add stops to the visited set until we can - // no longer do so. If we haven't found our target stop by then we'll return -1. - const visited = new Set(); - visited.add(source); - while (added) { - // These are all routes that are reachable from currently visited stops, represented as individual stops in the - // route. - const temp: Stop[] = []; - added = false; - result++; - - for (let i = 0; i < routes.length; i++) { - const route = routes[i]; - - for (let j = 0; j < route.length; j++) { - const stop = route[j]; - - if (visited.has(stop)) { - // Collect all stops in the route associated with the current stop. - temp.push(...route); - - // We just added all the stops in the route, and we are about to mark them all as visited. Clear the routes - // array at i because we do not have to visit them again, so there's no need to iterate through them - // anymore. - routes[i] = []; - - // We've found new stops to visit; break out of the (inner) loop and move onto the next route. We'll note - // that stops have been visited, so we should continue the outer while loop. - added = true; - break; - } - } - } - - // Mark all the stops we've seen in this iteration as visited. - temp.forEach(stop => visited.add(stop)); - - // Immediately return if we have ended up visiting our target destination. - if (visited.has(target)) { - return result; - } - } - - return -1; -} diff --git a/src/leetcode/heap/merge_k_sorted_lists.py b/src/leetcode/heap/merge_k_sorted_lists.py index b804c15..c696baa 100644 --- a/src/leetcode/heap/merge_k_sorted_lists.py +++ b/src/leetcode/heap/merge_k_sorted_lists.py @@ -6,7 +6,6 @@ # # See https://leetcode.com/problems/merge-k-sorted-lists from heapq import heappop, heappush - from leetcode.heap.common.list_node import ListNode diff --git a/test/leetcode/array/best_time_to_buy_and_sell_stock_ii_test.py b/test/leetcode/array/best_time_to_buy_and_sell_stock_ii_test.py index efe553f..9ddba28 100644 --- a/test/leetcode/array/best_time_to_buy_and_sell_stock_ii_test.py +++ b/test/leetcode/array/best_time_to_buy_and_sell_stock_ii_test.py @@ -1,6 +1,7 @@ -from leetcode.heap.furthest_building_you_can_reach import Solution +import leetcode.heap.furthest_building_you_can_reach as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/array/buildings_with_an_ocean_view_test.py b/test/leetcode/array/buildings_with_an_ocean_view_test.py index 731e918..27c637d 100644 --- a/test/leetcode/array/buildings_with_an_ocean_view_test.py +++ b/test/leetcode/array/buildings_with_an_ocean_view_test.py @@ -1,6 +1,7 @@ -from leetcode.array.buildings_with_an_ocean_view import Solution +import leetcode.array.buildings_with_an_ocean_view as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/array/design_hit_counter_test.py b/test/leetcode/array/design_hit_counter_test.py index d33b870..5eebfec 100644 --- a/test/leetcode/array/design_hit_counter_test.py +++ b/test/leetcode/array/design_hit_counter_test.py @@ -1,6 +1,7 @@ -from leetcode.array.design_hit_counter import HitCounter +import leetcode.array.design_hit_counter as lc -hc = HitCounter() + +hc = lc.HitCounter() def test_case_1(): diff --git a/test/leetcode/array/dot_product_of_two_sparse_vectors_test.py b/test/leetcode/array/dot_product_of_two_sparse_vectors_test.py index 83f8303..5061247 100644 --- a/test/leetcode/array/dot_product_of_two_sparse_vectors_test.py +++ b/test/leetcode/array/dot_product_of_two_sparse_vectors_test.py @@ -1,8 +1,8 @@ -from leetcode.array.dot_product_of_two_sparse_vectors import SparseVector +import leetcode.array.dot_product_of_two_sparse_vectors as lc def test_case_1(): - a = SparseVector([1, 0, 0, 2, 3]) - b = SparseVector([0, 3, 0, 4, 0]) + a = lc.SparseVector([1, 0, 0, 2, 3]) + b = lc.SparseVector([0, 3, 0, 4, 0]) assert a.dotProduct(b) == 8 diff --git a/test/leetcode/array/group_anagrams_test.py b/test/leetcode/array/group_anagrams_test.py index 864d213..04c14f9 100644 --- a/test/leetcode/array/group_anagrams_test.py +++ b/test/leetcode/array/group_anagrams_test.py @@ -1,6 +1,7 @@ -from leetcode.array.group_anagrams import Solution +import leetcode.array.group_anagrams as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(snapshot): diff --git a/test/leetcode/array/longest_consecutive_sequence_test.py b/test/leetcode/array/longest_consecutive_sequence_test.py index 9e9ea56..fef60e7 100644 --- a/test/leetcode/array/longest_consecutive_sequence_test.py +++ b/test/leetcode/array/longest_consecutive_sequence_test.py @@ -1,6 +1,7 @@ -from leetcode.array.longest_consecutive_sequence import Solution +import leetcode.array.longest_consecutive_sequence as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/array/max_chunks_to_make_sorted_test.py b/test/leetcode/array/max_chunks_to_make_sorted_test.py index c9946f8..81d2853 100644 --- a/test/leetcode/array/max_chunks_to_make_sorted_test.py +++ b/test/leetcode/array/max_chunks_to_make_sorted_test.py @@ -1,6 +1,7 @@ -from leetcode.array.max_chunks_to_make_sorted import Solution +import leetcode.array.max_chunks_to_make_sorted as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/array/maximum_swap_test.py b/test/leetcode/array/maximum_swap_test.py index fbf21c4..1ffd1b3 100644 --- a/test/leetcode/array/maximum_swap_test.py +++ b/test/leetcode/array/maximum_swap_test.py @@ -1,6 +1,7 @@ -from leetcode.array.maximum_swap import Solution +import leetcode.array.maximum_swap as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/array/merge_sorted_array_test.py b/test/leetcode/array/merge_sorted_array_test.py index 101a3bb..2113574 100644 --- a/test/leetcode/array/merge_sorted_array_test.py +++ b/test/leetcode/array/merge_sorted_array_test.py @@ -1,6 +1,7 @@ -from leetcode.array.merge_sorted_array import Solution +import leetcode.array.merge_sorted_array as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/array/pascals_triangle_test.py b/test/leetcode/array/pascals_triangle_test.py index 5ffc5f0..4509641 100644 --- a/test/leetcode/array/pascals_triangle_test.py +++ b/test/leetcode/array/pascals_triangle_test.py @@ -1,6 +1,7 @@ -from leetcode.array.pascals_triangle import Solution +import leetcode.array.pascals_triangle as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(snapshot): diff --git a/test/leetcode/array/snapshots/snap_group_anagrams_test.py b/test/leetcode/array/snapshots/snap_group_anagrams_test.py index c4bd240..3136f04 100644 --- a/test/leetcode/array/snapshots/snap_group_anagrams_test.py +++ b/test/leetcode/array/snapshots/snap_group_anagrams_test.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- # snapshottest: v1 - https://goo.gl/zC4yUc from __future__ import unicode_literals - from snapshottest import Snapshot + snapshots = Snapshot() snapshots["test_case_1 1"] = [["eat", "tea", "ate"], ["tan", "nat"], ["bat"]] diff --git a/test/leetcode/array/snapshots/snap_pascals_triangle_test.py b/test/leetcode/array/snapshots/snap_pascals_triangle_test.py index 6005bc4..197865d 100644 --- a/test/leetcode/array/snapshots/snap_pascals_triangle_test.py +++ b/test/leetcode/array/snapshots/snap_pascals_triangle_test.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- # snapshottest: v1 - https://goo.gl/zC4yUc from __future__ import unicode_literals - from snapshottest import Snapshot + snapshots = Snapshot() snapshots["test_case_1 1"] = [[1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1]] diff --git a/test/leetcode/array/top_k_frequent_elements_test.py b/test/leetcode/array/top_k_frequent_elements_test.py index 0432845..33d4da8 100644 --- a/test/leetcode/array/top_k_frequent_elements_test.py +++ b/test/leetcode/array/top_k_frequent_elements_test.py @@ -1,6 +1,7 @@ -from leetcode.array.top_k_frequent_elements import Solution +import leetcode.array.top_k_frequent_elements as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/array/two_sum_test.py b/test/leetcode/array/two_sum_test.py index 8484510..1c5de2f 100644 --- a/test/leetcode/array/two_sum_test.py +++ b/test/leetcode/array/two_sum_test.py @@ -1,6 +1,7 @@ -from leetcode.array.two_sum import Solution +import leetcode.array.two_sum as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py b/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py index f0867f0..73968d6 100644 --- a/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py +++ b/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py @@ -1,8 +1,7 @@ -from leetcode.binary_search.find_first_and_last_position_of_element_in_sorted_array import ( - Solution, -) +import leetcode.binary_search.find_first_and_last_position_of_element_in_sorted_array as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/binary_search/find_peak_element_test.py b/test/leetcode/binary_search/find_peak_element_test.py index d26a1c2..ba75cb8 100644 --- a/test/leetcode/binary_search/find_peak_element_test.py +++ b/test/leetcode/binary_search/find_peak_element_test.py @@ -1,6 +1,7 @@ -from leetcode.binary_search.find_peak_element import Solution +import leetcode.binary_search.find_peak_element as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py b/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py index c587ff1..10c26ce 100644 --- a/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py +++ b/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py @@ -1,6 +1,7 @@ -from leetcode.binary_search.find_the_smallest_divisor_given_a_threshold import Solution +import leetcode.binary_search.find_the_smallest_divisor_given_a_threshold as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/binary_search/kth_missing_positive_number_test.py b/test/leetcode/binary_search/kth_missing_positive_number_test.py index 0ad1f30..6589cb7 100644 --- a/test/leetcode/binary_search/kth_missing_positive_number_test.py +++ b/test/leetcode/binary_search/kth_missing_positive_number_test.py @@ -1,6 +1,7 @@ -from leetcode.binary_search.kth_missing_positive_number import Solution +import leetcode.binary_search.kth_missing_positive_number as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py b/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py index dcadba0..ba79188 100644 --- a/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py +++ b/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py @@ -1,6 +1,7 @@ -from leetcode.binary_search.kth_smallest_element_in_a_sorted_matrix import Solution +import leetcode.binary_search.kth_smallest_element_in_a_sorted_matrix as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/binary_search/longest_increasing_subsequence_test.py b/test/leetcode/binary_search/longest_increasing_subsequence_test.py index 29d22b7..dbf1514 100644 --- a/test/leetcode/binary_search/longest_increasing_subsequence_test.py +++ b/test/leetcode/binary_search/longest_increasing_subsequence_test.py @@ -1,6 +1,7 @@ -from leetcode.binary_search.longest_increasing_subsequence import Solution +import leetcode.binary_search.longest_increasing_subsequence as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/binary_search/median_of_two_sorted_arrays_test.py b/test/leetcode/binary_search/median_of_two_sorted_arrays_test.py index c18640b..073f8b5 100644 --- a/test/leetcode/binary_search/median_of_two_sorted_arrays_test.py +++ b/test/leetcode/binary_search/median_of_two_sorted_arrays_test.py @@ -1,6 +1,7 @@ -from leetcode.binary_search.median_of_two_sorted_arrays import Solution +import leetcode.binary_search.median_of_two_sorted_arrays as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/binary_search/search_in_rotated_sorted_array_test.py b/test/leetcode/binary_search/search_in_rotated_sorted_array_test.py index 64a938d..ff7aa72 100644 --- a/test/leetcode/binary_search/search_in_rotated_sorted_array_test.py +++ b/test/leetcode/binary_search/search_in_rotated_sorted_array_test.py @@ -1,6 +1,7 @@ -from leetcode.binary_search.search_in_rotated_sorted_array import Solution +import leetcode.binary_search.search_in_rotated_sorted_array as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py b/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py index 36fce50..f289f09 100644 --- a/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py +++ b/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py @@ -1,6 +1,7 @@ -from leetcode.dynamic_programming.arithmetic_slices_ii_subsequences import Solution +import leetcode.dynamic_programming.arithmetic_slices_ii_subsequences as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/dynamic_programming/max_subarray_test.py b/test/leetcode/dynamic_programming/max_subarray_test.py index cda2670..546e75d 100644 --- a/test/leetcode/dynamic_programming/max_subarray_test.py +++ b/test/leetcode/dynamic_programming/max_subarray_test.py @@ -1,6 +1,7 @@ -from leetcode.dynamic_programming.max_subarray import Solution +import leetcode.dynamic_programming.max_subarray as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/dynamic_programming/word_break_i_test.py b/test/leetcode/dynamic_programming/word_break_i_test.py index 96898d8..defe26f 100644 --- a/test/leetcode/dynamic_programming/word_break_i_test.py +++ b/test/leetcode/dynamic_programming/word_break_i_test.py @@ -1,6 +1,7 @@ -from leetcode.dynamic_programming.word_break_i import Solution +import leetcode.dynamic_programming.word_break_i as lc -soln = Solution() + +soln = lc.Solution() def test_case_i(): diff --git a/test/leetcode/graph/add_edges_to_make_degrees_even_test.py b/test/leetcode/graph/add_edges_to_make_degrees_even_test.py index 6b13f7f..9000996 100644 --- a/test/leetcode/graph/add_edges_to_make_degrees_even_test.py +++ b/test/leetcode/graph/add_edges_to_make_degrees_even_test.py @@ -1,6 +1,7 @@ -from leetcode.graph.add_edges_to_make_degrees_even import Solution +import leetcode.graph.add_edges_to_make_degrees_even as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/graph/bus-routes.test.ts b/test/leetcode/graph/bus-routes.test.ts deleted file mode 100644 index c7cd695..0000000 --- a/test/leetcode/graph/bus-routes.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -from - - -soln = Solution() - - const routes = [[7, 12], [4, 5, 15], [6], [15, 19], [9, 12, 13]]; - - expect(numBusesToDestination(routes, 15, 12)).toBe(-1); - - const data = fs.readFileSync(path.join(__dirname, '__data__', 'bus-routes.test.json')).toString(); - const routes = JSON.parse(data); - - expect(numBusesToDestination(routes, 0, 100000)).toBe(-1); \ No newline at end of file diff --git a/test/leetcode/graph/bus_routes_test.py b/test/leetcode/graph/bus_routes_test.py index fca8b9f..9f42569 100644 --- a/test/leetcode/graph/bus_routes_test.py +++ b/test/leetcode/graph/bus_routes_test.py @@ -1,14 +1,18 @@ -# from pathlib import Path -from leetcode.graph.bus_routes import Solution +import json +import pathlib +import leetcode.graph.bus_routes as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): assert soln.numBusesToDestination([[7, 12], [4, 5, 15], [6], [15, 19], [9, 12, 13]], 15, 12) == -1 -# def test_case_2(): -# with Path("leetcode/data/bus_routes.json").open("r") as file: -# routes = json.loads(file) -# assert soln.numBusesToDestination(routes, 0, 100000) == -1 +def test_case_2(): + path = pathlib.Path(__file__).parent / "data" / "bus_routes.json" + assert path.exists() + with path.open("r", encoding="utf-8") as file: + routes = json.load(file) + assert soln.numBusesToDestination(routes, 0, 100000) == -1 diff --git a/test/leetcode/heap/furthest_building_you_can_reach_test.py b/test/leetcode/heap/furthest_building_you_can_reach_test.py index efe553f..9ddba28 100644 --- a/test/leetcode/heap/furthest_building_you_can_reach_test.py +++ b/test/leetcode/heap/furthest_building_you_can_reach_test.py @@ -1,6 +1,7 @@ -from leetcode.heap.furthest_building_you_can_reach import Solution +import leetcode.heap.furthest_building_you_can_reach as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/heap/k_closest_points_to_origin_test.py b/test/leetcode/heap/k_closest_points_to_origin_test.py index 942e393..b0793ce 100644 --- a/test/leetcode/heap/k_closest_points_to_origin_test.py +++ b/test/leetcode/heap/k_closest_points_to_origin_test.py @@ -1,6 +1,7 @@ -from leetcode.heap.k_closest_points_to_origin import Solution +import leetcode.heap.k_closest_points_to_origin as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/heap/kth_largest_element_test.py b/test/leetcode/heap/kth_largest_element_test.py index 65ed2b0..b3c7291 100644 --- a/test/leetcode/heap/kth_largest_element_test.py +++ b/test/leetcode/heap/kth_largest_element_test.py @@ -1,6 +1,7 @@ -from leetcode.heap.kth_largest_element import Solution +import leetcode.heap.kth_largest_element as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/heap/max_stack_test.py b/test/leetcode/heap/max_stack_test.py index c05bae6..50e123e 100644 --- a/test/leetcode/heap/max_stack_test.py +++ b/test/leetcode/heap/max_stack_test.py @@ -1,6 +1,7 @@ -from leetcode.heap.max_stack import MaxStack +import leetcode.heap.max_stack as lc -stack = MaxStack() + +stack = lc.MaxStack() def test_case_1(): diff --git a/test/leetcode/heap/minimize_deviation_in_array_test.py b/test/leetcode/heap/minimize_deviation_in_array_test.py index e081def..4d1ea22 100644 --- a/test/leetcode/heap/minimize_deviation_in_array_test.py +++ b/test/leetcode/heap/minimize_deviation_in_array_test.py @@ -1,6 +1,7 @@ -from leetcode.heap.minimize_deviation_in_array import Solution +import leetcode.heap.minimize_deviation_in_array as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/heap/number_of_orders_in_the_backlog_test.py b/test/leetcode/heap/number_of_orders_in_the_backlog_test.py index 4ae1acf..00b4a02 100644 --- a/test/leetcode/heap/number_of_orders_in_the_backlog_test.py +++ b/test/leetcode/heap/number_of_orders_in_the_backlog_test.py @@ -1,6 +1,7 @@ -from leetcode.heap.number_of_orders_in_the_backlog import Solution +import leetcode.heap.number_of_orders_in_the_backlog as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/heap/task_scheduler_test.py b/test/leetcode/heap/task_scheduler_test.py index df349a8..ba42f03 100644 --- a/test/leetcode/heap/task_scheduler_test.py +++ b/test/leetcode/heap/task_scheduler_test.py @@ -1,6 +1,7 @@ -from leetcode.heap.task_scheduler import Solution +import leetcode.heap.task_scheduler as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/recursion/generate-parentheses.test.ts b/test/leetcode/recursion/generate-parentheses.test.ts deleted file mode 100644 index 8ef5d92..0000000 --- a/test/leetcode/recursion/generate-parentheses.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { generateParenthesis } from '../../src/recursion/generate-parentheses'; - -describe('generate parentheses', () => { - test('generate parenthesis - test case 3', async () => { - const set = new Set(generateParenthesis(2)); - - expect(set).toStrictEqual(new Set(['()()', '(())'])); - }); - - test('generate parenthesis - test case 4', async () => { - const set = new Set(generateParenthesis(3)); - - expect(set).toMatchSnapshot(); - }); - - test('generate parenthesis - test case 5', async () => { - const set = new Set(generateParenthesis(4)); - - expect(set).toMatchSnapshot(); - }); -}); diff --git a/test/leetcode/recursion/generate_parentheses_test.py b/test/leetcode/recursion/generate_parentheses_test.py index b299c9d..f9c48e9 100644 --- a/test/leetcode/recursion/generate_parentheses_test.py +++ b/test/leetcode/recursion/generate_parentheses_test.py @@ -1,6 +1,7 @@ -from leetcode.recursion.generate_parentheses import Solution +import leetcode.recursion.generate_parentheses as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/recursion/snapshots/snap_generate_parentheses_test.py b/test/leetcode/recursion/snapshots/snap_generate_parentheses_test.py index 0c30c61..ce7c056 100644 --- a/test/leetcode/recursion/snapshots/snap_generate_parentheses_test.py +++ b/test/leetcode/recursion/snapshots/snap_generate_parentheses_test.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- # snapshottest: v1 - https://goo.gl/zC4yUc from __future__ import unicode_literals - from snapshottest import Snapshot + snapshots = Snapshot() snapshots["test_case_2 1"] = ["((()))", "(()())", "(())()", "()(())", "()()()"] diff --git a/test/leetcode/string/valid-word-abbreviation.test.ts b/test/leetcode/string/valid-word-abbreviation.test.ts deleted file mode 100644 index afe9d13..0000000 --- a/test/leetcode/string/valid-word-abbreviation.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { validWordAbbreviation } from '../../src/string/valid-word-abbreviation'; - -describe('valid word abbreviation', () => { - test('valid word abbreviation - test case 1', async () => { - expect(validWordAbbreviation('substitition', 's10n')).toBe(true); - }); -}); diff --git a/test/leetcode/string/valid_parentheses_test.py b/test/leetcode/string/valid_parentheses_test.py index 643e75e..2c3ebe8 100644 --- a/test/leetcode/string/valid_parentheses_test.py +++ b/test/leetcode/string/valid_parentheses_test.py @@ -1,6 +1,7 @@ -from leetcode.stack.valid_parentheses import Solution +import leetcode.stack.valid_parentheses as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/string/valid_word_abbreviation_test.py b/test/leetcode/string/valid_word_abbreviation_test.py index 844c816..89e32c1 100644 --- a/test/leetcode/string/valid_word_abbreviation_test.py +++ b/test/leetcode/string/valid_word_abbreviation_test.py @@ -1,6 +1,7 @@ -from leetcode.string.valid_word_abbreviation import Solution +import leetcode.string.valid_word_abbreviation as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): diff --git a/test/leetcode/two_pointers/bag-of-tokens.test.ts b/test/leetcode/two_pointers/bag-of-tokens.test.ts deleted file mode 100644 index 781bfa2..0000000 --- a/test/leetcode/two_pointers/bag-of-tokens.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { bagOfTokensScore } from '../../src/two-pointer/bag-of-tokens'; - -describe('bag of tokens', () => { - test('bag of tokens - test case 1', async () => { - expect(bagOfTokensScore([100], 50)).toBe(0); - }); - - test('bag of tokens - test case 2', async () => { - expect(bagOfTokensScore([200, 100], 150)).toBe(1); - }); - - test('bag of tokens - test case 3', async () => { - expect(bagOfTokensScore([100, 200, 300, 400], 200)).toBe(2); - }); -}); diff --git a/test/leetcode/two_pointers/bag_of_tokens_test.py b/test/leetcode/two_pointers/bag_of_tokens_test.py index 1c8d861..67f65b9 100644 --- a/test/leetcode/two_pointers/bag_of_tokens_test.py +++ b/test/leetcode/two_pointers/bag_of_tokens_test.py @@ -1,6 +1,7 @@ -from leetcode.two_pointers.bag_of_tokens import Solution +import leetcode.two_pointers.bag_of_tokens as lc -soln = Solution() + +soln = lc.Solution() def test_case_1(): From 7faa00b6110efd6699de3f926d524c5c7222fdd3 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 12 Mar 2025 15:22:08 -0700 Subject: [PATCH 027/158] lint first --- .github/workflows/build-and-test.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 801510b..28a3ecb 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -51,6 +51,9 @@ jobs: - name: Run format run: poetry run format + - name: Run lint + run: poetry run lint + - name: Commit if: ${{ github.ref != 'refs/heads/main' }} id: commit @@ -60,10 +63,6 @@ jobs: author_email: github-actions[bot]@github.com message: Automatic commit via GitHub Actions - - name: Run lint - if: ${{ steps.commit.outputs.committed == 'false' }} - run: poetry run lint - - name: Test if: ${{ steps.commit.outputs.committed == 'false' }} run: poetry run test From fd5648ddfb9a352d5f0d0472d97256c149ab8935 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 12 Mar 2025 15:38:55 -0700 Subject: [PATCH 028/158] fixes importing --- .../find_the_smallest_divisor_given_a_threshold.py | 4 ++-- .../binary_search/median_of_two_sorted_arrays.py | 11 +++++++---- src/leetcode/heap/k_closest_points_to_origin.py | 4 ++-- src/leetcode/heap/merge_k_sorted_lists.py | 9 +++++---- src/leetcode/heap/minimize_deviation_in_array.py | 6 +++--- .../array/best_time_to_buy_and_sell_stock_ii_test.py | 4 ++-- .../array/buildings_with_an_ocean_view_test.py | 4 ++-- 7 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold.py b/src/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold.py index 30a934c..a1491aa 100644 --- a/src/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold.py +++ b/src/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold.py @@ -10,7 +10,7 @@ # The test cases are generated so that there will be an answer. # # See https://leetcode.com/problems/find-the-smallest-divisor-given-a-threshold -from math import ceil +import math class Solution: @@ -29,7 +29,7 @@ def compute(ys: list[int], divisor: int): # We do math.ceil because the problem asks us to round up. # # Note: there's no way to chain map -> reduce. Mind boggling. - return sum(ceil(x / divisor) for x in xs) + return sum(math.ceil(x / divisor) for x in xs) # Use the insert point binary search approach to find the divisor we want. left = 1 diff --git a/src/leetcode/binary_search/median_of_two_sorted_arrays.py b/src/leetcode/binary_search/median_of_two_sorted_arrays.py index c99f374..2ea41a1 100644 --- a/src/leetcode/binary_search/median_of_two_sorted_arrays.py +++ b/src/leetcode/binary_search/median_of_two_sorted_arrays.py @@ -5,6 +5,9 @@ # The overall run time complexity should be O(log (m+n)). # # See https://leetcode.com/problems/median-of-two-sorted-arrays +import math + + class Solution: def findMedianSortedArrays(self, xs: list[int], ys: list[int]) -> float: """ @@ -94,10 +97,10 @@ def findMedianSortedArrays(self, xs: list[int], ys: list[int]) -> float: # # xs = [] with lmax1 = -Infinity because there is no value, and everything is > lmax1. # xs = [1,2,3|] with rmax1 = +Infinity because there is no value, and everything is < rmin1. - lmax1 = float("-inf") if p1 == 0 else xs[p1 - 1] # If xs partition is empty, lmax1 value is unused. - rmin1 = float("inf") if p1 == m else xs[p1] # If xs partition is full, rmin1 value is unused. - lmax2 = float("-inf") if p2 == 0 else ys[p2 - 1] # If ys partition is empty, lmax2 value is unused. - rmin2 = float("inf") if p2 == n else ys[p2] # If ys partition is full, rmin2 value is unused. + lmax1 = -math.inf if p1 == 0 else xs[p1 - 1] # If xs partition is empty, lmax1 value is unused. + rmin1 = math.inf if p1 == m else xs[p1] # If xs partition is full, rmin1 value is unused. + lmax2 = -math.inf if p2 == 0 else ys[p2 - 1] # If ys partition is empty, lmax2 value is unused. + rmin2 = math.inf if p2 == n else ys[p2] # If ys partition is full, rmin2 value is unused. # If our inequalities hold, we have found the median. The value differs depending on if we have an odd # sized "merged" array or even. diff --git a/src/leetcode/heap/k_closest_points_to_origin.py b/src/leetcode/heap/k_closest_points_to_origin.py index 53b9c45..2ed8245 100644 --- a/src/leetcode/heap/k_closest_points_to_origin.py +++ b/src/leetcode/heap/k_closest_points_to_origin.py @@ -9,7 +9,7 @@ # # See https://leetcode.com/problems/k-closest-points-to-origin from heapq import heappop, heappush -from math import sqrt +import math class Solution: @@ -41,7 +41,7 @@ def kClosest(self, points: list[list[int]], k: int) -> list[list[int]]: # leaving it here for correctness. def distance(p: list[int]) -> float: [x, y] = p - return sqrt(x**2 + y**2) + return math.sqrt(x**2 + y**2) # Store max heap of (distance, point). LeetCode gives us the points as list of [x, y]. max_heap: list[tuple[float, list[int]]] = [] diff --git a/src/leetcode/heap/merge_k_sorted_lists.py b/src/leetcode/heap/merge_k_sorted_lists.py index c696baa..b6ea39e 100644 --- a/src/leetcode/heap/merge_k_sorted_lists.py +++ b/src/leetcode/heap/merge_k_sorted_lists.py @@ -14,12 +14,13 @@ def mergeKLists(self, lists: list[ListNode | None]) -> ListNode | None: """ SOLUTION: - Instead of only 2 lists, we have to merge k lists. The naive way is to do a linear scan of the head of every list - to find the smallest element then create a new list node with that element and append it to the result list. + Instead of only 2 lists, we have to merge k lists. The naive way is to do a linear scan of the head of every + list to find the smallest element then create a new list node with that element and append it to the result + list. However, we can do better by using a heap to store the head of each list so we can always find the minimum value - without a linear scan. Additionally, the problem does not say we cannot reuse the input lists, so we do not have to - create new list nodes. + without a linear scan. Additionally, the problem does not say we cannot reuse the input lists, so we do not + have to create new list nodes. COMPLEXITY: diff --git a/src/leetcode/heap/minimize_deviation_in_array.py b/src/leetcode/heap/minimize_deviation_in_array.py index fe0d8f3..a6d6164 100644 --- a/src/leetcode/heap/minimize_deviation_in_array.py +++ b/src/leetcode/heap/minimize_deviation_in_array.py @@ -20,7 +20,7 @@ # # See https://leetcode.com/problems/minimize-deviation-in-array from heapq import heappop, heappush -from sys import maxsize +import math class Solution: @@ -55,7 +55,7 @@ def minimumDeviation(self, nums: list[int]): heappush(heap, -value) minimum = min(nums) - deviation = maxsize + deviation = math.inf # Calculate the current deviation using the max element of the array, then half the max element and return it to # theheap. Then repeat to keep bringing the deviation down. @@ -75,4 +75,4 @@ def minimumDeviation(self, nums: list[int]): # Update the minimum value in case we've changed the minimum value by manipulating the maximum value. minimum = min(value, minimum) - return deviation + return int(deviation) diff --git a/test/leetcode/array/best_time_to_buy_and_sell_stock_ii_test.py b/test/leetcode/array/best_time_to_buy_and_sell_stock_ii_test.py index 9ddba28..583dcbe 100644 --- a/test/leetcode/array/best_time_to_buy_and_sell_stock_ii_test.py +++ b/test/leetcode/array/best_time_to_buy_and_sell_stock_ii_test.py @@ -1,7 +1,7 @@ -import leetcode.heap.furthest_building_you_can_reach as lc +from leetcode.heap.furthest_building_you_can_reach import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/array/buildings_with_an_ocean_view_test.py b/test/leetcode/array/buildings_with_an_ocean_view_test.py index 27c637d..bac7360 100644 --- a/test/leetcode/array/buildings_with_an_ocean_view_test.py +++ b/test/leetcode/array/buildings_with_an_ocean_view_test.py @@ -1,7 +1,7 @@ -import leetcode.array.buildings_with_an_ocean_view as lc +from leetcode.array.buildings_with_an_ocean_view import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): From 077503aa74659a37f8d955caf390c906cf0d09fa Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 12 Mar 2025 15:42:39 -0700 Subject: [PATCH 029/158] very silly stuff --- test/leetcode/array/design_hit_counter_test.py | 4 ++-- .../array/dot_product_of_two_sparse_vectors_test.py | 6 +++--- test/leetcode/array/group_anagrams_test.py | 4 ++-- test/leetcode/array/longest_consecutive_sequence_test.py | 4 ++-- test/leetcode/array/max_chunks_to_make_sorted_test.py | 4 ++-- test/leetcode/array/maximum_swap_test.py | 4 ++-- test/leetcode/array/merge_sorted_array_test.py | 4 ++-- test/leetcode/array/pascals_triangle_test.py | 4 ++-- test/leetcode/array/top_k_frequent_elements_test.py | 4 ++-- test/leetcode/array/two_sum_test.py | 4 ++-- ...rst_and_last_position_of_element_in_sorted_array_test.py | 6 ++++-- test/leetcode/binary_search/find_peak_element_test.py | 4 ++-- .../find_the_smallest_divisor_given_a_threshold_test.py | 4 ++-- .../binary_search/kth_missing_positive_number_test.py | 4 ++-- .../kth_smallest_element_in_a_sorted_matrix_test.py | 4 ++-- .../binary_search/longest_increasing_subsequence_test.py | 4 ++-- .../binary_search/median_of_two_sorted_arrays_test.py | 4 ++-- .../binary_search/search_in_rotated_sorted_array_test.py | 4 ++-- .../arithmetic_slices_ii_subsequences_test.py | 4 ++-- test/leetcode/dynamic_programming/max_subarray_test.py | 4 ++-- test/leetcode/dynamic_programming/word_break_i_test.py | 4 ++-- test/leetcode/graph/add_edges_to_make_degrees_even_test.py | 4 ++-- test/leetcode/graph/bus_routes_test.py | 4 ++-- test/leetcode/heap/furthest_building_you_can_reach_test.py | 4 ++-- test/leetcode/heap/k_closest_points_to_origin_test.py | 4 ++-- test/leetcode/heap/kth_largest_element_test.py | 4 ++-- test/leetcode/heap/max_stack_test.py | 4 ++-- test/leetcode/heap/minimize_deviation_in_array_test.py | 4 ++-- test/leetcode/heap/number_of_orders_in_the_backlog_test.py | 4 ++-- test/leetcode/heap/task_scheduler_test.py | 4 ++-- test/leetcode/recursion/generate_parentheses_test.py | 4 ++-- test/leetcode/string/valid_parentheses_test.py | 4 ++-- test/leetcode/string/valid_word_abbreviation_test.py | 4 ++-- test/leetcode/two_pointers/bag_of_tokens_test.py | 4 ++-- 34 files changed, 71 insertions(+), 69 deletions(-) diff --git a/test/leetcode/array/design_hit_counter_test.py b/test/leetcode/array/design_hit_counter_test.py index 5eebfec..7a5aa5a 100644 --- a/test/leetcode/array/design_hit_counter_test.py +++ b/test/leetcode/array/design_hit_counter_test.py @@ -1,7 +1,7 @@ -import leetcode.array.design_hit_counter as lc +from leetcode.array.design_hit_counter import HitCounter -hc = lc.HitCounter() +hc = HitCounter() def test_case_1(): diff --git a/test/leetcode/array/dot_product_of_two_sparse_vectors_test.py b/test/leetcode/array/dot_product_of_two_sparse_vectors_test.py index 5061247..83f8303 100644 --- a/test/leetcode/array/dot_product_of_two_sparse_vectors_test.py +++ b/test/leetcode/array/dot_product_of_two_sparse_vectors_test.py @@ -1,8 +1,8 @@ -import leetcode.array.dot_product_of_two_sparse_vectors as lc +from leetcode.array.dot_product_of_two_sparse_vectors import SparseVector def test_case_1(): - a = lc.SparseVector([1, 0, 0, 2, 3]) - b = lc.SparseVector([0, 3, 0, 4, 0]) + a = SparseVector([1, 0, 0, 2, 3]) + b = SparseVector([0, 3, 0, 4, 0]) assert a.dotProduct(b) == 8 diff --git a/test/leetcode/array/group_anagrams_test.py b/test/leetcode/array/group_anagrams_test.py index 04c14f9..09b620f 100644 --- a/test/leetcode/array/group_anagrams_test.py +++ b/test/leetcode/array/group_anagrams_test.py @@ -1,7 +1,7 @@ -import leetcode.array.group_anagrams as lc +from leetcode.array.group_anagrams import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(snapshot): diff --git a/test/leetcode/array/longest_consecutive_sequence_test.py b/test/leetcode/array/longest_consecutive_sequence_test.py index fef60e7..ae656d2 100644 --- a/test/leetcode/array/longest_consecutive_sequence_test.py +++ b/test/leetcode/array/longest_consecutive_sequence_test.py @@ -1,7 +1,7 @@ -import leetcode.array.longest_consecutive_sequence as lc +from leetcode.array.longest_consecutive_sequence import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/array/max_chunks_to_make_sorted_test.py b/test/leetcode/array/max_chunks_to_make_sorted_test.py index 81d2853..250e4a9 100644 --- a/test/leetcode/array/max_chunks_to_make_sorted_test.py +++ b/test/leetcode/array/max_chunks_to_make_sorted_test.py @@ -1,7 +1,7 @@ -import leetcode.array.max_chunks_to_make_sorted as lc +from leetcode.array.max_chunks_to_make_sorted import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/array/maximum_swap_test.py b/test/leetcode/array/maximum_swap_test.py index 1ffd1b3..c0702bb 100644 --- a/test/leetcode/array/maximum_swap_test.py +++ b/test/leetcode/array/maximum_swap_test.py @@ -1,7 +1,7 @@ -import leetcode.array.maximum_swap as lc +from leetcode.array.maximum_swap import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/array/merge_sorted_array_test.py b/test/leetcode/array/merge_sorted_array_test.py index 2113574..7b3ec07 100644 --- a/test/leetcode/array/merge_sorted_array_test.py +++ b/test/leetcode/array/merge_sorted_array_test.py @@ -1,7 +1,7 @@ -import leetcode.array.merge_sorted_array as lc +from leetcode.array.merge_sorted_array import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/array/pascals_triangle_test.py b/test/leetcode/array/pascals_triangle_test.py index 4509641..c02e794 100644 --- a/test/leetcode/array/pascals_triangle_test.py +++ b/test/leetcode/array/pascals_triangle_test.py @@ -1,7 +1,7 @@ -import leetcode.array.pascals_triangle as lc +from leetcode.array.pascals_triangle import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(snapshot): diff --git a/test/leetcode/array/top_k_frequent_elements_test.py b/test/leetcode/array/top_k_frequent_elements_test.py index 33d4da8..b6833a6 100644 --- a/test/leetcode/array/top_k_frequent_elements_test.py +++ b/test/leetcode/array/top_k_frequent_elements_test.py @@ -1,7 +1,7 @@ -import leetcode.array.top_k_frequent_elements as lc +from leetcode.array.top_k_frequent_elements import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/array/two_sum_test.py b/test/leetcode/array/two_sum_test.py index 1c5de2f..5f08564 100644 --- a/test/leetcode/array/two_sum_test.py +++ b/test/leetcode/array/two_sum_test.py @@ -1,7 +1,7 @@ -import leetcode.array.two_sum as lc +from leetcode.array.two_sum import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py b/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py index 73968d6..b7ac30b 100644 --- a/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py +++ b/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py @@ -1,7 +1,9 @@ -import leetcode.binary_search.find_first_and_last_position_of_element_in_sorted_array as lc +from leetcode.binary_search.find_first_and_last_position_of_element_in_sorted_array import ( + Solution, +) -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/binary_search/find_peak_element_test.py b/test/leetcode/binary_search/find_peak_element_test.py index ba75cb8..42f5995 100644 --- a/test/leetcode/binary_search/find_peak_element_test.py +++ b/test/leetcode/binary_search/find_peak_element_test.py @@ -1,7 +1,7 @@ -import leetcode.binary_search.find_peak_element as lc +from leetcode.binary_search.find_peak_element import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py b/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py index 10c26ce..6c757bc 100644 --- a/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py +++ b/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py @@ -1,7 +1,7 @@ -import leetcode.binary_search.find_the_smallest_divisor_given_a_threshold as lc +from leetcode.binary_search.find_the_smallest_divisor_given_a_threshold import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/binary_search/kth_missing_positive_number_test.py b/test/leetcode/binary_search/kth_missing_positive_number_test.py index 6589cb7..22a4a63 100644 --- a/test/leetcode/binary_search/kth_missing_positive_number_test.py +++ b/test/leetcode/binary_search/kth_missing_positive_number_test.py @@ -1,7 +1,7 @@ -import leetcode.binary_search.kth_missing_positive_number as lc +from leetcode.binary_search.kth_missing_positive_number import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py b/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py index ba79188..bce2baf 100644 --- a/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py +++ b/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py @@ -1,7 +1,7 @@ -import leetcode.binary_search.kth_smallest_element_in_a_sorted_matrix as lc +from leetcode.binary_search.kth_smallest_element_in_a_sorted_matrix import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/binary_search/longest_increasing_subsequence_test.py b/test/leetcode/binary_search/longest_increasing_subsequence_test.py index dbf1514..d73e756 100644 --- a/test/leetcode/binary_search/longest_increasing_subsequence_test.py +++ b/test/leetcode/binary_search/longest_increasing_subsequence_test.py @@ -1,7 +1,7 @@ -import leetcode.binary_search.longest_increasing_subsequence as lc +from leetcode.binary_search.longest_increasing_subsequence import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/binary_search/median_of_two_sorted_arrays_test.py b/test/leetcode/binary_search/median_of_two_sorted_arrays_test.py index 073f8b5..3b87ffc 100644 --- a/test/leetcode/binary_search/median_of_two_sorted_arrays_test.py +++ b/test/leetcode/binary_search/median_of_two_sorted_arrays_test.py @@ -1,7 +1,7 @@ -import leetcode.binary_search.median_of_two_sorted_arrays as lc +from leetcode.binary_search.median_of_two_sorted_arrays import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/binary_search/search_in_rotated_sorted_array_test.py b/test/leetcode/binary_search/search_in_rotated_sorted_array_test.py index ff7aa72..957acac 100644 --- a/test/leetcode/binary_search/search_in_rotated_sorted_array_test.py +++ b/test/leetcode/binary_search/search_in_rotated_sorted_array_test.py @@ -1,7 +1,7 @@ -import leetcode.binary_search.search_in_rotated_sorted_array as lc +from leetcode.binary_search.search_in_rotated_sorted_array import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py b/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py index f289f09..5b148f9 100644 --- a/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py +++ b/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py @@ -1,7 +1,7 @@ -import leetcode.dynamic_programming.arithmetic_slices_ii_subsequences as lc +from leetcode.dynamic_programming.arithmetic_slices_ii_subsequences import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/dynamic_programming/max_subarray_test.py b/test/leetcode/dynamic_programming/max_subarray_test.py index 546e75d..802a692 100644 --- a/test/leetcode/dynamic_programming/max_subarray_test.py +++ b/test/leetcode/dynamic_programming/max_subarray_test.py @@ -1,7 +1,7 @@ -import leetcode.dynamic_programming.max_subarray as lc +from leetcode.dynamic_programming.max_subarray import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/dynamic_programming/word_break_i_test.py b/test/leetcode/dynamic_programming/word_break_i_test.py index defe26f..dc88c05 100644 --- a/test/leetcode/dynamic_programming/word_break_i_test.py +++ b/test/leetcode/dynamic_programming/word_break_i_test.py @@ -1,7 +1,7 @@ -import leetcode.dynamic_programming.word_break_i as lc +from leetcode.dynamic_programming.word_break_i import Solution -soln = lc.Solution() +soln = Solution() def test_case_i(): diff --git a/test/leetcode/graph/add_edges_to_make_degrees_even_test.py b/test/leetcode/graph/add_edges_to_make_degrees_even_test.py index 9000996..35e7754 100644 --- a/test/leetcode/graph/add_edges_to_make_degrees_even_test.py +++ b/test/leetcode/graph/add_edges_to_make_degrees_even_test.py @@ -1,7 +1,7 @@ -import leetcode.graph.add_edges_to_make_degrees_even as lc +from leetcode.graph.add_edges_to_make_degrees_even import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/graph/bus_routes_test.py b/test/leetcode/graph/bus_routes_test.py index 9f42569..2c22d1c 100644 --- a/test/leetcode/graph/bus_routes_test.py +++ b/test/leetcode/graph/bus_routes_test.py @@ -1,9 +1,9 @@ import json import pathlib -import leetcode.graph.bus_routes as lc +from leetcode.graph.bus_routes import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/heap/furthest_building_you_can_reach_test.py b/test/leetcode/heap/furthest_building_you_can_reach_test.py index 9ddba28..583dcbe 100644 --- a/test/leetcode/heap/furthest_building_you_can_reach_test.py +++ b/test/leetcode/heap/furthest_building_you_can_reach_test.py @@ -1,7 +1,7 @@ -import leetcode.heap.furthest_building_you_can_reach as lc +from leetcode.heap.furthest_building_you_can_reach import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/heap/k_closest_points_to_origin_test.py b/test/leetcode/heap/k_closest_points_to_origin_test.py index b0793ce..814811b 100644 --- a/test/leetcode/heap/k_closest_points_to_origin_test.py +++ b/test/leetcode/heap/k_closest_points_to_origin_test.py @@ -1,7 +1,7 @@ -import leetcode.heap.k_closest_points_to_origin as lc +from leetcode.heap.k_closest_points_to_origin import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/heap/kth_largest_element_test.py b/test/leetcode/heap/kth_largest_element_test.py index b3c7291..b199e7a 100644 --- a/test/leetcode/heap/kth_largest_element_test.py +++ b/test/leetcode/heap/kth_largest_element_test.py @@ -1,7 +1,7 @@ -import leetcode.heap.kth_largest_element as lc +from leetcode.heap.kth_largest_element import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/heap/max_stack_test.py b/test/leetcode/heap/max_stack_test.py index 50e123e..66851ad 100644 --- a/test/leetcode/heap/max_stack_test.py +++ b/test/leetcode/heap/max_stack_test.py @@ -1,7 +1,7 @@ -import leetcode.heap.max_stack as lc +from leetcode.heap.max_stack import MaxStack -stack = lc.MaxStack() +stack = MaxStack() def test_case_1(): diff --git a/test/leetcode/heap/minimize_deviation_in_array_test.py b/test/leetcode/heap/minimize_deviation_in_array_test.py index 4d1ea22..773aa81 100644 --- a/test/leetcode/heap/minimize_deviation_in_array_test.py +++ b/test/leetcode/heap/minimize_deviation_in_array_test.py @@ -1,7 +1,7 @@ -import leetcode.heap.minimize_deviation_in_array as lc +from leetcode.heap.minimize_deviation_in_array import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/heap/number_of_orders_in_the_backlog_test.py b/test/leetcode/heap/number_of_orders_in_the_backlog_test.py index 00b4a02..1200c2a 100644 --- a/test/leetcode/heap/number_of_orders_in_the_backlog_test.py +++ b/test/leetcode/heap/number_of_orders_in_the_backlog_test.py @@ -1,7 +1,7 @@ -import leetcode.heap.number_of_orders_in_the_backlog as lc +from leetcode.heap.number_of_orders_in_the_backlog import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/heap/task_scheduler_test.py b/test/leetcode/heap/task_scheduler_test.py index ba42f03..b84cdd7 100644 --- a/test/leetcode/heap/task_scheduler_test.py +++ b/test/leetcode/heap/task_scheduler_test.py @@ -1,7 +1,7 @@ -import leetcode.heap.task_scheduler as lc +from leetcode.heap.task_scheduler import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/recursion/generate_parentheses_test.py b/test/leetcode/recursion/generate_parentheses_test.py index f9c48e9..f646b85 100644 --- a/test/leetcode/recursion/generate_parentheses_test.py +++ b/test/leetcode/recursion/generate_parentheses_test.py @@ -1,7 +1,7 @@ -import leetcode.recursion.generate_parentheses as lc +from leetcode.recursion.generate_parentheses import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/string/valid_parentheses_test.py b/test/leetcode/string/valid_parentheses_test.py index 2c3ebe8..50b9ecd 100644 --- a/test/leetcode/string/valid_parentheses_test.py +++ b/test/leetcode/string/valid_parentheses_test.py @@ -1,7 +1,7 @@ -import leetcode.stack.valid_parentheses as lc +from leetcode.stack.valid_parentheses import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/string/valid_word_abbreviation_test.py b/test/leetcode/string/valid_word_abbreviation_test.py index 89e32c1..9705506 100644 --- a/test/leetcode/string/valid_word_abbreviation_test.py +++ b/test/leetcode/string/valid_word_abbreviation_test.py @@ -1,7 +1,7 @@ -import leetcode.string.valid_word_abbreviation as lc +from leetcode.string.valid_word_abbreviation import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): diff --git a/test/leetcode/two_pointers/bag_of_tokens_test.py b/test/leetcode/two_pointers/bag_of_tokens_test.py index 67f65b9..a9026e0 100644 --- a/test/leetcode/two_pointers/bag_of_tokens_test.py +++ b/test/leetcode/two_pointers/bag_of_tokens_test.py @@ -1,7 +1,7 @@ -import leetcode.two_pointers.bag_of_tokens as lc +from leetcode.two_pointers.bag_of_tokens import Solution -soln = lc.Solution() +soln = Solution() def test_case_1(): From 0f72820fd689792c2dcc46f4c95f7cda69e5f18d Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 12 Mar 2025 17:12:21 -0700 Subject: [PATCH 030/158] word search 2 --- src/leetcode/graph/word-search-ii.ts | 125 ------------------ src/leetcode/graph/word_search_ii.py | 92 +++++++++++++ test/leetcode/graph/snapshots/__init__.py | 0 .../snapshots/snap_word_search_ii_test.py | 40 ++++++ test/leetcode/graph/word-search-ii.test.ts | 44 ------ test/leetcode/graph/word_search_ii_test.py | 32 +++++ 6 files changed, 164 insertions(+), 169 deletions(-) delete mode 100644 src/leetcode/graph/word-search-ii.ts create mode 100644 src/leetcode/graph/word_search_ii.py create mode 100644 test/leetcode/graph/snapshots/__init__.py create mode 100644 test/leetcode/graph/snapshots/snap_word_search_ii_test.py delete mode 100644 test/leetcode/graph/word-search-ii.test.ts create mode 100644 test/leetcode/graph/word_search_ii_test.py diff --git a/src/leetcode/graph/word-search-ii.ts b/src/leetcode/graph/word-search-ii.ts deleted file mode 100644 index ccfcdd4..0000000 --- a/src/leetcode/graph/word-search-ii.ts +++ /dev/null @@ -1,125 +0,0 @@ -// DIFFICULTY: HARD -// -// Given an m x n board of characters and a list of strings words, return all words on the board. -// -// Each word must be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or -// vertically neighboring. The same letter cell may not be used more than once in a word. -// -// See {@link https://leetcode.com/problems/word-search-ii/} -export { findWords }; - -// SOLUTION: -// -// This problem looks like it can be solved with a prefix trie. -// -// 1. Construct a prefix trie from the list of words. -// 2. Run DFS from each board cell, choosing to continue the search if the sequence seen so far is a valid prefix in -// the trie. -// 3. Collect valid words from each DFS and return the result. -// -// Easy peasy. -function findWords(board: string[][], words: string[]): string[] { - // Construct the prefix trie for efficient lookup. - const root = new TrieNode(); - words.forEach(word => { - root.add(word); - }); - - // Define a DFS function to run over each cell. We could potentially make the DFS more efficient by creating the - // visited matrix once and then mutating it over each DFS visitation. - const result = new Set(); - - function createdVisitedMatrix(matrix: string[][]) { - const visited: boolean[][] = Array(matrix.length); - for (let i = 0; i < matrix.length; i++) { - visited[i] = Array(matrix[i].length).fill(false); - } - return visited; - } - - function dfs(node: TrieNode, visited: boolean[][], word: string, row: number, column: number) { - // If we've reached the end of a word, do not return right away; we might be able to reach a longer word if we - // keep going. Record the one we've seen so far though. - if (node.isTerminated) { - result.add(word); - } - - // The frontier nodes could be any direction up, down, left, or right, but not diagonal. - const frontier = [ - [row + 1, column], - [row - 1, column], - [row, column + 1], - [row, column - 1] - ]; - for (const [x, y] of frontier) { - // Do not consider this frontier cells that are out of bounds. - if (x < 0 || x >= board.length) { - continue; - } - - // Do not consider this frontier cells that are out of bounds. - if (y < 0 || y >= board[x].length) { - continue; - } - - // Do not consider visited cells. - if (visited[x][y]) { - continue; - } - - // Do not consider cells that do not form a word. - const c = board[x][y]; - if (!node.children.has(c)) { - continue; - } - - const next = node.children.get(c)!; - - // Mark this cell as visited, and continue the DFS search. After the search completes, we should unmark visited - // cells (unlike regular DFS) because a different direction may yield different words, so we'll want reconsider - // these previously visited cells. - visited[x][y] = true; - dfs(next, visited, word + c, x, y); - visited[x][y] = false; - } - } - - // Perform DFS on every single cell. - for (let i = 0; i < board.length; i++) { - for (let j = 0; j < board[i].length; j++) { - const c = board[i][j]; - if (!root.children.has(c)) { - continue; - } - - const visited = createdVisitedMatrix(board); - const node = root.children.get(c)!; - - // Note that the DFS function won't mark the current cell as visited; we'll have to do that manually. - visited[i][j] = true; - dfs(node, visited, c, i, j); - } - } - - return [...result]; -} - -class TrieNode { - public children = new Map(); - - public isTerminated = false; - - public add(word: string) { - let node: TrieNode = this; - - for (let i = 0; i < word.length; i++) { - const c = word.charAt(i); - if (!node.children.has(c)) { - node.children.set(c, new TrieNode()); - } - node = node.children.get(c)!; - } - - node.isTerminated = true; - } -} diff --git a/src/leetcode/graph/word_search_ii.py b/src/leetcode/graph/word_search_ii.py new file mode 100644 index 0000000..24fb0f4 --- /dev/null +++ b/src/leetcode/graph/word_search_ii.py @@ -0,0 +1,92 @@ +# DIFFICULTY: HARD +# +# Given an m x n board of characters and a list of strings words, return all words on the board. +# +# Each word must be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or +# vertically neighboring. The same letter cell may not be used more than once in a word. +# +# See https://leetcode.com/problems/word-search-ii +class TrieNode: + def __init__(self) -> None: + self.children: dict[str, TrieNode] = {} + self.isTerminated = False + + def add(self, word: str) -> None: + node: TrieNode = self + for c in word: + if c not in node.children: + node.children[c] = TrieNode() + node = node.children[c] + node.isTerminated = True + + +class Solution: + def findWords(self, board: list[list[str]], words: list[str]) -> list[str]: + """ + SOLUTION: + + This problem looks like it can be solved with a prefix trie. + + 1. Construct a prefix trie from the list of words. + 2. Run DFS from each board cell, choosing to continue the search if the sequence seen so far is a valid prefix in + the trie. + 3. Collect valid words from each DFS and return the result. + + Easy peasy. + """ + # Construct the prefix trie for efficient lookup. + root = TrieNode() + for word in words: + root.add(word) + + # Define a DFS function to run over each cell. We could potentially make the DFS more efficient by creating the + # visited matrix once and then mutating it over each DFS visitation. + result: set[str] = set() + + def dfs(node: TrieNode, visited: set[tuple[int, int]], word: str, row: int, column: int) -> None: + # If we've reached the end of a word, do not return right away; we might be able to reach a longer word if + # we keep going. Record the one we've seen so far though. + if node.isTerminated: + result.add(word) + + # The frontier nodes could be any direction up, down, left, or right, but not diagonal. + frontier = [(row + 1, column), (row - 1, column), (row, column + 1), (row, column - 1)] + for x, y in frontier: + # Do not consider frontier cells that are out of bounds. + if not (0 <= x < len(board)): + continue + + if not (0 <= y < len(board[x])): + continue + + # Do not consider visited cells. + if (x, y) in visited: + continue + + # Do not consider cells that do not form a word. + c = board[x][y] + if c not in node.children: + continue + + # Continue DFS using the next TrieNode. + next = node.children[c] + + # Mark this cell as visited, and continue the DFS search. After the search completes, we should unmark + # visited cells (unlike regular DFS) because a different direction may yield different words, so we'll + # want reconsider these previously visited cells. + visited.add((x, y)) + dfs(next, visited, word + c, x, y) + visited.remove((x, y)) + + # Perform DFS on every cell. + for i in range(len(board)): + for j in range(len(board[i])): + c = board[i][j] + if c not in root.children: + continue + + node = root.children[c] + visited = {(i, j)} + dfs(node, visited, c, i, j) + + return list(result) diff --git a/test/leetcode/graph/snapshots/__init__.py b/test/leetcode/graph/snapshots/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/leetcode/graph/snapshots/snap_word_search_ii_test.py b/test/leetcode/graph/snapshots/snap_word_search_ii_test.py new file mode 100644 index 0000000..0dc9c58 --- /dev/null +++ b/test/leetcode/graph/snapshots/snap_word_search_ii_test.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# snapshottest: v1 - https://goo.gl/zC4yUc +from __future__ import unicode_literals + +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots['test_case_1 1'] = [ + 'oath', + 'eat' +] + +snapshots['test_case_4 [\'oath\', \'pea\', \'eat\', \'rain\', \'hklf\', \'hf\']'] = [ + [ + 'o', + 'a', + 'a', + 'n' + ], + [ + 'e', + 't', + 'a', + 'e' + ], + [ + 'i', + 'h', + 'k', + 'r' + ], + [ + 'i', + 'f', + 'l', + 'v' + ] +] diff --git a/test/leetcode/graph/word-search-ii.test.ts b/test/leetcode/graph/word-search-ii.test.ts deleted file mode 100644 index 0549401..0000000 --- a/test/leetcode/graph/word-search-ii.test.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { findWords } from '../../src/graph/word-search-ii'; - -describe('word search ii', () => { - test('word search ii - test case 1', async () => { - const board = [ - ['o', 'a', 'a', 'n'], - ['e', 't', 'a', 'e'], - ['i', 'h', 'k', 'r'], - ['i', 'f', 'l', 'v'] - ]; - const words = ['oath', 'pea', 'eat', 'rain']; - - expect(findWords(board, words)).toMatchSnapshot(); - }); - - test('word search ii - test case 2', async () => { - const board = [ - ['a', 'b'], - ['c', 'd'] - ]; - const words = ['abcb']; - - expect(findWords(board, words)).toStrictEqual([]); - }); - - test('word search ii - test case 3', async () => { - const board = [['a', 'a']]; - const words = ['aaa']; - - expect(findWords(board, words)).toStrictEqual([]); - }); - - test('word search ii - test case 4', async () => { - const board = [ - ['o', 'a', 'a', 'n'], - ['e', 't', 'a', 'e'], - ['i', 'h', 'k', 'r'], - ['i', 'f', 'l', 'v'] - ]; - const words = ['oath', 'pea', 'eat', 'rain', 'hklf', 'hf']; - - expect(findWords(board, words)).toMatchSnapshot(); - }); -}); diff --git a/test/leetcode/graph/word_search_ii_test.py b/test/leetcode/graph/word_search_ii_test.py new file mode 100644 index 0000000..8138664 --- /dev/null +++ b/test/leetcode/graph/word_search_ii_test.py @@ -0,0 +1,32 @@ +from leetcode.graph.word_search_ii import Solution + + +soln = Solution() + + +def test_case_1(snapshot): + board = [["o", "a", "a", "n"], ["e", "t", "a", "e"], ["i", "h", "k", "r"], ["i", "f", "l", "v"]] + words = ["oath", "pea", "eat", "rain"] + + snapshot.assert_match(soln.findWords(board, words)) + + +def test_case_2(): + board = [["a", "b"], ["c", "d"]] + words = ["abcb"] + + assert soln.findWords(board, words) == [] + + +def test_case_3(): + board = [["a", "a"]] + words = ["aaa"] + + assert soln.findWords(board, words) == [] + + +def test_case_4(snapshot): + board = [["o", "a", "a", "n"], ["e", "t", "a", "e"], ["i", "h", "k", "r"], ["i", "f", "l", "v"]] + words = ["oath", "pea", "eat", "rain", "hklf", "hf"] + + snapshot.assert_match(board, words) From ac2a4ed2625bb0f4a27d9d3a4aed614629bfcb68 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 13 Mar 2025 00:12:56 +0000 Subject: [PATCH 031/158] Automatic commit via GitHub Actions --- .../snapshots/snap_word_search_ii_test.py | 36 ++++--------------- 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/test/leetcode/graph/snapshots/snap_word_search_ii_test.py b/test/leetcode/graph/snapshots/snap_word_search_ii_test.py index 0dc9c58..c933e6b 100644 --- a/test/leetcode/graph/snapshots/snap_word_search_ii_test.py +++ b/test/leetcode/graph/snapshots/snap_word_search_ii_test.py @@ -1,40 +1,16 @@ # -*- coding: utf-8 -*- # snapshottest: v1 - https://goo.gl/zC4yUc from __future__ import unicode_literals - from snapshottest import Snapshot snapshots = Snapshot() -snapshots['test_case_1 1'] = [ - 'oath', - 'eat' -] +snapshots["test_case_1 1"] = ["oath", "eat"] -snapshots['test_case_4 [\'oath\', \'pea\', \'eat\', \'rain\', \'hklf\', \'hf\']'] = [ - [ - 'o', - 'a', - 'a', - 'n' - ], - [ - 'e', - 't', - 'a', - 'e' - ], - [ - 'i', - 'h', - 'k', - 'r' - ], - [ - 'i', - 'f', - 'l', - 'v' - ] +snapshots["test_case_4 ['oath', 'pea', 'eat', 'rain', 'hklf', 'hf']"] = [ + ["o", "a", "a", "n"], + ["e", "t", "a", "e"], + ["i", "h", "k", "r"], + ["i", "f", "l", "v"], ] From 2f4d60a218e40bcb80e2e705fd2e04e8470195b9 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 12 Mar 2025 17:19:58 -0700 Subject: [PATCH 032/158] updated --- src/leetcode/graph/word_search_ii.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/leetcode/graph/word_search_ii.py b/src/leetcode/graph/word_search_ii.py index 24fb0f4..439e2ad 100644 --- a/src/leetcode/graph/word_search_ii.py +++ b/src/leetcode/graph/word_search_ii.py @@ -28,11 +28,20 @@ def findWords(self, board: list[list[str]], words: list[str]) -> list[str]: This problem looks like it can be solved with a prefix trie. 1. Construct a prefix trie from the list of words. - 2. Run DFS from each board cell, choosing to continue the search if the sequence seen so far is a valid prefix in - the trie. + 2. Run DFS from each board cell, choosing to continue the search if the sequence seen so far is a valid prefix + in the trie. 3. Collect valid words from each DFS and return the result. Easy peasy. + + COMPLEXITY: + + Time complexity is O(w * k + m * n * 4^k) where w is the number of words, k is the maximum word length, m and n + are the dimensions of the board. Building the trie takes O(w * k). For DFS, we perform DFS on every cell (of + which there are m * n cells), and the length of each word in a DFS call is L, but there is a branching factor of + 4. + + Space complexity is O(w * k + k) due to the trie storage and the DFS stack. """ # Construct the prefix trie for efficient lookup. root = TrieNode() From 25ad8d92965f60c9ee7c9e14bf064f3f303941c9 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 12 Mar 2025 17:22:58 -0700 Subject: [PATCH 033/158] sort first --- .../__snapshots__/word-search-ii.test.ts.snap | 17 ----------------- .../graph/snapshots/snap_word_search_ii_test.py | 16 ++++++++++------ test/leetcode/graph/word_search_ii_test.py | 8 ++++++-- 3 files changed, 16 insertions(+), 25 deletions(-) delete mode 100644 test/leetcode/graph/__snapshots__/word-search-ii.test.ts.snap diff --git a/test/leetcode/graph/__snapshots__/word-search-ii.test.ts.snap b/test/leetcode/graph/__snapshots__/word-search-ii.test.ts.snap deleted file mode 100644 index 49811c9..0000000 --- a/test/leetcode/graph/__snapshots__/word-search-ii.test.ts.snap +++ /dev/null @@ -1,17 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`word search ii word search ii - test case 1 1`] = ` -[ - "oath", - "eat", -] -`; - -exports[`word search ii word search ii - test case 4 1`] = ` -[ - "oath", - "eat", - "hf", - "hklf", -] -`; diff --git a/test/leetcode/graph/snapshots/snap_word_search_ii_test.py b/test/leetcode/graph/snapshots/snap_word_search_ii_test.py index c933e6b..955fab0 100644 --- a/test/leetcode/graph/snapshots/snap_word_search_ii_test.py +++ b/test/leetcode/graph/snapshots/snap_word_search_ii_test.py @@ -1,16 +1,20 @@ # -*- coding: utf-8 -*- # snapshottest: v1 - https://goo.gl/zC4yUc from __future__ import unicode_literals + from snapshottest import Snapshot snapshots = Snapshot() -snapshots["test_case_1 1"] = ["oath", "eat"] +snapshots['test_case_1 1'] = [ + 'eat', + 'oath' +] -snapshots["test_case_4 ['oath', 'pea', 'eat', 'rain', 'hklf', 'hf']"] = [ - ["o", "a", "a", "n"], - ["e", "t", "a", "e"], - ["i", "h", "k", "r"], - ["i", "f", "l", "v"], +snapshots['test_case_4 1'] = [ + 'eat', + 'hf', + 'hklf', + 'oath' ] diff --git a/test/leetcode/graph/word_search_ii_test.py b/test/leetcode/graph/word_search_ii_test.py index 8138664..48cf660 100644 --- a/test/leetcode/graph/word_search_ii_test.py +++ b/test/leetcode/graph/word_search_ii_test.py @@ -8,7 +8,9 @@ def test_case_1(snapshot): board = [["o", "a", "a", "n"], ["e", "t", "a", "e"], ["i", "h", "k", "r"], ["i", "f", "l", "v"]] words = ["oath", "pea", "eat", "rain"] - snapshot.assert_match(soln.findWords(board, words)) + result = soln.findWords(board, words) + + snapshot.assert_match(sorted(result)) def test_case_2(): @@ -29,4 +31,6 @@ def test_case_4(snapshot): board = [["o", "a", "a", "n"], ["e", "t", "a", "e"], ["i", "h", "k", "r"], ["i", "f", "l", "v"]] words = ["oath", "pea", "eat", "rain", "hklf", "hf"] - snapshot.assert_match(board, words) + result = soln.findWords(board, words) + + snapshot.assert_match(sorted(result)) From 3bdb386396dcb03d9770d0d5e4e43a9de1e6ca30 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 13 Mar 2025 00:23:32 +0000 Subject: [PATCH 034/158] Automatic commit via GitHub Actions --- .../graph/snapshots/snap_word_search_ii_test.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/test/leetcode/graph/snapshots/snap_word_search_ii_test.py b/test/leetcode/graph/snapshots/snap_word_search_ii_test.py index 955fab0..0049be4 100644 --- a/test/leetcode/graph/snapshots/snap_word_search_ii_test.py +++ b/test/leetcode/graph/snapshots/snap_word_search_ii_test.py @@ -1,20 +1,11 @@ # -*- coding: utf-8 -*- # snapshottest: v1 - https://goo.gl/zC4yUc from __future__ import unicode_literals - from snapshottest import Snapshot snapshots = Snapshot() -snapshots['test_case_1 1'] = [ - 'eat', - 'oath' -] +snapshots["test_case_1 1"] = ["eat", "oath"] -snapshots['test_case_4 1'] = [ - 'eat', - 'hf', - 'hklf', - 'oath' -] +snapshots["test_case_4 1"] = ["eat", "hf", "hklf", "oath"] From 7bf09dae25a458deaf68fcb7dd588c219f465f5f Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 12 Mar 2025 18:01:59 -0700 Subject: [PATCH 035/158] this ... works? --- src/leetcode/graph/word_search.py | 82 +++++++++++++++++++ .../snapshots/snap_word_search_ii_test.py | 13 +-- test/leetcode/graph/word-search-i.test.ts | 14 ---- test/leetcode/graph/word_search_test.py | 11 +++ 4 files changed, 95 insertions(+), 25 deletions(-) create mode 100644 src/leetcode/graph/word_search.py delete mode 100644 test/leetcode/graph/word-search-i.test.ts create mode 100644 test/leetcode/graph/word_search_test.py diff --git a/src/leetcode/graph/word_search.py b/src/leetcode/graph/word_search.py new file mode 100644 index 0000000..01f189f --- /dev/null +++ b/src/leetcode/graph/word_search.py @@ -0,0 +1,82 @@ +# DIFFICULTY: MEDIUM +# +# Given an m x n grid of characters board and a string word, return true if word exists in the grid. +# +# The word can be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or +# vertically neighboring. The same letter cell may not be used more than once. +# +# See https://leetcode.com/problems/word-search +class Solution: + def exist(self, board: list[list[str]], word: str) -> bool: + """ + SOLUTION: + + This is just DFS; no need to build a prefix trie, in comparison to word search ii. BFS isn't going to work as + well because BFS will generate all possible words incrementally, whereas we want to just stop if we've found our + word. + + COMPLEXITY: + + Time complexity is O(m * n * 4^k) where m and n are the dimensions of the board, and k is the length of the + longest word. + + Space complexity is O(k). + """ + if len(word) == 0: + return False + + found = False + + def dfs(row: int, column: int, i: int) -> None: + nonlocal found + + if found: + return + + # Only continue down this path if the row is within bounds. + if not (0 <= row < len(board)): + return + + # Only continue down this path if the column is within bounds. + if not (0 <= column <= len(board[row])): + return + + # Only continue down this path if we've not visited this cell before. Instead of using a visited set, we'll + # modify the board itself to set a visited cell to "#". + if board[row][column] == "#": + return + + # Only continue down this path if our current string has fewer characters than our target. + if i == len(word): + return + + # Only continue down this path if the current character is one that we want. + c = board[row][column] + want = word[i] + if c is not want: + return + + # If we have reached the last index, check if we've found our target. + if i == len(word) - 1: + found = True + return + + # Save this row/column's value so we can mark it as visited; we'll use a sentinel value to do so. + board[row][column] = "#" + + dfs(row, column - 1, i + 1) + dfs(row, column + 1, i + 1) + dfs(row - 1, column, i + 1) + dfs(row + 1, column, i + 1) + + board[row][column] = c + + for i in range(len(board)): + for j in range(len(board[i])): + # Only start a DFS search if the word begins with the character we are looking at. + if board[i][j] == word[0]: + dfs(i, j, 0) + if found: + return True + + return False diff --git a/test/leetcode/graph/snapshots/snap_word_search_ii_test.py b/test/leetcode/graph/snapshots/snap_word_search_ii_test.py index 955fab0..0049be4 100644 --- a/test/leetcode/graph/snapshots/snap_word_search_ii_test.py +++ b/test/leetcode/graph/snapshots/snap_word_search_ii_test.py @@ -1,20 +1,11 @@ # -*- coding: utf-8 -*- # snapshottest: v1 - https://goo.gl/zC4yUc from __future__ import unicode_literals - from snapshottest import Snapshot snapshots = Snapshot() -snapshots['test_case_1 1'] = [ - 'eat', - 'oath' -] +snapshots["test_case_1 1"] = ["eat", "oath"] -snapshots['test_case_4 1'] = [ - 'eat', - 'hf', - 'hklf', - 'oath' -] +snapshots["test_case_4 1"] = ["eat", "hf", "hklf", "oath"] diff --git a/test/leetcode/graph/word-search-i.test.ts b/test/leetcode/graph/word-search-i.test.ts deleted file mode 100644 index 1129965..0000000 --- a/test/leetcode/graph/word-search-i.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { exist } from '../../src/graph/word-search-i'; - -describe('word search i', () => { - test('word search i - test case 1', async () => { - const board = [ - ['A', 'B', 'C', 'E'], - ['S', 'F', 'C', 'S'], - ['A', 'D', 'E', 'E'] - ]; - const word = 'ABCCED'; - - expect(exist(board, word)).toBe(true); - }); -}); diff --git a/test/leetcode/graph/word_search_test.py b/test/leetcode/graph/word_search_test.py new file mode 100644 index 0000000..60065b6 --- /dev/null +++ b/test/leetcode/graph/word_search_test.py @@ -0,0 +1,11 @@ +from leetcode.graph.word_search import Solution + + +soln = Solution() + + +def test_case_1(): + board = [["A", "B", "C", "E"], ["S", "F", "C", "S"], ["A", "D", "E", "E"]] + word = "ABCCED" + + assert soln.exist(board, word) From f16408ecdca60989cfdaa3b73e5efe96090fe3ab Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 12 Mar 2025 18:12:08 -0700 Subject: [PATCH 036/158] word search i --- src/leetcode/graph/word_search.py | 54 +++++++++---------------- test/leetcode/graph/word_search_test.py | 7 ++++ 2 files changed, 26 insertions(+), 35 deletions(-) diff --git a/src/leetcode/graph/word_search.py b/src/leetcode/graph/word_search.py index 01f189f..ce7c32e 100644 --- a/src/leetcode/graph/word_search.py +++ b/src/leetcode/graph/word_search.py @@ -25,58 +25,42 @@ def exist(self, board: list[list[str]], word: str) -> bool: if len(word) == 0: return False - found = False - - def dfs(row: int, column: int, i: int) -> None: - nonlocal found - - if found: - return + def dfs(row: int, column: int, i: int) -> bool: + if i == len(word): + return True # Only continue down this path if the row is within bounds. if not (0 <= row < len(board)): - return + return False # Only continue down this path if the column is within bounds. - if not (0 <= column <= len(board[row])): - return - - # Only continue down this path if we've not visited this cell before. Instead of using a visited set, we'll - # modify the board itself to set a visited cell to "#". - if board[row][column] == "#": - return + if not (0 <= column < len(board[row])): + return False - # Only continue down this path if our current string has fewer characters than our target. - if i == len(word): - return - - # Only continue down this path if the current character is one that we want. + # Only continue down this path if there's a match. c = board[row][column] - want = word[i] - if c is not want: - return - - # If we have reached the last index, check if we've found our target. - if i == len(word) - 1: - found = True - return + if c != word[i]: + return False # Save this row/column's value so we can mark it as visited; we'll use a sentinel value to do so. board[row][column] = "#" - dfs(row, column - 1, i + 1) - dfs(row, column + 1, i + 1) - dfs(row - 1, column, i + 1) - dfs(row + 1, column, i + 1) + found = ( + dfs(row, column - 1, i + 1) + or dfs(row, column + 1, i + 1) + or dfs(row - 1, column, i + 1) + or dfs(row + 1, column, i + 1) + ) board[row][column] = c + return found + for i in range(len(board)): for j in range(len(board[i])): # Only start a DFS search if the word begins with the character we are looking at. if board[i][j] == word[0]: - dfs(i, j, 0) - if found: - return True + if dfs(i, j, 0): + return True return False diff --git a/test/leetcode/graph/word_search_test.py b/test/leetcode/graph/word_search_test.py index 60065b6..33fbeba 100644 --- a/test/leetcode/graph/word_search_test.py +++ b/test/leetcode/graph/word_search_test.py @@ -9,3 +9,10 @@ def test_case_1(): word = "ABCCED" assert soln.exist(board, word) + + +def test_case_2(): + board = [["A", "B", "C", "E"], ["S", "F", "C", "S"], ["A", "D", "E", "E"]] + word = "SEE" + + assert soln.exist(board, word) From a04ea6829d6f04c0a64cdd47ed83835ea32465eb Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 12 Mar 2025 18:12:26 -0700 Subject: [PATCH 037/158] delete it --- src/leetcode/graph/word-search-i.ts | 89 ----------------------------- 1 file changed, 89 deletions(-) delete mode 100644 src/leetcode/graph/word-search-i.ts diff --git a/src/leetcode/graph/word-search-i.ts b/src/leetcode/graph/word-search-i.ts deleted file mode 100644 index d8f7aad..0000000 --- a/src/leetcode/graph/word-search-i.ts +++ /dev/null @@ -1,89 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an m x n grid of characters board and a string word, return true if word exists in the grid. -// -// The word can be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or -// vertically neighboring. The same letter cell may not be used more than once. -// -// See {@link https://leetcode.com/problems/word-search/} -export { exist }; - -// SOLUTION: -// -// This is just DFS; no need to build a prefix trie, in comparison to word search ii. BFS isn't going to work as well -// because BFS will generate all possible words incrementally, whereas we want to just stop if we've found our word. -function exist(board: string[][], word: string): boolean { - if (word.length === 0) { - return false; - } - - let found = false; - - // Here, as we do DFS, we are only going to go down paths that match the next character. So while we could build - // up a current: string as we go along, we do not need to. We just need to select DFS paths that correspond to - // the correct letter, and maintain the index of the letter that we are looking at. - function dfs(row: number, column: number, index: number) { - if (found) { - return; - } - - // Only continue down this path if the row is within bounds. - if (row < 0 || row >= board.length) { - return; - } - - // Only continue down this path if the column is within bounds. - if (column < 0 || column >= board[row].length) { - return; - } - - // Only continue down this path if we've not visited this cell before. - if (board[row][column] === '#') { - return; - } - - // Only continue down this path if our current string has fewer characters than our target. - if (index === word.length) { - return; - } - - // Only continue down this path if the current character is one that we want. - const c = board[row][column]; - const want = word[index]; - if (c !== want) { - return; - } - - // If we have reached the last index, check if we've found our target. - if (index === word.length - 1) { - found = true; - return; - } - - // Save this row/column's value so we can mark it as visited; we'll use a sentinel value to do so. - board[row][column] = '#'; - - dfs(row, column - 1, index + 1); - dfs(row, column + 1, index + 1); - dfs(row - 1, column, index + 1); - dfs(row + 1, column, index + 1); - - board[row][column] = c; - } - - for (let i = 0; i < board.length; i++) { - for (let j = 0; j < board[i].length; j++) { - // Only start a DFS search if the word begins with the character we are looking at. - if (board[i][j] === word[0]) { - dfs(i, j, 0); - } - - // If we've found the target, return immediately. - if (found) { - return true; - } - } - } - - return false; -} From 037f326b9d5439b39a13275d1b500e5842efe981 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 12 Mar 2025 21:10:29 -0700 Subject: [PATCH 038/158] add this --- src/leetcode/graph/common/nested_integer.py | 22 ++++++++++ .../graph/nested_list_weighted_sum.py | 43 +++++++++++++++++++ .../graph/nested_list_weighted_sum_test.py | 13 ++++++ 3 files changed, 78 insertions(+) create mode 100644 src/leetcode/graph/common/nested_integer.py create mode 100644 src/leetcode/graph/nested_list_weighted_sum.py create mode 100644 test/leetcode/graph/nested_list_weighted_sum_test.py diff --git a/src/leetcode/graph/common/nested_integer.py b/src/leetcode/graph/common/nested_integer.py new file mode 100644 index 0000000..35a3b79 --- /dev/null +++ b/src/leetcode/graph/common/nested_integer.py @@ -0,0 +1,22 @@ +# This class definition comes from the problem itself and we cannot change it, or else our submission will not be +# accepted. +class NestedInteger: + def __init__(self, value=None) -> None: + self.value: int | None = value + self.values: "list[NestedInteger]" = [] + + def isInteger(self) -> bool: + return self.value is not None + + def add(self, e: "NestedInteger") -> None: + self.value = None + self.values.append(e) + + def setInteger(self, value: int): + self.value = value + + def getInteger(self) -> int | None: + return self.value + + def getList(self) -> "list[NestedInteger]": + return self.values diff --git a/src/leetcode/graph/nested_list_weighted_sum.py b/src/leetcode/graph/nested_list_weighted_sum.py new file mode 100644 index 0000000..a466ed6 --- /dev/null +++ b/src/leetcode/graph/nested_list_weighted_sum.py @@ -0,0 +1,43 @@ +# DIFFICULTY: MEDIUM +# +# You are given a nested list of integers nestedList. Each element is either an integer or a list whose elements may +# also be integers or other lists. +# +# The depth of an integer is the number of lists that it is inside of. For example, the nested list [1,[2,2],[[3],2],1] +# has each integer's value set to its depth. +# +# Return the sum of each integer in nestedList multiplied by its depth. +# +# See https://leetcode.com/problems/nested-list-weight-sum +from leetcode.graph.common.nested_integer import NestedInteger + + +class Solution: + def depthSum(self, nestedList: list[NestedInteger]) -> int: + """SOLUTION: + + This is essentially a depth first search problem. However, because it a nested list, there's no need to keep track + of visited elements. Instead we'll just keep track of the depth as we traverse the list. + + COMPLEXITY: + + Time complexity is O(n) where n is the number of total integers and lists in the data structure. + + Space complexity is O(d) where d is the depth of the nested list, since we need a stack frame for each level of + recursion. + """ + + def compute(x: NestedInteger, depth: int) -> int: + # If we've encountered an integer, compute the weighted value according to the depth. + if x.isInteger(): + value = x.getInteger() or 0 + return value * depth + + # Otherwise, we have a list, so we'll recursively compute the weighted sum of the list. + return sum(compute(y, depth + 1) for y in x.getList()) + + x = NestedInteger() + for e in nestedList: + x.add(e) + + return compute(x, 0) diff --git a/test/leetcode/graph/nested_list_weighted_sum_test.py b/test/leetcode/graph/nested_list_weighted_sum_test.py new file mode 100644 index 0000000..f3aa899 --- /dev/null +++ b/test/leetcode/graph/nested_list_weighted_sum_test.py @@ -0,0 +1,13 @@ +from leetcode.graph.common.nested_integer import NestedInteger +from leetcode.graph.nested_list_weighted_sum import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.depthSum([NestedInteger(10)]) == 10 + + +def test_case_2(): + assert soln.depthSum([NestedInteger(10), NestedInteger(20)]) == 30 From 4e3930b6af8e87fa486b43db1d2a8b8edf8bdc29 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 12 Mar 2025 21:11:52 -0700 Subject: [PATCH 039/158] removed --- .../graph/nested-list-weighted-sum.ts | 52 ------------------- .../graph/nested_list_weighted_sum.py | 10 ++-- .../graph/nested-list-weighted-sum.test.ts | 12 ----- 3 files changed, 5 insertions(+), 69 deletions(-) delete mode 100644 src/leetcode/graph/nested-list-weighted-sum.ts delete mode 100644 test/leetcode/graph/nested-list-weighted-sum.test.ts diff --git a/src/leetcode/graph/nested-list-weighted-sum.ts b/src/leetcode/graph/nested-list-weighted-sum.ts deleted file mode 100644 index 962799e..0000000 --- a/src/leetcode/graph/nested-list-weighted-sum.ts +++ /dev/null @@ -1,52 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given a nested list of integers nestedList. Each element is either an integer or a list whose elements may -// also be integers or other lists. -// -// The depth of an integer is the number of lists that it is inside of. For example, the nested list [1,[2,2],[[3],2],1] -// has each integer's value set to its depth. -// -// Return the sum of each integer in nestedList multiplied by its depth. -// -// See {@link https://leetcode.com/problems/nested-list-weight-sum} -import { NestedInteger } from '../linked-list/common/nested-integer'; -export { depthSum }; - -// SOLUTION: -// -// This is essentially a depth first search problem. However, because it a nested list, there's no need to keep track -// of visited elements. Instead we'll just keep track of the depth as we traverse the list. -// -// COMPLEXITY: -// -// Time complexity is O(n) where n is the number of total integers and lists in the data structure. -// -// Space complexity is O(d) where d is the depth of the nested list, since we need a stack frame for each level of -// recursion. -function depthSum(nestedList: NestedInteger[]): number { - function compute(x: NestedInteger, depth: number): number { - // If we've encountered an integer, compute the weighted value according to the depth. - if (x.isInteger()) { - const value = x.getInteger() ?? 0; - const weight = depth; - return value * weight; - } - - // Otherwise, we have a list, so we'll recursively compute the weighted sum of the list. - let result = 0; - const ys = x.getList(); - for (let i = 0; i < ys.length; i++) { - const y = ys[i]; - result += compute(y, depth + 1); - } - - return result; - } - - const x = new NestedInteger(); - for (let i = 0; i < nestedList.length; i++) { - x.add(nestedList[i]); - } - - return compute(x, 0); -} diff --git a/src/leetcode/graph/nested_list_weighted_sum.py b/src/leetcode/graph/nested_list_weighted_sum.py index a466ed6..16869c8 100644 --- a/src/leetcode/graph/nested_list_weighted_sum.py +++ b/src/leetcode/graph/nested_list_weighted_sum.py @@ -14,7 +14,8 @@ class Solution: def depthSum(self, nestedList: list[NestedInteger]) -> int: - """SOLUTION: + """ + SOLUTION: This is essentially a depth first search problem. However, because it a nested list, there's no need to keep track of visited elements. Instead we'll just keep track of the depth as we traverse the list. @@ -26,6 +27,9 @@ def depthSum(self, nestedList: list[NestedInteger]) -> int: Space complexity is O(d) where d is the depth of the nested list, since we need a stack frame for each level of recursion. """ + x = NestedInteger() + for e in nestedList: + x.add(e) def compute(x: NestedInteger, depth: int) -> int: # If we've encountered an integer, compute the weighted value according to the depth. @@ -36,8 +40,4 @@ def compute(x: NestedInteger, depth: int) -> int: # Otherwise, we have a list, so we'll recursively compute the weighted sum of the list. return sum(compute(y, depth + 1) for y in x.getList()) - x = NestedInteger() - for e in nestedList: - x.add(e) - return compute(x, 0) diff --git a/test/leetcode/graph/nested-list-weighted-sum.test.ts b/test/leetcode/graph/nested-list-weighted-sum.test.ts deleted file mode 100644 index f8791e9..0000000 --- a/test/leetcode/graph/nested-list-weighted-sum.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { NestedInteger } from '../../src/linked-list/common/nested-integer'; -import { depthSum } from '../../src/graph/nested-list-weighted-sum'; - -describe('nested list weighted sum', () => { - test('nested list weighted sum - test case 1', async () => { - expect(depthSum([new NestedInteger(10)])).toBe(10); - }); - - test('nested list weighted sum - test case 2', async () => { - expect(depthSum([new NestedInteger(10), new NestedInteger(20)])).toBe(30); - }); -}); From eccaa41f1679b8c520e41679a399cbf0dd02fabf Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 13 Mar 2025 11:31:31 -0700 Subject: [PATCH 040/158] nested weight list 2 --- .../graph/nested-list-weighted-sum-ii.ts | 75 ------------------- .../graph/nested_list_weighted_sum.py | 4 +- .../graph/nested_list_weighted_sum_ii.py | 63 ++++++++++++++++ .../graph/nested-list-weighted-sum-ii.test.ts | 12 --- .../graph/nested_list_weighted_sum_ii_test.py | 13 ++++ 5 files changed, 78 insertions(+), 89 deletions(-) delete mode 100644 src/leetcode/graph/nested-list-weighted-sum-ii.ts create mode 100644 src/leetcode/graph/nested_list_weighted_sum_ii.py delete mode 100644 test/leetcode/graph/nested-list-weighted-sum-ii.test.ts create mode 100644 test/leetcode/graph/nested_list_weighted_sum_ii_test.py diff --git a/src/leetcode/graph/nested-list-weighted-sum-ii.ts b/src/leetcode/graph/nested-list-weighted-sum-ii.ts deleted file mode 100644 index 54d284c..0000000 --- a/src/leetcode/graph/nested-list-weighted-sum-ii.ts +++ /dev/null @@ -1,75 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given a nested list of integers nestedList. Each element is either an integer or a list whose elements may -// also be integers or other lists. -// -// The depth of an integer is the number of lists that it is inside of. For example, the nested list [1,[2,2],[[3],2],1] -// has each integer's value set to its depth. Let maxDepth be the maximum depth of any integer. -// -// The weight of an integer is maxDepth - (the depth of the integer) + 1. -// -// Return the sum of each integer in nestedList multiplied by its weight. -// -// See {@link https://leetcode.com/problems/nested-list-weight-sum-ii/} -import { NestedInteger } from '../linked-list/common/nested-integer'; -export { depthSumInverse }; - -// SOLUTION: -// -// Unlike the previous problem where we just multiply the integer by its depth, we need to multiply by the weight of the -// of the integer. Since we won't know the weight until after we've traversed the entire list, we'll need to keep track -// of depth to integers, then calculate the weight and sum up at the end. -// -// However, like before, this is still going to be a DFS search, except we don't need to keep track of visited nodes. -// -// COMPLEXITY: -// -// Similar to before, the time complexity is O(n) where n is the number of total integers and lists in the data -// structure. -// -// Similar to before, the space complexity is O(d) where d is the depth of the nested list. -function depthSumInverse(nestedList: NestedInteger[]): number { - type Integer = number; - type Depth = number; - - // Define a map of depth to a list of integers; each integer at that depth will have the same weight. We'll compute - // the max depth as we go. - const map = new Map(); - let max = 0; - - // Build up the map of depth -> ints recursively. - function compute(x: NestedInteger, depth: number): void { - if (depth > max) { - max = depth; - } - - if (x.isInteger()) { - const value = x.getInteger() ?? 0; - const list = map.get(depth) ?? []; - list.push(value); - map.set(depth, list); - return; - } - - const ys = x.getList(); - for (let i = 0; i < ys.length; i++) { - const y = ys[i]; - compute(y, depth + 1); - } - } - - for (let i = 0; i < nestedList.length; i++) { - const z = nestedList[i]; - compute(z, 0); - } - - // Once we have the map, just multiply each list of integers at each depth by the computed weight and sum. - let result = 0; - map.forEach((values, depth) => { - const weight = max - depth + 1; - const partial = values.reduce((x, y) => x + y) * weight; - result += partial; - }); - - return result; -} diff --git a/src/leetcode/graph/nested_list_weighted_sum.py b/src/leetcode/graph/nested_list_weighted_sum.py index 16869c8..4575f49 100644 --- a/src/leetcode/graph/nested_list_weighted_sum.py +++ b/src/leetcode/graph/nested_list_weighted_sum.py @@ -17,8 +17,8 @@ def depthSum(self, nestedList: list[NestedInteger]) -> int: """ SOLUTION: - This is essentially a depth first search problem. However, because it a nested list, there's no need to keep track - of visited elements. Instead we'll just keep track of the depth as we traverse the list. + This is essentially a depth first search problem. However, because it a nested list, there's no need to keep + track of visited elements. Instead we'll just keep track of the depth as we traverse the list. COMPLEXITY: diff --git a/src/leetcode/graph/nested_list_weighted_sum_ii.py b/src/leetcode/graph/nested_list_weighted_sum_ii.py new file mode 100644 index 0000000..40f9f86 --- /dev/null +++ b/src/leetcode/graph/nested_list_weighted_sum_ii.py @@ -0,0 +1,63 @@ +# DIFFICULTY: MEDIUM +# +# You are given a nested list of integers nestedList. Each element is either an integer or a list whose elements may +# also be integers or other lists. +# +# The depth of an integer is the number of lists that it is inside of. For example, the nested list [1,[2,2],[[3],2],1] +# has each integer's value set to its depth. Let maxDepth be the maximum depth of any integer. +# +# The weight of an integer is maxDepth - (the depth of the integer) + 1. +# +# Return the sum of each integer in nestedList multiplied by its weight. +# +# See https://leetcode.com/problems/nested-list-weight-sum-ii +from collections import defaultdict +from leetcode.graph.common.nested_integer import NestedInteger + + +class Solution: + def depthSumInverse(self, nestedList: list[NestedInteger]) -> int: + """ + SOLUTION: + + Unlike the previous problem where we just multiply the integer by its depth, we need to multiply by the weight + of the integer. Since we won't know the weight until after we've traversed the entire list, we'll need to keep + track of depth to integers, then calculate the weight and sum up at the end. + + However, like before, this is still going to be a DFS search, except we don't need to keep track of visited + nodes. + + COMPLEXITY: + + Time complexity is O(n) where n is the number of total integers and lists in the data structure. + + Space complexity is O(d) where d is the depth of the nested list. + """ + # Define a list of depth -> integers; each integer at that depth will have the same weight. We'll compute the + # max depth as we traverse. + map: dict[int, list[int]] = defaultdict(list[int]) + max_depth = 0 + + def compute(x: NestedInteger, depth: int) -> None: + nonlocal max_depth + max_depth = max(depth, max_depth) + + if x.isInteger(): + value = x.getInteger() or 0 + map[depth].append(value) + else: + values = x.getList() + for value in values: + compute(value, depth + 1) + + for nested in nestedList: + compute(nested, 0) + + # Now that we have the map, just multiply each list of integers at each depth by the computed weight, and sum. + result = 0 + for depth, values in map.items(): + weight = max_depth - depth + 1 + partial = sum(values) * weight + result += partial + + return result diff --git a/test/leetcode/graph/nested-list-weighted-sum-ii.test.ts b/test/leetcode/graph/nested-list-weighted-sum-ii.test.ts deleted file mode 100644 index ab6b936..0000000 --- a/test/leetcode/graph/nested-list-weighted-sum-ii.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { NestedInteger } from '../../src/linked-list/common/nested-integer'; -import { depthSumInverse } from '../../src/graph/nested-list-weighted-sum-ii'; - -describe('nested list weighted sum ii', () => { - test('nested list weighted sum ii - test case 1', async () => { - expect(depthSumInverse([new NestedInteger(10)])).toBe(10); - }); - - test('nested list weighted sum ii - test case 2', async () => { - expect(depthSumInverse([new NestedInteger(10), new NestedInteger(20)])).toBe(30); - }); -}); diff --git a/test/leetcode/graph/nested_list_weighted_sum_ii_test.py b/test/leetcode/graph/nested_list_weighted_sum_ii_test.py new file mode 100644 index 0000000..4df6b05 --- /dev/null +++ b/test/leetcode/graph/nested_list_weighted_sum_ii_test.py @@ -0,0 +1,13 @@ +from leetcode.graph.common.nested_integer import NestedInteger +from leetcode.graph.nested_list_weighted_sum_ii import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.depthSumInverse([NestedInteger(10)]) == 10 + + +def test_case_2(): + assert soln.depthSumInverse([NestedInteger(10), NestedInteger(20)]) == 30 From 9c5f39a320f38b561adb0e58088de8edb71ccb55 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 13 Mar 2025 12:03:47 -0700 Subject: [PATCH 041/158] updated docstrings --- .../best_time_to_buy_and_sell_stock_ii.py | 6 ++++-- .../array/buildings_with_an_ocean_view.py | 8 +++++-- src/leetcode/array/design_hit_counter.py | 6 ++++-- .../dot_product_of_two_sparse_vectors.py | 6 ++++-- src/leetcode/array/group_anagrams.py | 6 ++++-- .../array/longest_consecutive_sequence.py | 6 ++++-- .../array/max_chunks_to_make_sorted.py | 6 ++++-- src/leetcode/array/maximum_swap.py | 6 ++++-- src/leetcode/array/merge_sorted_array.py | 6 ++++-- src/leetcode/array/pascals_triangle.py | 3 ++- src/leetcode/array/top_k_frequent_elements.py | 6 ++++-- src/leetcode/graph/parallel_job_scheduling.py | 21 +++++++++++++++++++ 12 files changed, 65 insertions(+), 21 deletions(-) create mode 100644 src/leetcode/graph/parallel_job_scheduling.py diff --git a/src/leetcode/array/best_time_to_buy_and_sell_stock_ii.py b/src/leetcode/array/best_time_to_buy_and_sell_stock_ii.py index 2e9f9c4..e79150b 100644 --- a/src/leetcode/array/best_time_to_buy_and_sell_stock_ii.py +++ b/src/leetcode/array/best_time_to_buy_and_sell_stock_ii.py @@ -11,7 +11,8 @@ class Solution: def maxProfit(self, prices: list[int]) -> int: """ - SOLUTION: + SOLUTION + -------- The question is a bit contrived, as in reality this would never happen. Here, we are assuming we can go backwards in time to be able to buy at the low point and sell at the high point. Just keep that in mind: we @@ -23,7 +24,8 @@ def maxProfit(self, prices: list[int]) -> int: This actually makes the problem much easier because can simulate buying on every day and add to our profit if there is any. Honestly, this should be easy and the other one should be medium. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(n). diff --git a/src/leetcode/array/buildings_with_an_ocean_view.py b/src/leetcode/array/buildings_with_an_ocean_view.py index c2a33e9..faeb5eb 100644 --- a/src/leetcode/array/buildings_with_an_ocean_view.py +++ b/src/leetcode/array/buildings_with_an_ocean_view.py @@ -12,13 +12,15 @@ class Solution: def findBuildings(self, heights: list[int]) -> list[int]: """ - SOLUTION: + SOLUTION + -------- This is easier to do iterating from right to left (since the ocean is to the right). We can keep track of the tallest building we've seen so far, and if we encounter a building that is taller, we can add it to the list with ocean views. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(n) because we are iterating through the list of buildings once, and reversing once. @@ -33,4 +35,6 @@ def findBuildings(self, heights: list[int]) -> list[int]: tallest = height result.append(i) + # Because we want the buildings in increasing order and we iterated in reverse, reverse the result list. We + # could have used a deque, but converting the deque to a list will be O(n) anyways. return result[::-1] diff --git a/src/leetcode/array/design_hit_counter.py b/src/leetcode/array/design_hit_counter.py index a2c20a2..9b0c516 100644 --- a/src/leetcode/array/design_hit_counter.py +++ b/src/leetcode/array/design_hit_counter.py @@ -10,12 +10,14 @@ class HitCounter: def __init__(self) -> None: """ - SOLUTION: + SOLUTION + -------- To do this efficiently we'll have to use a circular array buffer. This is the same technique used by time series databases. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(1) for both methods since we fix the array size at 300. diff --git a/src/leetcode/array/dot_product_of_two_sparse_vectors.py b/src/leetcode/array/dot_product_of_two_sparse_vectors.py index ae2f059..3a47e6d 100644 --- a/src/leetcode/array/dot_product_of_two_sparse_vectors.py +++ b/src/leetcode/array/dot_product_of_two_sparse_vectors.py @@ -14,7 +14,8 @@ class SparseVector: def __init__(self, nums: list[int]) -> None: """ - SOLUTION: + SOLUTION + -------- If the vector is sparse, just store the non-zero values in a map of index to value. That will compress for storage; to compute the dot product you can either decompress and then do the dot product, or you can do the @@ -23,7 +24,8 @@ def __init__(self, nums: list[int]) -> None: Doing it without decompression is more efficient, and we should start with the smaller vector to minimize the number of operations. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(k) in the best case, where k is the number of non-zero elements. In the worst case, though, it could still be O(n) if both vectors are dense. diff --git a/src/leetcode/array/group_anagrams.py b/src/leetcode/array/group_anagrams.py index 9fcd92b..01ad75c 100644 --- a/src/leetcode/array/group_anagrams.py +++ b/src/leetcode/array/group_anagrams.py @@ -12,12 +12,14 @@ class Solution: def groupAnagrams(self, texts: list[str]) -> list[list[str]]: """ - SOLUTION: + SOLUTION + -------- Each anagram can be rearranged into canonical form by sorting the letters. Then simply map the canonical form to each anagram. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(n * m * log m) where n is the number of strings, and m is the length of the longest string. This is because we have to sort each string's characters in O(m * log m), and there are n strings. diff --git a/src/leetcode/array/longest_consecutive_sequence.py b/src/leetcode/array/longest_consecutive_sequence.py index 6c49637..c564e0d 100644 --- a/src/leetcode/array/longest_consecutive_sequence.py +++ b/src/leetcode/array/longest_consecutive_sequence.py @@ -8,7 +8,8 @@ class Solution: def longestConsecutive(self, xs: list[int]) -> int: """ - SOLUTION: + SOLUTION + -------- The problem is phrased in a very confusing way. The sequence doesn't ACTUALLY need to be consecutive within the array; it only needs to be consecutive if you plucked the sequence out of the array and sorted it. @@ -20,7 +21,8 @@ def longestConsecutive(self, xs: list[int]) -> int: Conceptually, we'll do this by throwing all the array elements into a set. Then, for each element `x`, we can figure out if it's part of a sequence by repeatedly checking its predecessor `x - 1` in the set. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(n). It may appear that the inner loop runs multiple times, but each element in the array is only processed once; the inner loop will skip `x - 1` if it was already part of some other sequence from a diff --git a/src/leetcode/array/max_chunks_to_make_sorted.py b/src/leetcode/array/max_chunks_to_make_sorted.py index a093bc3..4721876 100644 --- a/src/leetcode/array/max_chunks_to_make_sorted.py +++ b/src/leetcode/array/max_chunks_to_make_sorted.py @@ -11,14 +11,16 @@ class Solution: def maxChunksToSorted(self, xs: list[int]) -> int: """ - SOLUTION: + SOLUTION + -------- Because we know the elements in the array are a permutation of numbers less than n, we can use a greedy approach by keeping track of the max element seen so far. Unlike the other problem, this does not require using a stack. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(n). diff --git a/src/leetcode/array/maximum_swap.py b/src/leetcode/array/maximum_swap.py index 254ec09..ea682e6 100644 --- a/src/leetcode/array/maximum_swap.py +++ b/src/leetcode/array/maximum_swap.py @@ -8,7 +8,8 @@ class Solution: def maximumSwap(self, num: int) -> int: """ - SOLUTION: + SOLUTION + -------- The simple idea of swapping the largest number with the first digit doesn't always work. It works a lot of the time, but a couple of cases mess up this algorithm: @@ -21,7 +22,8 @@ def maximumSwap(self, num: int) -> int: 1. Find the FIRST digit that can be made bigger (larger digits appear later). 2. Swap it with the LAST occurrence of the largest digit. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(n) where n is the number of digits. We iterate through the digits once to create our array and map. We iterate through the digits a second time to find the swap point. There is a nested loop, but the diff --git a/src/leetcode/array/merge_sorted_array.py b/src/leetcode/array/merge_sorted_array.py index 117935a..7b69f79 100644 --- a/src/leetcode/array/merge_sorted_array.py +++ b/src/leetcode/array/merge_sorted_array.py @@ -13,7 +13,8 @@ class Solution: def merge(self, xs: list[int], m: int, ys: list[int], n: int) -> None: """ - SOLUTION: + SOLUTION + -------- TLDR: Work backwards. Send the largest elements from the smaller array to the end of the bigger array. @@ -40,7 +41,8 @@ def merge(self, xs: list[int], m: int, ys: list[int], n: int) -> None: The problem doesn't state this, but we assume using no extra memory either. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(m + n). diff --git a/src/leetcode/array/pascals_triangle.py b/src/leetcode/array/pascals_triangle.py index 885a70c..e66b8e5 100644 --- a/src/leetcode/array/pascals_triangle.py +++ b/src/leetcode/array/pascals_triangle.py @@ -12,7 +12,8 @@ def generate(self, numRows: int) -> list[list[int]]: A simple straightforward algorithm works. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(n). diff --git a/src/leetcode/array/top_k_frequent_elements.py b/src/leetcode/array/top_k_frequent_elements.py index 1e1f627..85867f2 100644 --- a/src/leetcode/array/top_k_frequent_elements.py +++ b/src/leetcode/array/top_k_frequent_elements.py @@ -10,11 +10,13 @@ class Solution: def topKFrequent(self, xs: list[int], k: int) -> list[int]: """ - SOLUTION: + SOLUTION + -------- Just map each number to its frequency then sort by frequency. Return the first k elements. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity dominated by the sort, which is O(n log n). diff --git a/src/leetcode/graph/parallel_job_scheduling.py b/src/leetcode/graph/parallel_job_scheduling.py new file mode 100644 index 0000000..5bdcf1b --- /dev/null +++ b/src/leetcode/graph/parallel_job_scheduling.py @@ -0,0 +1,21 @@ +# DIFFICULTY: MEDIUM +# +# Given a list tasks and their dependencies ['A:B,C', 'B:D,C,E', 'F:G'], return a list of parallelizable tasks in +# order. For example, in this situation [(D, C, E, F), (B, G), (A)] would be a valid order because all parallelizable +# tasks are grouped together. +# +# NOTE: This is not a LeetCode question but was asked by Cruise. +# +# See https://leetcode.com/discuss/interview-question/353830/google-phone-screen-parallel-job-scheduling +from collections import defaultdict, deque + +class Solution: + def parallelize(self, jobs: list[str]) -> list[list[str]]: + """ + SOLUTION + -------- + + Use BFS with Kahn's algorithm to perform topological sort. + """ + graph = defaultdict(set) + in_degree = defaultdict(int) \ No newline at end of file From ccd0fe87584874423390cf226430a00f446d0dae Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 13 Mar 2025 12:26:06 -0700 Subject: [PATCH 042/158] docstrings --- src/leetcode/array/two_sum.py | 10 ++++++---- ...ast_position_of_element_in_sorted_array.py | 6 ++++-- .../binary_search/find_peak_element.py | 6 ++++-- ..._the_smallest_divisor_given_a_threshold.py | 20 +++++++++++-------- .../kth_missing_positive_number.py | 6 ++++-- ...kth_smallest_element_in_a_sorted_matrix.py | 6 ++++-- .../longest_increasing_subsequence.py | 6 ++++-- .../median_of_two_sorted_arrays.py | 6 ++++-- src/leetcode/graph/parallel_job_scheduling.py | 5 +++-- 9 files changed, 45 insertions(+), 26 deletions(-) diff --git a/src/leetcode/array/two_sum.py b/src/leetcode/array/two_sum.py index 3e1d7cd..6e89b6b 100644 --- a/src/leetcode/array/two_sum.py +++ b/src/leetcode/array/two_sum.py @@ -11,15 +11,17 @@ class Solution: def twoSum(self, xs: list[int], target: int) -> list[int]: """ - SOLUTION: + SOLUTION + -------- - Use a hashmap to keep track of values we've already seen. This lets us avoid the O(n^2) solution of checking every - possible pair of values. + Use a hashmap to keep track of values we've already seen. This lets us avoid the O(n^2) solution of checking + every possible pair of values. When iterating, you can check if the complement nums[j] = target - nums[i] already exists in the hashmap. If it does then we've found the solution and can return the indices immediately. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(n). diff --git a/src/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array.py b/src/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array.py index 3f3b04b..b3f2ae6 100644 --- a/src/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array.py +++ b/src/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array.py @@ -10,7 +10,8 @@ class Solution: def searchRange(self, xs: list[int], target: int) -> list[int]: """ - SOLUTION: + SOLUTION + -------- The problem stipulates it must run in O(log n) time complexity, so that means we can't just do a standard binary search and then a linear scan for the start and end ranges. @@ -21,7 +22,8 @@ def searchRange(self, xs: list[int], target: int) -> list[int]: Technically we could combine both the left and right searches into one binary search, using a flag to denote the direction we are searching in. But that would be a bit more complex and harder to understand. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(log n) time complexity because of max two binary searches. diff --git a/src/leetcode/binary_search/find_peak_element.py b/src/leetcode/binary_search/find_peak_element.py index 19f8a2a..c72037f 100644 --- a/src/leetcode/binary_search/find_peak_element.py +++ b/src/leetcode/binary_search/find_peak_element.py @@ -14,7 +14,8 @@ class Solution: def findPeakElement(self, xs: list[int]) -> int: """ - SOLUTION: + SOLUTION + -------- This problem is not hard, but the wording is incredibly tricky. First off, let's talk about linear scan versus binary search. There are only 1000 elements max in the array, so either would work. However, the problem says @@ -31,7 +32,8 @@ def findPeakElement(self, xs: list[int]) -> int: the inputs are invalid (e.g. have no peaks), or that all inputs are valid. However, it does apply to pass if we assume peaks always exist! - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(log n). diff --git a/src/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold.py b/src/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold.py index a1491aa..e59438e 100644 --- a/src/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold.py +++ b/src/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold.py @@ -16,21 +16,22 @@ class Solution: def smallestDivisor(self, xs: list[int], threshold: int) -> int: """ - SOLUTION: + SOLUTION + -------- The smallest divisor is 1. This would maximize the sum. The largest divisor is Math.max(...nums). This would minimize the sum. We want a sum that is exactly the threshold or just belong. This is a good candidate to use binary search. - """ - def compute(ys: list[int], divisor: int): - # We do math.ceil because the problem asks us to round up. - # - # Note: there's no way to chain map -> reduce. Mind boggling. - return sum(math.ceil(x / divisor) for x in xs) + COMPLEXITY + ---------- + + Time complexity is O(n log k) where n is the length of the list and k is the max element in the list. + Space complexity is O(1). + """ # Use the insert point binary search approach to find the divisor we want. left = 1 right = max(xs) @@ -38,7 +39,10 @@ def compute(ys: list[int], divisor: int): while left < right: mid = (left + right) // 2 divisor = mid - value = compute(xs, divisor) + # We do math.ceil because the problem asks us to round up. + # + # Note: there's no way to chain map -> reduce. Mind boggling. + value = sum(math.ceil(x / divisor for x in xs)) # If the value is too large, our divisor was too small, so we should shift our left value to be mid + 1. if value > threshold: diff --git a/src/leetcode/binary_search/kth_missing_positive_number.py b/src/leetcode/binary_search/kth_missing_positive_number.py index 86e2c09..c2a59c0 100644 --- a/src/leetcode/binary_search/kth_missing_positive_number.py +++ b/src/leetcode/binary_search/kth_missing_positive_number.py @@ -8,7 +8,8 @@ class Solution: def findKthPositive(self, xs: list[int], k: int) -> int: """ - SOLUTION: + SOLUTION + -------- There are multiple naive ways to do this. One way is to throw all elements into a set, then iterate from 1 to the max set element, and check if the element is in the set. Do this k times to find the k-th missing element. @@ -22,7 +23,8 @@ def findKthPositive(self, xs: list[int], k: int) -> int: to scan the entire array. We can calculate how many missing numbers exist at each index, and use that information to determine if we should go left or right. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(log n). diff --git a/src/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix.py b/src/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix.py index 8cda580..92bc5f3 100644 --- a/src/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix.py +++ b/src/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix.py @@ -11,7 +11,8 @@ class Solution: def kthSmallest(self, matrix: list[list[int]], k: int) -> int: """ - SOLUTION: + SOLUTION + -------- We can't flatten the list because that would take O(n^2) memory. @@ -21,7 +22,8 @@ def kthSmallest(self, matrix: list[list[int]], k: int) -> int: We can use the mid element to tell us how close or far we are from the kth smallest element. The matrix is sorted, so for any mid === matrix[i][j] we can figure out how many elements are less than or equal to mid. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(n log m). We are using binary search on a range between the smallest and largest elements in the matrix, call it m. We will do O(log m) iterations, but in each iteration, we do have to count how many diff --git a/src/leetcode/binary_search/longest_increasing_subsequence.py b/src/leetcode/binary_search/longest_increasing_subsequence.py index e7826d3..425a974 100644 --- a/src/leetcode/binary_search/longest_increasing_subsequence.py +++ b/src/leetcode/binary_search/longest_increasing_subsequence.py @@ -6,7 +6,8 @@ class Solution: def lengthOfLIS(self, xs: list[int]) -> int: """ - SOLUTION: + SOLUTION + -------- The naive way is to do this with dynamic programming and two nested loops. @@ -21,7 +22,8 @@ def lengthOfLIS(self, xs: list[int]) -> int: As we iterate through the array, we will keep the tails array updated. At the end of iteration, the length of the tails array will tell us the longest increasing subsequence. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(n log n) because we are doing a binary search for each element in the array. diff --git a/src/leetcode/binary_search/median_of_two_sorted_arrays.py b/src/leetcode/binary_search/median_of_two_sorted_arrays.py index 2ea41a1..77106ce 100644 --- a/src/leetcode/binary_search/median_of_two_sorted_arrays.py +++ b/src/leetcode/binary_search/median_of_two_sorted_arrays.py @@ -11,7 +11,8 @@ class Solution: def findMedianSortedArrays(self, xs: list[int], ys: list[int]) -> float: """ - SOLUTION: + SOLUTION + -------- This is not a reasonable question to ask in an interview. It's quite algorithmically complex. @@ -45,7 +46,8 @@ def findMedianSortedArrays(self, xs: list[int], ys: list[int]) -> float: Continue updating the left and right pointers until we have found the median. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(log (m + n)). diff --git a/src/leetcode/graph/parallel_job_scheduling.py b/src/leetcode/graph/parallel_job_scheduling.py index 5bdcf1b..e6bef75 100644 --- a/src/leetcode/graph/parallel_job_scheduling.py +++ b/src/leetcode/graph/parallel_job_scheduling.py @@ -7,7 +7,8 @@ # NOTE: This is not a LeetCode question but was asked by Cruise. # # See https://leetcode.com/discuss/interview-question/353830/google-phone-screen-parallel-job-scheduling -from collections import defaultdict, deque +from collections import defaultdict + class Solution: def parallelize(self, jobs: list[str]) -> list[list[str]]: @@ -18,4 +19,4 @@ def parallelize(self, jobs: list[str]) -> list[list[str]]: Use BFS with Kahn's algorithm to perform topological sort. """ graph = defaultdict(set) - in_degree = defaultdict(int) \ No newline at end of file + in_degree = defaultdict(int) From 03b7bba0772e52cebd4c8eafc30c3cd41fbe3909 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 13 Mar 2025 12:39:06 -0700 Subject: [PATCH 043/158] docs --- .../binary_search/search_in_rotated_sorted_array.py | 10 +++++++++- src/leetcode/graph/add_edges_to_make_degrees_even.py | 6 ++++-- src/leetcode/graph/bus_routes.py | 6 ++++-- src/leetcode/graph/nested_list_weighted_sum.py | 6 ++++-- src/leetcode/graph/nested_list_weighted_sum_ii.py | 6 ++++-- src/leetcode/graph/word_search.py | 6 ++++-- src/leetcode/graph/word_search_ii.py | 6 ++++-- src/leetcode/heap/furthest_building_you_can_reach.py | 6 ++++-- src/leetcode/heap/k_closest_points_to_origin.py | 6 ++++-- src/leetcode/heap/kth_largest_element.py | 3 ++- src/leetcode/heap/max_stack.py | 8 ++++++-- src/leetcode/heap/merge_k_sorted_lists.py | 6 ++++-- src/leetcode/heap/minimize_deviation_in_array.py | 6 ++++-- src/leetcode/heap/number_of_orders_in_the_backlog.py | 6 ++++-- src/leetcode/heap/task_scheduler.py | 12 +++++++++++- src/leetcode/recursion/generate_parentheses.py | 6 ++++-- src/leetcode/stack/valid_parentheses.py | 6 ++++-- src/leetcode/string/valid_word_abbreviation.py | 6 ++++-- src/leetcode/two_pointers/bag_of_tokens.py | 6 ++++-- 19 files changed, 88 insertions(+), 35 deletions(-) diff --git a/src/leetcode/binary_search/search_in_rotated_sorted_array.py b/src/leetcode/binary_search/search_in_rotated_sorted_array.py index cab36d7..282fc4e 100644 --- a/src/leetcode/binary_search/search_in_rotated_sorted_array.py +++ b/src/leetcode/binary_search/search_in_rotated_sorted_array.py @@ -15,13 +15,21 @@ class Solution: def search(self, xs: list[int], target: int) -> int: """ - SOLUTION: + SOLUTION + -------- You can still use binary search for this; you just have to recognize when you have looped around in the array. This uses the standard binary search algorithm, but with recursion. The standard approach is used because we are looking for an exact match for an element in the array, and return -1 if the element is not found. + + COMPLEXITY + ---------- + + Time complexity is O(log n). + + Space complexity is O(log n). """ def searchInternal(ys: list[int], start: int, end: int, t: int) -> int: diff --git a/src/leetcode/graph/add_edges_to_make_degrees_even.py b/src/leetcode/graph/add_edges_to_make_degrees_even.py index afd044c..f117b76 100644 --- a/src/leetcode/graph/add_edges_to_make_degrees_even.py +++ b/src/leetcode/graph/add_edges_to_make_degrees_even.py @@ -16,7 +16,8 @@ class Solution: def isPossible(self, n: int, edges: list[list[int]]) -> bool: """ - SOLUTION: + SOLUTION + -------- When you add an edge between two nodes, you change the degree of those two nodes. We don't need to modify edges for any even degree nodes; they are already good. Instead we want to find nodes with an odd degree. @@ -24,7 +25,8 @@ def isPossible(self, n: int, edges: list[list[int]]) -> bool: If we join any two odd degree nodes with an edge, both of them will become even degree nodes. Since we can only add at most two edges, this can only be possible if we have 0, 2, or 4 nodes with an odd degree. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(m + n). Each node and edge must be examined. diff --git a/src/leetcode/graph/bus_routes.py b/src/leetcode/graph/bus_routes.py index 06c0bef..614bc66 100644 --- a/src/leetcode/graph/bus_routes.py +++ b/src/leetcode/graph/bus_routes.py @@ -18,7 +18,8 @@ class Solution: def numBusesToDestination(self, routes: list[list[int]], source: int, target: int) -> int: """ - SOLUTION: + SOLUTION + -------- This seems to be just a BFS problem. The fact that buses repeat forever doesn't seem to be relevant because there is no cost associated with waiting at a bus stop until the bus arrives; the only thing we are minimizing @@ -27,7 +28,8 @@ def numBusesToDestination(self, routes: list[list[int]], source: int, target: in Building a graph and then running BFS on it naively seems to exceed execution time so we'll have to do something more clever and dirty. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(n * m) where n is the number of bus routes, and m is the number of stops per route. diff --git a/src/leetcode/graph/nested_list_weighted_sum.py b/src/leetcode/graph/nested_list_weighted_sum.py index 4575f49..907a74e 100644 --- a/src/leetcode/graph/nested_list_weighted_sum.py +++ b/src/leetcode/graph/nested_list_weighted_sum.py @@ -15,12 +15,14 @@ class Solution: def depthSum(self, nestedList: list[NestedInteger]) -> int: """ - SOLUTION: + SOLUTION + -------- This is essentially a depth first search problem. However, because it a nested list, there's no need to keep track of visited elements. Instead we'll just keep track of the depth as we traverse the list. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(n) where n is the number of total integers and lists in the data structure. diff --git a/src/leetcode/graph/nested_list_weighted_sum_ii.py b/src/leetcode/graph/nested_list_weighted_sum_ii.py index 40f9f86..5b34efe 100644 --- a/src/leetcode/graph/nested_list_weighted_sum_ii.py +++ b/src/leetcode/graph/nested_list_weighted_sum_ii.py @@ -18,7 +18,8 @@ class Solution: def depthSumInverse(self, nestedList: list[NestedInteger]) -> int: """ - SOLUTION: + SOLUTION + -------- Unlike the previous problem where we just multiply the integer by its depth, we need to multiply by the weight of the integer. Since we won't know the weight until after we've traversed the entire list, we'll need to keep @@ -27,7 +28,8 @@ def depthSumInverse(self, nestedList: list[NestedInteger]) -> int: However, like before, this is still going to be a DFS search, except we don't need to keep track of visited nodes. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(n) where n is the number of total integers and lists in the data structure. diff --git a/src/leetcode/graph/word_search.py b/src/leetcode/graph/word_search.py index ce7c32e..6d5c111 100644 --- a/src/leetcode/graph/word_search.py +++ b/src/leetcode/graph/word_search.py @@ -9,13 +9,15 @@ class Solution: def exist(self, board: list[list[str]], word: str) -> bool: """ - SOLUTION: + SOLUTION + -------- This is just DFS; no need to build a prefix trie, in comparison to word search ii. BFS isn't going to work as well because BFS will generate all possible words incrementally, whereas we want to just stop if we've found our word. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(m * n * 4^k) where m and n are the dimensions of the board, and k is the length of the longest word. diff --git a/src/leetcode/graph/word_search_ii.py b/src/leetcode/graph/word_search_ii.py index 439e2ad..0ae74cf 100644 --- a/src/leetcode/graph/word_search_ii.py +++ b/src/leetcode/graph/word_search_ii.py @@ -23,7 +23,8 @@ def add(self, word: str) -> None: class Solution: def findWords(self, board: list[list[str]], words: list[str]) -> list[str]: """ - SOLUTION: + SOLUTION + -------- This problem looks like it can be solved with a prefix trie. @@ -34,7 +35,8 @@ def findWords(self, board: list[list[str]], words: list[str]) -> list[str]: Easy peasy. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(w * k + m * n * 4^k) where w is the number of words, k is the maximum word length, m and n are the dimensions of the board. Building the trie takes O(w * k). For DFS, we perform DFS on every cell (of diff --git a/src/leetcode/heap/furthest_building_you_can_reach.py b/src/leetcode/heap/furthest_building_you_can_reach.py index 9afd1a7..e008887 100644 --- a/src/leetcode/heap/furthest_building_you_can_reach.py +++ b/src/leetcode/heap/furthest_building_you_can_reach.py @@ -20,7 +20,8 @@ class Solution: def furthestBuilding(self, heights: list[int], bricks: int, ladders: int) -> int: """ - SOLUTION: + SOLUTION + -------- You can't do this problem using sliding window or two pointer technique easily. @@ -32,7 +33,8 @@ def furthestBuilding(self, heights: list[int], bricks: int, ladders: int) -> int A greedy algorithm would be better suited to solve this problem. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(n log n) time where n is number of buildings. diff --git a/src/leetcode/heap/k_closest_points_to_origin.py b/src/leetcode/heap/k_closest_points_to_origin.py index 2ed8245..4c8983d 100644 --- a/src/leetcode/heap/k_closest_points_to_origin.py +++ b/src/leetcode/heap/k_closest_points_to_origin.py @@ -15,7 +15,8 @@ class Solution: def kClosest(self, points: list[list[int]], k: int) -> list[list[int]]: """ - SOLUTION: + SOLUTION + -------- The request for "k-closest" indicates you'll want a heap. Using a either a min heap or max heap will work. @@ -28,7 +29,8 @@ def kClosest(self, points: list[list[int]], k: int) -> list[list[int]]: The max heap approach can be more efficient, so we'll go that route. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(n log k) with a max heap, which will be faster when k is much smaller than n. If we go with the min heap approach, it's O(n log n). diff --git a/src/leetcode/heap/kth_largest_element.py b/src/leetcode/heap/kth_largest_element.py index 16bd45f..7c0963b 100644 --- a/src/leetcode/heap/kth_largest_element.py +++ b/src/leetcode/heap/kth_largest_element.py @@ -13,7 +13,8 @@ class Solution: def findKthLargest(self, nums: list[int], k: int) -> int: """ - SOLUTION: + SOLUTION + -------- You can do this with quick select, similar to quick sort. The idea is to eliminate half of the search space at each step. You pick a pivot element (using heuristics, or even randomly), then partition the array into two diff --git a/src/leetcode/heap/max_stack.py b/src/leetcode/heap/max_stack.py index efaaa60..6cf9755 100644 --- a/src/leetcode/heap/max_stack.py +++ b/src/leetcode/heap/max_stack.py @@ -33,7 +33,8 @@ def __init__(self, key: int, value: int): class MaxStack: def __init__(self): """ - SOLUTION: + SOLUTION + -------- Use a heap and a doubly linked list to represent the max stack. Most operations can be performed in O(1) or O(log n). The only challenge is that when popping from the stack, it's easy to remove from the end of the @@ -45,9 +46,12 @@ def __init__(self): For the purposes of this solution, we won't consider edge cases like popping from an empty stack. This keeps the solution simple. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(1) for top and O(log n) for each other call. + + Space complexity is O(n). """ # Because the stack could have multiple duplicate elements, use a unique ID for each node. self.keys = 0 diff --git a/src/leetcode/heap/merge_k_sorted_lists.py b/src/leetcode/heap/merge_k_sorted_lists.py index b6ea39e..e809f59 100644 --- a/src/leetcode/heap/merge_k_sorted_lists.py +++ b/src/leetcode/heap/merge_k_sorted_lists.py @@ -12,7 +12,8 @@ class Solution: def mergeKLists(self, lists: list[ListNode | None]) -> ListNode | None: """ - SOLUTION: + SOLUTION + -------- Instead of only 2 lists, we have to merge k lists. The naive way is to do a linear scan of the head of every list to find the smallest element then create a new list node with that element and append it to the result @@ -22,7 +23,8 @@ def mergeKLists(self, lists: list[ListNode | None]) -> ListNode | None: without a linear scan. Additionally, the problem does not say we cannot reuse the input lists, so we do not have to create new list nodes. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(n log k), where n is the number of nodes and there are k lists, so each enqueuing operation will take O(log k) time. However, we are going to do this for every node in the list, so the total time diff --git a/src/leetcode/heap/minimize_deviation_in_array.py b/src/leetcode/heap/minimize_deviation_in_array.py index a6d6164..286cbb1 100644 --- a/src/leetcode/heap/minimize_deviation_in_array.py +++ b/src/leetcode/heap/minimize_deviation_in_array.py @@ -26,7 +26,8 @@ class Solution: def minimumDeviation(self, nums: list[int]): """ - SOLUTION: + SOLUTION + -------- To solve this problem, we have to multiply elements to make them bigger, or divide elements to make them smaller. We continue to do this until the minimum and maximum elements are as close as possible. @@ -35,7 +36,8 @@ def minimumDeviation(self, nums: list[int]): simultaneously trying to make numbers bigger and smaller. To do this, we multiply all odd numbers by 2, so that they all become even. Afterwards, we can choose to perform a division or not to make it smaller. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(n log m) where n is the number of elements in nums, and m is the number of times we have to halve a value. diff --git a/src/leetcode/heap/number_of_orders_in_the_backlog.py b/src/leetcode/heap/number_of_orders_in_the_backlog.py index f95b90c..d4f446c 100644 --- a/src/leetcode/heap/number_of_orders_in_the_backlog.py +++ b/src/leetcode/heap/number_of_orders_in_the_backlog.py @@ -30,13 +30,15 @@ class Solution: def getNumberOfBacklogOrders(self, orders: list[list[int]]) -> int: """ - SOLUTION: + SOLUTION + -------- This problem looks like it can be solved by maintaining a sorted list of buy orders and sell orders (aka heaps). For a sell order, you want buy orders in largest to smallest (max heap). For a buy order, you want sell orders from smallest to largest (min heap). - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(n log n) because we are using heaps to maintain the order of the orders. diff --git a/src/leetcode/heap/task_scheduler.py b/src/leetcode/heap/task_scheduler.py index d1a6d95..cc5fa4f 100644 --- a/src/leetcode/heap/task_scheduler.py +++ b/src/leetcode/heap/task_scheduler.py @@ -14,7 +14,8 @@ class Solution: def leastInterval(self, tasks: list[str], n: int) -> int: """ - SOLUTION: + SOLUTION + -------- It's not necessary to return an execution order, only the minimum number of cycles/intervals required to complete all of the tasks. @@ -31,6 +32,15 @@ def leastInterval(self, tasks: list[str], n: int) -> int: Here, even though task B appears 9 times, we can fit task B comfortably in between the 9 cycles of task A. This, however, doesn't quite work if there are a huge number of tasks; if there are 10 task A's and 9 task B's, but 50 other uniquely named tasks, then we will still take 50 cycles if (10 - 1) * n is a smaller number. + + COMPLEXITY + ---------- + + Time complexity is O(m + n) where m is the total number of tasks. While we do perform O(k log k) operations to + build the heap, k is always at most 26 characters, so that is still effectively constant time. Likewise, any + log k operations are effectively constant time as well. + + Space complexity is O(n). """ # Create a map of task to frequency. map: dict[str, int] = defaultdict(int) diff --git a/src/leetcode/recursion/generate_parentheses.py b/src/leetcode/recursion/generate_parentheses.py index 6080cad..6ac696c 100644 --- a/src/leetcode/recursion/generate_parentheses.py +++ b/src/leetcode/recursion/generate_parentheses.py @@ -6,7 +6,8 @@ class Solution: def generateParenthesis(self, n: int) -> list[int]: """ - SOLUTION: + SOLUTION + -------- A naive way to do this would be to do something like generate all the parentheses for n-1, then add more parentheses to each of the elements generated. For example, by transforming each element of the n-1 solution to @@ -20,7 +21,8 @@ def generateParenthesis(self, n: int) -> list[int]: method, for example, you don't need to worry about balancing the elements within the sub set. In contrast to generating a power set, you would have far fewer resulting elements. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is uh... we'll just go with O(4^n) since we can generate all possible 2 * n length strings using characters '(' and ')'. With 2 characters, it's 2^(2 * n) = 4^n. diff --git a/src/leetcode/stack/valid_parentheses.py b/src/leetcode/stack/valid_parentheses.py index a45ee18..5ebf3d3 100644 --- a/src/leetcode/stack/valid_parentheses.py +++ b/src/leetcode/stack/valid_parentheses.py @@ -12,11 +12,13 @@ class Solution: def isValid(self, text: str) -> bool: """ - SOLUTION: + SOLUTION + -------- Simple stack based solution. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(n) where n is the number of characters in the string. diff --git a/src/leetcode/string/valid_word_abbreviation.py b/src/leetcode/string/valid_word_abbreviation.py index 135776e..6a79905 100644 --- a/src/leetcode/string/valid_word_abbreviation.py +++ b/src/leetcode/string/valid_word_abbreviation.py @@ -23,7 +23,8 @@ class Solution: def validWordAbbreviation(self, word: str, abbr: str) -> bool: """ - SOLUTION: + SOLUTION + -------- To do this, we'll use two pointers, but not to iterate through a single string. Instead, each pointer iterates through its own string. If we see an numerical abbreviation, we'll advance through the other string a the @@ -31,7 +32,8 @@ def validWordAbbreviation(self, word: str, abbr: str) -> bool: At each stage of advancement we'll check if the characters match. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is O(max(m, n)) where m is the length of word, and n is the length of abbr. diff --git a/src/leetcode/two_pointers/bag_of_tokens.py b/src/leetcode/two_pointers/bag_of_tokens.py index 8147a6d..ee4c4dd 100644 --- a/src/leetcode/two_pointers/bag_of_tokens.py +++ b/src/leetcode/two_pointers/bag_of_tokens.py @@ -16,7 +16,8 @@ class Solution: def bagOfTokensScore(self, tokens: list[int], power: int) -> int: """ - SOLUTION: + SOLUTION + -------- You basically are allowed to gain power at the cost of score, or gain score at the cost of power. You want to maximize the score. The problem does not tell you this, but: @@ -29,7 +30,8 @@ def bagOfTokensScore(self, tokens: list[int], power: int) -> int: amount of power for score. That is, sacrifice smaller power tokens face up for score, play higher power tokens for their raw (power) value. - COMPLEXITY: + COMPLEXITY + ---------- Time complexity is dominated by sorting, making it O(n log n). From 004a54727f38613e80ae3d8eb3838c1d8e573135 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 13 Mar 2025 19:47:39 -0700 Subject: [PATCH 044/158] parallel job scheduling --- ..._the_smallest_divisor_given_a_threshold.py | 2 +- src/leetcode/graph/parallel-job-scheduling.ts | 77 ------------------- src/leetcode/graph/parallel_job_scheduling.py | 60 +++++++++++++-- .../parallel-job-scheduling.test.ts.snap | 36 --------- .../graph/parallel_job_scheduling_test.py | 12 +++ .../snap_parallel_job_scheduling_test.py | 39 ++++++++++ 6 files changed, 105 insertions(+), 121 deletions(-) delete mode 100644 src/leetcode/graph/parallel-job-scheduling.ts delete mode 100644 test/leetcode/graph/__snapshots__/parallel-job-scheduling.test.ts.snap create mode 100644 test/leetcode/graph/parallel_job_scheduling_test.py create mode 100644 test/leetcode/graph/snapshots/snap_parallel_job_scheduling_test.py diff --git a/src/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold.py b/src/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold.py index e59438e..898c46a 100644 --- a/src/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold.py +++ b/src/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold.py @@ -42,7 +42,7 @@ def smallestDivisor(self, xs: list[int], threshold: int) -> int: # We do math.ceil because the problem asks us to round up. # # Note: there's no way to chain map -> reduce. Mind boggling. - value = sum(math.ceil(x / divisor for x in xs)) + value = sum(math.ceil(x / divisor) for x in xs) # If the value is too large, our divisor was too small, so we should shift our left value to be mid + 1. if value > threshold: diff --git a/src/leetcode/graph/parallel-job-scheduling.ts b/src/leetcode/graph/parallel-job-scheduling.ts deleted file mode 100644 index 1503a07..0000000 --- a/src/leetcode/graph/parallel-job-scheduling.ts +++ /dev/null @@ -1,77 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given a list tasks and their dependencies ['A:B,C', 'B:D,C,E', 'F:G'], return a list of parallelizable tasks in -// order. For example, in this situation [(D, C, E, F), (B, G), (A)] would be a valid order because all parallelizable -// tasks are grouped together. -// -// NOTE: This is not a LeetCode question but was asked by Cruise. -// -// See {@link https://leetcode.com/discuss/interview-question/353830/google-phone-screen-parallel-job-scheduling/} -export { parallelize }; - -// SOLUTION: -function parallelize(jobs: string[]): string[][] { - const graph = new Map(); - const reversed = new Map(); - for (const job of jobs) { - const parts = job.split(':'); - const node = parts[0]; - const children = parts[1].split(','); - - // Build up an adjacency list graph. - graph.set(node, children); - - // We will also want a reverse mapping of child -> parent; this will become useful later. - for (const child of children) { - if (!graph.has(child)) { - graph.set(child, []); - } - - const parents = reversed.get(child) ?? []; - parents.push(node); - reversed.set(child, parents); - } - } - - const result: string[][] = []; - - // While we have nodes without dependencies, let's prune them from the graph. - const frontier = new Set(); - for (const [key, value] of graph) { - if (value.length === 0) { - frontier.add(key); - } - } - - while (frontier.size > 0) { - const subresult = [...frontier]; - frontier.clear(); - - for (const node of subresult) { - graph.delete(node); - - // A deletion could have caused a different node to become a leaf. To find them, let's get the reverse mapping - // of node to parents and check each parent if that parent became a leaf. - const parents = reversed.get(node) ?? []; - for (const parent of parents) { - if (!graph.has(parent)) { - continue; - } - - // Remove the node from the parent adjacency lists. - let children = graph.get(parent) ?? []; - children = children.filter(u => u !== node); - graph.set(parent, children); - - // If any list became empty, it is now prunable. - if (children.length === 0) { - frontier.add(parent); - } - } - } - - result.push(subresult); - } - - return result; -} diff --git a/src/leetcode/graph/parallel_job_scheduling.py b/src/leetcode/graph/parallel_job_scheduling.py index e6bef75..2816a4e 100644 --- a/src/leetcode/graph/parallel_job_scheduling.py +++ b/src/leetcode/graph/parallel_job_scheduling.py @@ -1,13 +1,11 @@ # DIFFICULTY: MEDIUM # -# Given a list tasks and their dependencies ['A:B,C', 'B:D,C,E', 'F:G'], return a list of parallelizable tasks in +# Given a list of tasks and their dependencies ['A:B,C', 'B:D,C,E', 'F:G'], return a list of parallelizable tasks in # order. For example, in this situation [(D, C, E, F), (B, G), (A)] would be a valid order because all parallelizable # tasks are grouped together. # -# NOTE: This is not a LeetCode question but was asked by Cruise. -# # See https://leetcode.com/discuss/interview-question/353830/google-phone-screen-parallel-job-scheduling -from collections import defaultdict +from collections import defaultdict, deque class Solution: @@ -16,7 +14,55 @@ def parallelize(self, jobs: list[str]) -> list[list[str]]: SOLUTION -------- - Use BFS with Kahn's algorithm to perform topological sort. + Use BFS with Kahn's algorithm to perform topological sort. When processing nodes in parallel batches, reduce + the in degree count each time we process. + + COMPLEXITY + ---------- + + Time complexity is O(v + e) to build the graph, find zero in degree nodes, and performing topological sort, + where v is the number of tasks and e is the number of dependencies between them. + + Space complexity is O(v + e) as well. """ - graph = defaultdict(set) - in_degree = defaultdict(int) + graph: dict[str, set[str]] = defaultdict(set) + in_degrees: dict[str, int] = defaultdict(int) + nodes: set[str] = set() + + # Build the graph and compute in degrees. + for job_spec in jobs: + parts = job_spec.split(":") + node = parts[0] + children = parts[1].split(",") if len(parts) > 1 else [] + nodes.add(node) + + for child in children: + graph[node].add(child) + in_degrees[child] += 1 + nodes.add(child) + + # Initialize BFS queue with zero in degree nodes. + queue: deque[str] = deque() + for node in nodes: + if in_degrees[node] == 0: + queue.append(node) + + # Process nodes level by level in parallel execution batches. + result: list[list[str]] = [] + while queue: + size = len(queue) + sub_result: list[str] = [] + + for _ in range(size): + node = queue.popleft() + sub_result.append(node) + + # Reduce the in degree for each child. + for child in graph[node]: + in_degrees[child] -= 1 + if in_degrees[child] == 0: + queue.append(child) + + result.append(sub_result) + + return result diff --git a/test/leetcode/graph/__snapshots__/parallel-job-scheduling.test.ts.snap b/test/leetcode/graph/__snapshots__/parallel-job-scheduling.test.ts.snap deleted file mode 100644 index 05a79f1..0000000 --- a/test/leetcode/graph/__snapshots__/parallel-job-scheduling.test.ts.snap +++ /dev/null @@ -1,36 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`parallel job scheduling parallel job scheduling - test case 1 1`] = ` -[ - [ - "C", - "D", - "E", - "F", - ], - [ - "B", - ], - [ - "A", - ], -] -`; - -exports[`parallel job scheduling parallel job scheduling - test case 2 1`] = ` -[ - [ - "C", - "D", - "E", - "G", - ], - [ - "B", - "F", - ], - [ - "A", - ], -] -`; diff --git a/test/leetcode/graph/parallel_job_scheduling_test.py b/test/leetcode/graph/parallel_job_scheduling_test.py new file mode 100644 index 0000000..68e488a --- /dev/null +++ b/test/leetcode/graph/parallel_job_scheduling_test.py @@ -0,0 +1,12 @@ +from leetcode.graph.parallel_job_scheduling import Solution + + +soln = Solution() + + +def test_case_1(snapshot): + snapshot.assert_match(sorted(soln.parallelize(["A:B,C", "B:D,E,F"]))) + + +def test_case_2(snapshot): + snapshot.assert_match(sorted(soln.parallelize(["A:B,C", "B:D,E", "F:G"]))) diff --git a/test/leetcode/graph/snapshots/snap_parallel_job_scheduling_test.py b/test/leetcode/graph/snapshots/snap_parallel_job_scheduling_test.py new file mode 100644 index 0000000..7b21a64 --- /dev/null +++ b/test/leetcode/graph/snapshots/snap_parallel_job_scheduling_test.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# snapshottest: v1 - https://goo.gl/zC4yUc +from __future__ import unicode_literals + +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots['test_case_1 1'] = [ + [ + 'A' + ], + [ + 'C', + 'B' + ], + [ + 'D', + 'F', + 'E' + ] +] + +snapshots['test_case_2 1'] = [ + [ + 'D', + 'E' + ], + [ + 'F', + 'A' + ], + [ + 'G', + 'C', + 'B' + ] +] From a27732dba87ff01d39458894b5979b2a012e2d0b Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 13 Mar 2025 19:55:15 -0700 Subject: [PATCH 045/158] do not reformat the world --- pyproject.toml | 3 +++ scripts.py | 8 +++---- .../graph/parallel_job_scheduling_test.py | 10 +++++++-- .../snap_parallel_job_scheduling_test.py | 22 +++++++++---------- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index bcec259..b9fbecf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,15 +24,18 @@ ruff = "^0.9.10" snapshottest = "^1.0.0a0" [tool.black] +exclude = "snapshots/" line-length = 120 [tool.isort] profile = "black" +skip = ["snapshots"] force_sort_within_sections = true lines_between_sections = 0 lines_after_imports = 2 [tool.ruff] +exclude = ["snapshots"] line-length = 120 [build-system] diff --git a/scripts.py b/scripts.py index 44dda45..a51881a 100644 --- a/scripts.py +++ b/scripts.py @@ -12,13 +12,13 @@ def run_all(): def run_lint(): - run_cmd("poetry run ruff check . --fix") - run_cmd("poetry run pyright .") + run_cmd("poetry run ruff check --fix src test") + run_cmd("poetry run pyright src test") def run_format(): - run_cmd("poetry run black .") - run_cmd("poetry run isort .") + run_cmd("poetry run black src test") + run_cmd("poetry run isort src test") def run_tests(): diff --git a/test/leetcode/graph/parallel_job_scheduling_test.py b/test/leetcode/graph/parallel_job_scheduling_test.py index 68e488a..4c00b8b 100644 --- a/test/leetcode/graph/parallel_job_scheduling_test.py +++ b/test/leetcode/graph/parallel_job_scheduling_test.py @@ -5,8 +5,14 @@ def test_case_1(snapshot): - snapshot.assert_match(sorted(soln.parallelize(["A:B,C", "B:D,E,F"]))) + result = soln.parallelize(["A:B,C", "B:D,E,F"]) + + expected = [sorted(xs) for xs in result] + snapshot.assert_match(sorted(expected)) def test_case_2(snapshot): - snapshot.assert_match(sorted(soln.parallelize(["A:B,C", "B:D,E", "F:G"]))) + result = soln.parallelize(["A:B,C", "B:D,E", "F:G"]) + + expected = [sorted(xs) for xs in result] + snapshot.assert_match(sorted(expected)) diff --git a/test/leetcode/graph/snapshots/snap_parallel_job_scheduling_test.py b/test/leetcode/graph/snapshots/snap_parallel_job_scheduling_test.py index 7b21a64..0016a1f 100644 --- a/test/leetcode/graph/snapshots/snap_parallel_job_scheduling_test.py +++ b/test/leetcode/graph/snapshots/snap_parallel_job_scheduling_test.py @@ -12,28 +12,28 @@ 'A' ], [ - 'C', - 'B' + 'B', + 'C' ], [ 'D', - 'F', - 'E' + 'E', + 'F' ] ] snapshots['test_case_2 1'] = [ [ - 'D', - 'E' + 'A', + 'F' ], [ - 'F', - 'A' + 'B', + 'C', + 'G' ], [ - 'G', - 'C', - 'B' + 'D', + 'E' ] ] From c73cece85ee54069f08ae32e04e4444bfe9f4d4a Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 13 Mar 2025 20:08:45 -0700 Subject: [PATCH 046/158] num islands --- src/leetcode/graph/number-of-islands.ts | 61 ------------------- src/leetcode/graph/number_of_islands.py | 54 ++++++++++++++++ test/leetcode/graph/number-of-islands.test.ts | 14 ----- test/leetcode/graph/number_of_islands_test.py | 10 +++ .../graph/parallel-job-scheduling.test.ts | 15 ----- 5 files changed, 64 insertions(+), 90 deletions(-) delete mode 100644 src/leetcode/graph/number-of-islands.ts create mode 100644 src/leetcode/graph/number_of_islands.py delete mode 100644 test/leetcode/graph/number-of-islands.test.ts create mode 100644 test/leetcode/graph/number_of_islands_test.py delete mode 100644 test/leetcode/graph/parallel-job-scheduling.test.ts diff --git a/src/leetcode/graph/number-of-islands.ts b/src/leetcode/graph/number-of-islands.ts deleted file mode 100644 index 8f0cc11..0000000 --- a/src/leetcode/graph/number-of-islands.ts +++ /dev/null @@ -1,61 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an m x n 2D binary grid grid which represents a map of '1's (land) and '0's (water), return the number of -// islands. -// -// An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may -// assume all four edges of the grid are all surrounded by water. -// -// See {@link https://leetcode.com/problems/number-of-islands/} -export { numIslands }; - -// SOLUTION: -// -// We can execute DFS from every land mass to find connected land masses. Each connected land mass constitutes 1 -// island. -// -// We can either store the coordinates (i, j) in a set to avoid visiting the same cell twice, or we can simply write -// to the cell '0' for a visited land mass. The latter will also avoid visiting the same cell twice. We do the -// latter because that's what most interviewers want to see. -function numIslands(grid: string[][]) { - let count = 0; - - // Use DFS to find all connected land masses. Avoid doing DFS calls to any coordinates that are out of bounds, and - // mark cells as visited by writing '0' (water) to them. - function dfs(m: string[][], i: number, j: number) { - if (m[i][j] === '0') { - return; - } - - m[i][j] = '0'; - - if (j + 1 < m[i].length) { - dfs(m, i, j + 1); - } - - if (j - 1 >= 0) { - dfs(m, i, j - 1); - } - - if (i - 1 >= 0) { - dfs(m, i - 1, j); - } - - if (i + 1 < m.length) { - dfs(m, i + 1, j); - } - } - - for (let i = 0; i < grid.length; i++) { - for (let j = 0; j < grid[i].length; j++) { - if (grid[i][j] === '0') { - continue; - } - - dfs(grid, i, j); - count++; - } - } - - return count; -} diff --git a/src/leetcode/graph/number_of_islands.py b/src/leetcode/graph/number_of_islands.py new file mode 100644 index 0000000..e6b0185 --- /dev/null +++ b/src/leetcode/graph/number_of_islands.py @@ -0,0 +1,54 @@ +# DIFFICULTY: MEDIUM +# +# Given an m x n 2D binary grid grid which represents a map of '1's (land) and '0's (water), return the number of +# islands. +# +# An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may +# assume all four edges of the grid are all surrounded by water. +# +# See https://leetcode.com/problems/number-of-islands +class Solution: + def numIslands(self, grid: list[list[str]]): + """ + SOLUTION + -------- + + We can execute DFS from every land mass to find connected land masses. Each connected land mass constitutes 1 + island. + + We can either store the coordinates (i, j) in a set to avoid visiting the same cell twice, or we can simply + write to the cell '0' for a visited land mass. The latter will also avoid visiting the same cell twice. We do + the latter because that's what most interviewers want to see. + + COMPLEXITY + ---------- + + Time complexity is O(v + e). + + Space complexity is O(v + e). We don't modify the input grid but the DFS stack frames do consume memory. + """ + count = 0 + + def dfs(i: int, j: int): + if not (0 <= i < len(grid)): + return + + if not (0 <= j < len(grid[i])): + return + + if grid[i][j] == "0": + return + + grid[i][j] = "0" + dfs(i, j + 1) + dfs(i, j - 1) + dfs(i - 1, j) + dfs(i + 1, j) + + for i in range(len(grid)): + for j in range(len(grid[i])): + if grid[i][j] == "1": + dfs(i, j) + count += 1 + + return count diff --git a/test/leetcode/graph/number-of-islands.test.ts b/test/leetcode/graph/number-of-islands.test.ts deleted file mode 100644 index debc8ff..0000000 --- a/test/leetcode/graph/number-of-islands.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { numIslands } from '../../src/graph/number-of-islands'; - -describe('number of islands', () => { - test('number of islands - test case 1', async () => { - const grid = [ - ['1', '1', '1', '1', '0'], - ['1', '1', '0', '1', '0'], - ['1', '1', '0', '0', '0'], - ['0', '0', '0', '0', '0'] - ]; - - expect(numIslands(grid)).toBe(1); - }); -}); diff --git a/test/leetcode/graph/number_of_islands_test.py b/test/leetcode/graph/number_of_islands_test.py new file mode 100644 index 0000000..67044ac --- /dev/null +++ b/test/leetcode/graph/number_of_islands_test.py @@ -0,0 +1,10 @@ +from leetcode.graph.number_of_islands import Solution + + +soln = Solution() + + +def test_case_1(): + grid = [["1", "1", "1", "1", "0"], ["1", "1", "0", "1", "0"], ["1", "1", "0", "0", "0"], ["0", "0", "0", "0", "0"]] + + assert soln.numIslands(grid) == 1 diff --git a/test/leetcode/graph/parallel-job-scheduling.test.ts b/test/leetcode/graph/parallel-job-scheduling.test.ts deleted file mode 100644 index 14610a4..0000000 --- a/test/leetcode/graph/parallel-job-scheduling.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { parallelize } from '../../src/graph/parallel-job-scheduling'; - -describe('parallel job scheduling', () => { - test('parallel job scheduling - test case 1', async () => { - const jobs = ['A:B,C', 'B:D,E,F']; - - expect(parallelize(jobs)).toMatchSnapshot(); - }); - - test('parallel job scheduling - test case 2', async () => { - const jobs = ['A:B,C', 'B:D,E', 'F:G']; - - expect(parallelize(jobs)).toMatchSnapshot(); - }); -}); From f3b51d24d5f9b53058ae6f73bc82fbd54b60df98 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 13 Mar 2025 20:41:55 -0700 Subject: [PATCH 047/158] shortest path in a binary matrix --- .../graph/shortest-path-in-binary-matrix.ts | 105 ------------------ .../graph/shortest_path_in_binary_matrix.py | 97 ++++++++++++++++ .../shortest-path-in-binary-matrix.test.ts | 25 ----- .../shortest_path_in_binary_matrix_test.py | 23 ++++ 4 files changed, 120 insertions(+), 130 deletions(-) delete mode 100644 src/leetcode/graph/shortest-path-in-binary-matrix.ts create mode 100644 src/leetcode/graph/shortest_path_in_binary_matrix.py delete mode 100644 test/leetcode/graph/shortest-path-in-binary-matrix.test.ts create mode 100644 test/leetcode/graph/shortest_path_in_binary_matrix_test.py diff --git a/src/leetcode/graph/shortest-path-in-binary-matrix.ts b/src/leetcode/graph/shortest-path-in-binary-matrix.ts deleted file mode 100644 index c21b10c..0000000 --- a/src/leetcode/graph/shortest-path-in-binary-matrix.ts +++ /dev/null @@ -1,105 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an n x n binary matrix grid, return the length of the shortest clear path in the matrix. If there is no clear -// path, return -1. -// -// A clear path in a binary matrix is a path from the top-left cell (i.e., (0, 0)) to the bottom-right cell -// (i.e., (n - 1, n - 1)) such that: -// -// - All the visited cells of the path are 0. -// - All the adjacent cells of the path are 8-directionally connected (i.e., they are different and they share an edge -// or a corner). -// - The length of a clear path is the number of visited cells of this path. -// -// See {@link https://leetcode.com/problems/shortest-path-in-binary-matrix/} -export { shortestPathBinaryMatrix }; - -// SOLUTION: -// -// Can be solved using BFS. Just start from the top left cell and explore adjacent cells that have 0 value. -// -// Note the following: -// -// - We can explore all 8 directions, so we can go backwards. -// - We might not find a path. -// - The first cell might not even be visitable. -// - With BFS the first path that reaches the end is guaranteed to be the shortest. -// - If we had used DFS, we'd need to explicitly keep track of the shortest path because the first path to finish may -// not be the shortest. -// -// COMPLEXITY: -// -// Each cell is visited at most once. On each visited a max of 8 directions are considered. With there being n rows -// and n columns, the time complexity is O(n^2). -// -// The space complexity is O(n^2) to store the visited cells and the queue. -function shortestPathBinaryMatrix(grid: number[][]): number { - // The problem says that you MUST start at the top left, so if the top left cell is 1, you have nowhere to go. - if (grid[0][0] === 1) { - return -1; - } - - const visited = new Set(); - const queue = [ - { - position: [0, 0], - length: 1 - } - ]; - while (queue.length > 0) { - const cell = queue.shift()!; - const [x, y] = cell.position; - - // First check if this cell has been visited, and if it has, simply skip it. Store the cell as a string in a set, - // since we can't store the object directly. - const key = `[${x},${y}]`; - if (visited.has(key)) { - continue; - } - - // Note that if we're at the very last cell, we can return it immediately. Because we are using BFS, the very first - // path that reaches the end is guaranteed to be the shortest. - // - // We don't have to store the path or otherwise distinguish between paths because all shortest paths have the same - // length. - if (x === grid.length - 1 && y === grid[0].length - 1) { - return cell.length; - } - - // Note that we can actually move backwards (and we might be required to) since we can explore all 8 directions. - // Generate the coordinates of the 8 directions, then filter out the ones that are out of bounds. - const coordinates = [ - [x - 1, y], - [x, y - 1], - [x - 1, y - 1], - [x + 1, y], - [x, y + 1], - [x + 1, y + 1], - [x - 1, y + 1], - [x + 1, y - 1] - ]; - const frontier = coordinates - .filter(([x, y]) => { - return ( - x >= 0 && - x < grid.length && // Check the x bounds. - y >= 0 && - y < grid[0].length && // Check the y bounds; note that it's n x n so grid.length works too. - grid[x][y] === 0 - ); // Check if the cell can actually be moved into. - }) - .map(p => { - return { - position: p, - length: cell.length + 1 - }; - }); - - // Update the visited set and push the frontier cells onto the queue. - visited.add(key); - queue.push(...frontier); - } - - // If we've exhausted all paths without reaching the end, that means we couldn't do it. - return -1; -} diff --git a/src/leetcode/graph/shortest_path_in_binary_matrix.py b/src/leetcode/graph/shortest_path_in_binary_matrix.py new file mode 100644 index 0000000..49f6050 --- /dev/null +++ b/src/leetcode/graph/shortest_path_in_binary_matrix.py @@ -0,0 +1,97 @@ +# DIFFICULTY: MEDIUM +# +# Given an n x n binary matrix grid, return the length of the shortest clear path in the matrix. If there is no clear +# path, return -1. +# +# A clear path in a binary matrix is a path from the top-left cell (i.e., (0, 0)) to the bottom-right cell +# (i.e., (n - 1, n - 1)) such that: +# +# - All the visited cells of the path are 0. +# - All the adjacent cells of the path are 8-directionally connected (i.e., they are different and they share an edge +# or a corner). +# - The length of a clear path is the number of visited cells of this path. +# +# See https://leetcode.com/problems/shortest-path-in-binary-matrix +from collections import deque + + +class Solution: + def shortestPathBinaryMatrix(self, grid: list[list[int]]) -> int: + """ + SOLUTION + -------- + + Can be solved using BFS. Just start from the top left cell and explore adjacent cells that have 0 value. + + Note the following: + + - We can explore all 8 directions, so we can go backwards. + - We might not find a path. + - The first cell might not even be visitable. + - With BFS the first path that reaches the end is guaranteed to be the shortest. + - If we had used DFS, we'd need to explicitly keep track of the shortest path because the first path to finish may + not be the shortest. + + COMPLEXITY + ---------- + + Time complexity is O(n^2). Each cell is visited at most once. On each visited a max of 8 directions are + considered. + + Space complexity is O(n^2) to store the visited cells and the queue. + """ + # The problem says that you MUST start at the top left, so if the top left cell is 1, you have nowhere to go. + if grid[0][0] == 1: + return -1 + + # Keep a set of visited cells for BFS. + visited: set[tuple[int, int]] = set() + + # Store a queue of (x, y, length). + queue: deque[tuple[int, int, int]] = deque() + + # Start at the (0, 0) cell with length = 1. + queue.append((0, 0, 1)) + while queue: + x, y, length = queue.popleft() + + # First check if this cell has been visited, and if it has, simply skip it. Store the cell as a string in a + # set, since we can't store the object directly. + if (x, y) in visited: + continue + + # Mark this cell as visited. + visited.add((x, y)) + + # Note that if we're at the very last cell, we can return it immediately. Because we are using BFS, the + # very first path that reaches the end is guaranteed to be the shortest. + # + # We don't have to store the path or otherwise distinguish between paths because all shortest paths have the + # same length. + if x == len(grid) - 1 and y == len(grid[0]) - 1: + return length + + # Note that we can actually move backwards (and we might be required to) since we can explore all 8 + # directions. Generate the coordinates of the 8 directions, then filter out the ones that are out of + # bounds, and explore the rest. + coordinates = [ + [x - 1, y], + [x, y - 1], + [x - 1, y - 1], + [x + 1, y], + [x, y + 1], + [x + 1, y + 1], + [x - 1, y + 1], + [x + 1, y - 1], + ] + for fx, fy in coordinates: + if not (0 <= fx < len(grid)): + continue + if not (0 <= fy < len(grid[0])): + continue + if grid[fx][fy] != 0: + continue + queue.append((fx, fy, length + 1)) + + # If we've exhausted all paths without reaching the end, that means we couldn't do it. + return -1 diff --git a/test/leetcode/graph/shortest-path-in-binary-matrix.test.ts b/test/leetcode/graph/shortest-path-in-binary-matrix.test.ts deleted file mode 100644 index 3784761..0000000 --- a/test/leetcode/graph/shortest-path-in-binary-matrix.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { shortestPathBinaryMatrix } from '../../src/graph/shortest-path-in-binary-matrix'; - -describe('shortest path in binary matrix', () => { - test('shortest path in binary matrix - test case 1', async () => { - const grid = [ - [0, 1], - [1, 0] - ]; - - expect(shortestPathBinaryMatrix(grid)).toBe(2); - }); - - test('shortest path in binary matrix - test case 2', async () => { - const grid = [ - [0, 1, 1, 0, 0, 0], - [0, 1, 0, 1, 1, 0], - [0, 1, 1, 0, 1, 0], - [0, 0, 0, 1, 1, 0], - [1, 1, 1, 1, 1, 0], - [1, 1, 1, 1, 1, 0] - ]; - - expect(shortestPathBinaryMatrix(grid)).toBe(14); - }); -}); diff --git a/test/leetcode/graph/shortest_path_in_binary_matrix_test.py b/test/leetcode/graph/shortest_path_in_binary_matrix_test.py new file mode 100644 index 0000000..ea2891d --- /dev/null +++ b/test/leetcode/graph/shortest_path_in_binary_matrix_test.py @@ -0,0 +1,23 @@ +from leetcode.graph.shortest_path_in_binary_matrix import Solution + + +soln = Solution() + + +def test_case_1(): + grid = [[0, 1], [1, 0]] + + assert soln.shortestPathBinaryMatrix(grid) == 2 + + +def test_case_2(): + grid = [ + [0, 1, 1, 0, 0, 0], + [0, 1, 0, 1, 1, 0], + [0, 1, 1, 0, 1, 0], + [0, 0, 0, 1, 1, 0], + [1, 1, 1, 1, 1, 0], + [1, 1, 1, 1, 1, 0], + ] + + assert soln.shortestPathBinaryMatrix(grid) == 14 From 34286e1accad1f18b36ab79316859a95812d0427 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 13 Mar 2025 21:02:24 -0700 Subject: [PATCH 048/158] smallest int --- ...est-greater-multiple-made-of-two-digits.ts | 74 ------------------ ...est_greater_multiple_made_of_two_digits.py | 77 +++++++++++++++++++ ...reater-multiple-made-of-two-digits.test.ts | 15 ---- ...reater_multiple_made_of_two_digits_test.py | 16 ++++ 4 files changed, 93 insertions(+), 89 deletions(-) delete mode 100644 src/leetcode/graph/smallest-greater-multiple-made-of-two-digits.ts create mode 100644 src/leetcode/graph/smallest_greater_multiple_made_of_two_digits.py delete mode 100644 test/leetcode/graph/smallest-greater-multiple-made-of-two-digits.test.ts create mode 100644 test/leetcode/graph/smallest_greater_multiple_made_of_two_digits_test.py diff --git a/src/leetcode/graph/smallest-greater-multiple-made-of-two-digits.ts b/src/leetcode/graph/smallest-greater-multiple-made-of-two-digits.ts deleted file mode 100644 index 3770dab..0000000 --- a/src/leetcode/graph/smallest-greater-multiple-made-of-two-digits.ts +++ /dev/null @@ -1,74 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given three integers, k, digit1, and digit2, you want to find the smallest integer that is: -// -// - Larger than k, -// - A multiple of k, and -// - Comprised of only the digits digit1 and/or digit2. -// -// Return the smallest such integer. If no such integer exists or the integer exceeds the limit of a signed 32-bit -// integer (2^31 - 1), return -1. -// -// See {@link https://leetcode.com/problems/smallest-greater-multiple-made-of-two-digits/} -export { findInteger }; - -// SOLUTION: -// -// In order to find our target number, we start with the first digit and generate all possible 2 digit numbers. Then -// from there we generate all possible 3 digit numbers, and so on. We also go back to checking the second digit, -// generating 2 digit, 3 digit, etc numbers. -// -// This can be done via a graph search algorithm. However, since we want to find the smallest such number, we should -// use BFS instead of DFS. -function findInteger(k: number, digit1: number, digit2: number): number { - const queue: string[] = []; - const seen = new Set(); - - // We always want to start generating numbers from the smaller digit first, so let's rearrange them so that the - // smaller digit is digit1. - if (digit2 < digit1) { - [digit1, digit2] = [digit2, digit1]; - } - - // For the first digits, do not push a 0, because generating a number starting at 0 is the same as generating a - // number using the rest of the digits. - if (digit1 !== 0) { - queue.push(`${digit1}`); - seen.add(`${digit1}`); - } - - if (digit2 !== 0 && digit1 !== digit2) { - queue.push(`${digit2}`); - seen.add(`${digit2}`); - } - - const max = 2 ** 31 - 1; - while (queue.length > 0) { - const s = queue.shift()!; - const value = Number.parseInt(s, 10 /* radix */); - if (value > max) { - continue; - } - - // Check if this number is both larger than k and a multiple of k; if so we have reached our target. - if (value > k && value % k === 0) { - return value; - } - - // Otherwise, mark this number as visited and use the digits to construct our frontier nodes. - const next1 = `${s}${digit1}`; - if (!seen.has(next1)) { - queue.push(next1); - seen.add(next1); - } - - // We know that by adding in this order, the smaller number gets added first (since digit1 < digit2). - const next2 = `${s}${digit2}`; - if (!seen.has(next2)) { - queue.push(next2); - seen.add(next2); - } - } - - return -1; -} diff --git a/src/leetcode/graph/smallest_greater_multiple_made_of_two_digits.py b/src/leetcode/graph/smallest_greater_multiple_made_of_two_digits.py new file mode 100644 index 0000000..7ec5e73 --- /dev/null +++ b/src/leetcode/graph/smallest_greater_multiple_made_of_two_digits.py @@ -0,0 +1,77 @@ +# DIFFICULTY: MEDIUM +# +# Given three integers, k, digit1, and digit2, you want to find the smallest integer that is: +# +# - Larger than k, +# - A multiple of k, and +# - Comprised of only the digits digit1 and/or digit2. +# +# Return the smallest such integer. If no such integer exists or the integer exceeds the limit of a signed 32-bit +# integer (2^31 - 1), return -1. +# +# See https://leetcode.com/problems/smallest-greater-multiple-made-of-two-digits +from collections import deque + + +class Solution: + def findInteger(self, k: int, digit1: int, digit2: int) -> int: + """ + SOLUTION + -------- + + In order to find our target number, we start with the first digit and generate all possible 2 digit numbers. + Then from there we generate all possible 3 digit numbers, and so on. We also go back to checking the second + digit, generating 2 digit, 3 digit, etc numbers. + + This can be done via a graph search algorithm. However, since we want to find the smallest such number, we + should use BFS instead of DFS. + + COMPLEXITY + ---------- + """ + queue: deque[str] = deque() + seen: set[str] = set() + + # We always want to start generating numbers from the smaller digit first, so let's rearrange them so that the + # smaller digit is digit1. + (d1, d2) = (digit1, digit2) if digit1 < digit2 else (digit2, digit1) + + # Convert the digits to strings; this will be useful in building our number later. + s1 = str(d1) + s2 = str(d2) + + # For the first digits, do not push a 0, because generating a number starting at 0 is the same as generating a + # number using the rest of the digits. + if d1 != 0: + queue.append(s1) + seen.add(s1) + + if d2 != 0 and d1 != d2: + queue.append(s2) + seen.add(s2) + + max_value = 2**31 - 1 + while queue: + s = queue.popleft() + + # From the problem description, this is the max integer we should consider. + value = int(s) + if value > max_value: + continue + + # Check if this number is both larger than k and a multiple of k; if so we have reached our target. + if value > k and value % k == 0: + return value + + # Otherwise, use the digits to construct our frontier nodes. We know that by adding in this order, the + # smaller number gets added first (since digit1 < digit2). + next1 = s + s1 + next2 = s + s2 + if next1 not in seen: + queue.append(next1) + seen.add(next1) + if next2 not in seen: + queue.append(next2) + seen.add(next2) + + return -1 diff --git a/test/leetcode/graph/smallest-greater-multiple-made-of-two-digits.test.ts b/test/leetcode/graph/smallest-greater-multiple-made-of-two-digits.test.ts deleted file mode 100644 index a25a398..0000000 --- a/test/leetcode/graph/smallest-greater-multiple-made-of-two-digits.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { findInteger } from '../../src/graph/smallest-greater-multiple-made-of-two-digits'; - -describe('smallest greater multiple made of two digits', () => { - test('smallest greater multiple made of two digits - test case 1', async () => { - expect(findInteger(2, 0, 2)).toBe(20); - }); - - test('smallest greater multiple made of two digits - test case 2', async () => { - expect(findInteger(3, 4, 2)).toBe(24); - }); - - test('smallest greater multiple made of two digits - test case 1', async () => { - expect(findInteger(2, 0, 0)).toBe(-1); - }); -}); diff --git a/test/leetcode/graph/smallest_greater_multiple_made_of_two_digits_test.py b/test/leetcode/graph/smallest_greater_multiple_made_of_two_digits_test.py new file mode 100644 index 0000000..5d21677 --- /dev/null +++ b/test/leetcode/graph/smallest_greater_multiple_made_of_two_digits_test.py @@ -0,0 +1,16 @@ +from leetcode.graph.smallest_greater_multiple_made_of_two_digits import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.findInteger(2, 0, 2) == 20 + + +def test_case_2(): + assert soln.findInteger(3, 4, 2) == 24 + + +def test_case_3(): + assert soln.findInteger(2, 0, 0) == -1 From c48b19afc2b82d9923c8d22cbe4538dec77fe14c Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 13 Mar 2025 21:03:22 -0700 Subject: [PATCH 049/158] comments --- src/leetcode/heap/common/list_node.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/leetcode/heap/common/list_node.py b/src/leetcode/heap/common/list_node.py index 9efd65c..bfe706e 100644 --- a/src/leetcode/heap/common/list_node.py +++ b/src/leetcode/heap/common/list_node.py @@ -1,3 +1,5 @@ +# This class definition comes from the problem itself and we cannot change it, or else our submission will not be +# accepted. class ListNode: def __init__(self, val=0, next=None): self.val = val From f36a5c55d92887ae2f18e3c38d3cd0741cbfc542 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Fri, 14 Mar 2025 18:06:07 -0700 Subject: [PATCH 050/158] update the readme --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 1009f22..a494795 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@ Problems and solutions for LeetCode. ## Development -1. Install poetry: `pip install poetry==2.1.1` -1. Configure poetry: `poetry config virtualenvs.in-project true` +1. Install poetry: `curl -sSL https://install.python-poetry.org | python` 1. Install dependencies: `poetry install` 1. Run tests: `poetry run test` From c74b76719f71610b7d707a543f853ab720ff6181 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Fri, 14 Mar 2025 20:25:15 -0700 Subject: [PATCH 051/158] list interval intersections --- .../interval/interval_list_intersections.py | 100 ++++++++++++++++++ test/conftest.py | 7 ++ test/leetcode/graph/snapshots/__init__.py | 0 .../interval_list_intersection_test.py | 11 ++ .../snap_interval_list_intersection_test.py | 27 +++++ 5 files changed, 145 insertions(+) create mode 100644 src/leetcode/interval/interval_list_intersections.py create mode 100644 test/conftest.py delete mode 100644 test/leetcode/graph/snapshots/__init__.py create mode 100644 test/leetcode/interval/interval_list_intersection_test.py create mode 100644 test/leetcode/interval/snapshots/snap_interval_list_intersection_test.py diff --git a/src/leetcode/interval/interval_list_intersections.py b/src/leetcode/interval/interval_list_intersections.py new file mode 100644 index 0000000..c3ae5a5 --- /dev/null +++ b/src/leetcode/interval/interval_list_intersections.py @@ -0,0 +1,100 @@ +# DIFFICULTY: MEDIUM +# +# You are given two lists of closed intervals, firstList and secondList, where firstList[i] = [starti, endi] and +# secondList[j] = [startj, endj]. Each list of intervals is pairwise disjoint and in sorted order. +# +# Return the intersection of these two interval lists. +# +# A closed interval [a, b] (with a <= b) denotes the set of real numbers x with a <= x <= b. +# +# The intersection of two closed intervals is a set of real numbers that are either empty or represented as a closed +# interval. For example, the intersection of [1, 3] and [2, 4] is [2, 3]. +# +# See https://leetcode.com/problems/interval-list-intersections +class Solution: + def intervalIntersection(self, firstList: list[list[int]], secondList: list[list[int]]) -> list[list[int]]: + """ + SOLUTION + -------- + + This is a very straightforward problem except there's a very strange wrinkle in that intersections can be empty + intervals. Because of this we can't just advance both pointers when we find an intersection. Instead, we have + to only advance the one with the smaller endpoint. + + COMPLEXITY + ---------- + + Time complexity is O(m + n) since it iterates through both lists once each. + + Space complexity is O(min(m, n)) since the intersections array is limited by the size of the smaller list. + """ + + def isIntersecting(a: list[int], b: list[int]) -> bool: + """ + The two intervals will intersect if we have a situation like this: + + A: [.......] + B: [.......] + + ...where B[x] is greater than A[x] and B[x] is less than A[y]. Also, we can get an intersection like this: + + A: [.......] + B: [.......] + + ...where A[x] is greater than B[x] and A[x] is less than B[y]. + """ + [ax, ay] = a + [bx, by] = b + return (ax <= bx <= ay) or (bx <= ax <= by) + + # Now we can iterate through both and check if they intersect. If no intersection exists, then we must have a + # a situation like one of these: + # + # A: [..] + # B: [.....] + # + # ...or... + # + # A: [.....] + # B: [..] + # + # In these cases, whichever of A[y] or B[y] is SMALLER is the one that should be dropped and advanced. That's + # because the intervals are disjoint, so the smaller of the y values will be the one that is further to the + # left, and not able to intersect with anything. + # + # We can stop as soon as one list is exhausted because there can't be any more intersections after that. + intersections: list[list[int]] = [] + i = 0 + j = 0 + while i < len(firstList) and j < len(secondList): + a = firstList[i] + b = firstList[j] + ax, ay = a[0], a[1] + bx, by = b[0], b[1] + + if isIntersecting(a, b): + # Calculate the intersection. + # + # A: [...|....] + # B: [....|...] + # + # We'll want the max of the two start points and the min of the two end points. + # + # It could also be the case that we have something like this: + # + # A: [....| + # B: |....] + # + # In which case the intersection is an empty interval. This is weird. But it will become relevant in + # the next step. We shouldn't just advance BOTH pointers because of this empty interval situation. + # Instead, we should just advance the smaller of the two end points. + c = [max(ax, bx), min(ay, by)] + intersections.append(c) + + # Advance the one that has the smaller end point. + if ay < by: + i += 1 + else: + j += 1 + + return intersections diff --git a/test/conftest.py b/test/conftest.py new file mode 100644 index 0000000..2752bbc --- /dev/null +++ b/test/conftest.py @@ -0,0 +1,7 @@ +import os + + +def pytest_sessionfinish(): + snap_init = "snapshots/__init__.py" + if os.path.exists(snap_init): + os.remove(snap_init) diff --git a/test/leetcode/graph/snapshots/__init__.py b/test/leetcode/graph/snapshots/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/leetcode/interval/interval_list_intersection_test.py b/test/leetcode/interval/interval_list_intersection_test.py new file mode 100644 index 0000000..5d3ba6e --- /dev/null +++ b/test/leetcode/interval/interval_list_intersection_test.py @@ -0,0 +1,11 @@ +from leetcode.interval.interval_list_intersections import Solution + + +soln = Solution() + + +def test_case_1(snapshot): + firstList = [[0, 2], [5, 10], [13, 23], [24, 25]] + secondList = [[1, 5], [8, 12], [15, 24], [25, 26]] + + snapshot.assert_match(soln.intervalIntersection(firstList, secondList)) diff --git a/test/leetcode/interval/snapshots/snap_interval_list_intersection_test.py b/test/leetcode/interval/snapshots/snap_interval_list_intersection_test.py new file mode 100644 index 0000000..ae55f52 --- /dev/null +++ b/test/leetcode/interval/snapshots/snap_interval_list_intersection_test.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# snapshottest: v1 - https://goo.gl/zC4yUc +from __future__ import unicode_literals + +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots['test_case_1 1'] = [ + [ + 0, + 2 + ], + [ + 5, + 10 + ], + [ + 13, + 23 + ], + [ + 24, + 25 + ] +] From 0e7f2fcfb5ea1aeb6f2905143c80785541781ffe Mon Sep 17 00:00:00 2001 From: Min Huang Date: Fri, 14 Mar 2025 20:25:54 -0700 Subject: [PATCH 052/158] deletions --- .../interval/interval-list-intersections.ts | 96 ------------------- .../interval-list-intersection.test.ts | 27 ------ 2 files changed, 123 deletions(-) delete mode 100644 src/leetcode/interval/interval-list-intersections.ts delete mode 100644 test/leetcode/interval/interval-list-intersection.test.ts diff --git a/src/leetcode/interval/interval-list-intersections.ts b/src/leetcode/interval/interval-list-intersections.ts deleted file mode 100644 index a6dd241..0000000 --- a/src/leetcode/interval/interval-list-intersections.ts +++ /dev/null @@ -1,96 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given two lists of closed intervals, firstList and secondList, where firstList[i] = [starti, endi] and -// secondList[j] = [startj, endj]. Each list of intervals is pairwise disjoint and in sorted order. -// -// Return the intersection of these two interval lists. -// -// A closed interval [a, b] (with a <= b) denotes the set of real numbers x with a <= x <= b. -// -// The intersection of two closed intervals is a set of real numbers that are either empty or represented as a closed -// interval. For example, the intersection of [1, 3] and [2, 4] is [2, 3]. -// -// See {@link https://leetcode.com/problems/interval-list-intersections/} -export { intervalIntersection }; - -// SOLUTION: -// -// This is a very straightforward problem except there's a very strange wrinkle in that intersections can be empty -// intervals. Because of this we can't just advance both pointers when we find an intersection. Instead, we have to -// only advance the one with the smaller endpoint. -// -// COMPLEXITY: -// -// Time complexity is O(m + n) since it iterates through both lists once each. -// -// Space complexity is O(min(m, n)) since the intersections array is limited by the size of the smaller list. -function intervalIntersection(firstList: number[][], secondList: number[][]): number[][] { - // The two intervals will intersect if we have a situation like this: - // - // A: [.......] - // B: [.......] - // - // ...where B[x] is greater than A[x] and B[x] is less than A[y]. Also, we can get an intersection like this: - // - // A: [.......] - // B: [.......] - // - // ...where A[x] is greater than B[x] and A[x] is less than B[y]. - function isIntersecting(a: number[], b: number[]) { - const [ax, ay] = a; - const [bx, by] = b; - return (bx >= ax && bx <= ay) || (ax >= bx && ax <= by); - } - - // Now we can iterate through both and check if they intersect. If no intersection exists, then we must have a - // a situation like one of these: - // - // A: [..] - // B: [.....] - // - // ...or... - // - // A: [.....] - // B: [..] - // - // In these cases, whichever of A[y] or B[y] is SMALLER is the one that should be dropped and advanced. That's - // because the intervals are disjoint, so the smaller of the y values will be the one that is further to the left, - // and not able to intersect with anything. - // - // We can stop as soon as one list is exhausted because there can't be any more intersections after that. - const intersections: number[][] = []; - let i = 0; - let j = 0; - while (i < firstList.length && j < secondList.length) { - const a = firstList[i]; - const b = secondList[j]; - if (isIntersecting(a, b)) { - // Calculate the intersection. - // - // A: [...|....] - // B: [....|...] - // - // We'll want the max of the two start points and the min of the two end points. - // - // It could also be the case that we have something like this: - // - // A: [....| - // B: |....] - // - // In which case the intersection is an empty interval. This is weird. But it will become relevant in the next - // step. We shouldn't just advance BOTH pointers because of this empty interval situation. Instead, we should - // just advance the smaller of the two end points. - const c = [Math.max(a[0], b[0]), Math.min(a[1], b[1])]; - intersections.push(c); - } - - // Advance the one that has the smaller end point. - if (a[1] < b[1]) { - i++; - } else { - j++; - } - } - - return intersections; -} diff --git a/test/leetcode/interval/interval-list-intersection.test.ts b/test/leetcode/interval/interval-list-intersection.test.ts deleted file mode 100644 index ce12705..0000000 --- a/test/leetcode/interval/interval-list-intersection.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { intervalIntersection } from '../../src/interval/interval-list-intersections'; - -describe('interval list intersection', () => { - test('interval list intersection - test case 1', async () => { - const firstList = [ - [0, 2], - [5, 10], - [13, 23], - [24, 25] - ]; - const secondList = [ - [1, 5], - [8, 12], - [15, 24], - [25, 26] - ]; - - expect(intervalIntersection(firstList, secondList)).toStrictEqual([ - [1, 2], - [5, 5], - [8, 10], - [15, 23], - [24, 24], - [25, 25] - ]); - }); -}); From 883bff66b29e3207fa0f389c65b05161a6926144 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Fri, 14 Mar 2025 20:29:08 -0700 Subject: [PATCH 053/158] fixed bug --- src/leetcode/interval/interval_list_intersections.py | 2 +- src/leetcode/interval/meeting_rooms.py | 0 .../snap_interval_list_intersection_test.py | 12 ++++++++++-- 3 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 src/leetcode/interval/meeting_rooms.py diff --git a/src/leetcode/interval/interval_list_intersections.py b/src/leetcode/interval/interval_list_intersections.py index c3ae5a5..1b0c173 100644 --- a/src/leetcode/interval/interval_list_intersections.py +++ b/src/leetcode/interval/interval_list_intersections.py @@ -68,7 +68,7 @@ def isIntersecting(a: list[int], b: list[int]) -> bool: j = 0 while i < len(firstList) and j < len(secondList): a = firstList[i] - b = firstList[j] + b = secondList[j] ax, ay = a[0], a[1] bx, by = b[0], b[1] diff --git a/src/leetcode/interval/meeting_rooms.py b/src/leetcode/interval/meeting_rooms.py new file mode 100644 index 0000000..e69de29 diff --git a/test/leetcode/interval/snapshots/snap_interval_list_intersection_test.py b/test/leetcode/interval/snapshots/snap_interval_list_intersection_test.py index ae55f52..02e0420 100644 --- a/test/leetcode/interval/snapshots/snap_interval_list_intersection_test.py +++ b/test/leetcode/interval/snapshots/snap_interval_list_intersection_test.py @@ -9,19 +9,27 @@ snapshots['test_case_1 1'] = [ [ - 0, + 1, 2 ], [ 5, + 5 + ], + [ + 8, 10 ], [ - 13, + 15, 23 ], [ 24, + 24 + ], + [ + 25, 25 ] ] From 354355183cb558f300a3720e30b033d653cea15c Mon Sep 17 00:00:00 2001 From: Min Huang Date: Fri, 14 Mar 2025 20:39:28 -0700 Subject: [PATCH 054/158] meeting rooms --- src/leetcode/heap/max_stack.py | 16 +++---- .../heap/number_of_orders_in_the_backlog.py | 8 ++-- src/leetcode/interval/meeting-rooms.ts | 47 ------------------- src/leetcode/interval/meeting_rooms.py | 37 +++++++++++++++ test/leetcode/interval/meeting-rooms.test.ts | 13 ----- test/leetcode/interval/meeting_rooms_test.py | 8 ++++ 6 files changed, 57 insertions(+), 72 deletions(-) delete mode 100644 src/leetcode/interval/meeting-rooms.ts delete mode 100644 test/leetcode/interval/meeting-rooms.test.ts create mode 100644 test/leetcode/interval/meeting_rooms_test.py diff --git a/src/leetcode/heap/max_stack.py b/src/leetcode/heap/max_stack.py index 6cf9755..461fdc3 100644 --- a/src/leetcode/heap/max_stack.py +++ b/src/leetcode/heap/max_stack.py @@ -80,14 +80,14 @@ def push(self, value: int) -> None: a = self.tail.previous b = node c = self.tail - self.__insert_node(a, b, c) + self.__insertNode(a, b, c) def pop(self) -> int: node = self.tail.previous assert node is not None # Remove the last element of the linked list. - self.__delete_node(node) + self.__deleteNode(node) # We also need to remove this element from the heap, but this will be difficult as we can't remove arbitrary # elements from the middle of the heap. Instead, we will note the deletion and defer it. @@ -100,7 +100,7 @@ def top(self) -> int: def peekMax(self) -> int: # Since peekMax is allowed to run in O(log n), perform the deferred heap deletions from any previous pops. - self.__delete_max() + self.__deleteMax() # Return the front of the of heap. (_, _, node) = self.max_heap[0] @@ -108,16 +108,16 @@ def peekMax(self) -> int: def popMax(self) -> int: # Since popMax is allowed to run in O(log n), perform the deferred heap deletions from any previous pops. - self.__delete_max() + self.__deleteMax() # Pop off the front of the heap, which is efficient to do so here (we don't have to delete from the middle). (_, _, node) = heappop(self.max_heap) # Delete from the linked list, which is also efficient to do here. - self.__delete_node(node) + self.__deleteNode(node) return node.value - def __delete_node(self, b: StackNode | None): + def __deleteNode(self, b: StackNode | None): assert b is not None a = b.previous @@ -128,7 +128,7 @@ def __delete_node(self, b: StackNode | None): a.next = c c.previous = a - def __insert_node(self, a: StackNode | None, b: StackNode, c: StackNode | None): + def __insertNode(self, a: StackNode | None, b: StackNode, c: StackNode | None): assert a is not None assert b is not None assert c is not None @@ -141,7 +141,7 @@ def __insert_node(self, a: StackNode | None, b: StackNode, c: StackNode | None): b.previous = a b.next = c - def __delete_max(self): + def __deleteMax(self): while self.max_heap: (_, _, node) = self.max_heap[0] diff --git a/src/leetcode/heap/number_of_orders_in_the_backlog.py b/src/leetcode/heap/number_of_orders_in_the_backlog.py index d4f446c..65549eb 100644 --- a/src/leetcode/heap/number_of_orders_in_the_backlog.py +++ b/src/leetcode/heap/number_of_orders_in_the_backlog.py @@ -51,7 +51,7 @@ def getNumberOfBacklogOrders(self, orders: list[list[int]]) -> int: sells: list[tuple[int, int]] = [] MOD = 10**9 + 7 - def handle_buy(buy_price: int, buy_amount: int): + def handleBuy(buy_price: int, buy_amount: int): while buy_amount > 0: # If we don't have any sellers, we can't match up a sale. So we should push the order into the buys # backlog. @@ -85,7 +85,7 @@ def handle_buy(buy_price: int, buy_amount: int): if buy_amount == 0: return - def handle_sell(sell_price: int, sell_amount: int): + def handleSell(sell_price: int, sell_amount: int): while sell_amount > 0: # If we don't have any buyers, we can't match up a sale. So we should push the order into the sells backlog. if not buys: @@ -122,9 +122,9 @@ def handle_sell(sell_price: int, sell_amount: int): continue if order_type == 0: - handle_buy(price, amount) + handleBuy(price, amount) else: - handle_sell(price, amount) + handleSell(price, amount) total = 0 for _, amount in buys: diff --git a/src/leetcode/interval/meeting-rooms.ts b/src/leetcode/interval/meeting-rooms.ts deleted file mode 100644 index 97f7ee0..0000000 --- a/src/leetcode/interval/meeting-rooms.ts +++ /dev/null @@ -1,47 +0,0 @@ -// DIFFICULTY: EASY -// -// Given an array of meeting time intervals where intervals[i] = [starti, endi], determine if a person could attend all -// meetings. -// -// See {@link https://leetcode.com/problems/meeting-rooms/} -export { canAttendMeetings }; - -// SOLUTION: -// -// See the merge intervals problem for a more detailed solution. -function canAttendMeetings(intervals: number[][]): boolean { - function compare(a: number[], b: number[]) { - if (a[0] < b[0]) { - return -1; - } - - if (a[0] > b[0]) { - return 1; - } - - return a[1] - b[1]; - } - - function isOverlap(a: number[], b: number[]) { - if (a[1] <= b[0]) { - return false; - } - - return true; - } - - if (intervals.length <= 1) { - return true; - } - - intervals.sort((a, b) => compare(a, b)); - for (let i = 1; i < intervals.length; i++) { - const previous = intervals[i - 1]; - const current = intervals[i]; - if (isOverlap(previous, current)) { - return false; - } - } - - return true; -} diff --git a/src/leetcode/interval/meeting_rooms.py b/src/leetcode/interval/meeting_rooms.py index e69de29..904b3ac 100644 --- a/src/leetcode/interval/meeting_rooms.py +++ b/src/leetcode/interval/meeting_rooms.py @@ -0,0 +1,37 @@ +# DIFFICULTY: EASY +# +# Given an array of meeting time intervals where intervals[i] = [starti, endi], determine if a person could attend all +# meetings. +# +# See https://leetcode.com/problems/meeting-rooms +class Solution: + def canAttendMeetings(self, intervals: list[list[int]]) -> bool: + """ + SOLUTION + -------- + + This is a very straightforward problem. We just need to sort the intervals by start time (and end time as a tie + breaker), then check if there is any overlap. + + COMPLEXITY + ---------- + + Time complexity is O(n log n) since we have to sort the intervals. + + Space complexity is O(1) since we don't use any extra space. + """ + # No overlapping intervals are possible if 0 or 1 meetings. + if len(intervals) <= 1: + return True + + # Sorts by start time, then end time as a tie breaker. + intervals.sort(key=lambda x: (x[0], x[1])) + + for i in range(1, len(intervals)): + previous = intervals[i - 1] + current = intervals[i] + # Overlaps exist if the previous end > current start. + if previous[1] > current[0]: + return False + + return True diff --git a/test/leetcode/interval/meeting-rooms.test.ts b/test/leetcode/interval/meeting-rooms.test.ts deleted file mode 100644 index f4b36fa..0000000 --- a/test/leetcode/interval/meeting-rooms.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { canAttendMeetings } from '../../src/interval/meeting-rooms'; - -describe('meeting rooms', () => { - test('meeting rooms - test case 1', async () => { - expect( - canAttendMeetings([ - [0, 30], - [5, 10], - [15, 20] - ]) - ).toBe(false); - }); -}); diff --git a/test/leetcode/interval/meeting_rooms_test.py b/test/leetcode/interval/meeting_rooms_test.py new file mode 100644 index 0000000..333a4df --- /dev/null +++ b/test/leetcode/interval/meeting_rooms_test.py @@ -0,0 +1,8 @@ +from leetcode.interval.meeting_rooms import Solution + + +soln = Solution() + + +def test_case_1(): + assert not soln.canAttendMeetings([[0, 30], [5, 10], [15, 20]]) From 4b71a112dd81c14eacc875347a2ef2fdbbac31f5 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Fri, 14 Mar 2025 20:52:35 -0700 Subject: [PATCH 055/158] simulate max heap --- .../heap/furthest_building_you_can_reach.py | 8 +++---- .../heap/k_closest_points_to_origin.py | 19 ++++++++++++----- src/leetcode/heap/max_stack.py | 19 +++++++++++++---- .../heap/minimize_deviation_in_array.py | 19 ++++++++++++----- src/leetcode/heap/task_scheduler.py | 21 ++++++++++++------- 5 files changed, 61 insertions(+), 25 deletions(-) diff --git a/src/leetcode/heap/furthest_building_you_can_reach.py b/src/leetcode/heap/furthest_building_you_can_reach.py index e008887..e12035a 100644 --- a/src/leetcode/heap/furthest_building_you_can_reach.py +++ b/src/leetcode/heap/furthest_building_you_can_reach.py @@ -40,7 +40,7 @@ def furthestBuilding(self, heights: list[int], bricks: int, ladders: int) -> int Space complexity is O(n) in worst case. """ - heap: list[int] = [] + min_heap: list[int] = [] for i in range(len(heights) - 1): # Calculate the delta that we need to "jump" across. @@ -51,17 +51,17 @@ def furthestBuilding(self, heights: list[int], bricks: int, ladders: int) -> int continue # A non-negative delta means we must use either bricks or a ladder to jump across. - heappush(heap, delta) + heappush(min_heap, delta) # Because the ladders can traverse any height, we'll "save" the ladders here to use on the the biggest # deltas. As long as we have ladders remaining we can maintain a heap size of deltas that we'll # eventually use ladders on. - if len(heap) <= ladders: + if len(min_heap) <= ladders: continue # If we have more elements on the heap than we do ladders, then SOME of these deltas need to be traversed # using bricks instead of ladders. Let's pop the smallest delta out and use bricks on it. - smallest = heappop(heap) + smallest = heappop(min_heap) bricks -= smallest # If we've run out of bricks, that's it. We can can only reach the building at position i. diff --git a/src/leetcode/heap/k_closest_points_to_origin.py b/src/leetcode/heap/k_closest_points_to_origin.py index 4c8983d..b3292ee 100644 --- a/src/leetcode/heap/k_closest_points_to_origin.py +++ b/src/leetcode/heap/k_closest_points_to_origin.py @@ -8,10 +8,22 @@ # You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in). # # See https://leetcode.com/problems/k-closest-points-to-origin -from heapq import heappop, heappush +import heapq as hq import math +def heappush(max_heap: list[tuple[float, list[int]]], item: tuple[float, list[int]]) -> None: + # Simulate a max heap by negating the distance. When storing a tuple in the heap, Python will sort by tuple + # ordering, so will first sort by the first element, then the second. + d, p = item + hq.heappush(max_heap, (-d, p)) + + +def heappop(max_heap: list[tuple[float, list[int]]]) -> tuple[float, list[int]]: + d, p = hq.heappop(max_heap) + return (-d, p) + + class Solution: def kClosest(self, points: list[list[int]], k: int) -> list[list[int]]: """ @@ -48,11 +60,8 @@ def distance(p: list[int]) -> float: # Store max heap of (distance, point). LeetCode gives us the points as list of [x, y]. max_heap: list[tuple[float, list[int]]] = [] for p in points: - # Simulate a max heap by negating the distance. When storing a tuple in the heap, Python will sort by tuple - # ordering, so will first sort by the first element, then the second. Sorting by the first element - # (distance) is all we need. d = distance(p) - heappush(max_heap, (-d, p)) + heappush(max_heap, (d, p)) # If we have more than k elements, remove the farthest point. if len(max_heap) > k: diff --git a/src/leetcode/heap/max_stack.py b/src/leetcode/heap/max_stack.py index 461fdc3..ea6e32a 100644 --- a/src/leetcode/heap/max_stack.py +++ b/src/leetcode/heap/max_stack.py @@ -15,7 +15,18 @@ # You must come up with a solution that supports O(1) for each top call and O(logn) for each other call. # # See https://leetcode.com/problems/max-stack -from heapq import heappop, heappush +import heapq as hq + + +def heappush(max_heap: list[tuple[int, int, "StackNode"]], item: tuple[int, int, "StackNode"]) -> None: + # Simulate a max heap by negating the values. When storing a tuple in the heap, Python will sort by tuple ordering. + value, key, node = item + hq.heappush(max_heap, (-value, -key, node)) + + +def heappop(max_heap: list[tuple[int, int, "StackNode"]]) -> tuple[int, int, "StackNode"]: + value, key, node = hq.heappop(max_heap) + return (-value, -key, node) class StackNode: @@ -72,9 +83,9 @@ def push(self, value: int) -> None: node = StackNode(self.keys, value) self.keys += 1 - # Push onto the heap, but because this is a max heap, negate the value. Because the stack might have dupes, - # we'll want the heap to be ordered by first the value, then the key. - heappush(self.max_heap, (-value, -node.key, node)) + # Push onto the heap; we want to store the node only, but Python sorts by tuple ordering, so order by the value + # first, then the key as a tie breaker. + heappush(self.max_heap, (value, node.key, node)) # Now add this node to the end of the linked list. a = self.tail.previous diff --git a/src/leetcode/heap/minimize_deviation_in_array.py b/src/leetcode/heap/minimize_deviation_in_array.py index 286cbb1..09546e3 100644 --- a/src/leetcode/heap/minimize_deviation_in_array.py +++ b/src/leetcode/heap/minimize_deviation_in_array.py @@ -19,10 +19,19 @@ # Return the minimum deviation the array can have after performing some number of operations. # # See https://leetcode.com/problems/minimize-deviation-in-array -from heapq import heappop, heappush +import heapq as hq import math +def heappush(max_heap: list[int], item: int) -> None: + # Simulate a max heap by negating the value. + hq.heappush(max_heap, -item) + + +def heappop(max_heap: list[int]) -> int: + return -hq.heappop(max_heap) + + class Solution: def minimumDeviation(self, nums: list[int]): """ @@ -44,7 +53,7 @@ def minimumDeviation(self, nums: list[int]): Space complexity is O(n). """ - heap: list[int] = [] + max_heap: list[int] = [] # Normalize all the numbers so that they are even. Now we can consider only division as a way to make numbers # smaller and closer to each other. If a previously odd number was too big, we will eventually resize it smaller @@ -54,7 +63,7 @@ def minimumDeviation(self, nums: list[int]): value *= 2 nums[i] = value - heappush(heap, -value) + heappush(max_heap, value) minimum = min(nums) deviation = math.inf @@ -62,7 +71,7 @@ def minimumDeviation(self, nums: list[int]): # Calculate the current deviation using the max element of the array, then half the max element and return it to # theheap. Then repeat to keep bringing the deviation down. while True: - maximum = -heappop(heap) + maximum = heappop(max_heap) deviation = min(deviation, maximum - minimum) # Oh no! If the max value was odd, we can't halve it and re-insert into the heap. This means that whatever @@ -72,7 +81,7 @@ def minimumDeviation(self, nums: list[int]): # Halve the max value and return it to the heap for re-processing. value = int(maximum / 2) - heappush(heap, -value) + heappush(max_heap, value) # Update the minimum value in case we've changed the minimum value by manipulating the maximum value. minimum = min(value, minimum) diff --git a/src/leetcode/heap/task_scheduler.py b/src/leetcode/heap/task_scheduler.py index cc5fa4f..61af1a4 100644 --- a/src/leetcode/heap/task_scheduler.py +++ b/src/leetcode/heap/task_scheduler.py @@ -8,7 +8,16 @@ # # See https://leetcode.com/problems/task-scheduler from collections import defaultdict, deque -from heapq import heappop, heappush +import heapq as hq + + +def heappush(max_heap: list[int], item: int) -> None: + # Simulate a max heap by negating the value. + hq.heappush(max_heap, -item) + + +def heappop(max_heap: list[int]) -> int: + return -hq.heappop(max_heap) class Solution: @@ -47,10 +56,10 @@ def leastInterval(self, tasks: list[str], n: int) -> int: for task in tasks: map[task] += 1 - # Create a max heap of frequency. To simulate max heap, negate the value. + # Create a max heap of frequency. max_heap: list[int] = [] for freq in map.values(): - heappush(max_heap, -freq) + heappush(max_heap, freq) # Create a queue of [freq, cycle] for cooldown tasks. queue: deque[tuple[int, int]] = deque() @@ -62,12 +71,10 @@ def leastInterval(self, tasks: list[str], n: int) -> int: (f, c) = queue[0] if c == cycle: queue.popleft() - # Negate frequency because it's a max heap. - heappush(max_heap, -f) + heappush(max_heap, f) if max_heap: - # Negate frequency because it's a max heap. - f = -heappop(max_heap) + f = heappop(max_heap) f -= 1 if f != 0: From 17fe32c444376b3a63f29e03c347f927a13963d4 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Fri, 14 Mar 2025 20:53:38 -0700 Subject: [PATCH 056/158] renames --- .../heap/number_of_orders_in_the_backlog.py | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/leetcode/heap/number_of_orders_in_the_backlog.py b/src/leetcode/heap/number_of_orders_in_the_backlog.py index 65549eb..bbf8082 100644 --- a/src/leetcode/heap/number_of_orders_in_the_backlog.py +++ b/src/leetcode/heap/number_of_orders_in_the_backlog.py @@ -46,9 +46,9 @@ def getNumberOfBacklogOrders(self, orders: list[list[int]]) -> int: """ # We want to match sellers with the largest price in the buy orders, so we want a max heap. Pro-tip: Negate the # values here. - buys: list[tuple[int, int]] = [] + buys_max_heap: list[tuple[int, int]] = [] # We want to match buyers with the lowest price in the sell orders, so we want a min heap. - sells: list[tuple[int, int]] = [] + sells_min_heap: list[tuple[int, int]] = [] MOD = 10**9 + 7 def handleBuy(buy_price: int, buy_amount: int): @@ -57,17 +57,17 @@ def handleBuy(buy_price: int, buy_amount: int): # backlog. # # Note that the buys backlog is a max heap, so we have to negate the price. - if not sells: - heappush(buys, (-buy_price, buy_amount)) + if not sells_min_heap: + heappush(buys_max_heap, (-buy_price, buy_amount)) return # If we do have a seller, we can check the lowest sale price. If the lowest sale price is still too # high, we can't match up a sale. So we should push the order into the buys backlog anyways. # # Note that the buys backlog is a max heap, so we have to negate the price. - sell_price, sell_amount = sells[0] + sell_price, sell_amount = sells_min_heap[0] if buy_price < sell_price: - heappush(buys, (-buy_price, buy_amount)) + heappush(buys_max_heap, (-buy_price, buy_amount)) return # Here the buyer and seller have agreed to a sale, so let's execute the order. We can buy only as many @@ -75,11 +75,11 @@ def handleBuy(buy_price: int, buy_amount: int): delta = min(buy_amount, sell_amount) buy_amount -= delta sell_amount -= delta - sells[0] = (sell_price, sell_amount) + sells_min_heap[0] = (sell_price, sell_amount) # If the seller isn't offering any more shares, then pop their order from the sells backlog. if sell_amount == 0: - heappop(sells) + heappop(sells_min_heap) # If the buyer doesn't want any more shares, we can stop processing the loop. if buy_amount == 0: @@ -88,18 +88,18 @@ def handleBuy(buy_price: int, buy_amount: int): def handleSell(sell_price: int, sell_amount: int): while sell_amount > 0: # If we don't have any buyers, we can't match up a sale. So we should push the order into the sells backlog. - if not buys: - heappush(sells, (sell_price, sell_amount)) + if not buys_max_heap: + heappush(sells_min_heap, (sell_price, sell_amount)) return # If we do have a buyer, we can check the highest buy price. If the highest buy price is still too low, # we can't match up a sale. So we should push the order into the buys backlog anyways. # # Note that this is a max heap so we need to negate the value. - buy_price, buy_amount = buys[0] + buy_price, buy_amount = buys_max_heap[0] buy_price *= -1 if sell_price > buy_price: - heappush(sells, (sell_price, sell_amount)) + heappush(sells_min_heap, (sell_price, sell_amount)) return # Here the buyer nad seller have agreed to a sale, so let's execute the order. We can only buy as many shares @@ -107,11 +107,11 @@ def handleSell(sell_price: int, sell_amount: int): delta = min(buy_amount, sell_amount) buy_amount -= delta sell_amount -= delta - buys[0] = (-buy_price, buy_amount) + buys_max_heap[0] = (-buy_price, buy_amount) # If the buyer isn't willing to buy any more shares, then pop their order from the buys backlog. if buy_amount == 0: - heappop(buys) + heappop(buys_max_heap) # If the seller doesn't want any more shares, stop processing the loop. if sell_amount == 0: @@ -127,8 +127,8 @@ def handleSell(sell_price: int, sell_amount: int): handleSell(price, amount) total = 0 - for _, amount in buys: + for _, amount in buys_max_heap: total = (total + amount) % MOD - for _, amount in sells: + for _, amount in sells_min_heap: total = (total + amount) % MOD return total From 64258f6f17a8ab00204bc98f4fad158b72481a37 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sat, 15 Mar 2025 00:33:38 -0700 Subject: [PATCH 057/158] meeting scheduler --- test/leetcode/interval/meeting_scheduler_test.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 test/leetcode/interval/meeting_scheduler_test.py diff --git a/test/leetcode/interval/meeting_scheduler_test.py b/test/leetcode/interval/meeting_scheduler_test.py new file mode 100644 index 0000000..7fa8144 --- /dev/null +++ b/test/leetcode/interval/meeting_scheduler_test.py @@ -0,0 +1,12 @@ +from leetcode.interval.meeting_scheduler import Solution + + +soln = Solution() + + +def test_case_1(): + slot1 = [[10, 50], [60, 120], [140, 210]] + slot2 = [[0, 15], [60, 70]] + duration = 8 + + assert soln.minAvailableDuration(slot1, slot2, duration) == [60, 68] From 956e07811daf7f4e832407494851a03dd678e13b Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sat, 15 Mar 2025 00:34:46 -0700 Subject: [PATCH 058/158] meeting scheduler --- src/leetcode/interval/meeting_scheduler.py | 46 ++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/leetcode/interval/meeting_scheduler.py diff --git a/src/leetcode/interval/meeting_scheduler.py b/src/leetcode/interval/meeting_scheduler.py new file mode 100644 index 0000000..313b178 --- /dev/null +++ b/src/leetcode/interval/meeting_scheduler.py @@ -0,0 +1,46 @@ +class Solution: + def minAvailableDuration(self, slots1: list[list[int]], slots2: list[list[int]], duration: int) -> list[int]: + """ + SOLUTION + -------- + + To solve this, sort the intervals and use the two pointer technique to find a time slot that works for + everybody. + + COMPLEXITY + ---------- + + Time complexity is O(n log n) where n is the number of slots. + + Space complexity is O(1). + """ + # Sort by start time, then end time as a tie breaker. + slots1.sort(key=lambda x: (x[0], x[1])) + slots2.sort(key=lambda x: (x[0], x[1])) + + i = 0 + j = 0 + while i < len(slots1) and j < len(slots2): + a = slots1[i] + b = slots2[j] + + # The start time is the max of the two individuals start times; we can't start before the other person is + # ready. The end time is the min of the two individuals end times; we can't end later than the other + # person's hard stop. + start = max(a[0], b[0]) + end = min(a[1], b[1]) + + # If we can accomodate the availability we should return the start time plus the duration; we shouldn't keep the + # meeting longer than it needs to be. + if end - start >= duration: + return [start, start + duration] + + # Otherwise, we should move the pointer for the person who has the earlier ending time; these times + # represent availability, so if an earlier availability doesn't match the other person's later availability, + # we shouldn't keep the meeting longer than it needs to be. + if a[1] < b[1]: + i += 1 + else: + j += 1 + + return [] From 0a16aff190f3ddb3c47df5997c939f7b372a5671 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sat, 15 Mar 2025 00:35:04 -0700 Subject: [PATCH 059/158] deletes the meeting scheduler test --- src/leetcode/interval/meeting-scheduler.ts | 66 ------------------- .../interval/meeting-scheduler.test.ts | 20 ------ 2 files changed, 86 deletions(-) delete mode 100644 src/leetcode/interval/meeting-scheduler.ts delete mode 100644 test/leetcode/interval/meeting-scheduler.test.ts diff --git a/src/leetcode/interval/meeting-scheduler.ts b/src/leetcode/interval/meeting-scheduler.ts deleted file mode 100644 index 84b9ceb..0000000 --- a/src/leetcode/interval/meeting-scheduler.ts +++ /dev/null @@ -1,66 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given the availability time slots arrays slots1 and slots2 of two people and a meeting duration duration, return the -// earliest time slot that works for both of them and is of duration duration. -// -// If there is no common time slot that satisfies the requirements, return an empty array. -// -// The format of a time slot is an array of two elements [start, end] representing an inclusive time range from start to -// end. -// -// It is guaranteed that no two availability slots of the same person intersect with each other. That is, for any two -// time slots [start1, end1] and [start2, end2] of the same person, either start1 > end2 or start2 > end1. -// -// See {@link https://leetcode.com/problems/meeting-scheduler/} -export { minAvailableDuration }; - -// SOLUTION: -// -// To solve this, sort the intervals and use the two pointer technique to find a time slot that works for everybody. -function minAvailableDuration(slots1: number[][], slots2: number[][], duration: number): number[] { - function compare(a: number[], b: number[]) { - if (a[0] < b[0]) { - return -1; - } - - if (a[0] > b[0]) { - return 1; - } - - // If a[0] and b[0] are the same value, we can just compare based on a[1] and b[1], and a normal comparator will - // give elements in ascending order via x - y. - return a[1] - b[1]; - } - - slots1.sort(compare); - slots2.sort(compare); - - let i = 0; - let j = 0; - while (i < slots1.length && j < slots2.length) { - const a = slots1[i]; - const b = slots2[j]; - - // The start time is the max of the two individuals start times; we can't start before the other person is ready. - // The end time is the min of the two individuals end times; we can't end later than the other person's hard stop. - const start = Math.max(a[0], b[0]); - const end = Math.min(a[1], b[1]); - - // If we can accomodate the availability we should return the start time plus the duration; we shouldn't keep the - // meeting longer than it needs to be. - if (end - start >= duration) { - return [start, start + duration]; - } - - // Otherwise, we should move the pointer for the person who has the earlier ending time; these times represent - // availability, so if an earlier availability doesn't match the other person's later availability, we should - // move the person with the earlier availability up. - if (a[1] < b[1]) { - i++; - } else { - j++; - } - } - - return []; -} diff --git a/test/leetcode/interval/meeting-scheduler.test.ts b/test/leetcode/interval/meeting-scheduler.test.ts deleted file mode 100644 index acf582e..0000000 --- a/test/leetcode/interval/meeting-scheduler.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { minAvailableDuration } from '../../src/interval/meeting-scheduler'; - -describe('meeting scheduler', () => { - test('meeting scheduler - test case 1', async () => { - expect( - minAvailableDuration( - [ - [10, 50], - [60, 120], - [140, 210] - ], - [ - [0, 15], - [60, 70] - ], - 8 - ) - ).toStrictEqual([60, 68]); - }); -}); From 570122880b0b31164c566c5ec03793643f9535e3 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sat, 15 Mar 2025 00:43:09 -0700 Subject: [PATCH 060/158] merge intervals test --- src/leetcode/interval/merge-intervals.ts | 61 ------------------- src/leetcode/interval/merge_intervals.py | 45 ++++++++++++++ .../leetcode/interval/merge-intervals.test.ts | 18 ------ .../leetcode/interval/merge_intervals_test.py | 9 +++ 4 files changed, 54 insertions(+), 79 deletions(-) delete mode 100644 src/leetcode/interval/merge-intervals.ts create mode 100644 src/leetcode/interval/merge_intervals.py delete mode 100644 test/leetcode/interval/merge-intervals.test.ts create mode 100644 test/leetcode/interval/merge_intervals_test.py diff --git a/src/leetcode/interval/merge-intervals.ts b/src/leetcode/interval/merge-intervals.ts deleted file mode 100644 index c037ec8..0000000 --- a/src/leetcode/interval/merge-intervals.ts +++ /dev/null @@ -1,61 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an array of intervals where intervals[i] = [starti, endi], merge all overlapping intervals, and return an array -// of the non-overlapping intervals that cover all the intervals in the input. -// -// See {@link https://leetcode.com/problems/merge-intervals/} -export { merge }; - -// SOLUTION: -function merge(intervals: number[][]): number[][] { - function compareInternal(a: number[], b: number[]) { - if (a[0] < b[0]) { - return -1; - } - - if (a[0] > b[0]) { - return 1; - } - - // If a[0] and b[0] are the same value, we can just compare based on a[1] and b[1], and a normal comparator will - // give elements in ascending order via x - y. - return a[1] - b[1]; - } - - function shouldMerge(a: number[], b: number[]) { - // If the intervals are disjoint, then a[1] is going to be strictly less than b[0]. In every other case, we - // should merge. We can make this assumption because we know the intervals are sorted, and a <= b. - if (a[1] < b[0]) { - return false; - } - - return true; - } - - function mergeInternal(a: number[], b: number[]) { - // Because the intervals are sorted (a <= b), a[0] is always going to be smaller or the same as b[0]. - return [a[0], Math.max(a[1], b[1])]; - } - - // First sort the interval, then run our merge algorithm. - intervals.sort((a, b) => compareInternal(a, b)); - - const merged: number[][] = []; - for (const interval of intervals) { - if (merged.length === 0) { - merged.push(interval); - continue; - } - - const a = merged[merged.length - 1]; - const b = interval; - if (!shouldMerge(a, b)) { - merged.push(b); - continue; - } - - merged[merged.length - 1] = mergeInternal(a, b); - } - - return merged; -} diff --git a/src/leetcode/interval/merge_intervals.py b/src/leetcode/interval/merge_intervals.py new file mode 100644 index 0000000..8e6484b --- /dev/null +++ b/src/leetcode/interval/merge_intervals.py @@ -0,0 +1,45 @@ +# DIFFICULTY: MEDIUM +# +# Given an array of intervals where intervals[i] = [starti, endi], merge all overlapping intervals, and return an array +# of the non-overlapping intervals that cover all the intervals in the input. +# +# See https://leetcode.com/problems/merge-intervals +class Solution: + def merge(self, intervals: list[list[int]]) -> list[list[int]]: + """ + SOLUTION + -------- + + Sort the intervals by start time, then merge overlapping intervals. + + COMPLEXITY + ---------- + + Time complexity is O(n log n) since we have to sort the intervals. + + Space complexity is O(n) since we have to store the merged intervals. + """ + # Sort by start time, then end time as a tie breaker. + intervals.sort(key=lambda x: (x[0], x[1])) + + merged: list[list[int]] = [] + for interval in intervals: + if len(merged) == 0: + merged.append(interval) + continue + + a = merged[-1] + b = interval + + # If the intervals are disjoint, then a[1] is going to be strictly less than b[0]. In which case, there is + # no overlap, and we just append the interval to the merged list. + # + # Note that we can make this assumption because the intervals are sorted. + if a[1] < b[0]: + merged.append(b) + # Otherwise, there is overlap, and we need to merge the intervals. + else: + # Because the intervals are sorted (a <= b), a[0] is always going to be smaller or the same as b[0]. + merged[-1] = [a[0], max(a[1], b[1])] + + return merged diff --git a/test/leetcode/interval/merge-intervals.test.ts b/test/leetcode/interval/merge-intervals.test.ts deleted file mode 100644 index 1ad3777..0000000 --- a/test/leetcode/interval/merge-intervals.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { merge } from '../../src/interval/merge-intervals'; - -describe('merge intervals', () => { - test('merge intervals - test case 1', async () => { - expect( - merge([ - [1, 3], - [2, 6], - [8, 10], - [15, 18] - ]) - ).toStrictEqual([ - [1, 6], - [8, 10], - [15, 18] - ]); - }); -}); diff --git a/test/leetcode/interval/merge_intervals_test.py b/test/leetcode/interval/merge_intervals_test.py new file mode 100644 index 0000000..d71daca --- /dev/null +++ b/test/leetcode/interval/merge_intervals_test.py @@ -0,0 +1,9 @@ +from leetcode.interval.merge_intervals import Solution + + +soln = Solution() + + +def test_case_1(): + intervals = [[1, 3], [2, 6], [8, 10], [15, 18]] + assert soln.merge(intervals) == [[1, 6], [8, 10], [15, 18]] From ce65f79f5ed5574eb78de6368d1d60a3eeefcd1b Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sat, 15 Mar 2025 00:43:42 -0700 Subject: [PATCH 061/158] appears to do nothing --- test/conftest.py | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 test/conftest.py diff --git a/test/conftest.py b/test/conftest.py deleted file mode 100644 index 2752bbc..0000000 --- a/test/conftest.py +++ /dev/null @@ -1,7 +0,0 @@ -import os - - -def pytest_sessionfinish(): - snap_init = "snapshots/__init__.py" - if os.path.exists(snap_init): - os.remove(snap_init) From bcb3672a1687903447efdd939669d9cf0f6a893e Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sat, 15 Mar 2025 14:42:47 -0700 Subject: [PATCH 062/158] adds two numbers --- src/leetcode/linked_list/add-two-numbers.ts | 42 -------------- src/leetcode/linked_list/add_two_numbers.py | 56 +++++++++++++++++++ src/leetcode/linked_list/common/list_node.py | 37 ++++++++++++ .../linked_list/add-two-numbers.test.ts | 30 ---------- .../linked_list/add_two_numbers_test.py | 32 +++++++++++ .../linked_list/snapshots/__init__.py | 0 .../snapshots/snap_add_two_numbers_test.py | 42 ++++++++++++++ 7 files changed, 167 insertions(+), 72 deletions(-) delete mode 100644 src/leetcode/linked_list/add-two-numbers.ts create mode 100644 src/leetcode/linked_list/add_two_numbers.py create mode 100644 src/leetcode/linked_list/common/list_node.py delete mode 100644 test/leetcode/linked_list/add-two-numbers.test.ts create mode 100644 test/leetcode/linked_list/add_two_numbers_test.py create mode 100644 test/leetcode/linked_list/snapshots/__init__.py create mode 100644 test/leetcode/linked_list/snapshots/snap_add_two_numbers_test.py diff --git a/src/leetcode/linked_list/add-two-numbers.ts b/src/leetcode/linked_list/add-two-numbers.ts deleted file mode 100644 index 938a63c..0000000 --- a/src/leetcode/linked_list/add-two-numbers.ts +++ /dev/null @@ -1,42 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse -// order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list. -// -// You may assume the two numbers do not contain any leading zero, except the number 0 itself. -// -// See {@link https://leetcode.com/problems/add-two-numbers/} -import { ListNode } from './common/list-node'; -export { addTwoNumbers }; - -// SOLUTION: -// -// A naive solution of converting the nodes to numbers, adding them, and them, and then reconstructing the linked list -// does work, but is a bunch more code. -// -// Since the lists are stored in reverse order, you can add the two head nodes, preserving a carry, and create a new -// node, in the same way you would do elementary school addition. -function addTwoNumbers(x: ListNode | null, y: ListNode | null): ListNode | null { - function add(u: ListNode | null, v: ListNode | null, carry: number) { - if (u === null && v === null) { - return carry > 0 ? new ListNode(1) : null; - } - - // Compute the sum and carry for the current node. - const a = u?.val ?? 0; - const b = v?.val ?? 0; - const sum = a + b + carry; - const val = sum < 10 ? sum : sum - 10; - const node = new ListNode(); - - // Compute the sum for the next node and attach it to this one. - const uNext = u?.next ?? null; - const vNext = v?.next ?? null; - const carryNext = sum < 10 ? 0 : 1; - node.val = val; - node.next = add(uNext, vNext, carryNext); - return node; - } - - return add(x, y, 0); -} diff --git a/src/leetcode/linked_list/add_two_numbers.py b/src/leetcode/linked_list/add_two_numbers.py new file mode 100644 index 0000000..d0a2d9e --- /dev/null +++ b/src/leetcode/linked_list/add_two_numbers.py @@ -0,0 +1,56 @@ +# DIFFICULTY: MEDIUM +# +# You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse +# order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list. +# +# You may assume the two numbers do not contain any leading zero, except the number 0 itself. +# +# See https://leetcode.com/problems/add-two-numbers +from leetcode.linked_list.common.list_node import ListNode + + +class Solution: + def addTwoNumbers(self, a: ListNode, b: ListNode) -> ListNode: + """ + SOLUTION + -------- + + A naive solution of converting the nodes to numbers, adding them, and them, and then reconstructing the linked + list does work, but is a bunch more code. + + Since the lists are stored in reverse order, you can add the two head nodes, preserving a carry, and create a + new node, in the same way you would do elementary school addition. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the length of the longer linked list. + + Space complexity is O(n) where n is the length of the longer linked list. + """ + + def add(u: ListNode | None, v: ListNode | None, carry: int) -> ListNode | None: + if not u and not v: + return ListNode(1) if carry > 0 else None + + # Compute the sum and carry for the current node. + u_val = u.val if u else 0 + v_val = v.val if v else 0 + sum_val = u_val + v_val + carry + next_val = sum_val if sum_val < 10 else sum_val - 10 + + # Compute the sum for the next node and attach it to this one. + u_next = u.next if u else None + v_next = v.next if v else None + carry_next = 1 if sum_val >= 10 else 0 + + node = ListNode() + node.val = next_val + node.next = add(u_next, v_next, carry_next) + + return node + + result = add(a, b, 0) + + assert result + return result diff --git a/src/leetcode/linked_list/common/list_node.py b/src/leetcode/linked_list/common/list_node.py new file mode 100644 index 0000000..46f4757 --- /dev/null +++ b/src/leetcode/linked_list/common/list_node.py @@ -0,0 +1,37 @@ +# This class definition comes from the problem itself and we cannot change it, or else our submission will not be +# accepted. +class ListNode: + def __init__(self, val=0, next=None): + self.val = val + self.next = next + + +def list2array(node: ListNode | None) -> list[int]: + if not node: + return [] + + xs: list[int] = [] + current = node + while current: + xs.append(current.val) + current = current.next + + return xs + + +def array2list(xs: list[int]) -> ListNode | None: + if not xs: + return None + + root: ListNode | None = None + current: ListNode | None = None + for x in xs: + if not current: + current = ListNode(x) + root = current + continue + else: + current.next = ListNode(x) + current = current.next + + return root diff --git a/test/leetcode/linked_list/add-two-numbers.test.ts b/test/leetcode/linked_list/add-two-numbers.test.ts deleted file mode 100644 index 93b7bbf..0000000 --- a/test/leetcode/linked_list/add-two-numbers.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { array2list, list2array } from '../../src/linked-list/common/list-node'; -import { addTwoNumbers } from '../../src/linked-list/add-two-numbers'; - -describe('add two numbers', () => { - test('add two numbers - test case 1', async () => { - const x = array2list([2, 4, 3]); - const y = array2list([5, 6, 4]); - const z = addTwoNumbers(x, y); - - expect(list2array(z)).toStrictEqual([7, 0, 8]); - }); - - test('add two numbers - test case 2', async () => { - const x = array2list([9, 9, 9, 9, 9, 9, 9]); - const y = array2list([9, 9, 9, 9]); - const z = addTwoNumbers(x, y); - - expect(list2array(z)).toStrictEqual([8, 9, 9, 9, 0, 0, 0, 1]); - }); - - test('add two numbers - test case 3', async () => { - const x = array2list([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); - const y = array2list([5, 6, 4]); - const z = addTwoNumbers(x, y); - - expect(list2array(z)).toStrictEqual([ - 6, 6, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 - ]); - }); -}); diff --git a/test/leetcode/linked_list/add_two_numbers_test.py b/test/leetcode/linked_list/add_two_numbers_test.py new file mode 100644 index 0000000..a5997ac --- /dev/null +++ b/test/leetcode/linked_list/add_two_numbers_test.py @@ -0,0 +1,32 @@ +from leetcode.linked_list.add_two_numbers import Solution +from leetcode.linked_list.common.list_node import array2list, list2array + + +soln = Solution() + + +def test_case_1(): + x = array2list([2, 4, 3]) + y = array2list([5, 6, 4]) + + assert x + assert y + assert list2array(soln.addTwoNumbers(x, y)) == [7, 0, 8] + + +def test_case_2(): + x = array2list([9, 9, 9, 9, 9, 9, 9]) + y = array2list([9, 9, 9, 9]) + + assert x + assert y + assert list2array(soln.addTwoNumbers(x, y)) == [8, 9, 9, 9, 0, 0, 0, 1] + + +def test_case_3(snapshot): + x = array2list([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]) + y = array2list([5, 6, 4]) + + assert x + assert y + snapshot.assert_match(list2array(soln.addTwoNumbers(x, y))) diff --git a/test/leetcode/linked_list/snapshots/__init__.py b/test/leetcode/linked_list/snapshots/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/leetcode/linked_list/snapshots/snap_add_two_numbers_test.py b/test/leetcode/linked_list/snapshots/snap_add_two_numbers_test.py new file mode 100644 index 0000000..ddbeafb --- /dev/null +++ b/test/leetcode/linked_list/snapshots/snap_add_two_numbers_test.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# snapshottest: v1 - https://goo.gl/zC4yUc +from __future__ import unicode_literals + +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots['test_case_3 1'] = [ + 6, + 6, + 4, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1 +] From d24aadc086a4acc09eb1c689516ad1093ab5d665 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sat, 15 Mar 2025 20:16:46 -0700 Subject: [PATCH 063/158] all one --- .gitignore | 1 + src/leetcode/linked_list/all-one.ts | 194 ------------------- src/leetcode/linked_list/all_one.py | 172 ++++++++++++++++ src/leetcode/linked_list/common/list-node.ts | 53 ----- test/leetcode/linked_list/all-one.test.ts | 36 ---- test/leetcode/linked_list/all_one_test.py | 34 ++++ 6 files changed, 207 insertions(+), 283 deletions(-) delete mode 100644 src/leetcode/linked_list/all-one.ts create mode 100644 src/leetcode/linked_list/all_one.py delete mode 100644 src/leetcode/linked_list/common/list-node.ts delete mode 100644 test/leetcode/linked_list/all-one.test.ts create mode 100644 test/leetcode/linked_list/all_one_test.py diff --git a/.gitignore b/.gitignore index a9fc15f..9bda36a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ __pycache__/ .env/ .mypy_cache/ .venv/ +.vscode/ build/ dist/ diff --git a/src/leetcode/linked_list/all-one.ts b/src/leetcode/linked_list/all-one.ts deleted file mode 100644 index accedd6..0000000 --- a/src/leetcode/linked_list/all-one.ts +++ /dev/null @@ -1,194 +0,0 @@ -// DIFFICULTY: HARD -// -// Design a data structure to store the strings' count with the ability to return the strings with minimum and maximum -// counts. -// -// Implement the AllOne class: -// -// - AllOne() Initializes the object of the data structure. -// - inc(String key) Increments the count of the string key by 1. If key does not exist in the data structure, insert -// it with count 1. -// - dec(String key) Decrements the count of the string key by 1. If the count of key is 0 after the decrement, remove -// it from the data structure. It is guaranteed that key exists in the data structure before the decrement. -// - getMaxKey() Returns one of the keys with the maximal count. If no element exists, return an empty string "". -// - getMinKey() Returns one of the keys with the minimum count. If no element exists, return an empty string "". -// -// Note that each function must run in O(1) average time complexity. -// -// See {@link https://leetcode.com/problems/all-oone-data-structure/} -export { AllOne }; - -// SOLUTION: -// -// For some reason, LeetCode does not permit the names TreeNode or ListNode; these interfaces are -// included for you somewhere... -interface OneNode { - count: number; - keys: Set; - previous: OneNode; - next: OneNode; -} - -class AllOne { - private readonly nodes: Map; - - private head: OneNode; - - private tail: OneNode; - - constructor() { - this.nodes = new Map(); - - // It's not necessary to have these sentinel values, but it makes things simpler in that you don't have to worry - // about undefined at all. The head and tail will refer to head and tail of the list, while min and max will - // refer to nodes that would've otherwise been the min and max values. - const head: Partial = { - count: Number.MIN_SAFE_INTEGER, - keys: new Set(), - previous: undefined, - next: undefined - }; - const tail: Partial = { - count: Number.MAX_SAFE_INTEGER, - keys: new Set(), - previous: undefined, - next: undefined - }; - - head.next = tail as OneNode; - tail.previous = head as OneNode; - - this.head = head as OneNode; - this.tail = tail as OneNode; - } - - inc(key: string): void { - // Node is present; move key from our node to next one. - if (this.nodes.has(key)) { - const node = this.nodes.get(key)!; - // Next node count === count + 1; just add to the next node and update. Note that head/tail have min/max values - // so this logic encompasses it. - if (node.next.count === node.count + 1) { - node.keys.delete(key); - node.next.keys.add(key); - this.nodes.set(key, node.next); - this.cleanup(node); - return; - } - - // Next node count < count + 1; add a new node. - const next = this.insertKeyBetween(node, key, node.next); - next.count = node.count + 1; - - // Delete the key from current node and possibly unlink the node. - node.keys.delete(key); - this.cleanup(node); - return; - } - - // No nodes at all; inserting new node as min/max node. - if (this.head.next === this.tail) { - this.insertKeyBetween(this.head, key, this.tail); - return; - } - - // At least one node is present, and node has count === 1 as well; update that node. - if (this.head.next.count === 1) { - const node = this.head.next; - node.keys.add(key); - this.nodes.set(key, node); - return; - } - - // At least one node is present, and node has count > 1, insert before that node. - this.insertKeyBetween(this.head, key, this.head.next); - } - - dec(key: string): void { - // Problem says this is guaranteed to be present. - const node = this.nodes.get(key)!; - - // Did we decrement to zero? Just delete the key from the node altogether. - if (node.count === 1) { - node.keys.delete(key); - this.nodes.delete(key); - this.cleanup(node); - return; - } - - // The previous node has count === count - 1, just move the key from our node to the previous one. - if (node.previous.count === node.count - 1) { - const { previous } = node; - previous.keys.add(key); - this.nodes.set(key, previous); - - node.keys.delete(key); - this.cleanup(node); - return; - } - - // The previous node has count too low; we need to make a new node for it. - const previous = this.insertKeyBetween(node.previous, key, node); - previous.count = node.count - 1; - - // Delete the key and cleanup the node. - node.keys.delete(key); - this.cleanup(node); - } - - getMaxKey(): string { - if (this.head.next === this.tail) { - return ''; - } - - return this.tail.previous.keys.values().next().value!; - } - - getMinKey(): string { - if (this.head.next === this.tail) { - return ''; - } - - return this.head.next.keys.values().next().value!; - } - - private insertKeyBetween(previous: OneNode, key: string, next: OneNode): OneNode { - const node: OneNode = { - count: 1, - keys: new Set(), - previous, - next - }; - - node.keys.add(key); - this.nodes.set(key, node); - - this.insertNodeBetween(previous, node, next); - return node; - } - - private insertNodeBetween(previous: OneNode, node: OneNode, next: OneNode) { - node.previous = previous; - node.next = next; - - previous.next = node; - - next.previous = node; - } - - private cleanup(node: OneNode) { - const { previous } = node; - const { next } = node; - - if (node === this.head || node === this.tail) { - return; - } - - if (node.keys.size > 0) { - return; - } - - previous.next = next; - next.previous = previous; - } -} diff --git a/src/leetcode/linked_list/all_one.py b/src/leetcode/linked_list/all_one.py new file mode 100644 index 0000000..a8b76f0 --- /dev/null +++ b/src/leetcode/linked_list/all_one.py @@ -0,0 +1,172 @@ +# DIFFICULTY: HARD +# +# Design a data structure to store the strings' count with the ability to return the strings with minimum and maximum +# counts. +# +# Implement the AllOne class: +# +# - AllOne() Initializes the object of the data structure. +# - inc(String key) Increments the count of the string key by 1. If key does not exist in the data structure, insert +# it with count 1. +# - dec(String key) Decrements the count of the string key by 1. If the count of key is 0 after the decrement, remove +# it from the data structure. It is guaranteed that key exists in the data structure before the decrement. +# - getMaxKey() Returns one of the keys with the maximal count. If no element exists, return an empty string "". +# - getMinKey() Returns one of the keys with the minimum count. If no element exists, return an empty string "". +# +# Note that each function must run in O(1) average time complexity. +# +# See https://leetcode.com/problems/all-oone-data-structure +import math +from typing import cast + + +class Node: + def __init__(self, count: float): + # This node represents a count/frequency and the keys that have this count. + self.count = count + # A set of keys that have this count. + self.keys: set[str] = set() + # For a doubly linked list, the non-sentinel nodes will have both a previous and next node. Only the sentinel + # nodes will have a pointer that points to None. + self.previous: "Node" = cast("Node", None) + self.next: "Node" = cast("Node", None) + + +class AllOne: + def __init__(self): + # Map of key -> node. + self.nodes: dict[str, Node] = {} + # We'll use this linked list to quickly find the min and max counts, which will lead us to the min and max keys. + # Create two sentinel nodes to simplify the logic. + self.head = Node(-math.inf) + self.tail = Node(math.inf) + self.head.next = self.tail + self.tail.previous = self.head + + def inc(self, key: str) -> None: + # If our key DOES NOT exist in the data structure, there are two cases: + # + # 1. The first node has count of 1. + # 2. The first node does not have a count of 1. + if key not in self.nodes: + # Find the first node (this could be the tail, but that's okay). Note that we choose a node name u so that + # the subsequent (next) node will be named v. + u = self.head.next + + # If the first node doesn't have a count of 1, we can't just add the key to it. Instead, we'll have to + # create a new node. + if u.count != 1: + u = self.__addNodeAfter(self.head, 1) + + # Now that we have the correct node (it cannot be the tail anymore). + u.keys.add(key) + self.nodes[key] = u + return + + # If our key DOES exist in the data structure, things get more complicated. + # + # 1. Find the current node with count k. + # 2. Find the next node with count k + 1, or create it if it doesn't exist, then add the key to it. + # 3. Remove the key from the current node, and remove the current node if it has no keys. + # 5. Update the key -> node mapping. + u = self.nodes[key] + + # Find the next node, or create it with the incremented count. If the next node has a count of k + 1, then we + # have found the correct one. Otherwise, we need to create a new node. + v = u.next + if v.count != u.count + 1: + v = self.__addNodeAfter(u, u.count + 1) + v.keys.add(key) + + # Remove the key from the current node, and remove the current node if it has no keys. + u.keys.remove(key) + if not u.keys: + self.__removeNode(u) + + # Update the key -> node mapping. + self.nodes[key] = v + + def dec(self, key: str) -> None: + # The problem guarantees that our key WILL exist in the data structure. + # + # 1. Find the current node with count k. + # 2. Find the previous node with count k - 1, or create it if it doesn't exist, then add the key to it. + # 3. Remove the key from the current node, and remove the current node if it has no keys. + # 4. Update the key -> node mapping. + # + # Note that we choose a name v for the current node, and u for the previous node. + v = self.nodes[key] + + # Find the previous node, or create it with the decremented count. If the previous node has a count of k - 1, + # then we have found the correct one. Otherwise, we need to create a new node. + # + # Unless! If k - 1 is 0, we don't create a new node, because we don't store keys with a count of 0. Instead, + # we just remove the key from the current node and remove the current node if it has no keys. + u = v.previous + if v.count == 1: + self.__removeKey(key) + return + # Otherwise, proceed in the same way as we handled inc. + elif u.count != v.count - 1: + u = self.__addNodeAfter(v.previous, v.count - 1) + u.keys.add(key) + + # Remove the key from the current node, and remove the current node if it has no keys. + v.keys.remove(key) + if not v.keys: + self.__removeNode(v) + + # Update the key -> node mapping. + self.nodes[key] = u + + def getMinKey(self) -> str: + if not self.nodes: + return "" + + return next(iter(self.head.next.keys)) + + def getMaxKey(self) -> str: + if not self.nodes: + return "" + + return next(iter(self.tail.previous.keys)) + + def __addNodeAfter(self, prev: Node, count: float) -> Node: + node = Node(count) + + # Line up the nodes in the desired order: a -> b -> c + a = prev + b = node + c = prev.next + + # Update pointers for node b. + b.previous = a + b.next = c + + # Update pointers for nodes a and c. + a.next = b + c.previous = b + + return node + + def __removeNode(self, node: Node) -> None: + # Line up the nodes in the desired order: a -> b -> c + a = node.previous + c = node.next + + # Update pointers for nodes a and c. + a.next = c + c.previous = a + + def __removeKey(self, key: str) -> None: + if key not in self.nodes: + return + + # Remove the key from the node. + node = self.nodes[key] + node.keys.remove(key) + + # Remove the node itself if it has no keys. + self.nodes.pop(key) + if not node.keys: + self.__removeNode(node) diff --git a/src/leetcode/linked_list/common/list-node.ts b/src/leetcode/linked_list/common/list-node.ts deleted file mode 100644 index c487bd7..0000000 --- a/src/leetcode/linked_list/common/list-node.ts +++ /dev/null @@ -1,53 +0,0 @@ -// This class definition comes from the problem itself and we cannot change it, or else our submission will not be -// accepted. -export class ListNode { - val: number; - - next: ListNode | null; - - constructor(val?: number, next?: ListNode | null) { - this.val = val === undefined ? 0 : val; - this.next = next === undefined ? null : next; - } -} - -export function list2array(node: ListNode | null): number[] { - if (node === null) { - return []; - } - - const xs: number[] = []; - let current: ListNode | null = node; - while (current !== null) { - xs.push(current.val); - current = current.next; - } - return xs; -} - -export function array2list(xs: number[] | null): ListNode | null { - if (xs === null) { - return null; - } - - let root: ListNode | null = null; - let current: ListNode | null = null; - for (let i = 0; i < xs.length; i++) { - if (current === null) { - current = { - val: xs[i], - next: null - }; - root = current; - continue; - } - - current.next = { - val: xs[i], - next: null - }; - current = current.next; - } - - return root; -} diff --git a/test/leetcode/linked_list/all-one.test.ts b/test/leetcode/linked_list/all-one.test.ts deleted file mode 100644 index 3924a6e..0000000 --- a/test/leetcode/linked_list/all-one.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { AllOne } from '../../src/linked-list/all-one'; - -describe('all O`one data structure', () => { - test('all one data structure - test case 1', async () => { - const allone = new AllOne(); - allone.inc('hello'); - allone.inc('hello'); - - expect(allone.getMaxKey()).toBe('hello'); - expect(allone.getMinKey()).toBe('hello'); - - allone.inc('leet'); - - expect(allone.getMaxKey()).toBe('hello'); - expect(allone.getMinKey()).toBe('leet'); - }); - - test('all one data structure - test case 2', async () => { - const allone = new AllOne(); - allone.inc('a'); - allone.inc('b'); - allone.inc('b'); - allone.inc('c'); - allone.inc('c'); - allone.inc('c'); - allone.dec('b'); - allone.dec('b'); - - expect(allone.getMinKey()).toBe('a'); - - allone.dec('a'); - - expect(allone.getMaxKey()).toBe('c'); - expect(allone.getMinKey()).toBe('c'); - }); -}); diff --git a/test/leetcode/linked_list/all_one_test.py b/test/leetcode/linked_list/all_one_test.py new file mode 100644 index 0000000..0d90e4c --- /dev/null +++ b/test/leetcode/linked_list/all_one_test.py @@ -0,0 +1,34 @@ +from leetcode.linked_list.all_one import AllOne + + +def test_case_1(): + allone = AllOne() + allone.inc("hello") + allone.inc("hello") + + assert allone.getMaxKey() == "hello" + assert allone.getMinKey() == "hello" + + allone.inc("leet") + + assert allone.getMaxKey() == "hello" + assert allone.getMinKey() == "leet" + + +def test_case_2(): + allone = AllOne() + allone.inc("a") + allone.inc("b") + allone.inc("b") + allone.inc("c") + allone.inc("c") + allone.inc("c") + allone.dec("b") + allone.dec("b") + + assert allone.getMinKey() == "a" + + allone.dec("a") + + assert allone.getMaxKey() == "c" + assert allone.getMinKey() == "c" From e6ba6e8fdb04f4adc4a4f6c6bd7390743a8c1697 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sat, 15 Mar 2025 20:33:31 -0700 Subject: [PATCH 064/158] copy list with random pointers --- .../linked_list/common/random_node.py | 7 ++ .../copy-list-with-random-pointer.ts | 62 ----------------- .../copy_list_with_random_pointers.py | 69 +++++++++++++++++++ 3 files changed, 76 insertions(+), 62 deletions(-) create mode 100644 src/leetcode/linked_list/common/random_node.py delete mode 100644 src/leetcode/linked_list/copy-list-with-random-pointer.ts create mode 100644 src/leetcode/linked_list/copy_list_with_random_pointers.py diff --git a/src/leetcode/linked_list/common/random_node.py b/src/leetcode/linked_list/common/random_node.py new file mode 100644 index 0000000..e926dc1 --- /dev/null +++ b/src/leetcode/linked_list/common/random_node.py @@ -0,0 +1,7 @@ +# This class definition comes from the problem itself and we cannot change it, or else our submission will not be +# accepted. +class Node: + def __init__(self, x: int, next: "Node | None" = None, random: "Node | None" = None): + self.val = int(x) + self.next = next + self.random = random diff --git a/src/leetcode/linked_list/copy-list-with-random-pointer.ts b/src/leetcode/linked_list/copy-list-with-random-pointer.ts deleted file mode 100644 index 9212cd6..0000000 --- a/src/leetcode/linked_list/copy-list-with-random-pointer.ts +++ /dev/null @@ -1,62 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// A linked list of length n is given such that each node contains an additional random pointer, which could point to -// any node in the list, or null. -// -// Construct a deep copy of the list. The deep copy should consist of exactly n brand new nodes, where each new node has -// its value set to the value of its corresponding original node. Both the next and random pointer of the new nodes -// should point to new nodes in the copied list such that the pointers in the original list and copied list represent -// the same list state. None of the pointers in the new list should point to nodes in the original list. -// -// For example, if there are two nodes X and Y in the original list, where X.random --> Y, then for the corresponding -// two nodes x and y in the copied list, x.random --> y. -// -// Return the head of the copied linked list. -// -// The linked list is represented in the input/output as a list of n nodes. Each node is represented as a pair of -// [val, random_index] where: -// -// - val: an integer representing Node.val -// - random_index: the index of the node (range from 0 to n-1) that the random pointer points to, or null if it does not point to any node. -// -// Your code will only be given the head of the original linked list. -// -// See {@link https://leetcode.com/problems/copy-list-with-random-pointer} -import { _Node } from './common/random-node'; -export { copyRandomList }; - -// SOLUTION: -// -// If there's no random pointer you can iterate through the nodes and create a copy of each node. With the random -// pointer you'll have to maintain a map of original node to copied node so you can assign the random pointers later. -// -// First iterate through the list once to make copies of each node in a map. Then iterate again and assign the next -// and random pointers. -function copyRandomList(head: _Node | null): _Node | null { - if (head === null) { - return null; - } - - type Original = _Node; - type Copy = _Node; - const map = new Map(); - - // First create a map of each node to its copy. - let node: _Node | null = head; - while (node !== null) { - map.set(node, new _Node(node.val)); - node = node.next; - } - - // Now assign the random pointers. - node = head; - while (node !== null) { - const copy = map.get(node)!; - copy.next = node.next !== null ? map.get(node.next)! : null; - copy.random = node.random !== null ? map.get(node.random)! : null; - node = node.next; - } - - // Return the head of the list. - return map.get(head)!; -} diff --git a/src/leetcode/linked_list/copy_list_with_random_pointers.py b/src/leetcode/linked_list/copy_list_with_random_pointers.py new file mode 100644 index 0000000..e7e0233 --- /dev/null +++ b/src/leetcode/linked_list/copy_list_with_random_pointers.py @@ -0,0 +1,69 @@ +# DIFFICULTY: MEDIUM +# +# A linked list of length n is given such that each node contains an additional random pointer, which could point to +# any node in the list, or null. +# +# Construct a deep copy of the list. The deep copy should consist of exactly n brand new nodes, where each new node has +# its value set to the value of its corresponding original node. Both the next and random pointer of the new nodes +# should point to new nodes in the copied list such that the pointers in the original list and copied list represent +# the same list state. None of the pointers in the new list should point to nodes in the original list. +# +# For example, if there are two nodes X and Y in the original list, where X.random --> Y, then for the corresponding +# two nodes x and y in the copied list, x.random --> y. +# +# Return the head of the copied linked list. +# +# The linked list is represented in the input/output as a list of n nodes. Each node is represented as a pair of +# [val, random_index] where: +# +# - val: an integer representing Node.val +# - random_index: the index of the node (range from 0 to n-1) that the random pointer points to, or null if it does not +# point to any node. +# +# Your code will only be given the head of the original linked list. +# +# See https://leetcode.com/problems/copy-list-with-random-pointer +from leetcode.linked_list.common.random_node import Node + + +class Solution: + def copyRandomList(self, head: "Node | None") -> "Node | None": + """ + SOLUTION + -------- + + If there's no random pointer you can iterate through the nodes and create a copy of each node. With the random + pointer you'll have to maintain a map of original node to copied node so you can assign the random pointers later. + + First iterate through the list once to make copies of each node in a map. Then iterate again and assign the next + and random pointers. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of nodes in the linked list. + + Space complexity is O(n) because we are storing the new nodes in a dictionary. + """ + if not head: + return None + + # Create a map of original nodes to new nodes. + map: dict[Node | None, Node] = {} + + # First, create a map of each node to its copy. + current = head + while current: + map[current] = Node(current.val) + current = current.next + + # Second, assign the pointers. + current = head + while current: + copy = map[current] + copy.next = map.get(current.next, None) + copy.random = map.get(current.random, None) + current = current.next + + # Return the head of the copied list. + return map[head] From ccd80682c15a25928695d35f71d2f05fda1b7bd5 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sat, 15 Mar 2025 20:56:02 -0700 Subject: [PATCH 065/158] lru cache --- src/leetcode/linked_list/all_one.py | 22 +++- .../linked_list/common/random-node.ts | 13 -- src/leetcode/linked_list/lru-cache.ts | 117 ------------------ src/leetcode/linked_list/lru_cache.py | 107 ++++++++++++++++ test/leetcode/linked_list/lru-cache.test.ts | 61 --------- test/leetcode/linked_list/lru_cache_test.py | 59 +++++++++ 6 files changed, 186 insertions(+), 193 deletions(-) delete mode 100644 src/leetcode/linked_list/common/random-node.ts delete mode 100644 src/leetcode/linked_list/lru-cache.ts create mode 100644 src/leetcode/linked_list/lru_cache.py delete mode 100644 test/leetcode/linked_list/lru-cache.test.ts create mode 100644 test/leetcode/linked_list/lru_cache_test.py diff --git a/src/leetcode/linked_list/all_one.py b/src/leetcode/linked_list/all_one.py index a8b76f0..3fe0d82 100644 --- a/src/leetcode/linked_list/all_one.py +++ b/src/leetcode/linked_list/all_one.py @@ -21,7 +21,7 @@ class Node: - def __init__(self, count: float): + def __init__(self, count: float) -> None: # This node represents a count/frequency and the keys that have this count. self.count = count # A set of keys that have this count. @@ -33,7 +33,25 @@ def __init__(self, count: float): class AllOne: - def __init__(self): + """ + SOLUTION + -------- + + Use a doubly linked list and hashmap to store the keys and their counts. Each node in the linked list will + represent a specific count, and the keys in that node have the count. + + The doubly linked list will let us quickly find the min and max counts, and the hashmap will let us quickly find any + key for inc/dec. + + COMPLEXITY + ---------- + + Time complexity is O(1) for all operations. + + Space complexity is O(n) where n is the number of keys in the data structure. + """ + + def __init__(self) -> None: # Map of key -> node. self.nodes: dict[str, Node] = {} # We'll use this linked list to quickly find the min and max counts, which will lead us to the min and max keys. diff --git a/src/leetcode/linked_list/common/random-node.ts b/src/leetcode/linked_list/common/random-node.ts deleted file mode 100644 index 1afd4b8..0000000 --- a/src/leetcode/linked_list/common/random-node.ts +++ /dev/null @@ -1,13 +0,0 @@ -// This class definition comes from the problem itself and we cannot change it, or else our submission will not be -// accepted. -export class _Node { - val: number; - next: _Node | null; - random: _Node | null; - - constructor(val?: number, next?: _Node, random?: _Node) { - this.val = val === undefined ? 0 : val; - this.next = next === undefined ? null : next; - this.random = random === undefined ? null : random; - } -} diff --git a/src/leetcode/linked_list/lru-cache.ts b/src/leetcode/linked_list/lru-cache.ts deleted file mode 100644 index c858d40..0000000 --- a/src/leetcode/linked_list/lru-cache.ts +++ /dev/null @@ -1,117 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Design a data structure that follows the constraints of a Least Recently Used (LRU) cache. -// -// Implement the LRUCache class: -// -// LRUCache(int capacity) -// Initialize the LRU cache with positive size capacity. -// int get(int key) -// Return the value of the key if the key exists, otherwise return -1. -// void put(int key, int value) -// Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache. If the number of -// keys exceeds the capacity from this operation, evict the least recently used key. -// -// The functions get and put must each run in O(1) average time complexity. -// -// See {@link https://leetcode.com/problems/lru-cache/} -export { LRUCache }; - -// SOLUTION: -// -// This class definition is provided by LeetCode and must be implemented by you. An LRU cache can be implemented -// using a hashmap and a linked list. -class LRUCache { - private map: Map; - private capacity: number; - private head: Node; - private tail: Node; - - constructor(capacity: number) { - this.map = new Map(); - this.capacity = capacity; - - // These are sentinel nodes that will never be directly accessed, but help us avoid certain undefined checks. - this.head = new Node(-1, -1); - this.tail = new Node(-1, -1); - this.head.next = this.tail; - this.tail.previous = this.head; - } - - get(key: number): number { - if (!this.map.has(key)) { - return -1; - } - - // Here, we can be assured that the key exists because we also store the key on the node. If we want to avoid - // storing the key on the node, we can just delete the last element from the list and then check for an undefined - // value here in case we deleted the key from being at capacity. - const node = this.map.get(key)!; - this.moveToHead(node); - return node.value; - } - - put(key: number, value: number): void { - // If we already have this value in the map, just update it and don't bother mucking with the capacity. - if (this.map.has(key)) { - const node = this.map.get(key)!; - this.moveToHead(node); - node.value = value; - return; - } - - const node = new Node(key, value); - this.map.set(key, node); - this.addToHead(node); - - // If we've exceed capacity, we have to remove the least used element, aka the tail. At this point, there is - // guaranteed to be at least one other element in the list because we just added one. Also, unless the capacity - // is 0, we will always at least have another. - // - // So we can safely access this.tail.previous! - if (this.map.size > this.capacity) { - const tail = this.tail.previous!; - this.removeNode(tail); - this.map.delete(tail.key); - } - } - - private addToHead(node: Node): void { - const a = this.head; - const b = node; - const c = this.head.next; - - // Updates the pointers for the node itself. - b.next = c; - b.previous = a; - - // Inserts the node at the front of the list. - a.next = b; - c!.previous = b; - } - - private moveToHead(node: Node): void { - this.removeNode(node); - this.addToHead(node); - } - - private removeNode(node: Node): void { - const b = node; - const a = b.previous!; - const c = b.next!; - - a.next = c; - c.previous = a; - } -} - -class Node { - public next?: Node; - - public previous?: Node; - - constructor( - public readonly key: number, - public value: number - ) {} -} diff --git a/src/leetcode/linked_list/lru_cache.py b/src/leetcode/linked_list/lru_cache.py new file mode 100644 index 0000000..7623cbf --- /dev/null +++ b/src/leetcode/linked_list/lru_cache.py @@ -0,0 +1,107 @@ +# DIFFICULTY: MEDIUM +# +# Design a data structure that follows the constraints of a Least Recently Used (LRU) cache. +# +# Implement the LRUCache class: +# +# LRUCache(int capacity) +# Initialize the LRU cache with positive size capacity. +# int get(int key) +# Return the value of the key if the key exists, otherwise return -1. +# void put(int key, int value) +# Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache. If the number of +# keys exceeds the capacity from this operation, evict the least recently used key. +# +# The functions get and put must each run in O(1) average time complexity. +# +# See https://leetcode.com/problems/lru-cache +from typing import cast + + +class Node: + def __init__(self, key: int, value: int) -> None: + self.key = key + self.value = value + self.previous: "Node" = cast("Node", None) + self.next: "Node" = cast("Node", None) + + +class LRUCache: + """ + SOLUTION + -------- + + This problem can be solved using a combination of a doubly linked list and a hashmap. + + COMPLEXITY + ---------- + + Time complexity is O(1) for all operations. + + Space complexity is O(n) where n is the number of keys in the data structure. + """ + + def __init__(self, capacity: int) -> None: + self.map: dict[int, Node] = {} + self.capacity = capacity + + # Create two sentinel nodes to simplify the logic. + self.head = Node(-1, -1) + self.tail = Node(-1, -1) + self.head.next = self.tail + self.tail.previous = self.head + + def get(self, key: int) -> int: + if key not in self.map: + return -1 + + node = self.map[key] + self.__updateTimestamp(node) + return node.value + + def put(self, key: int, value: int) -> None: + if key in self.map: + node = self.map[key] + node.value = value + self.__updateTimestamp(node) + return + + # If the key isn't in the map, we need to add it. + node = Node(key, value) + self.map[key] = node + self.__addLeftNode(node) + + # If we've exceeded the capacity, remove the least recently used key. + if len(self.map) > self.capacity: + node = self.tail.previous + self.map.pop(node.key) + self.__removeNode(node) + + def __removeNode(self, node: Node) -> None: + # Line up the node pointers in the desired order: a -> b -> c. + b = node + a = b.previous + c = b.next + + a.next = c + c.previous = a + + def __addLeftNode(self, node: Node) -> None: + # Line up the node pointers in the desired order: a -> b -> c. + a = self.head + b = node + c = a.next + + # Update the pointers for the node itself. + b.previous = a + b.next = c + + # Update the pointers for the surrounding nodes. + a.next = b + c.previous = b + + def __updateTimestamp(self, node: Node) -> None: + # Update the timestamp by moving the node to the front of the list. Moving to the front of the list can be + # simulated by removing the node then adding it to the front. + self.__removeNode(node) + self.__addLeftNode(node) diff --git a/test/leetcode/linked_list/lru-cache.test.ts b/test/leetcode/linked_list/lru-cache.test.ts deleted file mode 100644 index 554e002..0000000 --- a/test/leetcode/linked_list/lru-cache.test.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { LRUCache } from '../../src/linked-list/lru-cache'; - -describe('lru cache', () => { - test('lru cache - test case 1', async () => { - const cache = new LRUCache(2); - - cache.put(1, 1); - cache.put(2, 2); - - expect(cache.get(1)).toBe(1); - - cache.put(3, 3); - - expect(cache.get(2)).toBe(-1); - - cache.put(4, 4); - - expect(cache.get(1)).toBe(-1); - expect(cache.get(3)).toBe(3); - expect(cache.get(4)).toBe(4); - }); - - test('lru cache - test case 2', async () => { - const cache = new LRUCache(2); - - expect(cache.get(2)).toBe(-1); - expect(cache.get(2)).toBe(-1); - - cache.put(2, 6); - - expect(cache.get(1)).toBe(-1); - - cache.put(1, 5); - cache.put(1, 2); - - expect(cache.get(1)).toBe(2); - expect(cache.get(2)).toBe(6); - }); - - test('lru cache - test case 3', async () => { - const cache = new LRUCache(3); - - cache.put(1, 1); - cache.put(2, 2); - cache.put(3, 3); - cache.put(4, 4); - - expect(cache.get(4)).toBe(4); - expect(cache.get(3)).toBe(3); - expect(cache.get(2)).toBe(2); - expect(cache.get(1)).toBe(-1); - - cache.put(5, 5); - - expect(cache.get(1)).toBe(-1); - expect(cache.get(2)).toBe(2); - expect(cache.get(3)).toBe(3); - expect(cache.get(4)).toBe(-1); - expect(cache.get(5)).toBe(5); - }); -}); diff --git a/test/leetcode/linked_list/lru_cache_test.py b/test/leetcode/linked_list/lru_cache_test.py new file mode 100644 index 0000000..fc960f7 --- /dev/null +++ b/test/leetcode/linked_list/lru_cache_test.py @@ -0,0 +1,59 @@ +from leetcode.linked_list.lru_cache import LRUCache + + +def test_case_1(): + cache = LRUCache(2) + + cache.put(1, 1) + cache.put(2, 2) + + assert cache.get(1) == 1 + + cache.put(3, 3) + + assert cache.get(2) == -1 + + cache.put(4, 4) + + assert cache.get(1) == -1 + assert cache.get(3) == 3 + assert cache.get(4) == 4 + + +def test_case_2(): + cache = LRUCache(2) + + assert cache.get(2) == -1 + assert cache.get(2) == -1 + + cache.put(2, 6) + + assert cache.get(1) == -1 + + cache.put(1, 5) + cache.put(1, 2) + + assert cache.get(1) == 2 + assert cache.get(2) == 6 + + +def test_case_3(): + cache = LRUCache(3) + + cache.put(1, 1) + cache.put(2, 2) + cache.put(3, 3) + cache.put(4, 4) + + assert cache.get(4) == 4 + assert cache.get(3) == 3 + assert cache.get(2) == 2 + assert cache.get(1) == -1 + + cache.put(5, 5) + + assert cache.get(1) == -1 + assert cache.get(2) == 2 + assert cache.get(3) == 3 + assert cache.get(4) == -1 + assert cache.get(5) == 5 From 7a8d267678e765d50505b89ae2f9190bfc2a3a11 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sat, 15 Mar 2025 21:06:40 -0700 Subject: [PATCH 066/158] merge 2 sorted lists --- src/leetcode/linked_list/README.md | 14 ++--- .../linked_list/common/nested-integer.ts | 42 -------------- .../linked_list/merge-two-sorted-lists.ts | 55 ------------------- .../linked_list/merge_two_sorted_lists.py | 51 +++++++++++++++++ .../merge-two-sorted-lists.test.ts | 44 --------------- .../merge_two_sorted_lists_test.py | 39 +++++++++++++ 6 files changed, 97 insertions(+), 148 deletions(-) delete mode 100644 src/leetcode/linked_list/common/nested-integer.ts delete mode 100644 src/leetcode/linked_list/merge-two-sorted-lists.ts create mode 100644 src/leetcode/linked_list/merge_two_sorted_lists.py delete mode 100644 test/leetcode/linked_list/merge-two-sorted-lists.test.ts create mode 100644 test/leetcode/linked_list/merge_two_sorted_lists_test.py diff --git a/src/leetcode/linked_list/README.md b/src/leetcode/linked_list/README.md index 0fb5aa2..4930e02 100644 --- a/src/leetcode/linked_list/README.md +++ b/src/leetcode/linked_list/README.md @@ -1,14 +1,14 @@ # Linked List -It's usually easy to identify a linked list problem. But there are some data structure combos that appear with common phrases. +Its usually easy to identify a linked list problem. But there are some data structure combos that appear with common phrases. ## Common Phrases (HashMap) These phrases indicate a hashmap might be useful: -- 'cache' -- 'LRU', 'MRU' -- 'deep copy with pointers' -- 'random pointers' -- 'detect cycles' -- 'check duplicates' +- cache +- LRU, MRU +- deep copy with pointers +- random pointers +- detect cycles +- check duplicates diff --git a/src/leetcode/linked_list/common/nested-integer.ts b/src/leetcode/linked_list/common/nested-integer.ts deleted file mode 100644 index 64dd994..0000000 --- a/src/leetcode/linked_list/common/nested-integer.ts +++ /dev/null @@ -1,42 +0,0 @@ -// This class definition comes from the problem itself and we cannot change it, or else our submission will not be -// accepted. -export class NestedInteger { - private value: number | null; - - private values: NestedInteger[]; - - // If value is provided, then it holds a single integer - // Otherwise it holds an empty nested list - constructor(value?: number) { - this.value = value ?? null; - this.values = []; - } - - // Return true if this NestedInteger holds a single integer, rather than a nested list. - isInteger(): boolean { - return this.value !== null; - } - - // Return the single integer that this NestedInteger holds, if it holds a single integer - // Return null if this NestedInteger holds a nested list - getInteger(): number | null { - return this.value; - } - - // Set this NestedInteger to hold a single integer equal to value. - setInteger(value: number) { - this.value = value; - } - - // Set this NestedInteger to hold a nested list and adds a nested integer elem to it. - add(elem: NestedInteger) { - this.value = null; - this.values.push(elem); - } - - // Return the nested list that this NestedInteger holds, - // or an empty list if this NestedInteger holds a single integer - getList(): NestedInteger[] { - return this.values; - } -} diff --git a/src/leetcode/linked_list/merge-two-sorted-lists.ts b/src/leetcode/linked_list/merge-two-sorted-lists.ts deleted file mode 100644 index 648f5b5..0000000 --- a/src/leetcode/linked_list/merge-two-sorted-lists.ts +++ /dev/null @@ -1,55 +0,0 @@ -// DIFFICULTY: EASY -// -// You are given the heads of two sorted linked lists list1 and list2. -// -// Merge the two lists into one sorted list. The list should be made by splicing together the nodes of the first two lists. -// -// Return the head of the merged linked list. -// -// See {@link https://leetcode.com/problems/merge-two-sorted-lists/} -import { ListNode } from './common/list-node'; -export { mergeTwoLists }; - -// SOLUTION: -// -// A straightforward solution works. -function mergeTwoLists(list1: ListNode | null, list2: ListNode | null): ListNode | null { - let head: ListNode | null = null; - let tail: ListNode | null = null; - let left = list1; - let right = list2; - - while (left !== null || right !== null) { - let node = null; - - if (left === null && right !== null) { - node = right; - right = right.next; - } - - if (left !== null && right === null) { - node = left; - left = left.next; - } - - if (left !== null && right !== null) { - if (left.val <= right.val) { - node = left; - left = left.next; - } else { - node = right; - right = right.next; - } - } - - if (tail === null) { - tail = node; - head = node; - } else { - tail.next = node; - tail = tail.next; - } - } - - return head; -} diff --git a/src/leetcode/linked_list/merge_two_sorted_lists.py b/src/leetcode/linked_list/merge_two_sorted_lists.py new file mode 100644 index 0000000..45e0c1c --- /dev/null +++ b/src/leetcode/linked_list/merge_two_sorted_lists.py @@ -0,0 +1,51 @@ +# DIFFICULTY: EASY +# +# You are given the heads of two sorted linked lists list1 and list2. +# +# Merge the two lists into one sorted list. The list should be made by splicing together the nodes of the first two lists. +# +# Return the head of the merged linked list. +# +# See https://leetcode.com/problems/merge-two-sorted-lists +from leetcode.linked_list.common.list_node import ListNode + + +class Solution: + def mergeTwoLists(self, list1: "ListNode | None", list2: "ListNode | None") -> "ListNode | None": + """ + SOLUTION + -------- + + Start with a sentinel node and continue appending the smaller value of the two lists to the merged list. If + there are any remaining nodes in either list, append them to the end of the list (they are already sorted). + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of nodes in the merged list. + + Space complexity is O(1). + """ + # Create a sentinel head node to simplify the logic. + head = ListNode(-1) + node = head + + # While there are nodes in both list1 and list2, compare the values and append the smaller value to the merged + # list. + while list1 and list2: + if list1.val < list2.val: + node.next = list1 + list1 = list1.next + else: + node.next = list2 + list2 = list2.next + node = node.next + + # If there are any remaining nodes in list1 or list2, append them to the merged list; they are already sorted. + if list1: + node.next = list1 + if list2: + node.next = list2 + + # Return the head node of the merged list. + return head.next diff --git a/test/leetcode/linked_list/merge-two-sorted-lists.test.ts b/test/leetcode/linked_list/merge-two-sorted-lists.test.ts deleted file mode 100644 index 7b09e9e..0000000 --- a/test/leetcode/linked_list/merge-two-sorted-lists.test.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { array2list, list2array } from '../../src/linked-list/common/list-node'; -import { mergeTwoLists } from '../../src/linked-list/merge-two-sorted-lists'; - -describe('merge two sorted lists', () => { - test('merge two sorted lists - test case 1', async () => { - const a = null; - const b = null; - const c = mergeTwoLists(a, b); - - expect(c).toBeNull(); - }); - - test('merge two sorted lists - test case 2', async () => { - const a = array2list([1]); - const b = null; - const c = mergeTwoLists(a, b); - - expect(list2array(c)).toStrictEqual([1]); - }); - - test('merge two sorted lists - test case 3', async () => { - const a = null; - const b = array2list([1]); - const c = mergeTwoLists(a, b); - - expect(list2array(c)).toStrictEqual([1]); - }); - - test('merge two sorted lists - test case 4', async () => { - const a = array2list([1]); - const b = array2list([2]); - const c = mergeTwoLists(a, b); - - expect(list2array(c)).toStrictEqual([1, 2]); - }); - - test('merge two sorted lists - test case 5', async () => { - const a = array2list([1, 2, 4]); - const b = array2list([2, 3, 4]); - const c = mergeTwoLists(a, b); - - expect(list2array(c)).toStrictEqual([1, 2, 2, 3, 4, 4]); - }); -}); diff --git a/test/leetcode/linked_list/merge_two_sorted_lists_test.py b/test/leetcode/linked_list/merge_two_sorted_lists_test.py new file mode 100644 index 0000000..6d04610 --- /dev/null +++ b/test/leetcode/linked_list/merge_two_sorted_lists_test.py @@ -0,0 +1,39 @@ +from leetcode.linked_list.common.list_node import array2list, list2array +from leetcode.linked_list.merge_two_sorted_lists import Solution + + +soln = Solution() + + +def test_case_1(): + a = None + b = None + assert soln.mergeTwoLists(a, b) is None + + +def test_case_2(): + a = array2list([1]) + b = None + + assert list2array(soln.mergeTwoLists(a, b)) == [1] + + +def test_case_3(): + a = None + b = array2list([1]) + + assert list2array(soln.mergeTwoLists(a, b)) == [1] + + +def test_case_4(): + a = array2list([1]) + b = array2list([2]) + + assert list2array(soln.mergeTwoLists(a, b)) == [1, 2] + + +def test_case_5(): + a = array2list([1, 2, 4]) + b = array2list([2, 3, 4]) + + assert list2array(soln.mergeTwoLists(a, b)) == [1, 2, 2, 3, 4, 4] From 80c541f93851d47dfa7f0b4ea1d340ab0206f6d8 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sat, 15 Mar 2025 21:20:47 -0700 Subject: [PATCH 067/158] remove nth node --- .../remove-nth-node-from-end-of-list.ts | 58 ------------------- .../remove_nth_node_from_end_of_list.py | 49 ++++++++++++++++ .../linked_list/merge-k-sorted-lists.test.ts | 53 ----------------- 3 files changed, 49 insertions(+), 111 deletions(-) delete mode 100644 src/leetcode/linked_list/remove-nth-node-from-end-of-list.ts create mode 100644 src/leetcode/linked_list/remove_nth_node_from_end_of_list.py delete mode 100644 test/leetcode/linked_list/merge-k-sorted-lists.test.ts diff --git a/src/leetcode/linked_list/remove-nth-node-from-end-of-list.ts b/src/leetcode/linked_list/remove-nth-node-from-end-of-list.ts deleted file mode 100644 index 3e364f0..0000000 --- a/src/leetcode/linked_list/remove-nth-node-from-end-of-list.ts +++ /dev/null @@ -1,58 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given the head of a linked list, remove the nth node from the end of the list and return its head. -// -// @see {@link https://leetcode.com/problems/remove-nth-node-from-end-of-list/} -import { ListNode } from './common/list-node'; -export { removeNthFromEnd }; - -// SOLUTION: -// -// To do this in one iteration, throw all the nodes onto a list. Then we can easily find the nth node from the end of -// the list. After that, we just have to point the (k - 1)th node to the (k + 1)th node. Assuming both exist. -// -// You can also do this using two pointers, but this solution is a more straightforward. -// -// COMPLEXITY: -// -// Runs in O(n) time because we are iterating through the list once. Runs in O(n) space because we are storing the list -// in an array. -function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null { - // Just convert the linked list to an array so we can easily find the nth node from the end. - const list: ListNode[] = []; - let node: ListNode | null = head; - while (node !== null) { - list.push(node); - node = node.next; - } - - // Now make sure this operation can even be done. If n is 0 or greater than the length of the list, then we can't - // then we can't remove anything! - if (head === null || n <= 0 || n > list.length) { - return head; - } - - // Create sentinel nodes to make the logic easier, and so we can return the head of the list later. - const sentinel = new ListNode(); - sentinel.next = head; - - // Now that we know n is within bounds, let's find the node we want to remove. For example, if we want to remove the - // 2nd node from the end of the list with 3 elements, then the index is 1 (3 - 2 = 1). - // - // After this we just have point the (k - 1)th node to the (k + 1)th node. Assuming both exist. - const k = list.length - n; - - // The previous node to remove is the a = (k - 1)th node. If it turns out the (k - 1)th node doesn't exist, then node - // a is the sentinel node we created. - const a = list[k - 1] ?? sentinel; - - // The next node to link it to is the c = (k + 1)th node. If it turns out that the (k + 1)th node doesn't exist, then - // node c is null. - const c = list[k + 1] ?? null; - - // Unlink the node b in position list[k] by setting the a node's next pointer to c. - a.next = c; - - // Return the list minus the sentinel. - return sentinel.next; -} diff --git a/src/leetcode/linked_list/remove_nth_node_from_end_of_list.py b/src/leetcode/linked_list/remove_nth_node_from_end_of_list.py new file mode 100644 index 0000000..6f340a7 --- /dev/null +++ b/src/leetcode/linked_list/remove_nth_node_from_end_of_list.py @@ -0,0 +1,49 @@ +# DIFFICULTY: MEDIUM +# +# Given the head of a linked list, remove the nth node from the end of the list and return its head. +# +# See https://leetcode.com/problems/remove-nth-node-from-end-of-list +from leetcode.linked_list.common.list_node import ListNode + + +class Solution: + def removeNthFromEnd(self, head: "ListNode | None", n: int) -> "ListNode | None": + """ + SOLUTION + -------- + + The naive solution that accomplishes this in one iteration is to throw all the nodes into an array. Then we can + easily find the nth node from the end of the array by indexing into it. This solution requires O(m) space, + where m is the length of the list. + + There is a more efficient solution that uses two pointers. The first pointer will be n nodes ahead of the + second pointer. Move both pointers until the first pointer reaches the end of the list. At this point, the + second pointer will be pointing to the node that we want to remove. + + The problem says we won't have invalid inputs, so ignore pyright warnings. + + COMPLEXITY + ---------- + + Time complexity is O(m) where time because we are iterating through the list once. + + Space complexity is O(1). + """ + # Create a sentinel head node to simplify the logic. + sentinel = ListNode(-1) + sentinel.next = head + faster: ListNode | None = sentinel + slower: ListNode | None = sentinel + + # Move the faster pointer n nodes ahead of the slower pointer. + for _ in range(n + 1): + faster = faster.next # pyright: ignore + + # Move both pointers until the faster pointer reaches the end of the list. + while faster: + faster = faster.next # pyright: ignore + slower = slower.next # pyright: ignore + + # Remove the slower node from the list. + slower.next = slower.next.next # pyright: ignore + return sentinel.next diff --git a/test/leetcode/linked_list/merge-k-sorted-lists.test.ts b/test/leetcode/linked_list/merge-k-sorted-lists.test.ts deleted file mode 100644 index 3c220f3..0000000 --- a/test/leetcode/linked_list/merge-k-sorted-lists.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { array2list, list2array } from '../../src/linked-list/common/list-node'; -import { mergeKLists } from '../../src/heap/merge-k-sorted-lists'; - -describe('merge k sorted lists', () => { - test('merge k sorted lists - test case 1', async () => { - const lists = [null, null].map(array2list); - const merged = mergeKLists(lists); - - expect(merged).toBeNull(); - }); - - test('merge k sorted lists - test case 2', async () => { - const lists = [[1], null].map(array2list); - const merged = mergeKLists(lists); - - expect(list2array(merged)).toStrictEqual([1]); - }); - - test('merge k sorted lists - test case 3', async () => { - const lists = [null, [1]].map(array2list); - const merged = mergeKLists(lists); - - expect(list2array(merged)).toStrictEqual([1]); - }); - - test('merge k sorted lists - test case 4', async () => { - const lists = [[1], [2]].map(array2list); - const merged = mergeKLists(lists); - - expect(list2array(merged)).toStrictEqual([1, 2]); - }); - - test('merge k sorted lists - test case 5', async () => { - const lists = [ - [1, 2, 4], - [1, 3, 4] - ].map(array2list); - const merged = mergeKLists(lists); - - expect(list2array(merged)).toStrictEqual([1, 1, 2, 3, 4, 4]); - }); - - test('merge k sorted lists - test case 6', async () => { - const lists = [ - [1, 4, 5], - [1, 3, 4], - [2, 6] - ].map(array2list); - const merged = mergeKLists(lists); - - expect(list2array(merged)).toStrictEqual([1, 1, 2, 3, 4, 4, 5, 6]); - }); -}); From 4de544e6b73d8d3ebe2ff11a66b14ec73c045f76 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 14:17:47 -0700 Subject: [PATCH 068/158] number of one bits set --- src/leetcode/math/number-of-one-bits.ts | 31 ----------------- src/leetcode/math/number_of_one_bits.py | 34 +++++++++++++++++++ test/leetcode/math/number-of-one-bits.test.ts | 8 ----- test/leetcode/math/number_of_one_bits_test.py | 9 +++++ 4 files changed, 43 insertions(+), 39 deletions(-) delete mode 100644 src/leetcode/math/number-of-one-bits.ts create mode 100644 src/leetcode/math/number_of_one_bits.py delete mode 100644 test/leetcode/math/number-of-one-bits.test.ts create mode 100644 test/leetcode/math/number_of_one_bits_test.py diff --git a/src/leetcode/math/number-of-one-bits.ts b/src/leetcode/math/number-of-one-bits.ts deleted file mode 100644 index f791db4..0000000 --- a/src/leetcode/math/number-of-one-bits.ts +++ /dev/null @@ -1,31 +0,0 @@ -// DIFFICULTY: EASY -// -// Write a function that takes the binary representation of a positive integer and returns the number of -// set bits it has (also known as the Hamming weight). -// -// See {@link https://leetcode.com/problems/number-of-1-bits/} -export { hammingWeight }; - -// SOLUTION: -// -// Note that the problem here says it "takes the binary representation" of a positive integer. However, the signature -// of the solution takes a number argument, not a string. -// -// This solution takes a number, converts it to a string, then does the calculation. But it's likely the interviewer -// will be asking for bit manipulation. -function hammingWeight(n: number) { - let count = 0; - - while (n !== 0) { - // This will be true if the least significant bit (the one all the way on the right) is 1. - if (n & 1) { - count++; - } - - // Do an unsigned shift to the right (so that the most sigificant bit becomes 0), and so that the bit we just - // counted (the least significant bit) gets shifted off. - n >>>= 1; - } - - return count; -} diff --git a/src/leetcode/math/number_of_one_bits.py b/src/leetcode/math/number_of_one_bits.py new file mode 100644 index 0000000..66b55ec --- /dev/null +++ b/src/leetcode/math/number_of_one_bits.py @@ -0,0 +1,34 @@ +# DIFFICULTY: EASY +# +# Write a function that takes the binary representation of a positive integer and returns the number of set bits it has +# (also known as the Hamming weight). +# +# See https://leetcode.com/problems/number-of-1-bits +class Solution: + def hammingWeight(self, n: int) -> int: + """ + SOLUTION + -------- + + Use the bitwise AND operator to check if the rightmost bit is set. If it is, increment the count of one bits, + then right shift by one to remove the rightmost bit. Repeat this process until there are no more bits. + + COMPLEXITY + ---------- + + Time complexity is O(1) because the number of bits in an integer is technically fixed. Or it's O(m) where m is + the number of bits to check. + + Space complexity is O(1). + """ + count = 0 + + while n: + # This will be true if the least significant bit is set. + if n & 1: + count += 1 + + # Right shift by one to remove the least significant bit. + n >>= 1 + + return count diff --git a/test/leetcode/math/number-of-one-bits.test.ts b/test/leetcode/math/number-of-one-bits.test.ts deleted file mode 100644 index 23e3833..0000000 --- a/test/leetcode/math/number-of-one-bits.test.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { hammingWeight } from '../../src/math/number-of-one-bits'; - -describe('number of one bits', () => { - test('number of one bits - test case 1', async () => { - // Because 11 = 0b1001 - expect(hammingWeight(11)).toBe(3); - }); -}); diff --git a/test/leetcode/math/number_of_one_bits_test.py b/test/leetcode/math/number_of_one_bits_test.py new file mode 100644 index 0000000..48ac0d8 --- /dev/null +++ b/test/leetcode/math/number_of_one_bits_test.py @@ -0,0 +1,9 @@ +from leetcode.math.number_of_one_bits import Solution + + +soln = Solution() + + +def test_case_1(): + # Because 11 = 0b1001 + assert soln.hammingWeight(11) == 3 From 255de762e7259e768487f3e49db3f29660002a1f Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 14:30:13 -0700 Subject: [PATCH 069/158] power of 2 --- src/leetcode/math/pow_x_n.py | 67 ++++++++++++++++++++++++++++++ test/leetcode/math/pow_x_n_test.py | 32 ++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 src/leetcode/math/pow_x_n.py create mode 100644 test/leetcode/math/pow_x_n_test.py diff --git a/src/leetcode/math/pow_x_n.py b/src/leetcode/math/pow_x_n.py new file mode 100644 index 0000000..75d6585 --- /dev/null +++ b/src/leetcode/math/pow_x_n.py @@ -0,0 +1,67 @@ +# DIFFICULTY: MEDIUM +# +# Implement pow(x, n), which calculates x raised to the power n (i.e., xn). +# +# See https://leetcode.com/problems/powx-n +class Solution: + def myPow(self, x: float, n: int) -> float: + """ + SOLUTION + -------- + + The naive solution is to multiply x by itself n times. This is O(n) time complexity. + + The faster solution is based on the concept of exponentiation by squaring. Basically, we can reduce the number + of multiplications if we notice that: + + x^n = (x^2)^(n/2) if x n is even + x^n = x * (x^(n-1)/2) if n is odd + + Exponentiation by squaring is faster because the naive method requires n multiplications, but squaring each + time only requires log(n) multiplications. To implement this: + + 1. Check if n is negative. If it is, then we can just invert x and make n positive. + 2. Check if n === 0. If it is, then we can just return 1. + 3. Check if n is even. If it is, then we can return myPow(x * x, n / 2). That is, square the number and half + the exponent. + 4. Check if n is odd. If it is, we can do the same thing, except just return x * myPow(x * x, (n - 1) / 2). + That is, we half the exponent minus 1 to make it even (so we can do our squaring), then multiply the result + by x. + + This can be done even faster if we use bit shift operations and convert the recursive solution to an iterative + one. + + COMPLEXITY + ---------- + + Time complexity is O(log n). + + Space complexity is O(1). + """ + if n == 0: + return 1 + + xi = x + ni = n + + # Check if n is negative. If it is, then we can just invert x and make n positive, then continue as normal. + if ni < 0: + xi = 1 / xi + ni = -ni + + result = 1 + while ni > 0: + # If n is odd, multiply the result by x as we discussed above. Afterwards we can proceed with the division + # by two (note that by doing >>, the division rounds down giving us the correct result). + # + # Rather than use ni % 2 to check if a number is even, use bitwise and. If the result is 1, then the least + # significant bit is a 1. Otherwise, the bit is 0. + if ni & 1 != 0: + result *= xi + + # Perform the squaring operation and halve the exponent; happens regardless of whether n is even or odd. + xi *= xi + # Rather than divide by 2, we can use the right bit shift operator to do a faster division. + ni >>= 1 + + return result diff --git a/test/leetcode/math/pow_x_n_test.py b/test/leetcode/math/pow_x_n_test.py new file mode 100644 index 0000000..02cd0a7 --- /dev/null +++ b/test/leetcode/math/pow_x_n_test.py @@ -0,0 +1,32 @@ +from leetcode.math.pow_x_n import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.myPow(2, -2) == 0.25 + + +def test_case_2(): + assert soln.myPow(2, -1) == 0.5 + + +def test_case_3(): + assert soln.myPow(2, 0) == 1 + + +def test_case_4(): + assert soln.myPow(2, 1) == 2 + + +def test_case_5(): + assert soln.myPow(2, 4) == 16 + + +def test_case_6(): + assert soln.myPow(2, 5) == 32 + + +def test_case_7(): + assert soln.myPow(2, -2147483648) == 0 From 15e25d9dbf3ef82ea8ac4364d8760b18cd6caf87 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 14:30:26 -0700 Subject: [PATCH 070/158] removes ts --- src/leetcode/math/pow-x-n.ts | 69 ------------------------------ test/leetcode/math/pow-x-n.test.ts | 31 -------------- 2 files changed, 100 deletions(-) delete mode 100644 src/leetcode/math/pow-x-n.ts delete mode 100644 test/leetcode/math/pow-x-n.test.ts diff --git a/src/leetcode/math/pow-x-n.ts b/src/leetcode/math/pow-x-n.ts deleted file mode 100644 index 72b5865..0000000 --- a/src/leetcode/math/pow-x-n.ts +++ /dev/null @@ -1,69 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Implement pow(x, n), which calculates x raised to the power n (i.e., xn). -// -// See {@link https://leetcode.com/problems/powx-n/} -export { myPow }; - -// SOLUTION: -// -// The faster solution is based on the concept of exponentiation by squaring. Basically, we can reduce the number of -// multiplications if we notice that: -// -// x^n = (x^2)^(n/2) if x n is even -// x^n = x * (x^(n-1)/2) if n is odd -// -// Exponentiation by squaring is faster because the naive method requires n multiplications, but squaring each -// time only requires log(n) multiplications. To implement this: -// -// 1. Check if n is negative. If it is, then we can just invert x and make n positive. -// 2. Check if n === 0. If it is, then we can just return 1. -// 3. Check if n is even. If it is, then we can return myPow(x * x, n / 2). That is, square the number and half the -// exponent. -// 4. Check if n is odd. If it is, we can do the same thing, except just return x * myPow(x * x, (n - 1) / 2). That -// is, we half the exponent minus 1 to make it even (so we can do our squaring), then multiply the result by x. -// -// This can be done even faster if we use bit shift operations and convert the recursive solution to an iterative one. -// -// COMPLEXITY: -// -// Time complexity is O(log n) because we are halving the exponent each time. -// -// Space complexity is O(1) because we are not using any extra space. -function myPow(x: number, n: number) { - if (n === 0) { - return 1; - } - - let xi = x; - let ni = n; - - // Check if n is negative. If it is, then we can just invert x and make n positive, then continue as normal. - if (ni < 0) { - xi = 1 / x; - ni = -n; - } - - let result = 1; - while (ni > 0) { - // Rather than use ni % 2 to check if a number is even, use bitwise and. If the result is 1, then the least - // significant bit is a 1. Otherwise, the bit is 0. - const isEven = (ni & 1) === 0; - if (isEven) { - xi *= xi; - // Rather than divide by 2, we can use the right bit shift operator to do a faster division. The `>>>` - // operation is an unsigned bit shift to the right. This means the leftmost bits are filled with 0's regardless - // of the sign of the original number. - ni >>>= 1; - } else { - result *= xi; - xi *= xi; - // Rather than divide by 2, we can use the right bit shift operator to do a faster division. The `>>>` - // operation is an unsigned bit shift to the right. This means the leftmost bits are filled with 0's regardless - // of the sign of the original number. - ni >>>= 1; - } - } - - return result; -} diff --git a/test/leetcode/math/pow-x-n.test.ts b/test/leetcode/math/pow-x-n.test.ts deleted file mode 100644 index 7c01722..0000000 --- a/test/leetcode/math/pow-x-n.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { myPow } from '../../src/math/pow-x-n'; - -describe('pow', () => { - test('pow - test case 1', async () => { - expect(myPow(2, -2)).toBe(0.25); - }); - - test('pow - test case 2', async () => { - expect(myPow(2, -1)).toBe(0.5); - }); - - test('pow - test case 3', async () => { - expect(myPow(2, 0)).toBe(1); - }); - - test('pow - test case 4', async () => { - expect(myPow(2, 1)).toBe(2); - }); - - test('pow - test case 5', async () => { - expect(myPow(2, 4)).toBe(16); - }); - - test('pow - test case 6', async () => { - expect(myPow(2, 5)).toBe(32); - }); - - test('pow - test case 7', async () => { - expect(myPow(2, -2147483648)).toBe(0); - }); -}); From 5c8b9e5688e9c518400e34403349ecd073c4271a Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 15:00:00 -0700 Subject: [PATCH 071/158] reverse integer --- src/leetcode/math/reverse-integer.ts | 47 --------------------- src/leetcode/math/reverse_integer.py | 48 ++++++++++++++++++++++ test/leetcode/math/reverse-integer.test.ts | 7 ---- test/leetcode/math/reverse_integer_test.py | 8 ++++ 4 files changed, 56 insertions(+), 54 deletions(-) delete mode 100644 src/leetcode/math/reverse-integer.ts create mode 100644 src/leetcode/math/reverse_integer.py delete mode 100644 test/leetcode/math/reverse-integer.test.ts create mode 100644 test/leetcode/math/reverse_integer_test.py diff --git a/src/leetcode/math/reverse-integer.ts b/src/leetcode/math/reverse-integer.ts deleted file mode 100644 index 5536611..0000000 --- a/src/leetcode/math/reverse-integer.ts +++ /dev/null @@ -1,47 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given a signed 32-bit integer x, return x with its digits reversed. If reversing x causes the value to go outside the -// signed 32-bit integer range [-231, 231 - 1], then return 0. -// -// Assume the environment does not allow you to store 64-bit integers (signed or unsigned). -// -// See {@link https://leetcode.com/problems/reverse-integer/} -export { reverse }; - -// SOLUTION: -// -// You can do this by reversing a string, but you can also just do this with arithmetic. -function reverse(x: number) { - // There is no builtin const for this, but you can calculate the min 32 bit value like this (31 bits due to sign). - const min = -1 * 2 ** 31; - // There is no builtin const for this, but you can calculate the min 32 bit value like this (31 bits due to sign). - // Minus 1 to account for 0. - const max = 2 ** 31 - 1; - const sign = x > 0 ? 1 : -1; - - let n = Math.abs(x); - let r = 0; - while (n > 0) { - // Get the least significant digit of N; this will be the most significant digit of R in the first iteration, - // the second most in the next iteration, and so on. - const lastDigit = n % 10; - - if (r === 0) { - // When r === 0, just make last digit the most significant digit. - r = lastDigit; - } else { - // Otherwise shift R to the left by 10, then add the last digit we got from N. - r = r * 10 + lastDigit; - } - - // Shift N to the right by 10 so we drop the last digit, then go to the next iteration. - n = Math.floor(n / 10); - } - - const value = sign * r; - if (value < min || value > max) { - return 0; - } - - return sign * r; -} diff --git a/src/leetcode/math/reverse_integer.py b/src/leetcode/math/reverse_integer.py new file mode 100644 index 0000000..af75cbc --- /dev/null +++ b/src/leetcode/math/reverse_integer.py @@ -0,0 +1,48 @@ +# DIFFICULTY: MEDIUM +# +# Given a signed 32-bit integer x, return x with its digits reversed. If reversing x causes the value to go outside the +# signed 32-bit integer range [-231, 231 - 1], then return 0. +# +# Assume the environment does not allow you to store 64-bit integers (signed or unsigned). +# +# See https://leetcode.com/problems/reverse-integer +import math + + +class Solution: + def reverse(self, x: int) -> int: + """ + SOLUTION + -------- + + The medium level solution assumes we are reversing with arithmetic operations and not converting to a string. + + COMPLEXITY + ---------- + + Time complexity is O(log n) where n is the number of digits in x. + + Space complexity is O(1). + """ + min_value = -1 * 2**31 + max_value = 2**31 - 1 + sign = 1 if x >= 0 else -1 + + n = abs(x) + r = 0 + while n: + # Get the least significant digit of n; this will be the most significant digit of r in the first iteration, + # the second most in the next iteration, and so on. + last = n % 10 + + if r == 0: + # When r === 0, just make last digit the most significant digit. + r = last + else: + # Otherwise shift r to the left by 10, then add the last digit we got from n. + r = r * 10 + last + + n = math.floor(n / 10) + + value = sign * r + return value if min_value < value < max_value else 0 diff --git a/test/leetcode/math/reverse-integer.test.ts b/test/leetcode/math/reverse-integer.test.ts deleted file mode 100644 index 8c29656..0000000 --- a/test/leetcode/math/reverse-integer.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { reverse } from '../../src/math/reverse-integer'; - -describe('reverse integer', () => { - test('reverse integer - test case 1', async () => { - expect(reverse(123)).toBe(321); - }); -}); diff --git a/test/leetcode/math/reverse_integer_test.py b/test/leetcode/math/reverse_integer_test.py new file mode 100644 index 0000000..7b9f5bc --- /dev/null +++ b/test/leetcode/math/reverse_integer_test.py @@ -0,0 +1,8 @@ +from leetcode.math.reverse_integer import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.reverse(123) == 321 From aafc84a74fc3d82dc42c263100dd97ff870bf45c Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 15:07:40 -0700 Subject: [PATCH 072/158] rotate image --- src/leetcode/math/reverse_integer.py | 5 +- src/leetcode/math/rotate-image.ts | 69 ----------------- src/leetcode/math/rotate_image.py | 76 +++++++++++++++++++ .../math/__data__/task-scheduler.test.json | 1 - test/leetcode/math/rotate-image.test.ts | 19 ----- test/leetcode/math/rotate_image_test.py | 12 +++ 6 files changed, 89 insertions(+), 93 deletions(-) delete mode 100644 src/leetcode/math/rotate-image.ts create mode 100644 src/leetcode/math/rotate_image.py delete mode 100644 test/leetcode/math/__data__/task-scheduler.test.json delete mode 100644 test/leetcode/math/rotate-image.test.ts create mode 100644 test/leetcode/math/rotate_image_test.py diff --git a/src/leetcode/math/reverse_integer.py b/src/leetcode/math/reverse_integer.py index af75cbc..f419482 100644 --- a/src/leetcode/math/reverse_integer.py +++ b/src/leetcode/math/reverse_integer.py @@ -6,9 +6,6 @@ # Assume the environment does not allow you to store 64-bit integers (signed or unsigned). # # See https://leetcode.com/problems/reverse-integer -import math - - class Solution: def reverse(self, x: int) -> int: """ @@ -42,7 +39,7 @@ def reverse(self, x: int) -> int: # Otherwise shift r to the left by 10, then add the last digit we got from n. r = r * 10 + last - n = math.floor(n / 10) + n = n // 10 value = sign * r return value if min_value < value < max_value else 0 diff --git a/src/leetcode/math/rotate-image.ts b/src/leetcode/math/rotate-image.ts deleted file mode 100644 index d69462f..0000000 --- a/src/leetcode/math/rotate-image.ts +++ /dev/null @@ -1,69 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given an n x n 2D matrix representing an image, rotate the image by 90 degrees (clockwise). -// -// You have to rotate the image in-place, which means you have to modify the input 2D matrix directly. DO NOT allocate -// another 2D matrix and do the rotation. -// -// See {@link https://leetcode.com/problems/rotate-image/} -export { rotate }; - -// SOLUTION: -// -// To rotate an image by 90 degrees, we can transpose the matrix (exchange [i, j] with [j, i]), then reverse each -// row. -// -// Generally you can accomplish any number of different rotations by transposition and reversing. -// -// 90 degrees clockwise: transpose, then reverse rows. -// 90 degrees counter-clockwise: transpose, then reverse columns. -// 180 degrees: reverse rows, reverse columns. -// 270 degrees clockwise: same as 90 degrees counter-clockwise. -// 270 degrees counter-clockwise: same as 90 degrees clockwise. -// -// The above approach requires a transposition then reversing each element. It is O(n^2) for the transpose operation -// but then O(n^2) again for reversals. A more complicated algorithm can achieve the transposition in a single pass -// through the matrix. -// -// For a 4 element matrix: -// -// - The element at (0, 0) moves to (0, 3). -// - The element at (0, 3) moves to (3, 3). -// - The element at (3, 3) moves to (3, 0). -// - The element at (3, 0) moves to (0, 0). -// -// This pattern applies for the outer layer of the square matrix, then the next layer below that, and so on. Here -// a "layer" means an square (so the outer square), then the next inner square and so on. -function rotate(matrix: number[][]): void { - // We'll have to apply our algorithm to each "layer" of our matrix. For a matrix of size N, there are N/2 layers. - const n = matrix.length; - const layers = Math.floor(n / 2); - - // Operate our algorithm for each layer; again, an NxN matrix will have N/2 layers. - for (let i = 0; i < layers; i++) { - // By setting j = i, we will descend into an inner layer and process them one by one. That is, the first square - // starts at (0, 0), then the next square starts at (1, 1), and so on. - for (let j = i; j < n - i - 1; j++) { - // Lets define our 4 points so we can get our cells correctly. - const left = i; - const right = n - i - 1; - const top = j; - const bottom = n - j - 1; - - // Temporarily save the top left cell so we can swap it later. - const t = matrix[left][top]; - - // The (left row, top column) cell should get the (bottom row, left column) value. - matrix[left][top] = matrix[bottom][left]; - - // The (bottom row, left column) cell should get the (right row, bottom column) value. - matrix[bottom][left] = matrix[right][bottom]; - - // The (right row, bottom column) cell should get the (top row, right column) value. - matrix[right][bottom] = matrix[top][right]; - - // The (top row, right column) cell should get the (top row, left column) value (which we saved). - matrix[top][right] = t; - } - } -} diff --git a/src/leetcode/math/rotate_image.py b/src/leetcode/math/rotate_image.py new file mode 100644 index 0000000..9453a29 --- /dev/null +++ b/src/leetcode/math/rotate_image.py @@ -0,0 +1,76 @@ +# DIFFICULTY: MEDIUM +# +# You are given an n x n 2D matrix representing an image, rotate the image by 90 degrees (clockwise). +# +# You have to rotate the image in-place, which means you have to modify the input 2D matrix directly. DO NOT allocate +# another 2D matrix and do the rotation. +# +# See https://leetcode.com/problems/rotate-image +class Solution: + def rotate(self, matrix: list[list[int]]) -> None: + """ + SOLUTION + -------- + + To rotate an image by 90 degrees, we can transpose the matrix (exchange [i, j] with [j, i]), then reverse each + row. + + Generally you can accomplish any number of different rotations by transposition and reversing. + + 90 degrees clockwise: transpose, then reverse rows. + 90 degrees counter-clockwise: transpose, then reverse columns. + 180 degrees: reverse rows, reverse columns. + 270 degrees clockwise: same as 90 degrees counter-clockwise. + 270 degrees counter-clockwise: same as 90 degrees clockwise. + + The above approach requires a transposition then reversing each element. It is O(n^2) for the transpose + operation but then O(n^2) again for reversals. A more complicated algorithm can achieve the transposition in a + single pass through the matrix. + + For a 4 element matrix: + + - The element at (0, 0) moves to (0, 3). + - The element at (0, 3) moves to (3, 3). + - The element at (3, 3) moves to (3, 0). + - The element at (3, 0) moves to (0, 0). + + This pattern applies for the outer layer of the square matrix, then the next layer below that, and so on. Here + a "layer" means an square (so the outer square), then the next inner square and so on. + + COMPLEXITY + ---------- + + Time complexity is O(n^2) because we are iterating through all elements in the matrix. + + Space complexity is O(1) because we are modifying the matrix in place. + """ + # We'll have to apply our algorithm to each "layer" of our matrix. For a matrix of size N, there are N/2 + # layers. + n = len(matrix) + layers = n // 2 + + # Operate our algorithm for each layer; again, an NxN matrix will have N/2 layers. + for i in range(layers): + # By setting j = i, we will descend into an inner layer and process them one by one. That is, the first + # square starts at (0, 0), then the next square starts at (1, 1), and so on. + for j in range(i, n - i - 1): + # Lets define our 4 points so we can get our cells correctly. + left = i + right = n - i - 1 + top = j + bottom = n - j - 1 + + # Temporarily save the top left cell so we can swap it later. + t = matrix[left][top] + + # The (left row, top column) cell should get the (bottom row, left column) value. + matrix[left][top] = matrix[bottom][left] + + # The (bottom row, left column) cell should get the (right row, bottom column) value. + matrix[bottom][left] = matrix[right][bottom] + + # The (right row, bottom column) cell should get the (top row, right column) value. + matrix[right][bottom] = matrix[top][right] + + # The (top row, right column) cell should get the (top row, left column) value (which we saved). + matrix[top][right] = t diff --git a/test/leetcode/math/__data__/task-scheduler.test.json b/test/leetcode/math/__data__/task-scheduler.test.json deleted file mode 100644 index e5a6199..0000000 --- a/test/leetcode/math/__data__/task-scheduler.test.json +++ /dev/null @@ -1 +0,0 @@ -["G","C","A","H","A","G","G","F","G","J","H","C","A","G","E","A","H","E","F","D","B","D","H","H","E","G","F","B","C","G","F","H","J","F","A","C","G","D","I","J","A","G","D","F","B","F","H","I","G","J","G","H","F","E","H","J","C","E","H","F","C","E","F","H","H","I","G","A","G","D","C","B","I","D","B","C","J","I","B","G","C","H","D","I","A","B","A","J","C","E","B","F","B","J","J","D","D","H","I","I","B","A","E","H","J","J","A","J","E","H","G","B","F","C","H","C","B","J","B","A","H","B","D","I","F","A","E","J","H","C","E","G","F","G","B","G","C","G","A","H","E","F","H","F","C","G","B","I","E","B","J","D","B","B","G","C","A","J","B","J","J","F","J","C","A","G","J","E","G","J","C","D","D","A","I","A","J","F","H","J","D","D","D","C","E","D","D","F","B","A","J","D","I","H","B","A","F","E","B","J","A","H","D","E","I","B","H","C","C","C","G","C","B","E","A","G","H","H","A","I","A","B","A","D","A","I","E","C","C","D","A","B","H","D","E","C","A","H","B","I","A","B","E","H","C","B","A","D","H","E","J","B","J","A","B","G","J","J","F","F","H","I","A","H","F","C","H","D","H","C","C","E","I","G","J","H","D","E","I","J","C","C","H","J","C","G","I","E","D","E","H","J","A","H","D","A","B","F","I","F","J","J","H","D","I","C","G","J","C","C","D","B","E","B","E","B","G","B","A","C","F","E","H","B","D","C","H","F","A","I","A","E","J","F","A","E","B","I","G","H","D","B","F","D","B","I","B","E","D","I","D","F","A","E","H","B","I","G","F","D","E","B","E","C","C","C","J","J","C","H","I","B","H","F","H","F","D","J","D","D","H","H","C","D","A","J","D","F","D","G","B","I","F","J","J","C","C","I","F","G","F","C","E","G","E","F","D","A","I","I","H","G","H","H","A","J","D","J","G","F","G","E","E","A","H","B","G","A","J","J","E","I","H","A","G","E","C","D","I","B","E","A","G","A","C","E","B","J","C","B","A","D","J","E","J","I","F","F","C","B","I","H","C","F","B","C","G","D","A","A","B","F","C","D","B","I","I","H","H","J","A","F","J","F","J","F","H","G","F","D","J","G","I","E","B","C","G","I","F","F","J","H","H","G","A","A","J","C","G","F","B","A","A","E","E","A","E","I","G","F","D","B","I","F","A","B","J","F","F","J","B","F","J","F","J","F","I","E","J","H","D","G","G","D","F","G","B","J","F","J","A","J","E","G","H","I","E","G","D","I","B","D","J","A","A","G","A","I","I","A","A","I","I","H","E","C","A","G","I","F","F","C","D","J","J","I","A","A","F","C","J","G","C","C","H","E","A","H","F","B","J","G","I","A","A","H","G","B","E","G","D","I","C","G","J","C","C","I","H","B","D","J","H","B","J","H","B","F","J","E","J","A","G","H","B","E","H","B","F","F","H","E","B","E","G","H","J","G","J","B","H","C","H","A","A","B","E","I","H","B","I","D","J","J","C","D","G","I","J","G","J","D","F","J","E","F","D","E","B","D","B","C","B","B","C","C","I","F","D","E","I","G","G","I","B","H","G","J","A","A","H","I","I","H","A","I","F","C","D","A","C","G","E","G","E","E","H","D","C","G","D","I","A","G","G","D","A","H","H","I","F","E","I","A","D","H","B","B","G","I","C","G","B","I","I","D","F","F","C","C","A","I","E","A","E","J","A","H","C","D","A","C","B","G","H","G","J","G","I","H","B","A","C","H","I","D","D","C","F","G","B","H","E","B","B","H","C","B","G","G","C","F","B","E","J","B","B","I","D","H","D","I","I","A","A","H","G","F","B","J","F","D","E","G","F","A","G","G","D","A","B","B","B","J","A","F","H","H","D","C","J","I","A","H","G","C","J","I","F","J","C","A","E","C","H","J","H","H","F","G","E","A","C","F","J","H","D","G","G","D","D","C","B","H","B","C","E","F","B","D","J","H","J","J","J","A","F","F","D","E","F","C","I","B","H","H","D","E","A","I","A","B","F","G","F","F","I","E","E","G","A","I","D","F","C","H","E","C","G","H","F","F","H","J","H","G","A","E","H","B","G","G","D","D","D","F","I","A","F","F","D","E","H","J","E","D","D","A","J","F","E","E","E","F","I","D","A","F","F","J","E","I","J","D","D","G","A","C","G","G","I","E","G","E","H","E","D","E","J","B","G","I","J","C","H","C","C","A","A","B","C","G","B","D","I","D","E","H","J","J","B","F","E","J","H","H","I","G","B","D"] \ No newline at end of file diff --git a/test/leetcode/math/rotate-image.test.ts b/test/leetcode/math/rotate-image.test.ts deleted file mode 100644 index 2904663..0000000 --- a/test/leetcode/math/rotate-image.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { rotate } from '../../src/math/rotate-image'; - -describe('rotate image', () => { - test('rotate image - test case 1', async () => { - const matrix = [ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9] - ]; - - rotate(matrix); - - expect(matrix).toStrictEqual([ - [7, 4, 1], - [8, 5, 2], - [9, 6, 3] - ]); - }); -}); diff --git a/test/leetcode/math/rotate_image_test.py b/test/leetcode/math/rotate_image_test.py new file mode 100644 index 0000000..a885d39 --- /dev/null +++ b/test/leetcode/math/rotate_image_test.py @@ -0,0 +1,12 @@ +from leetcode.math.rotate_image import Solution + + +soln = Solution() + + +def test_case_1(): + matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] + + soln.rotate(matrix) + + assert matrix == [[7, 4, 1], [8, 5, 2], [9, 6, 3]] From 28240797772ef0291e45f24d5f781f83f6c80bad Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 15:14:22 -0700 Subject: [PATCH 073/158] sequential digits --- src/leetcode/math/sequential-digits.ts | 48 ----------------- src/leetcode/math/sequential_digits.py | 52 +++++++++++++++++++ .../sequential-digits.test.ts.snap | 20 ------- test/leetcode/math/sequential-digits.test.ts | 11 ---- test/leetcode/math/sequential_digits_test.py | 12 +++++ test/leetcode/math/snapshots/__init__.py | 0 .../snapshots/snap_sequential_digits_test.py | 23 ++++++++ 7 files changed, 87 insertions(+), 79 deletions(-) delete mode 100644 src/leetcode/math/sequential-digits.ts create mode 100644 src/leetcode/math/sequential_digits.py delete mode 100644 test/leetcode/math/__snapshots__/sequential-digits.test.ts.snap delete mode 100644 test/leetcode/math/sequential-digits.test.ts create mode 100644 test/leetcode/math/sequential_digits_test.py create mode 100644 test/leetcode/math/snapshots/__init__.py create mode 100644 test/leetcode/math/snapshots/snap_sequential_digits_test.py diff --git a/src/leetcode/math/sequential-digits.ts b/src/leetcode/math/sequential-digits.ts deleted file mode 100644 index d0c3073..0000000 --- a/src/leetcode/math/sequential-digits.ts +++ /dev/null @@ -1,48 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// An integer has sequential digits if and only if each digit in the number is one more than the previous digit. -// -// Return a sorted list of all the integers in the range [low, high] inclusive that have sequential digits. -// -// See {@link https://leetcode.com/problems/sequential-digits/} -export { sequentialDigits }; - -// SOLUTION: -// -// Rather than take a list from low to high and filtering out the numbers that match our criteria, it will be much -// faster to generate a list of numbers with this quality with the correct number of digits, then filter out by low -// and high ranges. -function sequentialDigits(low: number, high: number) { - const results: number[] = []; - - // Sequential digits end at 9, so the largest sequential number you can have is 123456789. Start generating them - // at i = 1. We'll handle 0 in a special case. - for (let i = 1; i < 9; i++) { - // Set the value to be xxxi, and the next digit following xxxi to be i + 1. We will append i + 1 to the end of - // this number. - let value = i; - let next = i + 1; - - while (next <= 9 && value <= high) { - // Shift the current number's digits over to the left, leaving a 0 in the one's place. - value *= 10; - - // Add the next digit to the one's place. - value += next; - next++; - - // If it's within bounds, add it to the array. - if (value >= low && value <= high) { - results.push(value); - } - } - } - - // The above logic doesn't handle the sequential digit number 0; so add it if necessary. - if (low === 0) { - results.push(0); - } - - results.sort((a, b) => a - b); - return results; -} diff --git a/src/leetcode/math/sequential_digits.py b/src/leetcode/math/sequential_digits.py new file mode 100644 index 0000000..68e4387 --- /dev/null +++ b/src/leetcode/math/sequential_digits.py @@ -0,0 +1,52 @@ +# DIFFICULTY: MEDIUM +# +# An integer has sequential digits if and only if each digit in the number is one more than the previous digit. +# +# Return a sorted list of all the integers in the range [low, high] inclusive that have sequential digits. +# +# See https://leetcode.com/problems/sequential-digits +class Solution: + def sequentialDigits(self, low: int, high: int) -> list[int]: + """ + SOLUTION + -------- + + Rather than take a list from low to high and filtering out the numbers that match our criteria, it will be much + faster to generate a list of numbers with this quality with the correct number of digits, then filter out by low + and high ranges. + + COMPLEXITY + ---------- + + Time complexity is O(1) because the number of digits in an integer is fixed. Or it's O(m) where m is the number + of digits. + + Space complexity is O(1). + """ + results: list[int] = [] + + # Sequential digits end at 9, so the largest sequential number you can have is 123456789. Start generating them + # at i = 1. We'll handle 0 in a special case. + for i in range(1, 10): + # Set the value to be xxxi, and the next digit following xxxi to be i + 1. We will append i + 1 to the end + # of this number. + value = i + next_digit = i + 1 + + while next_digit <= 9 and value <= high: + # Shift the current number's digits over to the left, leaving a 0 in the one's place. + value *= 10 + + # Add the next digit to the one's place. + value += next_digit + next_digit += 1 + + # If it's within bounds, add it to the array. + if low <= value <= high: + results.append(value) + + # Handle the special case where low is 0. + if low == 0: + results.append(0) + + return sorted(results) diff --git a/test/leetcode/math/__snapshots__/sequential-digits.test.ts.snap b/test/leetcode/math/__snapshots__/sequential-digits.test.ts.snap deleted file mode 100644 index 2549a1c..0000000 --- a/test/leetcode/math/__snapshots__/sequential-digits.test.ts.snap +++ /dev/null @@ -1,20 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`sequential digits sequential digits - test case 1 1`] = ` -[ - 123, - 234, -] -`; - -exports[`sequential digits sequential digits - test case 2 1`] = ` -[ - 1234, - 2345, - 3456, - 4567, - 5678, - 6789, - 12345, -] -`; diff --git a/test/leetcode/math/sequential-digits.test.ts b/test/leetcode/math/sequential-digits.test.ts deleted file mode 100644 index 17b70b1..0000000 --- a/test/leetcode/math/sequential-digits.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { sequentialDigits } from '../../src/math/sequential-digits'; - -describe('sequential digits', () => { - test('sequential digits - test case 1', async () => { - expect(sequentialDigits(100, 300)).toMatchSnapshot(); - }); - - test('sequential digits - test case 2', async () => { - expect(sequentialDigits(1000, 13000)).toMatchSnapshot(); - }); -}); diff --git a/test/leetcode/math/sequential_digits_test.py b/test/leetcode/math/sequential_digits_test.py new file mode 100644 index 0000000..75715a4 --- /dev/null +++ b/test/leetcode/math/sequential_digits_test.py @@ -0,0 +1,12 @@ +from leetcode.math.sequential_digits import Solution + + +soln = Solution() + + +def test_case_1(snapshot): + snapshot.assert_match(soln.sequentialDigits(100, 300)) + + +def test_case_2(snapshot): + snapshot.assert_match(soln.sequentialDigits(1000, 13000)) diff --git a/test/leetcode/math/snapshots/__init__.py b/test/leetcode/math/snapshots/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/leetcode/math/snapshots/snap_sequential_digits_test.py b/test/leetcode/math/snapshots/snap_sequential_digits_test.py new file mode 100644 index 0000000..f2cf46e --- /dev/null +++ b/test/leetcode/math/snapshots/snap_sequential_digits_test.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# snapshottest: v1 - https://goo.gl/zC4yUc +from __future__ import unicode_literals + +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots['test_case_1 1'] = [ + 123, + 234 +] + +snapshots['test_case_2 1'] = [ + 1234, + 2345, + 3456, + 4567, + 5678, + 6789, + 12345 +] From b72348e0ec227b05bd6f41121c874772db1bbcee Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 15:24:12 -0700 Subject: [PATCH 074/158] robot --- .../matrix/robot_bounded_in_circle.py | 76 +++++++++++++++++++ test/leetcode/math/snapshots/__init__.py | 0 .../matrix/robot_bounded_in_circle_test.py | 8 ++ 3 files changed, 84 insertions(+) create mode 100644 src/leetcode/matrix/robot_bounded_in_circle.py delete mode 100644 test/leetcode/math/snapshots/__init__.py create mode 100644 test/leetcode/matrix/robot_bounded_in_circle_test.py diff --git a/src/leetcode/matrix/robot_bounded_in_circle.py b/src/leetcode/matrix/robot_bounded_in_circle.py new file mode 100644 index 0000000..dd56eaa --- /dev/null +++ b/src/leetcode/matrix/robot_bounded_in_circle.py @@ -0,0 +1,76 @@ +# DIFFICULTY: MEDIUM +# +# On an infinite plane, a robot initially stands at (0, 0) and faces north. Note that: +# +# - The north direction is the positive direction of the y-axis. +# - The south direction is the negative direction of the y-axis. +# - The east direction is the positive direction of the x-axis. +# - The west direction is the negative direction of the x-axis. +# +# The robot can receive one of three instructions: +# +# - "G": go straight 1 unit. +# - "L": turn 90 degrees to the left (i.e., anti-clockwise direction). +# - "R": turn 90 degrees to the right (i.e., clockwise direction). +# - The robot performs the instructions given in order, and repeats them forever. +# +# Return true if and only if there exists a circle in the plane such that the robot never leaves the circle. +# +# See https://leetcode.com/problems/robot-bounded-in-circle +class Solution: + def isRobotBounded(self, instructions: str) -> bool: + """ + SOLUTION + -------- + + The problem is worded a little confusingly, but the gist is that if you can draw any circle around the robot's + path then return true. Otherwise, return false. + + Okay, so there are a few possibilities that can be put into two categories: + + 1. It follows the path and returns to the starting position. + 2. It follows the path and does not return to the starting position. + + In the first case, regardless of what direction the robot is facing, it will follow the same path and return to + the starting position again and again, so we can bound it with a circle. + + In the second case, it is *possible* that even if it's not in the starting position, it can follow a path that + is bounded by a circle. Let's explore the possibilities. + + - The direction the robot is facing is the same as the initial direction. In this case, the robot will continue + to move in that same direction forever, making the path unbounded. + - The direction the robot is facing is different from the initial direction. In this case, the robot will + follow along a path that can be bounded by a circle. + + The second condition is true because the new direction is the new "north" for the robot, and it will eventually + return to the origin following that path for 4 iterations. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of instructions. + + Space complexity is O(1). + """ + directions = {"N": (0, 1), "E": (1, 0), "S": (0, -1), "W": (-1, 0)} + + leftTurns = {"N": "W", "W": "S", "S": "E", "E": "N"} + + rightTurns = {"N": "E", "E": "S", "S": "W", "W": "N"} + + (x, y) = (0, 0) + facing = "N" + + for instruction in instructions: + if instruction == "G": + dx, dy = directions[facing] + x += dx + y += dy + elif instruction == "L": + facing = leftTurns[facing] + elif instruction == "R": + facing = rightTurns[facing] + + # If the robot returned to the origin, or it's facing a different direction than the initial direction, then we + # are good to go. + return (x == 0 and y == 0) or facing != "N" diff --git a/test/leetcode/math/snapshots/__init__.py b/test/leetcode/math/snapshots/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/leetcode/matrix/robot_bounded_in_circle_test.py b/test/leetcode/matrix/robot_bounded_in_circle_test.py new file mode 100644 index 0000000..9849313 --- /dev/null +++ b/test/leetcode/matrix/robot_bounded_in_circle_test.py @@ -0,0 +1,8 @@ +from leetcode.matrix.robot_bounded_in_circle import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.isRobotBounded("GGLLGG") From 674467783006b320b29135f766d1a39d334694ee Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 15:30:37 -0700 Subject: [PATCH 075/158] matrix --- .../matrix/robot-bounded-in-circle.ts | 104 ------------------ src/leetcode/matrix/valid-word-square.ts | 24 ---- src/leetcode/matrix/valid_word_square.py | 33 ++++++ .../matrix/robot-bounded-in-circle.test.ts | 9 -- .../leetcode/matrix/valid-word-square.test.ts | 15 --- .../leetcode/matrix/valid_word_square_test.py | 12 ++ 6 files changed, 45 insertions(+), 152 deletions(-) delete mode 100644 src/leetcode/matrix/robot-bounded-in-circle.ts delete mode 100644 src/leetcode/matrix/valid-word-square.ts create mode 100644 src/leetcode/matrix/valid_word_square.py delete mode 100644 test/leetcode/matrix/robot-bounded-in-circle.test.ts delete mode 100644 test/leetcode/matrix/valid-word-square.test.ts create mode 100644 test/leetcode/matrix/valid_word_square_test.py diff --git a/src/leetcode/matrix/robot-bounded-in-circle.ts b/src/leetcode/matrix/robot-bounded-in-circle.ts deleted file mode 100644 index ab0599c..0000000 --- a/src/leetcode/matrix/robot-bounded-in-circle.ts +++ /dev/null @@ -1,104 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// On an infinite plane, a robot initially stands at (0, 0) and faces north. Note that: -// -// - The north direction is the positive direction of the y-axis. -// - The south direction is the negative direction of the y-axis. -// - The east direction is the positive direction of the x-axis. -// - The west direction is the negative direction of the x-axis. -// -// The robot can receive one of three instructions: -// -// - "G": go straight 1 unit. -// - "L": turn 90 degrees to the left (i.e., anti-clockwise direction). -// - "R": turn 90 degrees to the right (i.e., clockwise direction). -// - The robot performs the instructions given in order, and repeats them forever. -// -// Return true if and only if there exists a circle in the plane such that the robot never leaves the circle. -// -// See {@link https://leetcode.com/problems/robot-bounded-in-circle/} -export { isRobotBounded }; - -// SOLUTION: -// -// The problem is worded a little confusingly, but the gist is that if you can draw any circle around the robot's path -// then return true. Otherwise, return false. -// -// Okay, so there are a few possibilities that can be put into two categories: -// -// 1. It follows the path and returns to the starting position. -// 2. It follows the path and does not return to the starting position. -// -// In the first case, regardless of what direction the robot is facing, it will follow the same path and return to the -// starting position again and again, so we can bound it with a circle. -// -// In the second case, it is *possible* that even if it's not in the starting position, it can follow a path that is -// bounded by a circle. Let's explore the possibilities. -// -// - The direction the robot is facing is the same as the initial direction. In this case, the robot will continue to -// move in that same direction forever, making the path unbounded. -// - The direction the robot is facing is different from the initial direction. In this case, the robot will follow -// along a path that can be bounded by a circle. -// -// The second condition is true because the new direction is the new "north" for the robot, and it will eventually -// return to the origin following that path for 4 iterations. -function isRobotBounded(instructions: string): boolean { - const directions = new Map([ - ['north', { x: 0, y: 1 }], - ['east', { x: 1, y: 0 }], - ['west', { x: -1, y: 0 }], - ['south', { x: 0, y: -1 }] - ]); - - // Simulate the robot's path. - let [x, y] = [0, 0]; - let facing = 'north'; - for (const instruction of instructions) { - if (instruction === 'G') { - x += directions.get(facing)!.x; - y += directions.get(facing)!.y; - } else if (instruction === 'L') { - facing = turnLeft(facing); - } else if (instruction === 'R') { - facing = turnRight(facing); - } - } - - // If the robot returned to the origin, then we are good to go. - if (x === 0 && y === 0) { - return true; - } - - // If it didn't return to the origin, but we aren't facing the same direction as we started, then we are good to go. - return facing !== 'north'; -} - -function turnLeft(facing: string) { - switch (facing) { - case 'north': - return 'west'; - case 'east': - return 'north'; - case 'west': - return 'south'; - case 'south': - return 'east'; - default: - throw new Error(); - } -} - -function turnRight(facing: string) { - switch (facing) { - case 'north': - return 'east'; - case 'east': - return 'south'; - case 'west': - return 'north'; - case 'south': - return 'west'; - default: - throw new Error(); - } -} diff --git a/src/leetcode/matrix/valid-word-square.ts b/src/leetcode/matrix/valid-word-square.ts deleted file mode 100644 index 190455a..0000000 --- a/src/leetcode/matrix/valid-word-square.ts +++ /dev/null @@ -1,24 +0,0 @@ -// Given an array of strings words, return true if it forms a valid word square. -// -// A sequence of strings forms a valid word square if the kth row and column read the same string, where -// 0 <= k < max(numRows, numColumns). -// -// See {@link https://leetcode.com/problems/valid-word-square/} -export { validWordSquare }; - -// SOLUTION: -function validWordSquare(words: string[]): boolean { - for (let i = 0; i < words.length; i++) { - for (let j = 0; j < words[i].length; j++) { - try { - if (words[i][j] !== words[j][i]) { - return false; - } - } catch { - return false; - } - } - } - - return true; -} diff --git a/src/leetcode/matrix/valid_word_square.py b/src/leetcode/matrix/valid_word_square.py new file mode 100644 index 0000000..7d52c46 --- /dev/null +++ b/src/leetcode/matrix/valid_word_square.py @@ -0,0 +1,33 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given an array of strings words, return true if it forms a valid word square. +# +# A sequence of strings forms a valid word square if the kth row and column read the same string, where +# 0 <= k < max(numRows, numColumns). +# +# See https://leetcode.com/problems/valid-word-square +class Solution: + def validWordSquare(self, words: list[str]) -> bool: + """ + SOLUTION + -------- + + A naive solution works. + + COMPLEXITY + ---------- + + Time complexity is O(m * n). + + Space complexity is O(1). + """ + for i in range(len(words)): + for j in range(len(words[i])): + try: + if words[i][j] != words[j][i]: + return False + except IndexError: + return False + + return True diff --git a/test/leetcode/matrix/robot-bounded-in-circle.test.ts b/test/leetcode/matrix/robot-bounded-in-circle.test.ts deleted file mode 100644 index 28ac884..0000000 --- a/test/leetcode/matrix/robot-bounded-in-circle.test.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { isRobotBounded } from '../../src/matrix/robot-bounded-in-circle'; - -describe('robot bounded in circle', () => { - test('isRobotBounded - test case 1', () => { - const instructions = 'GGLLGG'; - - expect(isRobotBounded(instructions)).toEqual(true); - }); -}); diff --git a/test/leetcode/matrix/valid-word-square.test.ts b/test/leetcode/matrix/valid-word-square.test.ts deleted file mode 100644 index 9ed763b..0000000 --- a/test/leetcode/matrix/valid-word-square.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { validWordSquare } from '../../src/matrix/valid-word-square'; - -describe('valid word square', () => { - test('valid word square - test case 1', async () => { - const words = ['abcd', 'bnrt', 'crmy', 'dtye']; - - expect(validWordSquare(words)).toBe(true); - }); - - test('valid word square - test case 2', async () => { - const words = ['abcd', 'bnrt', 'crm', 'dt']; - - expect(validWordSquare(words)).toBe(true); - }); -}); diff --git a/test/leetcode/matrix/valid_word_square_test.py b/test/leetcode/matrix/valid_word_square_test.py new file mode 100644 index 0000000..db63b89 --- /dev/null +++ b/test/leetcode/matrix/valid_word_square_test.py @@ -0,0 +1,12 @@ +from leetcode.matrix.valid_word_square import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.validWordSquare(["abcd", "bnrt", "crmy", "dtye"]) + + +def test_case_2(): + assert soln.validWordSquare(["abcd", "bnrt", "crm", "dt"]) From 856dbd8b2716f2fe3d82607f849dbc126c77d90d Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 16:57:42 -0700 Subject: [PATCH 076/158] replacements --- .../best_time_to_buy_and_sell_stock_ii.py | 1 + .../array/buildings_with_an_ocean_view.py | 1 + src/leetcode/array/design_hit_counter.py | 1 + src/leetcode/array/group_anagrams.py | 1 + .../array/longest_consecutive_sequence.py | 1 + .../array/max_chunks_to_make_sorted.py | 1 + src/leetcode/array/maximum_swap.py | 1 + src/leetcode/array/pascals_triangle.py | 1 + src/leetcode/array/top_k_frequent_elements.py | 1 + ...ast_position_of_element_in_sorted_array.py | 1 + .../binary_search/find_peak_element.py | 1 + ..._the_smallest_divisor_given_a_threshold.py | 1 + ...kth_smallest_element_in_a_sorted_matrix.py | 1 + .../longest_increasing_subsequence.py | 1 + .../search_in_rotated_sorted_array.py | 1 + .../dynamic_programming/max_subarray.py | 1 + .../dynamic_programming/word_break_i.py | 1 + .../graph/nested_list_weighted_sum.py | 1 + .../graph/nested_list_weighted_sum_ii.py | 1 + src/leetcode/graph/number_of_islands.py | 1 + src/leetcode/graph/parallel_job_scheduling.py | 1 + .../graph/shortest_path_in_binary_matrix.py | 1 + ...est_greater_multiple_made_of_two_digits.py | 1 + src/leetcode/graph/word_search.py | 1 + .../heap/furthest_building_you_can_reach.py | 3 +- .../heap/k_closest_points_to_origin.py | 1 + src/leetcode/heap/kth_largest_element.py | 3 +- .../heap/number_of_orders_in_the_backlog.py | 3 +- src/leetcode/heap/task_scheduler.py | 1 + .../interval/interval_list_intersections.py | 1 + src/leetcode/interval/merge_intervals.py | 1 + src/leetcode/linked_list/add_two_numbers.py | 1 + .../copy_list_with_random_pointers.py | 1 + src/leetcode/linked_list/lru_cache.py | 1 + .../remove_nth_node_from_end_of_list.py | 1 + src/leetcode/math/pow_x_n.py | 1 + src/leetcode/math/reverse_integer.py | 1 + src/leetcode/math/rotate_image.py | 1 + src/leetcode/math/sequential_digits.py | 1 + .../matrix/robot_bounded_in_circle.py | 1 + src/leetcode/prefix_sum/README.md | 10 +-- .../prefix_sum/continuous_subarray_sum.py | 65 +++++++++++++++++++ .../recursion/generate_parentheses.py | 1 + src/leetcode/two_pointers/bag_of_tokens.py | 3 +- 44 files changed, 116 insertions(+), 9 deletions(-) create mode 100644 src/leetcode/prefix_sum/continuous_subarray_sum.py diff --git a/src/leetcode/array/best_time_to_buy_and_sell_stock_ii.py b/src/leetcode/array/best_time_to_buy_and_sell_stock_ii.py index e79150b..f49d185 100644 --- a/src/leetcode/array/best_time_to_buy_and_sell_stock_ii.py +++ b/src/leetcode/array/best_time_to_buy_and_sell_stock_ii.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # You are given an integer array prices where prices[i] is the price of a given stock on the ith day. # diff --git a/src/leetcode/array/buildings_with_an_ocean_view.py b/src/leetcode/array/buildings_with_an_ocean_view.py index faeb5eb..fbda59f 100644 --- a/src/leetcode/array/buildings_with_an_ocean_view.py +++ b/src/leetcode/array/buildings_with_an_ocean_view.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # There are n buildings in a line. You are given an integer array heights of size n that represents the heights of the # buildings in the line. diff --git a/src/leetcode/array/design_hit_counter.py b/src/leetcode/array/design_hit_counter.py index 9b0c516..00003d1 100644 --- a/src/leetcode/array/design_hit_counter.py +++ b/src/leetcode/array/design_hit_counter.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # Design a hit counter which counts the number of hits received in the past 5 minutes (i.e., the past 300 seconds). # diff --git a/src/leetcode/array/group_anagrams.py b/src/leetcode/array/group_anagrams.py index 01ad75c..d0f654b 100644 --- a/src/leetcode/array/group_anagrams.py +++ b/src/leetcode/array/group_anagrams.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # Given an array of strings `strs`, group the anagrams together. You can return the answer in any order. # diff --git a/src/leetcode/array/longest_consecutive_sequence.py b/src/leetcode/array/longest_consecutive_sequence.py index c564e0d..3323014 100644 --- a/src/leetcode/array/longest_consecutive_sequence.py +++ b/src/leetcode/array/longest_consecutive_sequence.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # Given an unsorted array of integers nums, return the length of the longest consecutive elements sequence. # diff --git a/src/leetcode/array/max_chunks_to_make_sorted.py b/src/leetcode/array/max_chunks_to_make_sorted.py index 4721876..cf8780f 100644 --- a/src/leetcode/array/max_chunks_to_make_sorted.py +++ b/src/leetcode/array/max_chunks_to_make_sorted.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # You are given an integer array arr of length n that represents a permutation of the integers in the range [0, n - 1]. # diff --git a/src/leetcode/array/maximum_swap.py b/src/leetcode/array/maximum_swap.py index ea682e6..e646ed7 100644 --- a/src/leetcode/array/maximum_swap.py +++ b/src/leetcode/array/maximum_swap.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # You are given an integer num. You can swap two digits at most once to get the maximum valued number. # diff --git a/src/leetcode/array/pascals_triangle.py b/src/leetcode/array/pascals_triangle.py index e66b8e5..8c576b1 100644 --- a/src/leetcode/array/pascals_triangle.py +++ b/src/leetcode/array/pascals_triangle.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # Given an integer numRows, return the first numRows of Pascal's triangle. # diff --git a/src/leetcode/array/top_k_frequent_elements.py b/src/leetcode/array/top_k_frequent_elements.py index 85867f2..fd04095 100644 --- a/src/leetcode/array/top_k_frequent_elements.py +++ b/src/leetcode/array/top_k_frequent_elements.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # Given an integer array nums and an integer k, return the k most frequent elements. You may return the answer in any # order. diff --git a/src/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array.py b/src/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array.py index b3f2ae6..8c61dbe 100644 --- a/src/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array.py +++ b/src/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # Given an array of integers nums sorted in non-decreasing order, find the starting and ending position of a given target value. # diff --git a/src/leetcode/binary_search/find_peak_element.py b/src/leetcode/binary_search/find_peak_element.py index c72037f..0498bf8 100644 --- a/src/leetcode/binary_search/find_peak_element.py +++ b/src/leetcode/binary_search/find_peak_element.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # A peak element is an element that is strictly greater than its neighbors. # diff --git a/src/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold.py b/src/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold.py index 898c46a..bd29004 100644 --- a/src/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold.py +++ b/src/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # Given an array of integers nums and an integer threshold, we will choose a positive integer divisor, divide all the # array by it, and sum the division's result. Find the smallest divisor such that the result mentioned above is less diff --git a/src/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix.py b/src/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix.py index 92bc5f3..49ca712 100644 --- a/src/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix.py +++ b/src/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # Given an n x n matrix where each of the rows and columns is sorted in ascending order, return the kth smallest # element in the matrix. diff --git a/src/leetcode/binary_search/longest_increasing_subsequence.py b/src/leetcode/binary_search/longest_increasing_subsequence.py index 425a974..680b2fa 100644 --- a/src/leetcode/binary_search/longest_increasing_subsequence.py +++ b/src/leetcode/binary_search/longest_increasing_subsequence.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # Given an integer array nums, return the length of the longest strictly increasing subsequence. # diff --git a/src/leetcode/binary_search/search_in_rotated_sorted_array.py b/src/leetcode/binary_search/search_in_rotated_sorted_array.py index 282fc4e..e346fcb 100644 --- a/src/leetcode/binary_search/search_in_rotated_sorted_array.py +++ b/src/leetcode/binary_search/search_in_rotated_sorted_array.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # There is an integer array nums sorted in ascending order (with distinct values). # diff --git a/src/leetcode/dynamic_programming/max_subarray.py b/src/leetcode/dynamic_programming/max_subarray.py index f1db977..4ad7262 100644 --- a/src/leetcode/dynamic_programming/max_subarray.py +++ b/src/leetcode/dynamic_programming/max_subarray.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # Given an integer array nums, find the subarray with the largest sum, and return its sum. # diff --git a/src/leetcode/dynamic_programming/word_break_i.py b/src/leetcode/dynamic_programming/word_break_i.py index 88c5299..852b82c 100644 --- a/src/leetcode/dynamic_programming/word_break_i.py +++ b/src/leetcode/dynamic_programming/word_break_i.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # Given a string s and a dictionary of strings wordDict, return true if s can be segmented into a space-separated # sequence of one or more dictionary words. diff --git a/src/leetcode/graph/nested_list_weighted_sum.py b/src/leetcode/graph/nested_list_weighted_sum.py index 907a74e..b3d9a1e 100644 --- a/src/leetcode/graph/nested_list_weighted_sum.py +++ b/src/leetcode/graph/nested_list_weighted_sum.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # You are given a nested list of integers nestedList. Each element is either an integer or a list whose elements may # also be integers or other lists. diff --git a/src/leetcode/graph/nested_list_weighted_sum_ii.py b/src/leetcode/graph/nested_list_weighted_sum_ii.py index 5b34efe..24a5ef6 100644 --- a/src/leetcode/graph/nested_list_weighted_sum_ii.py +++ b/src/leetcode/graph/nested_list_weighted_sum_ii.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # You are given a nested list of integers nestedList. Each element is either an integer or a list whose elements may # also be integers or other lists. diff --git a/src/leetcode/graph/number_of_islands.py b/src/leetcode/graph/number_of_islands.py index e6b0185..087e666 100644 --- a/src/leetcode/graph/number_of_islands.py +++ b/src/leetcode/graph/number_of_islands.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # Given an m x n 2D binary grid grid which represents a map of '1's (land) and '0's (water), return the number of # islands. diff --git a/src/leetcode/graph/parallel_job_scheduling.py b/src/leetcode/graph/parallel_job_scheduling.py index 2816a4e..2853872 100644 --- a/src/leetcode/graph/parallel_job_scheduling.py +++ b/src/leetcode/graph/parallel_job_scheduling.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # Given a list of tasks and their dependencies ['A:B,C', 'B:D,C,E', 'F:G'], return a list of parallelizable tasks in # order. For example, in this situation [(D, C, E, F), (B, G), (A)] would be a valid order because all parallelizable diff --git a/src/leetcode/graph/shortest_path_in_binary_matrix.py b/src/leetcode/graph/shortest_path_in_binary_matrix.py index 49f6050..d9ebcb8 100644 --- a/src/leetcode/graph/shortest_path_in_binary_matrix.py +++ b/src/leetcode/graph/shortest_path_in_binary_matrix.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # Given an n x n binary matrix grid, return the length of the shortest clear path in the matrix. If there is no clear # path, return -1. diff --git a/src/leetcode/graph/smallest_greater_multiple_made_of_two_digits.py b/src/leetcode/graph/smallest_greater_multiple_made_of_two_digits.py index 7ec5e73..366f800 100644 --- a/src/leetcode/graph/smallest_greater_multiple_made_of_two_digits.py +++ b/src/leetcode/graph/smallest_greater_multiple_made_of_two_digits.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # Given three integers, k, digit1, and digit2, you want to find the smallest integer that is: # diff --git a/src/leetcode/graph/word_search.py b/src/leetcode/graph/word_search.py index 6d5c111..f99706c 100644 --- a/src/leetcode/graph/word_search.py +++ b/src/leetcode/graph/word_search.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # Given an m x n grid of characters board and a string word, return true if word exists in the grid. # diff --git a/src/leetcode/heap/furthest_building_you_can_reach.py b/src/leetcode/heap/furthest_building_you_can_reach.py index e12035a..33ea4bd 100644 --- a/src/leetcode/heap/furthest_building_you_can_reach.py +++ b/src/leetcode/heap/furthest_building_you_can_reach.py @@ -1,4 +1,5 @@ -# DIFFICULTY: Medium +# DIFFICULTY: MEDIUM +# ------------------ # # You are given an integer array heights representing the heights of buildings, some bricks, and some ladders. # diff --git a/src/leetcode/heap/k_closest_points_to_origin.py b/src/leetcode/heap/k_closest_points_to_origin.py index b3292ee..f26a01c 100644 --- a/src/leetcode/heap/k_closest_points_to_origin.py +++ b/src/leetcode/heap/k_closest_points_to_origin.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # Given an array of points where points[i] = [xi, yi] represents a point on the X-Y plane and an integer k, return the # k closest points to the origin (0, 0). diff --git a/src/leetcode/heap/kth_largest_element.py b/src/leetcode/heap/kth_largest_element.py index 7c0963b..c5378a0 100644 --- a/src/leetcode/heap/kth_largest_element.py +++ b/src/leetcode/heap/kth_largest_element.py @@ -1,4 +1,5 @@ -# DIFFICULTY: Medium +# DIFFICULTY: MEDIUM +# ------------------ # # Given an integer array nums and an integer k, return the kth largest element in the array. # diff --git a/src/leetcode/heap/number_of_orders_in_the_backlog.py b/src/leetcode/heap/number_of_orders_in_the_backlog.py index bbf8082..def3bd0 100644 --- a/src/leetcode/heap/number_of_orders_in_the_backlog.py +++ b/src/leetcode/heap/number_of_orders_in_the_backlog.py @@ -1,4 +1,5 @@ -# DIFFICULTY: Medium +# DIFFICULTY: MEDIUM +# ------------------ # # You are given a 2D integer array orders, where each orders[i] = [pricei, amounti, orderTypei] denotes that amounti # orders have been placed of type orderTypei at the price pricei. The orderTypei is: diff --git a/src/leetcode/heap/task_scheduler.py b/src/leetcode/heap/task_scheduler.py index 61af1a4..958820e 100644 --- a/src/leetcode/heap/task_scheduler.py +++ b/src/leetcode/heap/task_scheduler.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # You are given an array of CPU tasks, each represented by letters A to Z, and a cooling time, n. Each cycle or # interval allows the completion of one task. Tasks can be completed in any order, but there's a constraint: identical diff --git a/src/leetcode/interval/interval_list_intersections.py b/src/leetcode/interval/interval_list_intersections.py index 1b0c173..9d1e989 100644 --- a/src/leetcode/interval/interval_list_intersections.py +++ b/src/leetcode/interval/interval_list_intersections.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # You are given two lists of closed intervals, firstList and secondList, where firstList[i] = [starti, endi] and # secondList[j] = [startj, endj]. Each list of intervals is pairwise disjoint and in sorted order. diff --git a/src/leetcode/interval/merge_intervals.py b/src/leetcode/interval/merge_intervals.py index 8e6484b..9d40dd7 100644 --- a/src/leetcode/interval/merge_intervals.py +++ b/src/leetcode/interval/merge_intervals.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # Given an array of intervals where intervals[i] = [starti, endi], merge all overlapping intervals, and return an array # of the non-overlapping intervals that cover all the intervals in the input. diff --git a/src/leetcode/linked_list/add_two_numbers.py b/src/leetcode/linked_list/add_two_numbers.py index d0a2d9e..0d7b1b2 100644 --- a/src/leetcode/linked_list/add_two_numbers.py +++ b/src/leetcode/linked_list/add_two_numbers.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse # order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list. diff --git a/src/leetcode/linked_list/copy_list_with_random_pointers.py b/src/leetcode/linked_list/copy_list_with_random_pointers.py index e7e0233..51f1d03 100644 --- a/src/leetcode/linked_list/copy_list_with_random_pointers.py +++ b/src/leetcode/linked_list/copy_list_with_random_pointers.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # A linked list of length n is given such that each node contains an additional random pointer, which could point to # any node in the list, or null. diff --git a/src/leetcode/linked_list/lru_cache.py b/src/leetcode/linked_list/lru_cache.py index 7623cbf..28308ab 100644 --- a/src/leetcode/linked_list/lru_cache.py +++ b/src/leetcode/linked_list/lru_cache.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # Design a data structure that follows the constraints of a Least Recently Used (LRU) cache. # diff --git a/src/leetcode/linked_list/remove_nth_node_from_end_of_list.py b/src/leetcode/linked_list/remove_nth_node_from_end_of_list.py index 6f340a7..9790533 100644 --- a/src/leetcode/linked_list/remove_nth_node_from_end_of_list.py +++ b/src/leetcode/linked_list/remove_nth_node_from_end_of_list.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # Given the head of a linked list, remove the nth node from the end of the list and return its head. # diff --git a/src/leetcode/math/pow_x_n.py b/src/leetcode/math/pow_x_n.py index 75d6585..6471dbf 100644 --- a/src/leetcode/math/pow_x_n.py +++ b/src/leetcode/math/pow_x_n.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # Implement pow(x, n), which calculates x raised to the power n (i.e., xn). # diff --git a/src/leetcode/math/reverse_integer.py b/src/leetcode/math/reverse_integer.py index f419482..1638605 100644 --- a/src/leetcode/math/reverse_integer.py +++ b/src/leetcode/math/reverse_integer.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # Given a signed 32-bit integer x, return x with its digits reversed. If reversing x causes the value to go outside the # signed 32-bit integer range [-231, 231 - 1], then return 0. diff --git a/src/leetcode/math/rotate_image.py b/src/leetcode/math/rotate_image.py index 9453a29..d767dbd 100644 --- a/src/leetcode/math/rotate_image.py +++ b/src/leetcode/math/rotate_image.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # You are given an n x n 2D matrix representing an image, rotate the image by 90 degrees (clockwise). # diff --git a/src/leetcode/math/sequential_digits.py b/src/leetcode/math/sequential_digits.py index 68e4387..8992932 100644 --- a/src/leetcode/math/sequential_digits.py +++ b/src/leetcode/math/sequential_digits.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # An integer has sequential digits if and only if each digit in the number is one more than the previous digit. # diff --git a/src/leetcode/matrix/robot_bounded_in_circle.py b/src/leetcode/matrix/robot_bounded_in_circle.py index dd56eaa..463288e 100644 --- a/src/leetcode/matrix/robot_bounded_in_circle.py +++ b/src/leetcode/matrix/robot_bounded_in_circle.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # On an infinite plane, a robot initially stands at (0, 0) and faces north. Note that: # diff --git a/src/leetcode/prefix_sum/README.md b/src/leetcode/prefix_sum/README.md index f87c647..92725e4 100644 --- a/src/leetcode/prefix_sum/README.md +++ b/src/leetcode/prefix_sum/README.md @@ -4,8 +4,8 @@ These phrases indicate a prefix sum might be useful: -- 'range sum query' -- 'sum from index to index' -- 'difference between sums' -- 'rolling sum' -- 'running total' +- range sum query +- sum from index to index +- difference between sums +- rolling sum +- running total diff --git a/src/leetcode/prefix_sum/continuous_subarray_sum.py b/src/leetcode/prefix_sum/continuous_subarray_sum.py new file mode 100644 index 0000000..8649ba6 --- /dev/null +++ b/src/leetcode/prefix_sum/continuous_subarray_sum.py @@ -0,0 +1,65 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an integer array nums and an integer k, return true if nums has a good subarray or false otherwise. +# +# A good subarray is a subarray where: +# +# - its length is at least two, and +# - the sum of the elements of the subarray is a multiple of k. +# +# Note that: +# +# - A subarray is a contiguous part of the array. +# - An integer x is a multiple of k if there exists an integer n such that x = n * k. 0 is always a multiple of k. +# +# See https://leetcode.com/problems/continuous-subarray-sum +class Solution: + def checkSubarraySum(self, nums: list[int], k: int) -> bool: + """ + SOLUTION + -------- + + The brute force solution is to calculate the sum of all subarrays of length 2 or more and check if it's a + multiple of of k. To do so, compute the prefix sum of the array and use it to calculate the sum of the subarray + from [i, j]. + + A subarray sum from [i, j] can be calculated as prefixSum[j] - prefixSum[i] + nums[i]. We can check each + subarray sum to see if it's a multiple of k and return true if we find one. It is; however, O(n^2) to calculate + subarray sums in this way. Unfortunately, this will exceed the runtime limit for large arrays in LeetCode. + + To get a better solution we have to note that: + + sum[i, j] = prefixSum[j] - prefixSum[i] + nums[i] + sum[i, j] = prefixSum[j] - prefixSum[i - 1] + + This is because the sum[i, j] is inclusive, and subtracting prefixSum[i] subtracts out nums[i]. That's why we + either have to add it back in or use i - 1 as the index instead. Afterwards, we can look at the sum modulo k: + + sum[i, j] (mod k) = (prefixSum[j] - prefixSum[i - 1]) (mod k) + + If we want sum[i, j] % k === 0, then we should write: + + (prefixSum[j] - prefixSum[i - 1]) (mod k) = 0 + prefixSum[j] (mod k) = prefixSum[i - 1] (mod k) + + In other words, if prefixSums at positions (i - 1) and j have the same remainder modulo k, then the subarray sum + from [i, j] has remainder 0 modulo k. + + COMPLEXITY + ---------- + + Time complexity is O(n) because we are iterating through the list once. + + Space complexity is O(n) because we are storing the prefix sum array. + """ + prefix_sum = [0] + for num in nums: + prefix_sum.append(prefix_sum[-1] + num) + + for i in range(2, len(prefix_sum)): + for j in range(i - 2): + if (prefix_sum[i] - prefix_sum[j]) % k == 0: + return True + + return False \ No newline at end of file diff --git a/src/leetcode/recursion/generate_parentheses.py b/src/leetcode/recursion/generate_parentheses.py index 6ac696c..9790073 100644 --- a/src/leetcode/recursion/generate_parentheses.py +++ b/src/leetcode/recursion/generate_parentheses.py @@ -1,4 +1,5 @@ # DIFFICULTY: MEDIUM +# ------------------ # # Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses. # diff --git a/src/leetcode/two_pointers/bag_of_tokens.py b/src/leetcode/two_pointers/bag_of_tokens.py index ee4c4dd..b40c7dd 100644 --- a/src/leetcode/two_pointers/bag_of_tokens.py +++ b/src/leetcode/two_pointers/bag_of_tokens.py @@ -1,4 +1,5 @@ -# DIFFICULTY: Medium +# DIFFICULTY: MEDIUM +# ------------------ # # You start with an initial power of `power`, an initial score of 0, and a bag of tokens given as an integer array # tokens, where each tokens[i] denotes the value of tokeni. From 05f7560c9b44cd02ae8700b22aa0c0cc18f678a3 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 16:58:04 -0700 Subject: [PATCH 077/158] add diff --- src/leetcode/array/dot_product_of_two_sparse_vectors.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/leetcode/array/dot_product_of_two_sparse_vectors.py b/src/leetcode/array/dot_product_of_two_sparse_vectors.py index 3a47e6d..81e2125 100644 --- a/src/leetcode/array/dot_product_of_two_sparse_vectors.py +++ b/src/leetcode/array/dot_product_of_two_sparse_vectors.py @@ -1,3 +1,6 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# # Given two sparse vectors, compute their dot product. # # Implement class SparseVector: From db42db0025176ee081d02b91787e5d8b3eea7d64 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 16:59:04 -0700 Subject: [PATCH 078/158] add diff --- src/leetcode/array/merge_sorted_array.py | 1 + src/leetcode/array/two_sum.py | 1 + src/leetcode/binary_search/kth_missing_positive_number.py | 1 + src/leetcode/interval/meeting_rooms.py | 1 + src/leetcode/linked_list/merge_two_sorted_lists.py | 1 + src/leetcode/math/number_of_one_bits.py | 1 + src/leetcode/matrix/valid_word_square.py | 8 ++++---- src/leetcode/stack/valid_parentheses.py | 1 + src/leetcode/string/valid_word_abbreviation.py | 1 + 9 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/leetcode/array/merge_sorted_array.py b/src/leetcode/array/merge_sorted_array.py index 7b69f79..4b81eb4 100644 --- a/src/leetcode/array/merge_sorted_array.py +++ b/src/leetcode/array/merge_sorted_array.py @@ -1,4 +1,5 @@ # DIFFICULTY: EASY +# ---------------- # # You are given two integer arrays nums1 and nums2, sorted in non-decreasing order, and two integers m and n, # representing the number of elements in nums1 and nums2 respectively. diff --git a/src/leetcode/array/two_sum.py b/src/leetcode/array/two_sum.py index 6e89b6b..fc80933 100644 --- a/src/leetcode/array/two_sum.py +++ b/src/leetcode/array/two_sum.py @@ -1,4 +1,5 @@ # DIFFICULTY: EASY +# ---------------- # # Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to # target. diff --git a/src/leetcode/binary_search/kth_missing_positive_number.py b/src/leetcode/binary_search/kth_missing_positive_number.py index c2a59c0..c6970fa 100644 --- a/src/leetcode/binary_search/kth_missing_positive_number.py +++ b/src/leetcode/binary_search/kth_missing_positive_number.py @@ -1,4 +1,5 @@ # DIFFICULTY: EASY +# ---------------- # # Given an array arr of positive integers sorted in a strictly increasing order, and an integer k. # diff --git a/src/leetcode/interval/meeting_rooms.py b/src/leetcode/interval/meeting_rooms.py index 904b3ac..2055b7c 100644 --- a/src/leetcode/interval/meeting_rooms.py +++ b/src/leetcode/interval/meeting_rooms.py @@ -1,4 +1,5 @@ # DIFFICULTY: EASY +# ---------------- # # Given an array of meeting time intervals where intervals[i] = [starti, endi], determine if a person could attend all # meetings. diff --git a/src/leetcode/linked_list/merge_two_sorted_lists.py b/src/leetcode/linked_list/merge_two_sorted_lists.py index 45e0c1c..85e69f0 100644 --- a/src/leetcode/linked_list/merge_two_sorted_lists.py +++ b/src/leetcode/linked_list/merge_two_sorted_lists.py @@ -1,4 +1,5 @@ # DIFFICULTY: EASY +# ---------------- # # You are given the heads of two sorted linked lists list1 and list2. # diff --git a/src/leetcode/math/number_of_one_bits.py b/src/leetcode/math/number_of_one_bits.py index 66b55ec..e7f4bcd 100644 --- a/src/leetcode/math/number_of_one_bits.py +++ b/src/leetcode/math/number_of_one_bits.py @@ -1,4 +1,5 @@ # DIFFICULTY: EASY +# ---------------- # # Write a function that takes the binary representation of a positive integer and returns the number of set bits it has # (also known as the Hamming weight). diff --git a/src/leetcode/matrix/valid_word_square.py b/src/leetcode/matrix/valid_word_square.py index 7d52c46..937b0e9 100644 --- a/src/leetcode/matrix/valid_word_square.py +++ b/src/leetcode/matrix/valid_word_square.py @@ -12,14 +12,14 @@ def validWordSquare(self, words: list[str]) -> bool: """ SOLUTION -------- - + A naive solution works. - + COMPLEXITY ---------- - + Time complexity is O(m * n). - + Space complexity is O(1). """ for i in range(len(words)): diff --git a/src/leetcode/stack/valid_parentheses.py b/src/leetcode/stack/valid_parentheses.py index 5ebf3d3..bec4194 100644 --- a/src/leetcode/stack/valid_parentheses.py +++ b/src/leetcode/stack/valid_parentheses.py @@ -1,4 +1,5 @@ # DIFFICULTY: EASY +# ---------------- # # Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid. # diff --git a/src/leetcode/string/valid_word_abbreviation.py b/src/leetcode/string/valid_word_abbreviation.py index 6a79905..944efc5 100644 --- a/src/leetcode/string/valid_word_abbreviation.py +++ b/src/leetcode/string/valid_word_abbreviation.py @@ -1,4 +1,5 @@ # DIFFICULTY: EASY +# ---------------- # # A string can be abbreviated by replacing any number of non-adjacent, non-empty substrings with their lengths. The # lengths should not have leading zeros. From 9b55e0e0c15d0bdd931531031040c749827d61d2 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 16:59:42 -0700 Subject: [PATCH 079/158] hard questions --- src/leetcode/binary_search/median_of_two_sorted_arrays.py | 1 + .../dynamic_programming/arithmetic_slices_ii_subsequences.py | 1 + src/leetcode/graph/add_edges_to_make_degrees_even.py | 1 + src/leetcode/graph/bus_routes.py | 1 + src/leetcode/graph/word_search_ii.py | 1 + src/leetcode/heap/max_stack.py | 1 + src/leetcode/heap/merge_k_sorted_lists.py | 1 + src/leetcode/heap/minimize_deviation_in_array.py | 3 ++- src/leetcode/linked_list/all_one.py | 1 + 9 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/leetcode/binary_search/median_of_two_sorted_arrays.py b/src/leetcode/binary_search/median_of_two_sorted_arrays.py index 77106ce..1446dcb 100644 --- a/src/leetcode/binary_search/median_of_two_sorted_arrays.py +++ b/src/leetcode/binary_search/median_of_two_sorted_arrays.py @@ -1,4 +1,5 @@ # DIFFICULTY: HARD +# ---------------- # # Given two sorted arrays nums1 and nums2 of size m and n respectively, return the median of the two sorted arrays. # diff --git a/src/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences.py b/src/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences.py index 81c3c74..90a548f 100644 --- a/src/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences.py +++ b/src/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences.py @@ -1,4 +1,5 @@ # DIFFICULTY: HARD +# ---------------- # # Given an integer array nums, return the number of all the arithmetic subsequences of nums. # diff --git a/src/leetcode/graph/add_edges_to_make_degrees_even.py b/src/leetcode/graph/add_edges_to_make_degrees_even.py index f117b76..8f2fda0 100644 --- a/src/leetcode/graph/add_edges_to_make_degrees_even.py +++ b/src/leetcode/graph/add_edges_to_make_degrees_even.py @@ -1,4 +1,5 @@ # DIFFICULTY: HARD +# ---------------- # # There is an undirected graph consisting of n nodes numbered from 1 to n. You are given the integer n and a 2D array # edges where edges[i] = [ai, bi] indicates that there is an edge between nodes ai and bi. The graph can be disconnected. diff --git a/src/leetcode/graph/bus_routes.py b/src/leetcode/graph/bus_routes.py index 614bc66..3273947 100644 --- a/src/leetcode/graph/bus_routes.py +++ b/src/leetcode/graph/bus_routes.py @@ -1,4 +1,5 @@ # DIFFICULTY: HARD +# ---------------- # # You are given an array routes representing bus routes where routes[i] is a bus route that the ith bus repeats # forever. diff --git a/src/leetcode/graph/word_search_ii.py b/src/leetcode/graph/word_search_ii.py index 0ae74cf..bd7a030 100644 --- a/src/leetcode/graph/word_search_ii.py +++ b/src/leetcode/graph/word_search_ii.py @@ -1,4 +1,5 @@ # DIFFICULTY: HARD +# ---------------- # # Given an m x n board of characters and a list of strings words, return all words on the board. # diff --git a/src/leetcode/heap/max_stack.py b/src/leetcode/heap/max_stack.py index ea6e32a..1cfecc6 100644 --- a/src/leetcode/heap/max_stack.py +++ b/src/leetcode/heap/max_stack.py @@ -1,4 +1,5 @@ # DIFFICULTY: HARD +# ---------------- # # Design a max stack data structure that supports the stack operations and supports finding the stack's maximum element. # diff --git a/src/leetcode/heap/merge_k_sorted_lists.py b/src/leetcode/heap/merge_k_sorted_lists.py index e809f59..2a85fa3 100644 --- a/src/leetcode/heap/merge_k_sorted_lists.py +++ b/src/leetcode/heap/merge_k_sorted_lists.py @@ -1,4 +1,5 @@ # DIFFICULTY: HARD +# ---------------- # # You are given an array of k linked-lists lists, each linked-list is sorted in ascending order. # diff --git a/src/leetcode/heap/minimize_deviation_in_array.py b/src/leetcode/heap/minimize_deviation_in_array.py index 09546e3..f1c4612 100644 --- a/src/leetcode/heap/minimize_deviation_in_array.py +++ b/src/leetcode/heap/minimize_deviation_in_array.py @@ -1,4 +1,5 @@ -# DIFFICULTY: Hard +# DIFFICULTY: HARD +# ---------------- # # You are given an array nums of n positive integers. # diff --git a/src/leetcode/linked_list/all_one.py b/src/leetcode/linked_list/all_one.py index 3fe0d82..cc9743f 100644 --- a/src/leetcode/linked_list/all_one.py +++ b/src/leetcode/linked_list/all_one.py @@ -1,4 +1,5 @@ # DIFFICULTY: HARD +# ---------------- # # Design a data structure to store the strings' count with the ability to return the strings with minimum and maximum # counts. From 0d388c3b625ec91e8bbb1bd04de2743dd7f3c2ec Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 17:08:32 -0700 Subject: [PATCH 080/158] subarray sum --- src/leetcode/matrix/valid_word_square.py | 8 +-- .../prefix_sum/continuous_subarray_sum.py | 69 +++++++++++++------ .../continuous-subarray-sum.test.ts | 15 ---- .../continuous_subarray_sum_test.py | 16 +++++ 4 files changed, 68 insertions(+), 40 deletions(-) delete mode 100644 test/leetcode/prefix_sum/continuous-subarray-sum.test.ts create mode 100644 test/leetcode/prefix_sum/continuous_subarray_sum_test.py diff --git a/src/leetcode/matrix/valid_word_square.py b/src/leetcode/matrix/valid_word_square.py index 937b0e9..7d52c46 100644 --- a/src/leetcode/matrix/valid_word_square.py +++ b/src/leetcode/matrix/valid_word_square.py @@ -12,14 +12,14 @@ def validWordSquare(self, words: list[str]) -> bool: """ SOLUTION -------- - + A naive solution works. - + COMPLEXITY ---------- - + Time complexity is O(m * n). - + Space complexity is O(1). """ for i in range(len(words)): diff --git a/src/leetcode/prefix_sum/continuous_subarray_sum.py b/src/leetcode/prefix_sum/continuous_subarray_sum.py index 8649ba6..4d37da1 100644 --- a/src/leetcode/prefix_sum/continuous_subarray_sum.py +++ b/src/leetcode/prefix_sum/continuous_subarray_sum.py @@ -19,47 +19,74 @@ def checkSubarraySum(self, nums: list[int], k: int) -> bool: """ SOLUTION -------- - + The brute force solution is to calculate the sum of all subarrays of length 2 or more and check if it's a multiple of of k. To do so, compute the prefix sum of the array and use it to calculate the sum of the subarray from [i, j]. - + A subarray sum from [i, j] can be calculated as prefixSum[j] - prefixSum[i] + nums[i]. We can check each subarray sum to see if it's a multiple of k and return true if we find one. It is; however, O(n^2) to calculate subarray sums in this way. Unfortunately, this will exceed the runtime limit for large arrays in LeetCode. - + To get a better solution we have to note that: - + sum[i, j] = prefixSum[j] - prefixSum[i] + nums[i] sum[i, j] = prefixSum[j] - prefixSum[i - 1] - + This is because the sum[i, j] is inclusive, and subtracting prefixSum[i] subtracts out nums[i]. That's why we either have to add it back in or use i - 1 as the index instead. Afterwards, we can look at the sum modulo k: - + sum[i, j] (mod k) = (prefixSum[j] - prefixSum[i - 1]) (mod k) - + If we want sum[i, j] % k === 0, then we should write: - + (prefixSum[j] - prefixSum[i - 1]) (mod k) = 0 prefixSum[j] (mod k) = prefixSum[i - 1] (mod k) - + In other words, if prefixSums at positions (i - 1) and j have the same remainder modulo k, then the subarray sum from [i, j] has remainder 0 modulo k. - + COMPLEXITY ---------- - + Time complexity is O(n) because we are iterating through the list once. - + Space complexity is O(n) because we are storing the prefix sum array. """ - prefix_sum = [0] - for num in nums: - prefix_sum.append(prefix_sum[-1] + num) - - for i in range(2, len(prefix_sum)): - for j in range(i - 2): - if (prefix_sum[i] - prefix_sum[j]) % k == 0: + # Use a map of remainders -> index to store the prefix sum remainders, so we can check later if we have seen + # any remainders that are the same modulo k. + remainders: dict[int, int] = {} + + # Maintain a running prefix sum as we loop through the array. + prefix_sum = 0 + for i, num in enumerate(nums): + prefix_sum += num + r = prefix_sum % k + + # It might be the case that the remainder is negative, just as a quirk of how JavaScript handles the modulo + # operator. + # + # For example, -5 % 3 = -2. But this is wrong because -2 is not congruent to -5 modulo 3. Instead: + # + # -5 (mod 3) = -5 + 3 (mod 3) = -2 (mod 3) = -2 + 3 (mod 3) = 1 (mod 3) + # + # That is, we want -5 % 3 = 1 instead. To fix this, we half to add the modulus to the remainder if it's + # negative. + if r < 0: + r += k + + # It's possible that the prefix sum already is a multiple of k. And it's possible that the remainder 0 + # isn't in the remainders map yet. In this case, just check if we have a subarray of length 2 or more. + if r == 0 and i >= 1: + return True + + # If this is a previously seen remainder, we can check if we have a "good" subarray. + if r in remainders: + j = remainders[r] + if abs(i - j) >= 2: return True - - return False \ No newline at end of file + # If we're seeing this remainder for the first time, store it in the map. + else: + remainders[r] = i + + return False diff --git a/test/leetcode/prefix_sum/continuous-subarray-sum.test.ts b/test/leetcode/prefix_sum/continuous-subarray-sum.test.ts deleted file mode 100644 index ff98222..0000000 --- a/test/leetcode/prefix_sum/continuous-subarray-sum.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { checkSubarraySum } from '../../src/prefix-sum/continuous-subarray-sum'; - -describe('continuous subarray sum', () => { - test('checkSubarraySum - test case 1', () => { - expect(checkSubarraySum([23, 2, 4, 6, 7], 6)).toBe(true); - }); - - test('checkSubarraySum - test case 2', () => { - expect(checkSubarraySum([1, 1], 2)).toBe(true); - }); - - test('checkSubarraySum - test case 3', () => { - expect(checkSubarraySum([-10, 10], 1)).toBe(true); - }); -}); diff --git a/test/leetcode/prefix_sum/continuous_subarray_sum_test.py b/test/leetcode/prefix_sum/continuous_subarray_sum_test.py new file mode 100644 index 0000000..cca6874 --- /dev/null +++ b/test/leetcode/prefix_sum/continuous_subarray_sum_test.py @@ -0,0 +1,16 @@ +from leetcode.prefix_sum.continuous_subarray_sum import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.checkSubarraySum([23, 2, 4, 6, 7], 6) + + +def test_case_2(): + assert soln.checkSubarraySum([1, 1], 2) + + +def test_case_3(): + assert soln.checkSubarraySum([-10, 10], 1) From 62ce5086d92e486df7cffc1424703abb7aee371e Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 17:19:53 -0700 Subject: [PATCH 081/158] prefix sum --- .../prefix_sum/continuous-subarray-sum.ts | 95 ------------------- .../prefix_sum/running-sum-of-1d-array.ts | 25 ----- .../prefix_sum/running_sum_of_1d_array.py | 29 ++++++ ...ax-sum-obtained-of-any-permutation.test.ts | 15 --- .../running-sum-of-1d-array.test.ts | 7 -- .../running_sum_of_1d_array_test.py | 8 ++ 6 files changed, 37 insertions(+), 142 deletions(-) delete mode 100644 src/leetcode/prefix_sum/continuous-subarray-sum.ts delete mode 100644 src/leetcode/prefix_sum/running-sum-of-1d-array.ts create mode 100644 src/leetcode/prefix_sum/running_sum_of_1d_array.py delete mode 100644 test/leetcode/prefix_sum/max-sum-obtained-of-any-permutation.test.ts delete mode 100644 test/leetcode/prefix_sum/running-sum-of-1d-array.test.ts create mode 100644 test/leetcode/prefix_sum/running_sum_of_1d_array_test.py diff --git a/src/leetcode/prefix_sum/continuous-subarray-sum.ts b/src/leetcode/prefix_sum/continuous-subarray-sum.ts deleted file mode 100644 index e5916a0..0000000 --- a/src/leetcode/prefix_sum/continuous-subarray-sum.ts +++ /dev/null @@ -1,95 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an integer array nums and an integer k, return true if nums has a good subarray or false otherwise. -// -// A good subarray is a subarray where: -// -// - its length is at least two, and -// - the sum of the elements of the subarray is a multiple of k. -// -// Note that: -// -// - A subarray is a contiguous part of the array. -// - An integer x is a multiple of k if there exists an integer n such that x = n * k. 0 is always a multiple of k. -// -// See {@link https://leetcode.com/problems/continuous-subarray-sum/} -export { checkSubarraySum }; - -// SOLUTION: -// -// The brute force solution is to calculate the sum of all subarrays of length 2 or more and check if it's a multiple of -// of k. To do so, compute the prefix sum of the array and use it to calculate the sum of the subarray from [i, j]. -// -// A subarray sum from [i, j] can be calculated as prefixSum[j] - prefixSum[i] + nums[i]. We can check each subarray -// sum to see if it's a multiple of k and return true if we find one. It is; however, O(n^2) to calculate subarray -// sums in this way. Unfortunately, this will exceed the runtime limit for large arrays in LeetCode. -// -// To get a better solution we have to note that: -// -// sum[i, j] = prefixSum[j] - prefixSum[i] + nums[i] -// sum[i, j] = prefixSum[j] - prefixSum[i - 1] -// -// This is because the sum[i, j] is inclusive, and subtracting prefixSum[i] subtracts out nums[i]. That's why we either -// have to add it back in or use i - 1 as the index instead. Afterwards, we can look at the sum modulo k: -// -// sum[i, j] (mod k) = (prefixSum[j] - prefixSum[i - 1]) (mod k) -// -// If we want sum[i, j] % k === 0, then we should write: -// -// (prefixSum[j] - prefixSum[i - 1]) (mod k) = 0 -// prefixSum[j] (mod k) = prefixSum[i - 1] (mod k) -// -// In other words, if prefixSums at positions (i - 1) and j have the same remainder modulo k, then the subarray sum from -// [i, j] has remainder 0 modulo k. -// -// COMPLEXITY: -// -// Time complexity is O(n^2) because we are doing an outer and inner loop on the prefix sum array. -// -// Space complexity is O(n) because we are storing the prefix sum array. -function checkSubarraySum(nums: number[], k: number): boolean { - // Use a map of remainders to positions to store the prefix sum remainders, so we can check later if we have seen any - // remainders that are the same modulo k. - type Remainder = number; - type Index = number; - const remainders = new Map(); - - // Maintain a running prefix sum as we loop through the array. - let prefixSum = 0; - for (let i = 0; i < nums.length; i++) { - prefixSum += nums[i]; - let remainder = prefixSum % k; - - // It might be the case that the remainder is negative, just as a quirk of how JavaScript handles the modulo - // operator. - // - // For example, -5 % 3 = -2. But this is wrong because -2 is not congruent to -5 modulo 3. Instead: - // - // -5 (mod 3) = -5 + 3 (mod 3) = -2 (mod 3) = -2 + 3 (mod 3) = 1 (mod 3) - // - // That is, we want -5 % 3 = 1 instead. To fix this, we half to add the modulus to the remainder if it's negative. - if (remainder < 0) { - remainder += k; - } - - // It's possible that the prefix sum already is a multiple of k. And it's possible that the remainder 0 isn't in - // the remainders map yet. In this case, just check if we have a subarray of length 2 or more. - if (remainder === 0 && i >= 1) { - return true; - } - - // If this is a previously seen remainder, we can check if we have a "good" subarray. - if (remainders.has(remainder)) { - const j = remainders.get(remainder)!; - if (Math.abs(i - j) >= 2) { - return true; - } - } - // If we're seeing this remainder for the first time, store it in the map. - else { - remainders.set(remainder, i); - } - } - - return false; -} diff --git a/src/leetcode/prefix_sum/running-sum-of-1d-array.ts b/src/leetcode/prefix_sum/running-sum-of-1d-array.ts deleted file mode 100644 index 0de63ce..0000000 --- a/src/leetcode/prefix_sum/running-sum-of-1d-array.ts +++ /dev/null @@ -1,25 +0,0 @@ -// DIFFICULTY: EASY -// -// Given an array nums. We define a running sum of an array as runningSum[i] = sum(nums[0]…nums[i]). -// -// Return the running sum of nums. -// -// See {@link https://leetcode.com/problems/running-sum-of-1d-array/description/} -export { runningSum }; - -// SOLUTION: -// -// This is known as a prefix sum. -// -// See {@link https://en.wikipedia.org/wiki/Prefix_sum} -function runningSum(nums: number[]): number[] { - if (nums.length <= 1) { - return nums; - } - - const result = [nums[0]]; - for (let i = 1; i < nums.length; i++) { - result[i] = result[i - 1] + nums[i]; - } - return result; -} diff --git a/src/leetcode/prefix_sum/running_sum_of_1d_array.py b/src/leetcode/prefix_sum/running_sum_of_1d_array.py new file mode 100644 index 0000000..9c929d7 --- /dev/null +++ b/src/leetcode/prefix_sum/running_sum_of_1d_array.py @@ -0,0 +1,29 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given an array nums. We define a running sum of an array as runningSum[i] = sum(nums[0]…nums[i]). +# +# Return the running sum of nums. +# +# See https://leetcode.com/problems/running-sum-of-1d-array +from itertools import accumulate + + +class Solution: + """ + SOLUTION + -------- + + This is known as a prefix sum. + + See https://en.wikipedia.org/wiki/Prefix_sum + + COMPLEXITY + ---------- + + Time complexity is O(n) because we are iterating through the list once. + + Space complexity is O(n) because we are storing the prefix sum array. + """ + def runningSum(self, nums: list[int]) -> list[int]: + return list(accumulate(nums)) \ No newline at end of file diff --git a/test/leetcode/prefix_sum/max-sum-obtained-of-any-permutation.test.ts b/test/leetcode/prefix_sum/max-sum-obtained-of-any-permutation.test.ts deleted file mode 100644 index 26feb52..0000000 --- a/test/leetcode/prefix_sum/max-sum-obtained-of-any-permutation.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { maxSumRangeQuery } from '../../src/prefix-sum/max-sum-obtained-of-any-permutation'; - -describe('maximum sum obtained of any permutation', () => { - test('maximum sum obtained of any permutation - test case 1', async () => { - expect( - maxSumRangeQuery( - [1, 2, 3, 4, 5], - [ - [1, 3], - [0, 1] - ] - ) - ).toBe(19); - }); -}); diff --git a/test/leetcode/prefix_sum/running-sum-of-1d-array.test.ts b/test/leetcode/prefix_sum/running-sum-of-1d-array.test.ts deleted file mode 100644 index 248c013..0000000 --- a/test/leetcode/prefix_sum/running-sum-of-1d-array.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { runningSum } from '../../src/prefix-sum/running-sum-of-1d-array'; - -describe('running sum of 1d array', () => { - test('running sum - test case 1', async () => { - expect(runningSum([1, 2, 3, 4])).toStrictEqual([1, 3, 6, 10]); - }); -}); diff --git a/test/leetcode/prefix_sum/running_sum_of_1d_array_test.py b/test/leetcode/prefix_sum/running_sum_of_1d_array_test.py new file mode 100644 index 0000000..d240628 --- /dev/null +++ b/test/leetcode/prefix_sum/running_sum_of_1d_array_test.py @@ -0,0 +1,8 @@ +from leetcode.prefix_sum.running_sum_of_1d_array import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.runningSum([1, 2, 3, 4]) == [1, 3, 6, 10] From 70ee0ac5abb238ad59ef1904b3601951baba9ed0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 17 Mar 2025 00:20:27 +0000 Subject: [PATCH 082/158] Automatic commit via GitHub Actions --- src/leetcode/prefix_sum/running_sum_of_1d_array.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/leetcode/prefix_sum/running_sum_of_1d_array.py b/src/leetcode/prefix_sum/running_sum_of_1d_array.py index 9c929d7..23550dd 100644 --- a/src/leetcode/prefix_sum/running_sum_of_1d_array.py +++ b/src/leetcode/prefix_sum/running_sum_of_1d_array.py @@ -13,9 +13,9 @@ class Solution: """ SOLUTION -------- - + This is known as a prefix sum. - + See https://en.wikipedia.org/wiki/Prefix_sum COMPLEXITY @@ -25,5 +25,6 @@ class Solution: Space complexity is O(n) because we are storing the prefix sum array. """ + def runningSum(self, nums: list[int]) -> list[int]: - return list(accumulate(nums)) \ No newline at end of file + return list(accumulate(nums)) From 16bc336290a91dc6bf1a6a585a65da92f7edf108 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 17:30:47 -0700 Subject: [PATCH 083/158] max sum --- .../max-sum-obtained-of-any-permutation.ts | 71 ----------------- .../max_sum_obtained_of_any_permutation.py | 76 +++++++++++++++++++ .../prefix_sum/running_sum_of_1d_array.py | 7 +- ...ax_sum_obtained_of_any_permutation_test.py | 8 ++ 4 files changed, 88 insertions(+), 74 deletions(-) delete mode 100644 src/leetcode/prefix_sum/max-sum-obtained-of-any-permutation.ts create mode 100644 src/leetcode/prefix_sum/max_sum_obtained_of_any_permutation.py create mode 100644 test/leetcode/prefix_sum/max_sum_obtained_of_any_permutation_test.py diff --git a/src/leetcode/prefix_sum/max-sum-obtained-of-any-permutation.ts b/src/leetcode/prefix_sum/max-sum-obtained-of-any-permutation.ts deleted file mode 100644 index 5ffc575..0000000 --- a/src/leetcode/prefix_sum/max-sum-obtained-of-any-permutation.ts +++ /dev/null @@ -1,71 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// We have an array of integers, nums, and an array of requests where requests[i] = [starti, endi]. The ith request asks -// for the sum of nums[starti] + nums[starti + 1] + ... + nums[endi - 1] + nums[endi]. Both starti and endi are -// 0-indexed. -// -// Return the maximum total sum of all requests among all permutations of nums. -// -// Since the answer may be too large, return it modulo 10^9 + 7. -// -// See {@link https://leetcode.com/problems/maximum-sum-obtained-of-any-permutation/} -export { maxSumRangeQuery }; - -// SOLUTION: -// -// This question is asking for a very weird thing. -// -// Here, the request ranges don't change; they want to sum the values of the range. However, the nums array can be -// in any order you want. The ask is to find the order of nums that gives you the maximum sum across ALL requests, -// and then return that maximum sum (not the specific order). -// -// This will happen if the most frequently asked for numbers are matched up with the largest numbers. So basically, -// sort the nums in descending order by frequency. -function maxSumRangeQuery(nums: number[], requests: number[][]): number { - // First, calculate for each index i, how many times that index is requested. - const freq: number[] = new Array(nums.length).fill(0); - for (const request of requests) { - const [start, end] = request; - - // This is the difference array technique, usable when we want to efficiently apply multiple operations to a - // single array. Here, the naive way to compute frequency would be to do an inner loop and and increment each - // element between start and end by x (here x = 1). - // - // Instead, use the difference array and prefix sums to compute the actual frequencies. A prefix sum will carry - // the value of x (here x = 1) from start through to the end of the array. However, if we set freq[end+1] = -x, - // then the propagated sum effectively gets "turned off" after adding x to freq[end]. - freq[start]++; - if (end + 1 < nums.length) { - freq[end + 1]--; - } - } - - // Next, we can calculate the actual frequency of each element by taking the prefix sum of the array. This - // propagates sums of values at i through to the end of the array (except for spots where we have strategically - // "turned off" the propagation to make it only apply to a range). - for (let i = 1; i < freq.length; i++) { - freq[i] += freq[i - 1]; - } - - // Now we want to sort the frequencies AND nums in descending order, so we can match up the highest frequency asks - // with the largest number. - // - // Note that this makes us lose track of the indices, but that's okay. Every single element in freq represents the - // number of times some value is asked for. Every request is honored, so every value will be asked for. - // - // On top of that, the question asks for the maximum sum across ALL queries, so it's not even necessary to keep - // track of any individual query. In the end we'll sum everything together anyways. - nums.sort((a, b) => b - a); - freq.sort((a, b) => b - a); - - // Now it's just a matter of summing every single request value by matching it up with the largest number. Remember - // that the totality of freq's request values will cover all range queries. Since we don't care about the max - // for any individual query, we can just add them ALL up. - const MODULUS = 1e9 + 7; - let result = 0; - for (let i = 0; i < nums.length; i++) { - result = (result + nums[i] * freq[i]) % MODULUS; - } - - return result; -} diff --git a/src/leetcode/prefix_sum/max_sum_obtained_of_any_permutation.py b/src/leetcode/prefix_sum/max_sum_obtained_of_any_permutation.py new file mode 100644 index 0000000..854d92d --- /dev/null +++ b/src/leetcode/prefix_sum/max_sum_obtained_of_any_permutation.py @@ -0,0 +1,76 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# We have an array of integers, nums, and an array of requests where requests[i] = [starti, endi]. The ith request asks +# for the sum of nums[starti] + nums[starti + 1] + ... + nums[endi - 1] + nums[endi]. Both starti and endi are +# 0-indexed. +# +# Return the maximum total sum of all requests among all permutations of nums. +# +# Since the answer may be too large, return it modulo 10^9 + 7. +# +# See https://leetcode.com/problems/maximum-sum-obtained-of-any-permutation +class Solution: + def maxSumRangeQuery(self, nums: list[int], requests: list[list[int]]) -> int: + """ + SOLUTION + -------- + + This question is asking for a very weird thing. + + Here, the request ranges don't change; they want to sum the values of the range. However, the nums array can be + in any order you want. The ask is to find the order of nums that gives you the maximum sum across ALL requests, + and then return that maximum sum (not the specific order). + + This will happen if the most frequently asked for numbers are matched up with the largest numbers. So + basically, sort the nums in descending order by frequency. + + COMPLEXITY + ---------- + + Time complexity is O(n log n) because we are sorting the requests. + + Space complexity is O(n) because we are storing the frequency of each index. + """ + # First, calculate for each index i, how many times that index is requested. + freq = [0] * len(nums) + for request in requests: + [start, end] = request + + # This is the difference array technique, usable when we want to efficiently apply multiple operations to a + # single array. Here, the naive way to compute frequency would be to do an inner loop and and increment + # each element between start and end by x (here x = 1). + # + # Instead, use the difference array and prefix sums to compute the actual frequencies. A prefix sum will + # carry the value of x (here x = 1) from start through to the end of the array. However, if we set + # freq[end+1] = -x, then the propagated sum effectively gets "turned off" after adding x to freq[end]. + freq[start] += 1 + if end + 1 < len(nums): + freq[end + 1] -= 1 + + # Next, we can calculate the actual frequency of each element by taking the prefix sum of the array. This + # propagates sums of values at i through to the end of the array (except for spots where we have strategically + # "turned off" the propagation to make it only apply to a range). + for i in range(1, len(freq)): + freq[i] += freq[i - 1] + + # Now we want to sort the frequencies AND nums in descending order, so we can match up the highest frequency + # asks with the largest number. + # + # Note that this makes us lose track of the indices, but that's okay. Every single element in freq represents + # the number of times some value is asked for. Every request is honored, so every value will be asked for. + # + # On top of that, the question asks for the maximum sum across ALL queries, so it's not even necessary to keep + # track of any individual query. In the end we'll sum everything together anyways. + nums.sort(reverse=True) + freq.sort(reverse=True) + + # Now it's just a matter of summing every single request value by matching it up with the largest number. + # Remember that the totality of freq's request values will cover all range queries. Since we don't care about + # the max for any individual query, we can just add them ALL up. + modulus = 1e9 + 7 + result = 0 + for i in range(len(nums)): + result = (result + nums[i] * freq[i]) % modulus + + return int(result) diff --git a/src/leetcode/prefix_sum/running_sum_of_1d_array.py b/src/leetcode/prefix_sum/running_sum_of_1d_array.py index 9c929d7..23550dd 100644 --- a/src/leetcode/prefix_sum/running_sum_of_1d_array.py +++ b/src/leetcode/prefix_sum/running_sum_of_1d_array.py @@ -13,9 +13,9 @@ class Solution: """ SOLUTION -------- - + This is known as a prefix sum. - + See https://en.wikipedia.org/wiki/Prefix_sum COMPLEXITY @@ -25,5 +25,6 @@ class Solution: Space complexity is O(n) because we are storing the prefix sum array. """ + def runningSum(self, nums: list[int]) -> list[int]: - return list(accumulate(nums)) \ No newline at end of file + return list(accumulate(nums)) diff --git a/test/leetcode/prefix_sum/max_sum_obtained_of_any_permutation_test.py b/test/leetcode/prefix_sum/max_sum_obtained_of_any_permutation_test.py new file mode 100644 index 0000000..987dc7f --- /dev/null +++ b/test/leetcode/prefix_sum/max_sum_obtained_of_any_permutation_test.py @@ -0,0 +1,8 @@ +from leetcode.prefix_sum.max_sum_obtained_of_any_permutation import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.maxSumRangeQuery([1, 2, 3, 4, 5], [[1, 3], [0, 1]]) == 19 From 5bd05bb940d1600c615a5b86ec8336146eb7ab7a Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 17:36:43 -0700 Subject: [PATCH 084/158] prefix usm --- .../product-of-array-except-self.ts | 56 ------------------ .../product_of_array_except_self.py | 58 +++++++++++++++++++ .../product_of_array_except_self_test.py | 8 +++ 3 files changed, 66 insertions(+), 56 deletions(-) delete mode 100644 src/leetcode/prefix_sum/product-of-array-except-self.ts create mode 100644 src/leetcode/prefix_sum/product_of_array_except_self.py create mode 100644 test/leetcode/prefix_sum/product_of_array_except_self_test.py diff --git a/src/leetcode/prefix_sum/product-of-array-except-self.ts b/src/leetcode/prefix_sum/product-of-array-except-self.ts deleted file mode 100644 index 1b76e6f..0000000 --- a/src/leetcode/prefix_sum/product-of-array-except-self.ts +++ /dev/null @@ -1,56 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an integer array nums, return an array answer such that answer[i] is equal to the product of all the elements -// of nums except nums[i]. -// -// The product of any prefix or suffix of nums is guaranteed to fit in a 32-bit integer. -// -// You must write an algorithm that runs in O(n) time and without using the division operation. -// -// See {@link https://leetcode.com/problems/product-of-array-except-self/} -export { productExceptSelf }; - -// SOLUTION: -// -// If we could use division, we could just compute the product of the array (excluding zeroes), then we can divide the -// non-zero product by the current element if there were no zeroes, return the non-zero product if there were zeroes, -// or just return zero if there were multiple zeroes. -// -// Without using division, we'll have to use more space to compute the product-except-self in each array index. To -// do this, we have to construct two new arrays: -// -// - One array to hold the partial product of all elements before, but not including element i, called `prefix`. -// This array is a "prefix product", analogous to the "prefix sum" computational technique used preprocess an array -// for quick range queries. -// - One array to hold the partial product of all elements after, but not including element i, called `suffix`. -// This array is a "suffix product", analogous to the "suffix sum" computational technique used to preprocess an -// array for quick range queries. -// -// To compute the product-except-self, the ith value will be `befores[i] * afters[i]`. -// -// This isn't exactly a prefix sum problem, but well, close enough. -function productExceptSelf(xs: number[]): number[] { - if (xs.length === 0) { - return []; - } - - // Compute the product up to the ith index. - const prefix = Array(xs.length).fill(1); - for (let i = 1; i < xs.length; i++) { - prefix[i] = prefix[i - 1] * xs[i - 1]; - } - - // Compute the product after the ith index. - const suffix = Array(xs.length).fill(1); - for (let i = xs.length - 2; i >= 0; i--) { - suffix[i] = suffix[i + 1] * xs[i + 1]; - } - - // Compute the product except for the element at the ith index. - const products = Array(xs.length).fill(0); - for (let i = 0; i < xs.length; i++) { - products[i] = prefix[i] * suffix[i]; - } - - return products; -} diff --git a/src/leetcode/prefix_sum/product_of_array_except_self.py b/src/leetcode/prefix_sum/product_of_array_except_self.py new file mode 100644 index 0000000..1833924 --- /dev/null +++ b/src/leetcode/prefix_sum/product_of_array_except_self.py @@ -0,0 +1,58 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an integer array nums, return an array answer such that answer[i] is equal to the product of all the elements +# of nums except nums[i]. +# +# The product of any prefix or suffix of nums is guaranteed to fit in a 32-bit integer. +# +# You must write an algorithm that runs in O(n) time and without using the division operation. +# +# See https://leetcode.com/problems/product-of-array-except-self +class Solution: + def productExceptSelf(self, nums: list[int]) -> list[int]: + """ + SOLUTION + -------- + + If we could use division, we could just compute the product of the array (excluding zeroes), then we can divide + the non-zero product by the current element if there were no zeroes, return the non-zero product if there were + zeroes, or just return zero if there were multiple zeroes. + + Without using division, we'll have to use more space to compute the product-except-self in each array index. To + do this, we have to construct two new arrays: + + - One array to hold the partial product of all elements before, but not including element i, called `prefix`. + This array is a "prefix product", analogous to the "prefix sum" computational technique used preprocess an + array for quick range queries. + - One array to hold the partial product of all elements after, but not including element i, called `suffix`. + This array is a "suffix product", analogous to the "suffix sum" computational technique used to preprocess an + array for quick range queries. + + To compute the product-except-self, the ith value will be `befores[i] * afters[i]`. + + This isn't exactly a prefix sum problem, but well, close enough. + + COMPLEXITY + ---------- + + Time complexity is O(n) because we are iterating through the list twice. + + Space complexity is O(n) because we are storing the prefix and suffix product arrays. + """ + if not nums: + return [] + + # Compute the product up to the ith index. + prefix = [1] * len(nums) + for i in range(1, len(nums)): + prefix[i] = prefix[i - 1] * nums[i - 1] + + # Compute the product after the ith index. + suffix = [1] * len(nums) + for i in range(len(nums) - 2, -1, -1): + suffix[i] = suffix[i + 1] * nums[i + 1] + + # Compute the product except for the element at the ith index. + products = [prefix[i] * suffix[i] for i in range(len(nums))] + return products diff --git a/test/leetcode/prefix_sum/product_of_array_except_self_test.py b/test/leetcode/prefix_sum/product_of_array_except_self_test.py new file mode 100644 index 0000000..df3425a --- /dev/null +++ b/test/leetcode/prefix_sum/product_of_array_except_self_test.py @@ -0,0 +1,8 @@ +from leetcode.prefix_sum.product_of_array_except_self import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.productExceptSelf([1, 2, 3, 4]) == [24, 12, 8, 6] From 899ee0c486f1ff8dbf4a951385650a656bc161a9 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 17:46:17 -0700 Subject: [PATCH 085/158] random pick with weight --- .../prefix_sum/random_pick_with_weight.py | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 src/leetcode/prefix_sum/random_pick_with_weight.py diff --git a/src/leetcode/prefix_sum/random_pick_with_weight.py b/src/leetcode/prefix_sum/random_pick_with_weight.py new file mode 100644 index 0000000..ae8eba2 --- /dev/null +++ b/src/leetcode/prefix_sum/random_pick_with_weight.py @@ -0,0 +1,82 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given a 0-indexed array of positive integers w where w[i] describes the weight of the ith index. +# +# You need to implement the function pickIndex(), which randomly picks an index in the range [0, w.length - 1] +# (inclusive) and returns it. The probability of picking an index i is w[i] / sum(w). +# +# For example, if w = [1, 3], the probability of picking index 0 is 1 / (1 + 3) = 0.25 (i.e., 25%), and the +# probability of picking index 1 is 3 / (1 + 3) = 0.75 (i.e., 75%). +# +# See https://leetcode.com/problems/random-pick-with-weight +import random + + +class Solution: + def __init__(self, w: list[int]) -> None: + """ + SOLUTION + -------- + + The naive way to do this is to take the weights and create a new array that's the size of the sum of the + weights. For example, if you have the input [1, 3], create an array [0, 1, 1, 1]. Then, generate a random + number using floor(random() * 4) to get a number between 0 and 3 inclusive. That's the index you should pick. + + This will work, but once you have a large number of weights, you'll be creating quite large arrays. Probably + not a good idea. + + Instead, we can compute a prefix sum to avoid expanding an array. In the previous example, you can turn the + input array [1, 3] into the prefix sum array [1, 4]. The cumulative weights divide the range in this way: + + [0, 1) -> Index 0 + [1, 4) -> Index 1 + + So if you generate a random number between [0, 4), you can do a linear scan of the prefix sum array to find the + index that you should pick. This works because each range's weight increases as you move to the right. + + Likewise, if you had the input array [1, 4, 2], the prefix sum array would be [1, 5, 7]. The ranges would be: + + [0, 1) -> Index 0 + [1, 5) -> Index 1 + [5, 7) -> Index 2 + + And again, picking a random number between [0, 7) would allow you to do a linear scan to find the index to pick. + + But wait! Instead of doing a linear scan, it's much more efficient to do a binary search, which is what this + solution does. + + COMPLEXITY + ---------- + + Time complexity in O(n). It is O(n) to create the prefix sum array, then O(n) to perform a linear scan, or + O(log n) to perform a binary search. + + Space complexity is O(n) because we are storing the prefix sum array. + """ + self.prefix_sum = [0] * len(w) + self.total = 0 + for i, weight in enumerate(w): + self.total += weight + self.prefix_sum[i] = self.total + + def pickIndex(self) -> int: + # Pick a random number between [0, total). Doesn't have to be an integer at all; it's just a number between 0 + # and the total. + target = random.randint(0, self.total - 1) + + # Use insertion point binary search instead of exact match. We aren't looking for an exact match of the element + # in the array. In fact, it's unlikely to even be in the array. Instead we'll use insertion point binary + # search which is more conceptually appropriate. + left = 0 + right = len(self.prefix_sum) + + while left < right: + mid = (left + right) // 2 + if self.prefix_sum[mid] <= target: + left = mid + 1 + else: + right = mid + + # The "insertion point" is the index that corresponds to the random number. + return left From c53c70c976739e2ece934b722105862ee358d7f7 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 17:46:28 -0700 Subject: [PATCH 086/158] deletes random pick --- .../prefix_sum/random-pick-with-weight.ts | 85 ------------------- 1 file changed, 85 deletions(-) delete mode 100644 src/leetcode/prefix_sum/random-pick-with-weight.ts diff --git a/src/leetcode/prefix_sum/random-pick-with-weight.ts b/src/leetcode/prefix_sum/random-pick-with-weight.ts deleted file mode 100644 index 453950d..0000000 --- a/src/leetcode/prefix_sum/random-pick-with-weight.ts +++ /dev/null @@ -1,85 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given a 0-indexed array of positive integers w where w[i] describes the weight of the ith index. -// -// You need to implement the function pickIndex(), which randomly picks an index in the range [0, w.length - 1] -// (inclusive) and returns it. The probability of picking an index i is w[i] / sum(w). -// -// For example, if w = [1, 3], the probability of picking index 0 is 1 / (1 + 3) = 0.25 (i.e., 25%), and the -// probability of picking index 1 is 3 / (1 + 3) = 0.75 (i.e., 75%). -// -// See {@link https://leetcode.com/problems/random-pick-with-weight/} -export { Solution }; - -// SOLUTION: -// -// The naive way to do this is to take the weights and create a new array that's the size of the sum of the weights. -// For example, if you have the input [1, 3], create an array [0, 1, 1, 1]. Then, generate a random number using -// floor(random() * 4) to get a number between 0 and 3 inclusive. That's the index you should pick. -// -// This will work, but once you have a large number of weights, you'll be creating quite large arrays. Probably not a -// good idea. -// -// Instead, we can compute a prefix sum to avoid expanding an array. In the previous example, you can turn the input -// array [1, 3] into the prefix sum array [1, 4]. The cumulative weights divide the range in this way: -// -// [0, 1) -> Index 0 -// [1, 4) -> Index 1 -// -// So if you generate a random number between [0, 4), you can do a linear scan of the prefix sum array to find the index -// that you should pick. This works because each range's weight increases as you move to the right. -// -// Likewise, if you had the input array [1, 4, 2], the prefix sum array would be [1, 5, 7]. The ranges would be: -// -// [0, 1) -> Index 0 -// [1, 5) -> Index 1 -// [5, 7) -> Index 2 -// -// And again, picking a random number between [0, 7) would allow you to do a linear scan to find the index to pick. -// -// But wait! Instead of doing a linear scan, it's much more efficient to do a binary search, which is what this -// solution does. -// -// COMPLEXITY: -// -// It's O(n) to create the prefix sum array. Then O(n) to perform the linear scan or O(log n) to perform the binary -// search. -// -// The space complexity is O(n) to store the prefix sum array. -class Solution { - private sums: number[]; - private total: number; - - constructor(w: number[]) { - this.sums = []; - this.total = 0; - - for (let i = 0; i < w.length; i++) { - this.total += w[i]; - this.sums[i] = this.total; - } - } - - pickIndex(): number { - // Pick a random number between [0, total). Doesn't have to be an integer at all; it's just a number between 0 and - // the total. - const target = Math.random() * this.total; - - // Use insertion point binary search instead of exact match. We aren't looking for an exact match of the element in - // the array. In fact, it's unlikely to even be in the array. Instead we'll use insertion point binary search - // which is more conceptually appropriate. - let left = 0; - let right = this.sums.length; - while (left < right) { - const mid = Math.floor((left + right) / 2); - if (this.sums[mid] < target) { - left = mid + 1; - } else { - right = mid; - } - } - - // The "insertion point" is the index that corresponds to the random number. - return left; - } -} From 5b7341fd6b6dcc0f0fd27d6e8798ee9de701c2e3 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 17:53:46 -0700 Subject: [PATCH 087/158] rangesum --- .../prefix_sum/range-sum-query-immutable.ts | 44 ------------------- .../prefix_sum/range_sum_query_immutable.py | 44 +++++++++++++++++++ .../range-sum-query-immutable.test.ts | 11 ----- .../range_sum_query_immutable_test.py | 9 ++++ 4 files changed, 53 insertions(+), 55 deletions(-) delete mode 100644 src/leetcode/prefix_sum/range-sum-query-immutable.ts create mode 100644 src/leetcode/prefix_sum/range_sum_query_immutable.py delete mode 100644 test/leetcode/prefix_sum/range-sum-query-immutable.test.ts create mode 100644 test/leetcode/prefix_sum/range_sum_query_immutable_test.py diff --git a/src/leetcode/prefix_sum/range-sum-query-immutable.ts b/src/leetcode/prefix_sum/range-sum-query-immutable.ts deleted file mode 100644 index ab548d0..0000000 --- a/src/leetcode/prefix_sum/range-sum-query-immutable.ts +++ /dev/null @@ -1,44 +0,0 @@ -// DIFFICULTY: EASY -// -// Given an integer array nums, handle multiple queries of the following type: -// -// Calculate the sum of the elements of nums between indices left and right inclusive where left <= right. -// Implement the NumArray class: -// -// - NumArray(int[] nums) Initializes the object with the integer array nums. -// - int sumRange(int left, int right) Returns the sum of the elements of nums between indices left and right inclusive -// (i.e. nums[left] + nums[left + 1] + ... + nums[right]). -export { NumArray }; - -// SOLUTION: -// -// Implement a prefix sum array for efficient range queries. -class NumArray { - private readonly prefixSum: number[]; - - constructor(nums: number[]) { - this.prefixSum = []; - - for (let i = 0; i < nums.length; i++) { - if (i === 0) { - this.prefixSum[0] = nums[0]; - continue; - } - - this.prefixSum[i] = this.prefixSum[i - 1] + nums[i]; - } - } - - sumRange(left: number, right: number): number { - // This is the sum of the elements from 0 to right, inclusive. - const rvalue = this.prefixSum[right]; - - // We'll want to subtract out the sum of elements from 0 to left, excluding the left element. This means we should - // calculate the sum from 0 to left - 1, giving us prefixSum[left - 1]. - // - // If the left index is 0, then we don't need to subtract anything, so the value is 0. - const lvalue = left > 0 ? this.prefixSum[left - 1] : 0; - - return rvalue - lvalue; - } -} diff --git a/src/leetcode/prefix_sum/range_sum_query_immutable.py b/src/leetcode/prefix_sum/range_sum_query_immutable.py new file mode 100644 index 0000000..50ba1c8 --- /dev/null +++ b/src/leetcode/prefix_sum/range_sum_query_immutable.py @@ -0,0 +1,44 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given an integer array nums, handle multiple queries of the following type: +# +# Calculate the sum of the elements of nums between indices left and right inclusive where left <= right. +# Implement the NumArray class: +# +# - NumArray(int[] nums) Initializes the object with the integer array nums. +# - int sumRange(int left, int right) Returns the sum of the elements of nums between indices left and right inclusive +# (i.e. nums[left] + nums[left + 1] + ... + nums[right]). +# +# See https://leetcode.com/problems/range-sum-query-immutable +from itertools import accumulate + + +class NumArray: + def __init__(self, nums: list[int]) -> None: + """ + SOLUTION + -------- + + This can be done with a prefix sum array. + + COMPLEXITY + ---------- + + Time complexity is O(n) because we are iterating through the list once. + + Space complexity is O(n) because we are storing the prefix sum array. + """ + self.prefix_sum = list(accumulate(nums)) + + def sumRange(self, left: int, right: int) -> int: + # This is the sum of the elements from 0 to right, inclusive. + rvalue = self.prefix_sum[right] + + # We'll want to subtract out the sum of elements from 0 to left, excluding the left element. This means we + # should calculate the sum from 0 to left - 1, giving us prefixSum[left - 1]. + # + # If the left index is 0, then we don't need to subtract anything, so the value is 0. + lvalue = self.prefix_sum[left - 1] if left > 0 else 0 + + return rvalue - lvalue diff --git a/test/leetcode/prefix_sum/range-sum-query-immutable.test.ts b/test/leetcode/prefix_sum/range-sum-query-immutable.test.ts deleted file mode 100644 index d50c2d8..0000000 --- a/test/leetcode/prefix_sum/range-sum-query-immutable.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { NumArray } from '../../src/prefix-sum/range-sum-query-immutable'; - -describe('range sum query immutable', () => { - test('range sum query immutable - test case 1', async () => { - const arr = new NumArray([-2, 0, 3, -5, 2, -1]); - - expect(arr.sumRange(0, 2)).toBe(1); - expect(arr.sumRange(2, 5)).toBe(-1); - expect(arr.sumRange(0, 5)).toBe(-3); - }); -}); diff --git a/test/leetcode/prefix_sum/range_sum_query_immutable_test.py b/test/leetcode/prefix_sum/range_sum_query_immutable_test.py new file mode 100644 index 0000000..14e3ea9 --- /dev/null +++ b/test/leetcode/prefix_sum/range_sum_query_immutable_test.py @@ -0,0 +1,9 @@ +from leetcode.prefix_sum.range_sum_query_immutable import NumArray + + +def test_case_1(): + arr = NumArray([-2, 0, 3, -5, 2, -1]) + assert arr.sumRange(0, 2) == 1 + assert arr.sumRange(2, 5) == -1 + assert arr.sumRange(0, 5) == -3 + assert arr.sumRange(0, 0) == -2 From 2bfedb4abc75c874c59b7e54b412fca98299c12d Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 18:01:02 -0700 Subject: [PATCH 088/158] subarray sum --- .../prefix_sum/subarray-sum-equals-k.ts | 61 ----------------- .../prefix_sum/subarray_sum_equals_k.py | 68 +++++++++++++++++++ .../prefix_sum/subarray-sum-equals-k.test.ts | 7 -- .../prefix_sum/subarray_sum_equals_k_test.py | 8 +++ 4 files changed, 76 insertions(+), 68 deletions(-) delete mode 100644 src/leetcode/prefix_sum/subarray-sum-equals-k.ts create mode 100644 src/leetcode/prefix_sum/subarray_sum_equals_k.py delete mode 100644 test/leetcode/prefix_sum/subarray-sum-equals-k.test.ts create mode 100644 test/leetcode/prefix_sum/subarray_sum_equals_k_test.py diff --git a/src/leetcode/prefix_sum/subarray-sum-equals-k.ts b/src/leetcode/prefix_sum/subarray-sum-equals-k.ts deleted file mode 100644 index 8badf01..0000000 --- a/src/leetcode/prefix_sum/subarray-sum-equals-k.ts +++ /dev/null @@ -1,61 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an array of integers nums and an integer k, return the total number of subarrays whose sum equals to k. -// -// A subarray is a contiguous non-empty sequence of elements within an array. -// -// See {@link https://leetcode.com/problems/subarray-sum-equals-k/} -export { subarraySum }; - -// SOLUTION: -// -// Many fixed size subarray problems can be solved using the sliding window technique. Here, if we ONLY had positive -// numbers, we could use the sliding window technique to find subarrays that sum to k. However, there could be negative -// numbers and zeroes, so we can't do it. -// -// The brute force approach is to generate all subarrays and check if the sum equals k. This is O(n^2) time complexity. -// Instead, we'll use a prefix sum and hashmap to reduce the time complexity to O(n). -// -// - Use a prefix sum to track sums up to each index. -// - Use a hashmap to track the frequency of each sum. -// - Check if a prefix sum difference equals k to find valid subarrays. -// -// That is, if prefix[j] - prefix[i] = k, then we know that a subarray from (i, j] has a sum of k. Rearranging, we get -// then prefix[j] = prefix[i] - k. If prefix[i] exists in our map, we know at least one subarray exists that sums to k. -function subarraySum(nums: number[], k: number): number { - // This is the number of subarrays that sum to k. - let result = 0; - - // This is prefix sum; it's not actually necessary to create an array to store the prefix sum. We can just keep a - // running sum. - let pj = 0; - - // This is our map of prefix sums to their frequency. - // - // Note that we do need to maintain a map here, instead of just a set of prefix sums. This is because the array could - // have negative numbers, repeated values, and zeroes. This would cause prefix sums to repeat. - const map = new Map(); - - // This is a critical step; don't forget to seed the map with a prefix sum of 0 with frequency 1. Without this seed, - // we'd miss subarrays that start at the beginning of the array. - map.set(0, 1); - - for (const num of nums) { - // This is the our running sum. - pj += num; - - // If prefix[j] - prefix[i] = k, then we know for sure that the subarray from (i, j] has a sum of k. We can compute - // prefix[i] = prefix[j] - k. - const pi = pj - k; - - // If prefix[j] appears in the frequency map, we have at LEAST one subarray that sums to k. Add the total frequency - // to the result. - result += map.get(pi) ?? 0; - - // Now update the frequency map to account for this new prefix sum up to j. - const freq = map.get(pj) ?? 0; - map.set(pj, freq + 1); - } - - return result; -} diff --git a/src/leetcode/prefix_sum/subarray_sum_equals_k.py b/src/leetcode/prefix_sum/subarray_sum_equals_k.py new file mode 100644 index 0000000..2f24f06 --- /dev/null +++ b/src/leetcode/prefix_sum/subarray_sum_equals_k.py @@ -0,0 +1,68 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an array of integers nums and an integer k, return the total number of subarrays whose sum equals to k. +# +# A subarray is a contiguous non-empty sequence of elements within an array. +# +# See https://leetcode.com/problems/subarray-sum-equals-k +class Solution: + def subarraySum(self, nums: list[int], k: int) -> int: + """ + SOLUTION + -------- + + Many fixed size subarray problems can be solved using the sliding window technique. Here, if we ONLY had + positive numbers, we could use the sliding window technique to find subarrays that sum to k. However, there + could be negative numbers and zeroes, so we can't do it. + + The brute force approach is to generate all subarrays and check if the sum equals k. This is O(n^2) time + complexity. Instead, we'll use a prefix sum and hashmap to reduce the time complexity to O(n). + + - Use a prefix sum to track sums up to each index. + - Use a hashmap to track the frequency of each sum. + - Check if a prefix sum difference equals k to find valid subarrays. + + That is, if prefix[j] - prefix[i] = k, then we know that a subarray from (i, j] has a sum of k. Rearranging, we + get then prefix[j] = prefix[i] - k. If prefix[i] exists in our map, we know at least one subarray exists that + sums to k. + + COMPLEXITY + ---------- + + Time complexity is O(n) because we are iterating through the list once. + + Space complexity is O(n) because we are storing the prefix sum array. + """ + # This is the number of subarrays that sum to k. + result = 0 + + # This is prefix sum; it's not actually necessary to create an array to store the prefix sum. We can just keep + # a running sum. + pj = 0 + + # This is our map of prefix sums to their frequency. + # + # Note that we do need to maintain a map here, instead of just a set of prefix sums. This is because the array + # could have negative numbers, repeated values, and zeroes. This would cause prefix sums to repeat. + # + # Don't forget to seed the map with a prefix sum of 0 with frequency 1. Without this seed, we'd miss subarrays + # that start at the beginning of the array. + map: dict[int, int] = {0: 1} + + for num in nums: + pj += num + + # If prefix[j] - prefix[i] = k, then we know for sure that the subarray from (i, j] has a sum of k. We can + # compute prefix[i] = prefix[j] - k. + pi = pj - k + + # If prefix[j] appears in the frequency map, we have at LEAST one subarray that sums to k. Add the total + # frequency to the result. + result += map.get(pi, 0) + + # Now update the frequency map to account for this new prefix sum up to j. + freq = map.get(pj, 0) + map[pj] = freq + 1 + + return result diff --git a/test/leetcode/prefix_sum/subarray-sum-equals-k.test.ts b/test/leetcode/prefix_sum/subarray-sum-equals-k.test.ts deleted file mode 100644 index 91be976..0000000 --- a/test/leetcode/prefix_sum/subarray-sum-equals-k.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { subarraySum } from '../../src/prefix-sum/subarray-sum-equals-k'; - -describe('subarray sum equals k', () => { - test('subarray sum equals k', async () => { - expect(subarraySum([1, 1, 1], 2)).toBe(2); - }); -}); diff --git a/test/leetcode/prefix_sum/subarray_sum_equals_k_test.py b/test/leetcode/prefix_sum/subarray_sum_equals_k_test.py new file mode 100644 index 0000000..cb869c2 --- /dev/null +++ b/test/leetcode/prefix_sum/subarray_sum_equals_k_test.py @@ -0,0 +1,8 @@ +from leetcode.prefix_sum.subarray_sum_equals_k import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.subarraySum([1, 1, 1], 2) == 2 From b55355be4ac218e5a4840b1f4ff63c51fa25946e Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 21:15:31 -0700 Subject: [PATCH 089/158] max operations --- ...umber-of-operations-with-the-same-score.ts | 92 ------------------ ...umber_of_operations_with_the_same_score.py | 94 +++++++++++++++++++ ...-of-operations-with-the-same-score.test.ts | 15 --- ..._of_operations_with_the_same_score_test.py | 16 ++++ 4 files changed, 110 insertions(+), 107 deletions(-) delete mode 100644 src/leetcode/recursion/maximum-number-of-operations-with-the-same-score.ts create mode 100644 src/leetcode/recursion/maximum_number_of_operations_with_the_same_score.py delete mode 100644 test/leetcode/recursion/maximum-number-of-operations-with-the-same-score.test.ts create mode 100644 test/leetcode/recursion/maximum_number_of_operations_with_the_same_score_test.py diff --git a/src/leetcode/recursion/maximum-number-of-operations-with-the-same-score.ts b/src/leetcode/recursion/maximum-number-of-operations-with-the-same-score.ts deleted file mode 100644 index 897a669..0000000 --- a/src/leetcode/recursion/maximum-number-of-operations-with-the-same-score.ts +++ /dev/null @@ -1,92 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an array of integers called nums, you can perform any of the following operation while nums contains at least -// 2 elements: -// -// - Choose the first two elements of nums and delete them. -// - Choose the last two elements of nums and delete them. -// - Choose the first and the last elements of nums and delete them. -// -// The score of the operation is the sum of the deleted elements. -// -// Your task is to find the maximum number of operations that can be performed, such that all operations have the same -// score. -// -// Return the maximum number of operations possible that satisfy the condition mentioned above. -// -// See {@link https://leetcode.com/problems/maximum-number-of-operations-with-the-same-score-ii/} -export { maxOperations }; - -// SOLUTION: -// -// The range of possible scores is determined by applying the operation once. Subsequent operations must produce the -// same score, so there are only three possible operations. For each operation, we should apply them, then follow -// paths of operations that result in the same score until the array is exhausted. -// -// We COULD use the sliding window technique by applying each operation and then only following the operation paths -// that have the same score as the one we are considering. However, this would only work if at each choice of three -// operations, the scores are unique. If there are two or more choices with the same score, then we need to explore -// multiple operation paths at once. -// -// To explore multiple operation paths at once, we'll need to use a backtracking approach. -function maxOperations(nums: number[]): number { - if (nums.length < 2) { - return 0; - } - - // These are the range of possible scores we can have as we truncate the array. - const scores = new Set(); - scores.add(nums[0] + nums[1]); - scores.add(nums[nums.length - 1] + nums[nums.length - 2]); - scores.add(nums[0] + nums[nums.length - 1]); - - // There is an opportunity for optimization when using backtracking. We can maintain a memoization table to keep - // track of previously computed values. - const memo = new Map(); - - // Adapt the sliding window approach so we can explore multiple paths at once by backtracking. - function backtrack(score: number, left: number, right: number, paths: number): number { - if (left >= right) { - return paths; - } - - // Check the memoization table to see if we've previously computed this value. - const key = `${score},${left},${right}`; - if (memo.has(key)) { - return memo.get(key)!; - } - - // This is the local max count, after exploring all possible paths. - let local = paths; - - // Check if the first 2 elements are equal to the score, and if the left and right indices are in range. - // - // To be in range, the array must at least look like this: [left, left + 1/right]. - if (left + 1 <= right && nums[left] + nums[left + 1] === score) { - local = Math.max(local, backtrack(score, left + 2, right, paths + 1)); - } - - // Check if the last 2 elements are equal to the score, and if the left and right indices are in range. - // - // To be in range, the array must at least look like this: [left/right - 1, right] - if (left <= right - 1 && nums[right] + nums[right - 1] === score) { - local = Math.max(local, backtrack(score, left, right - 2, paths + 1)); - } - - // Check if the first and last elements are equal to the score, and if the left and right indices are in range. - // - // To be in range, the array must at least look like this: [left, right] - if (left < right && nums[left] + nums[right] === score) { - local = Math.max(local, backtrack(score, left + 1, right - 1, paths + 1)); - } - - // Cache the result for a future computation. - memo.set(key, local); - return local; - } - - // For each score, check how many times we can keep applying operations. To perform a map, we' - const list = [...scores]; - const maxes = list.map(score => backtrack(score, 0 /* left */, nums.length - 1 /* right */, 0 /* paths */)); - return maxes.reduce((a, b) => Math.max(a, b)); -} diff --git a/src/leetcode/recursion/maximum_number_of_operations_with_the_same_score.py b/src/leetcode/recursion/maximum_number_of_operations_with_the_same_score.py new file mode 100644 index 0000000..50a0cc5 --- /dev/null +++ b/src/leetcode/recursion/maximum_number_of_operations_with_the_same_score.py @@ -0,0 +1,94 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an array of integers called nums, you can perform any of the following operation while nums contains at least +# 2 elements: +# +# - Choose the first two elements of nums and delete them. +# - Choose the last two elements of nums and delete them. +# - Choose the first and the last elements of nums and delete them. +# +# The score of the operation is the sum of the deleted elements. +# +# Your task is to find the maximum number of operations that can be performed, such that all operations have the same +# score. +# +# Return the maximum number of operations possible that satisfy the condition mentioned above. +# +# See https://leetcode.com/problems/maximum-number-of-operations-with-the-same-score-ii +class Solution: + def maxOperations(self, nums: list[int]) -> int: + """ + SOLUTION + -------- + + The range of possible scores is determined by applying the operation once. Subsequent operations must produce + the same score, so there are only three possible operations. For each operation, we should apply them, then + follow paths of operations that result in the same score until the array is exhausted. + + We COULD use the sliding window technique by applying each operation and then only following the operation paths + that have the same score as the one we are considering. However, this would only work if at each choice of + three operations, the scores are unique. If there are two or more choices with the same score, then we need to + explore multiple operation paths at once. + + To explore multiple operation paths at once, we'll need to use a backtracking approach. + + COMPLEXITY + ---------- + + Time complexity is O(3^n) because we are exploring all possible operations. + + Space complexity is O(n) because we are storing the operations in stack frames. + """ + if len(nums) < 2: + return 0 + + # These are the range of possible scores we can have as we truncate the array. + scores: set[int] = set() + scores.add(nums[0] + nums[1]) + scores.add(nums[0] + nums[-1]) + scores.add(nums[-1] + nums[-2]) + + # There is an opportunity for optimization when using backtracking. We can maintain a memoization table to keep + # track of previously computed values. + memo: dict[tuple[int, int, int], int] = {} + + # Adapt the sliding window approach so we can explore multiple paths at once by backtracking. + def backtrack(score: int, left: int, right: int, paths: int) -> int: + if left >= right: + return paths + + # Check the memoization table to see if we've previously computed this value. + if (score, left, right) in memo: + return memo[(score, left, right)] + + # This is the local max count, after exploring all possible paths. + local = paths + + # Check if the first 2 elements are equal to the score, and if the left and right indices are in range. + # + # To be in range, the array must at least look like this: [left, left + 1/right]. + if left + 1 <= right and nums[left] + nums[left + 1] == score: + local = max(local, backtrack(score, left + 2, right, paths + 1)) + + # Check if the last 2 elements are equal to the score, and if the left and right indices are in range. + # + # To be in range, the array must at least look like this: [left/right - 1, right] + if left <= right - 1 and nums[right] + nums[right - 1] == score: + local = max(local, backtrack(score, left, right - 2, paths + 1)) + + # Check if the first and last elements are equal to the score, and if the left and right indices are in + # range. + # + # To be in range, the array must at least look like this: [left, right] + if left < right and nums[left] + nums[right] == score: + local = max(local, backtrack(score, left + 1, right - 1, paths + 1)) + + # Update the memoization table. + memo[(score, left, right)] = local + return local + + # For each score, check out how many times we can keep applying operations. + xs = list(scores) + maxes = [backtrack(x, 0, len(nums) - 1, 0) for x in xs] + return max(maxes) diff --git a/test/leetcode/recursion/maximum-number-of-operations-with-the-same-score.test.ts b/test/leetcode/recursion/maximum-number-of-operations-with-the-same-score.test.ts deleted file mode 100644 index 02af347..0000000 --- a/test/leetcode/recursion/maximum-number-of-operations-with-the-same-score.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { maxOperations } from '../../src/recursion/maximum-number-of-operations-with-the-same-score'; - -describe('maximum number of operations with the same score', () => { - test('max operations - test case 1', async () => { - expect(maxOperations([3, 2, 1, 2, 3, 4])).toBe(3); - }); - - test('max operations - test case 2', async () => { - expect(maxOperations([3, 2, 6, 1, 4])).toBe(2); - }); - - test('max operations - test case 3', async () => { - expect(maxOperations([3, 2, 1, 4, 1])).toBe(2); - }); -}); diff --git a/test/leetcode/recursion/maximum_number_of_operations_with_the_same_score_test.py b/test/leetcode/recursion/maximum_number_of_operations_with_the_same_score_test.py new file mode 100644 index 0000000..43bbf7f --- /dev/null +++ b/test/leetcode/recursion/maximum_number_of_operations_with_the_same_score_test.py @@ -0,0 +1,16 @@ +from leetcode.recursion.maximum_number_of_operations_with_the_same_score import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.maxOperations([3, 2, 1, 2, 3, 4]) == 3 + + +def test_case_2(): + assert soln.maxOperations([3, 2, 6, 1, 4]) == 2 + + +def test_case_3(): + assert soln.maxOperations([3, 2, 1, 4, 1]) == 2 From 02721e7cd8afaea2ef365a1877fd1619bce4ac52 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 21:37:56 -0700 Subject: [PATCH 090/158] optimnal --- .../recursion/optimal-account-balancing.ts | 95 ---------------- .../recursion/optimal_account_balancing.py | 107 ++++++++++++++++++ .../optimal-account-balancing.test.ts | 34 ------ .../optimal_account_balancing_test.py | 16 +++ 4 files changed, 123 insertions(+), 129 deletions(-) delete mode 100644 src/leetcode/recursion/optimal-account-balancing.ts create mode 100644 src/leetcode/recursion/optimal_account_balancing.py delete mode 100644 test/leetcode/recursion/optimal-account-balancing.test.ts create mode 100644 test/leetcode/recursion/optimal_account_balancing_test.py diff --git a/src/leetcode/recursion/optimal-account-balancing.ts b/src/leetcode/recursion/optimal-account-balancing.ts deleted file mode 100644 index 9223c03..0000000 --- a/src/leetcode/recursion/optimal-account-balancing.ts +++ /dev/null @@ -1,95 +0,0 @@ -// DIFFICULTY: HARD -// -// You are given an array of transactions transactions where transactions[i] = [fromi, toi, amounti] indicates that the -// person with ID = fromi gave amounti $ to the person with ID = toi. -// -// Return the minimum number of transactions required to settle the debt. -// -// See {@link https://leetcode.com/problems/optimal-account-balancing/} -export { minTransfers }; - -// SOLUTION: -function minTransfers(transactions: number[][]): number { - type Person = number; - type Amount = number; - const map = new Map(); - - // First, figure out the balance that each person holds. - for (const transaction of transactions) { - const [from, to, amount] = transaction; - map.set(from, (map.get(from) ?? 0) - amount); - map.set(to, (map.get(to) ?? 0) + amount); - } - - // Second, figure out all non-zero balances. If we needed to know who is paying whom to achieve the minimum number - // of transactions, we will need to have an array indexed by person. However, we don't need to know that. - // - // We just need to know the number of transactions, so we can just get a list of non-zero balances and try to zero - // them out. - const balances: number[] = []; - for (const value of map.values()) { - if (value !== 0) { - balances.push(value); - } - } - - // To settle debts efficiently, we should should match opposite balances together, and have them settle with each - // other. For example, if somebody has +5 balance, we should match then up with a -5 balance. Matching two - // balances with the same sign together will just result in extra transactions. - // - // Therefore, we should examine sequences of pairs that work towards an all zero balance sheet. However, different - // sequences of pairs could result in different numbers of transactions. So we will need to examine *all* possible - // pairs and keep track of the minimum tx's across all sequences. - // - // To do this effectively we need to use a recursive backtracking technique by trying to settle the ith balance - // with i+1, i+2, i+3, etc and seeing how many tx's we incur at each step. - function settle(left: number) { - // Skip over the already settled balances; if we've skipped over all balances, the minimum number of tx's is 0. - while (left < balances.length && balances[left] === 0) { - left++; - } - - if (left === balances.length) { - return 0; - } - - // Try to settle the ith balance with the first opposite signed balance. - let min = Infinity; - for (let right = left + 1; right < balances.length; right++) { - // These balances were either both positive, or both negative, so do not bother executing a transaction with - // them. - if (balances[left] * balances[right] > 0) { - continue; - } - - // Attempt to settle (i, j) by adding the ith balance to the jth balance. Technically we don't have to - // decrement balance[i] at all, since the next call to settle will start at j (and i will be skipped). But it - // is included here for clarity. - // - // Note that we also do not have to ensure that we only add the minimum amount to balance. For example, if - // balances[i] = 10 and balances[j] = -5, we don't need to only add 5 to balances[j]. As we recursively move - // down the list, balances[j] will eventually zero out (and a more efficient path may be discovered). - // - // Also, we are guaranteed that the balances *do* eventually zero out because there isn't any extra money in - // the system. - const value = balances[left]; - balances[right] += value; - balances[left] = 0; - - // The minimum number of transactions, if we've settled the ith balance, is 1 + the minimum after settling the - // i+1th balance. - // - // As a further optimization we can use dynamic programming to avoid redundant calculations. To do so we'll - // need to use a bit mask to keep track of balance state. - min = Math.min(min, 1 + settle(left + 1)); - - // Backtrack by restoring the balances. - balances[right] -= value; - balances[left] = value; - } - - return min; - } - - return settle(0); -} diff --git a/src/leetcode/recursion/optimal_account_balancing.py b/src/leetcode/recursion/optimal_account_balancing.py new file mode 100644 index 0000000..1b3fcbb --- /dev/null +++ b/src/leetcode/recursion/optimal_account_balancing.py @@ -0,0 +1,107 @@ +# DIFFICULTY: HARD +# ---------------- +# +# You are given an array of transactions transactions where transactions[i] = [fromi, toi, amounti] indicates that the +# person with ID = fromi gave amounti $ to the person with ID = toi. +# +# Return the minimum number of transactions required to settle the debt. +# +# See https://leetcode.com/problems/optimal-account-balancing +import math + + +class Solution: + def minTransfers(self, transactions: list[list[int]]) -> int: + """ + SOLUTION + -------- + + We can model this problem as a graph where each person is a node and the amount they owe or are owed is the + weight of the edge between them. The goal is to find the minimum number of transactions to settle all debts. + + We can use a recursive backtracking algorithm to find the minimum number of transactions. We will iterate + through each person and try to settle their debt with every other person. If the debt is settled, we will move + on to the next person. If the debt is not settled, we will try to settle the debt with another person. + + COMPLEXITY + ---------- + + Time complexity is O(2^n) because we are iterating through all possible transactions. + + Space complexity is O(n) because we are using recursion to solve the problem. + """ + # Create a map of person -> amount. + map: dict[int, int] = {} + + # First, figure out the balance that each person holds. + for transaction in transactions: + [from_person, to_person, amount] = transaction + map[from_person] = map.get(from_person, 0) - amount + map[to_person] = map.get(to_person, 0) + amount + + # Second, figure out all non-zero balances. If we needed to know who is paying whom to achieve the minimum + # number of transactions, we will need to have an array indexed by person. However, we don't need to know that. + # + # We just need to know the number of transactions, so we can just get a list of non-zero balances and try to + # zero them out. + balances: list[int] = [] + for value in map.values(): + if value != 0: + balances.append(value) + + # To settle debts efficiently, we should should match opposite balances together, and have them settle with each + # other. For example, if somebody has +5 balance, we should match then up with a -5 balance. Matching two + # balances with the same sign together will just result in extra transactions. + # + # Therefore, we should examine sequences of pairs that work towards an all zero balance sheet. However, + # different sequences of pairs could result in different numbers of transactions. So we will need to examine + # *all* possible pairs and keep track of the minimum tx's across all sequences. + # + # To do this effectively we need to use a recursive backtracking technique by trying to settle the ith balance + # with i+1, i+2, i+3, etc and seeing how many tx's we incur at each step. + def settle(left: int): + # Skip over the already settled balances; if we've skipped over all balances, the minimum number of tx's is + # zero. + while left < len(balances) and balances[left] == 0: + left += 1 + + if left == len(balances): + return 0 + + # Try to settle the ith balance with the first opposite signed balance. + min_transactions = math.inf + for right in range(left + 1, len(balances)): + # These balances were either both positive, or both negative, so do not bother executing a transaction + # with them. + if balances[left] * balances[right] > 0: + continue + + # Attempt to settle (i, j) by adding the ith balance to the jth balance. Technically we don't have to + # decrement balance[i] at all, since the next call to settle will start at j (and i will be skipped). + # But it is included here for clarity. + # + # Note that we also do not have to ensure that we only add the minimum amount to balance. For example, + # if # balances[i] = 10 and balances[j] = -5, we don't need to only add 5 to balances[j]. As we + # recursively move down the list, balances[j] will eventually zero out (and a more efficient path may be + # discovered). + # + # Also, we are guaranteed that the balances *do* eventually zero out because there isn't any extra money + # in the system. + value = balances[left] + balances[right] += value + balances[left] = 0 + + # The minimum number of transactions, if we've settled the ith balance, is 1 + the minimum after + # settling the i+1th balance. + # + # As a further optimization we can use dynamic programming to avoid redundant calculations. To do so + # we'll need to use a bit mask to keep track of balance state. + min_transactions = min(min_transactions, 1 + settle(left + 1)) + + # Backtrack by undoing the transaction. + balances[right] -= value + balances[left] = value + + return min_transactions + + return int(settle(0)) diff --git a/test/leetcode/recursion/optimal-account-balancing.test.ts b/test/leetcode/recursion/optimal-account-balancing.test.ts deleted file mode 100644 index c19f855..0000000 --- a/test/leetcode/recursion/optimal-account-balancing.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { minTransfers } from '../../src/recursion/optimal-account-balancing'; - -describe('optimal account balancing', () => { - test('min transfers - test case 1', async () => { - expect( - minTransfers([ - [0, 1, 10], - [2, 0, 5] - ]) - ).toBe(2); - }); - - test('min transfers - test case 2', async () => { - expect( - minTransfers([ - [0, 1, 10], - [1, 0, 1], - [1, 2, 5], - [2, 0, 5] - ]) - ).toBe(1); - }); - - test('min transfers - test case 3', async () => { - expect( - minTransfers([ - [0, 1, 1], - [1, 2, 1], - [2, 3, 4], - [3, 4, 5] - ]) - ).toBe(3); - }); -}); diff --git a/test/leetcode/recursion/optimal_account_balancing_test.py b/test/leetcode/recursion/optimal_account_balancing_test.py new file mode 100644 index 0000000..1c152e8 --- /dev/null +++ b/test/leetcode/recursion/optimal_account_balancing_test.py @@ -0,0 +1,16 @@ +from leetcode.recursion.optimal_account_balancing import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.minTransfers([[0, 1, 10], [2, 0, 5]]) == 2 + + +def test_case_2(): + assert soln.minTransfers([[0, 1, 10], [1, 0, 1], [1, 2, 5], [2, 0, 5]]) == 1 + + +def test_case_3(): + assert soln.minTransfers([[0, 1, 1], [1, 2, 1], [2, 3, 4], [3, 4, 5]]) == 3 From 34f1b73a560c6129bc5b62252d26d89ba4bb648b Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 21:46:12 -0700 Subject: [PATCH 091/158] permutations --- src/leetcode/recursion/permutations.py | 43 ++++++++++++++++++++ src/leetcode/recursion/permutations.ts | 43 -------------------- test/leetcode/recursion/permutations.test.ts | 19 --------- test/leetcode/recursion/permutations_test.py | 20 +++++++++ 4 files changed, 63 insertions(+), 62 deletions(-) create mode 100644 src/leetcode/recursion/permutations.py delete mode 100644 src/leetcode/recursion/permutations.ts delete mode 100644 test/leetcode/recursion/permutations.test.ts create mode 100644 test/leetcode/recursion/permutations_test.py diff --git a/src/leetcode/recursion/permutations.py b/src/leetcode/recursion/permutations.py new file mode 100644 index 0000000..8c88521 --- /dev/null +++ b/src/leetcode/recursion/permutations.py @@ -0,0 +1,43 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order. +# +# See https://leetcode.com/problems/permutations +class Solution: + def permute(self, nums: list[int]) -> list[list[int]]: + """ + SOLUTION + -------- + + To do this, recursively generate all permutations of elements without the head. Then take all permutations + created and add x to the front of the list. This should result in n! number of arrays. + + The general strategy is to split the list into a head and a tail. Generate all the permutations of the list + without the head element, then put the head in front of each of the generated permutations. Do this recursively + for each element in the list will give all permutations. + + COMPLEXITY + ---------- + + Time complexity is O(n!) where n is the number of elements in the list. + + Space complexity is O(n) because we are storing the permutations in stack frames. + """ + if not nums: + return [] + + if len(nums) == 1: + return [nums] + + result: list[list[int]] = [] + for i, x in enumerate(nums): + # Generate all permutations of the list without x. + without = nums[:i] + nums[i + 1 :] + ps = self.permute(without) + + # For each of the permutations, put the ith element in front of the permutations. + for p in ps: + result.append([x] + p) + + return result diff --git a/src/leetcode/recursion/permutations.ts b/src/leetcode/recursion/permutations.ts deleted file mode 100644 index 511c729..0000000 --- a/src/leetcode/recursion/permutations.ts +++ /dev/null @@ -1,43 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order. -// -// See {@link https://leetcode.com/problems/permutations/} -export { permute }; - -// SOLUTION: -// -// To do this, recursively generate all permutations of elements without the head. Then take all permutations created -// and add x to the front of the list. This should result in n! number of arrays. -// -// The general strategy is to split the list into a head and a tail. Generate all the permutations of the list -// without the head element, then put the head in front of each of the generated permutations. Do this recursively -// for each element in the list will give all permutations. -function permute(xs: number[]): number[][] { - if (xs.length === 0) { - return [[]]; - } - - if (xs.length === 1) { - return [xs]; - } - - const result = []; - - for (let i = 0; i < xs.length; i += 1) { - // Get the ith element; we will remove this element from the array, and generate permutations without this - // element. - const x = xs[i]; - - // Generates all permutations without the ith element. - const without = [...xs.slice(0, i), ...xs.slice(i + 1, xs.length)]; - const ps = permute(without); - - // For each of the permutations, put the ith element in front of the permutations, to get all possible - // permutations. - ps.forEach(p => p.unshift(x)); - result.push(...ps); - } - - return result; -} diff --git a/test/leetcode/recursion/permutations.test.ts b/test/leetcode/recursion/permutations.test.ts deleted file mode 100644 index b28985a..0000000 --- a/test/leetcode/recursion/permutations.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { permute } from '../../src/recursion/permutations'; - -describe('permute', () => { - test('permute - test case 1', async () => { - expect(permute([])).toStrictEqual([[]]); - }); - - test('permute - test case 2', async () => { - expect(permute([1, 2, 3])).toMatchSnapshot(); - }); - - test('permute - test case 3', async () => { - expect(permute([0, 1])).toMatchSnapshot(); - }); - - test('permute - test case 4', async () => { - expect(permute([1])).toStrictEqual([[1]]); - }); -}); diff --git a/test/leetcode/recursion/permutations_test.py b/test/leetcode/recursion/permutations_test.py new file mode 100644 index 0000000..d870597 --- /dev/null +++ b/test/leetcode/recursion/permutations_test.py @@ -0,0 +1,20 @@ +from leetcode.recursion.permutations import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.permute([1, 2, 3]) == [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] + + +def test_case_2(): + assert soln.permute([0, 1]) == [[0, 1], [1, 0]] + + +def test_case_3(): + assert soln.permute([1]) == [[1]] + + +def test_case_4(): + assert soln.permute([]) == [] From 460142ed142453528a1641dd2ab81b2fb446ce3b Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 21:57:10 -0700 Subject: [PATCH 092/158] subsets --- src/leetcode/recursion/subsets.py | 46 +++++++++++++++++++++++++++++ src/leetcode/recursion/subsets.ts | 48 ------------------------------- 2 files changed, 46 insertions(+), 48 deletions(-) create mode 100644 src/leetcode/recursion/subsets.py delete mode 100644 src/leetcode/recursion/subsets.ts diff --git a/src/leetcode/recursion/subsets.py b/src/leetcode/recursion/subsets.py new file mode 100644 index 0000000..775dd2c --- /dev/null +++ b/src/leetcode/recursion/subsets.py @@ -0,0 +1,46 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an integer array nums of unique elements, return all possible subsets (the power set). +# +# The solution set must not contain duplicate subsets. Return the solution in any order. +# +# See https://leetcode.com/problems/subsets +class Solution: + def subsets(self, nums: list[int]) -> list[list[int]]: + """ + SOLUTION + -------- + To do this, recursively generate all combinations of elements of the set `xs - x`, then take all the subsets + created and add x to them. This should result in 2^n number of subsets. + + COMPLEXITY + ---------- + Time complexity is O(2^n) where n is the number of elements in the list. + + Space complexity is O(n) because we are storing the subsets in stack frames. + """ + + def generate(xs: set[int]) -> set[set[int]]: + if len(xs) == 0: + return set(set()) + + # Find the first element of xs and call it x; remove it from the set. + x = xs.pop() + + # Make a copy of xs without x and generate all subsets of that set. + rest = set(xs) + excludeds = generate(rest) + + # Now generate all subsets with x by adding x to each excluded subset. + includeds: set[set[int]] = set() + for excluded in excludeds: + included = set(excluded) + included.add(x) + includeds.add(included) + + # Combine the excluded and included subsets. + return set.union(excludeds, includeds) + + result = generate(set(nums)) + return [list(subset) for subset in result] diff --git a/src/leetcode/recursion/subsets.ts b/src/leetcode/recursion/subsets.ts deleted file mode 100644 index 46af7f3..0000000 --- a/src/leetcode/recursion/subsets.ts +++ /dev/null @@ -1,48 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an integer array nums of unique elements, return all possible subsets (the power set). -// -// The solution set must not contain duplicate subsets. Return the solution in any order. -// -// See {@link https://leetcode.com/problems/subsets/} -export { subsets }; - -// SOLUTION: -// -// To do this, recursively generate all combinations of elements of the set `xs - x`, then take all the subsets -// created and add x to them. This should result in 2^n number of subsets. -function subsets(nums: number[]) { - function run(xs: Set): Set> { - if (xs.size === 0) { - // Note that new Set(new Set()) gives you a set with zero elements, because the constructor takes all elements of - // the first argument and adds them to the set. So you actually want to construct a new iterable based on the - // empty set. - return new Set([new Set()]); - } - - // Find the first element of xs and call it x; remove it from the list. - const x = xs.values().next().value!; - - // Generate all subsets without x. - const rest = new Set(xs); - rest.delete(x); - - const excluded: Set> = run(rest); - - // Add x to all of the subsets from above. - const included = new Set>(); - excluded.forEach(ex => { - const set = new Set(ex); - set.add(x); - - included.add(set); - }); - - // The subsets with x and the subsets without x constitute the power set. - return new Set([...excluded, ...included]); - } - - // LeetCode expects the result to be an array of arrays. - const result = run(new Set(nums)); - return [...result].map(xs => [...xs]); -} From 6f3e734de23044b782b4160f11f8fbbb0ddecea5 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Sun, 16 Mar 2025 22:14:00 -0700 Subject: [PATCH 093/158] subsets --- src/leetcode/recursion/subsets.py | 30 +++++++------- .../recursion/snapshots/snap_subsets_test.py | 39 +++++++++++++++++++ test/leetcode/recursion/subsets.test.ts | 15 ------- test/leetcode/recursion/subsets_test.py | 12 ++++++ 4 files changed, 66 insertions(+), 30 deletions(-) create mode 100644 test/leetcode/recursion/snapshots/snap_subsets_test.py delete mode 100644 test/leetcode/recursion/subsets.test.ts create mode 100644 test/leetcode/recursion/subsets_test.py diff --git a/src/leetcode/recursion/subsets.py b/src/leetcode/recursion/subsets.py index 775dd2c..7dc081c 100644 --- a/src/leetcode/recursion/subsets.py +++ b/src/leetcode/recursion/subsets.py @@ -21,26 +21,26 @@ def subsets(self, nums: list[int]) -> list[list[int]]: Space complexity is O(n) because we are storing the subsets in stack frames. """ - def generate(xs: set[int]) -> set[set[int]]: - if len(xs) == 0: - return set(set()) - - # Find the first element of xs and call it x; remove it from the set. - x = xs.pop() - - # Make a copy of xs without x and generate all subsets of that set. - rest = set(xs) + # Note that you cannot put a set inside of a set; sets are mutable and cannot be hashed. However, frozensets + # are immutable, so you can use those instead. + def generate(xs: set[int]) -> set[frozenset[int]]: + if not xs: + # Note that set(frozenset()) creates an empty set, not a set with an empty set in it. + return {frozenset()} + + # Find the first element of xs and call it x; remove it from the set and call it rest. + ys = list(xs) + x = ys[0] + rest = set(ys[1:]) + + # Generate all subsets of that set. excludeds = generate(rest) # Now generate all subsets with x by adding x to each excluded subset. - includeds: set[set[int]] = set() - for excluded in excludeds: - included = set(excluded) - included.add(x) - includeds.add(included) + includeds = {subset | {x} for subset in excludeds} # Combine the excluded and included subsets. - return set.union(excludeds, includeds) + return excludeds | includeds result = generate(set(nums)) return [list(subset) for subset in result] diff --git a/test/leetcode/recursion/snapshots/snap_subsets_test.py b/test/leetcode/recursion/snapshots/snap_subsets_test.py new file mode 100644 index 0000000..20e4d9b --- /dev/null +++ b/test/leetcode/recursion/snapshots/snap_subsets_test.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# snapshottest: v1 - https://goo.gl/zC4yUc +from __future__ import unicode_literals + +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots['test_case_1 1'] = set([ + frozenset([ + 2 + ]), + frozenset([ + 2, + 3 + ]), + frozenset([ + 1, + 2 + ]), + frozenset([ + 3 + ]), + frozenset([ + ]), + frozenset([ + 1 + ]), + frozenset([ + 1, + 2, + 3 + ]), + frozenset([ + 1, + 3 + ]) +]) diff --git a/test/leetcode/recursion/subsets.test.ts b/test/leetcode/recursion/subsets.test.ts deleted file mode 100644 index e5b1017..0000000 --- a/test/leetcode/recursion/subsets.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { subsets } from '../../src/recursion/subsets'; - -describe('subsets', () => { - test('subsets - test case 1', async () => { - expect(subsets([])).toStrictEqual([[]]); - }); - - test('subsets - test case 2', async () => { - expect(subsets([1, 2, 3])).toMatchSnapshot(); - }); - - test('subsets - test case 3', async () => { - expect(subsets([0])).toMatchSnapshot(); - }); -}); diff --git a/test/leetcode/recursion/subsets_test.py b/test/leetcode/recursion/subsets_test.py new file mode 100644 index 0000000..90c8c24 --- /dev/null +++ b/test/leetcode/recursion/subsets_test.py @@ -0,0 +1,12 @@ +from leetcode.recursion.subsets import Solution + + +soln = Solution() + + +def test_case_1(snapshot): + subsets = soln.subsets([1, 2, 3]) + + subsets = {frozenset(subset) for subset in subsets} + + snapshot.assert_match(subsets) From 4a308dbfb5b7110111d847bc88c130401bdc6732 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 17 Mar 2025 18:06:47 -0700 Subject: [PATCH 094/158] word break 2 --- src/leetcode/recursion/word-break-ii.ts | 70 ------------------- src/leetcode/recursion/word_break_ii.py | 70 +++++++++++++++++++ test/leetcode/recursion/word-break-ii.test.ts | 24 ------- test/leetcode/recursion/word_break_test.py | 20 ++++++ 4 files changed, 90 insertions(+), 94 deletions(-) delete mode 100644 src/leetcode/recursion/word-break-ii.ts create mode 100644 src/leetcode/recursion/word_break_ii.py delete mode 100644 test/leetcode/recursion/word-break-ii.test.ts create mode 100644 test/leetcode/recursion/word_break_test.py diff --git a/src/leetcode/recursion/word-break-ii.ts b/src/leetcode/recursion/word-break-ii.ts deleted file mode 100644 index d233de2..0000000 --- a/src/leetcode/recursion/word-break-ii.ts +++ /dev/null @@ -1,70 +0,0 @@ -// DIFFICULTY: HARD -// -// Given a string s and a dictionary of strings wordDict, add spaces in s to construct a sentence where each word is a -// valid dictionary word. Return all such possible sentences in any order. -// -// Note that the same word in the dictionary may be reused multiple times in the segmentation. -// -// See {@link https://leetcode.com/problems/word-break-ii/} -export { wordBreak }; - -// SOLUTION: -function wordBreak(s: string, wordDict: string[]): string[] { - const set = new Set(wordDict); - - // Since values can be re-calculated, let's memoize the results. - const memo = new Map(); - - // This generates all possible sentences using words that start at index i (if any exist) recursively. - // - // Since the only words we have available to us are the ones in the wordDict, we'll just have to loop through from - // i to the end of the string to see if any of those substrings form a 'word'. - function generate(i: number): string[] { - // Because we are slicing from [i, j), and slice is exclusive on the second index, we will be starting at i + 1. - // Therefore, do the bounds check for i === s.length. - if (i === s.length) { - return []; - } - - if (memo.has(i)) { - return memo.get(i)!; - } - - const sentences: string[] = []; - - // Attempt to generate sentences beginning with the 'word' from i to j. Note that we want to slice from i to j, - // so we actually want j to start at i + 1, and we also want to include j === s.length, because the slice function - // is inclusive for the first index, and exclusive for the second index. - for (let j = i + 1; j <= s.length; j++) { - const word = s.slice(i, j); - if (!set.has(word)) { - continue; - } - - // Now generate all possible sentences using words that start at index j (if any exist), and put the word from - // index i in front. - const rest = generate(j); - - // If the problem allowed us to generate sentences using a partial selection of characters (e.g. if we couldn't - // make more words at this point), we could just add the current word to the result, even if there were unused - // characters remaining. - // - // However, the problem does not allow us to do this, so we only add the current word if there are no unused - // characters remaining. - if (rest.length === 0 && j === s.length) { - sentences.push(word); - continue; - } - - // If we could generate words starting at j, prepend the word starting at index i. - for (const sentence of rest) { - sentences.push(`${word} ${sentence}`); - } - } - - memo.set(i, sentences); - return sentences; - } - - return generate(0); -} diff --git a/src/leetcode/recursion/word_break_ii.py b/src/leetcode/recursion/word_break_ii.py new file mode 100644 index 0000000..9254176 --- /dev/null +++ b/src/leetcode/recursion/word_break_ii.py @@ -0,0 +1,70 @@ +# DIFFICULTY: HARD +# +# Given a string s and a dictionary of strings wordDict, add spaces in s to construct a sentence where each word is a +# valid dictionary word. Return all such possible sentences in any order. +# +# Note that the same word in the dictionary may be reused multiple times in the segmentation. +# +# See https://leetcode.com/problems/word-break-ii +class Solution: + def wordBreak(self, s: str, wordDict: list[str]) -> list[str]: + """ + SOLUTION + -------- + + To do this, we can use a recursive backtracking algorithm. + + COMPLEXITY + ---------- + + Time complexity is O(2^n) where n is the number of words in the string. + + Space complexity is O(n) because we are storing the words in stack frames. + """ + uniques = set(wordDict) + memo: dict[int, list[str]] = {} + + # This generates all possible sentences using words that start at index i (if any exist) recursively. + # + # Since the only words we have available to us are the ones in the wordDict, we'll just have to loop through + # from i to the end of the string to see if any of those substrings form a 'word'. + def generate(i: int) -> list[str]: + # Because we are slicing from [i, j), and slice is exclusive on the second index, we will be starting at + # i + 1. Therefore, do the bounds check for i == len(s). + if i == len(s): + return [] + + if i in memo: + return memo[i] + + sentences: list[str] = [] + # Attempt to generate sentences beginning with the 'word' from i to j. Note that we want to slice from i to + # j, so we actually want j to start at i + 1, and we also want to include j === s.length, because the slice + # function is inclusive for the first index, and exclusive for the second index. + for j in range(i + 1, len(s) + 1): + word = s[i:j] + if word not in uniques: + continue + + # Now generate all possible sentences using words that start at index j (if any exist), and put the word + # from index i in front. + rest = generate(j) + + # If the problem allowed us to generate sentences using a partial selection of characters (e.g. if we + # couldn't make more words at this point), we could just add the current word to the result, even if + # there were unused characters remaining. + # + # However, the problem does not allow us to do this, so we only add the current word if there are no + # unused characters remaining. + if len(rest) == 0 and j == len(s): + sentences.append(word) + continue + + # If we could generate words starting at j, prepend the word starting at index i. + for sentence in rest: + sentences.append(word + " " + sentence) + + memo[i] = sentences + return sentences + + return generate(0) diff --git a/test/leetcode/recursion/word-break-ii.test.ts b/test/leetcode/recursion/word-break-ii.test.ts deleted file mode 100644 index bd59dab..0000000 --- a/test/leetcode/recursion/word-break-ii.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { wordBreak } from '../../src/recursion/word-break-ii'; - -describe('word break ii', () => { - test('word break ii - test case 1', async () => { - const s = 'catsanddog'; - const words = ['cat', 'cats', 'and', 'sand', 'dog']; - - expect(wordBreak(s, words)).toMatchSnapshot(); - }); - - test('word break ii - test case 2', async () => { - const s = 'pineapplepenapple'; - const words = ['apple', 'pen', 'applepen', 'pine', 'pineapple']; - - expect(wordBreak(s, words)).toMatchSnapshot(); - }); - - test('word break ii - test case 2', async () => { - const s = 'catsandog'; - const words = ['cats', 'dog', 'sand', 'and', 'cat']; - - expect(wordBreak(s, words)).toStrictEqual([]); - }); -}); diff --git a/test/leetcode/recursion/word_break_test.py b/test/leetcode/recursion/word_break_test.py new file mode 100644 index 0000000..8feeb75 --- /dev/null +++ b/test/leetcode/recursion/word_break_test.py @@ -0,0 +1,20 @@ +from leetcode.recursion.word_break_ii import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.wordBreak("catsanddog", ["cat", "cats", "and", "sand", "dog"]) == ["cat sand dog", "cats and dog"] + + +def test_case_2(): + assert soln.wordBreak("pineapplepenapple", ["apple", "pen", "applepen", "pine", "pineapple"]) == [ + "pine apple pen apple", + "pine applepen apple", + "pineapple pen apple", + ] + + +def test_case_3(): + assert soln.wordBreak("catsandog", ["cats", "dog", "sand", "and", "cat"]) == [] From eb500c6d6cf8ed3635e4d97ecb081fcb38fca4ac Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 17 Mar 2025 18:18:55 -0700 Subject: [PATCH 095/158] best time --- src/leetcode/recursion/word_break_ii.py | 1 + src/leetcode/sliding_window/README.md | 16 +++--- .../best-time-to-buy-and-sell-stock-i.ts | 46 ---------------- .../best_time_to_buy_and_sell_stock.py | 52 +++++++++++++++++++ .../best-time-to-buy-and-sell-stock-i.test.ts | 11 ---- .../best_time_to_buy_and_sell_stock_test.py | 12 +++++ 6 files changed, 73 insertions(+), 65 deletions(-) delete mode 100644 src/leetcode/sliding_window/best-time-to-buy-and-sell-stock-i.ts create mode 100644 src/leetcode/sliding_window/best_time_to_buy_and_sell_stock.py delete mode 100644 test/leetcode/sliding_window/best-time-to-buy-and-sell-stock-i.test.ts create mode 100644 test/leetcode/sliding_window/best_time_to_buy_and_sell_stock_test.py diff --git a/src/leetcode/recursion/word_break_ii.py b/src/leetcode/recursion/word_break_ii.py index 9254176..a95d907 100644 --- a/src/leetcode/recursion/word_break_ii.py +++ b/src/leetcode/recursion/word_break_ii.py @@ -1,4 +1,5 @@ # DIFFICULTY: HARD +# ---------------- # # Given a string s and a dictionary of strings wordDict, add spaces in s to construct a sentence where each word is a # valid dictionary word. Return all such possible sentences in any order. diff --git a/src/leetcode/sliding_window/README.md b/src/leetcode/sliding_window/README.md index eddd942..02a59c9 100644 --- a/src/leetcode/sliding_window/README.md +++ b/src/leetcode/sliding_window/README.md @@ -4,13 +4,13 @@ These phrases indicate the sliding window technique might be useful: -- 'fixed-size subarray with a condition' -- 'longest subarray with a condition' -- 'at most k', 'at least k', 'no more than k' -- 'max sum over a range', 'min sum over a range' -- 'continuous subarray' -- 'substring' -- 'dynamic constraints' +- fixed-size subarray with a condition +- longest subarray with a condition +- at most k, at least k, no more than k +- max sum over a range, min sum over a range +- continuous subarray +- substring +- dynamic constraints ## Notable Problems @@ -18,7 +18,7 @@ These phrases indicate the sliding window technique might be useful: The [sliding window maximum](https://leetcode.com/problems/sliding-window-maximum/) (hard) problem can be solved with a deque; the sliding window itself is implicitly maintained by the deque. -The above resembles the [maximum subarray](https://leetcode.com/problems/maximum-subarray/) (medium) problem, which can be solved optimally using [Kadane's Algorithm](https://en.wikipedia.org/wiki/Maximum_subarray_problem), or with prefix sum for a more direct and explicit solution. +The above resembles the [maximum subarray](https://leetcode.com/problems/maximum-subarray/) (medium) problem, which can be solved optimally using [Kadanes Algorithm](https://en.wikipedia.org/wiki/Maximum_subarray_problem), or with prefix sum for a more direct and explicit solution. ### Longest Substring with at Most K Distinct Characters diff --git a/src/leetcode/sliding_window/best-time-to-buy-and-sell-stock-i.ts b/src/leetcode/sliding_window/best-time-to-buy-and-sell-stock-i.ts deleted file mode 100644 index 40c0703..0000000 --- a/src/leetcode/sliding_window/best-time-to-buy-and-sell-stock-i.ts +++ /dev/null @@ -1,46 +0,0 @@ -// DIFFICULTY: EASY -// -// You are given an array prices where prices[i] is the price of a given stock on the ith day. -// -// You want to maximize your profit by choosing a single day to buy one stock and choosing a different day in the future -// to sell that stock. -// -// Return the maximum profit you can achieve from this transaction. If you cannot achieve any profit, return 0. -// -// See {@link https://leetcode.com/problems/best-time-to-buy-and-sell-stock/} -export { maxProfit }; - -// SOLUTION: -// -// The question is a bit contrived, as in reality this would never happen. Here, we are assuming we can go backwards -// in time to be able to buy at the low point and sell at the high point. Just keep that in mind: we have a time -// machine. -// -// This type of problem is easily solved with a modified version of the sliding window technique. We will maintain -// a minimum price and update that value whenever we see a lower value, from there we can calculate proposed profit -// on any given day. -// -// By iterating through the array we'll find the maximum profit at the end. -function maxProfit(prices: number[]): number { - // We want to buy at the lowest point, and then sell at the highest point. To do this, keep track of the minimum - // stock price at any point, and use that minimum stock price to determine our maximum profit. - let minPrice = Infinity; - let maxProfit = 0; - - for (let i = 0; i < prices.length; i++) { - const price = prices[i]; - - // If the price ends up being lower than what we've ever seen, buy it and see if we can profit. - if (price < minPrice) { - minPrice = price; - continue; - } - - // Unfortunately, it can be the case that we bought at a low point but the stock continues or fall or trades flat. - // If that is the case, we'll keep our previous value of max profit. - const profit = price - minPrice; - maxProfit = Math.max(maxProfit, profit); - } - - return maxProfit; -} diff --git a/src/leetcode/sliding_window/best_time_to_buy_and_sell_stock.py b/src/leetcode/sliding_window/best_time_to_buy_and_sell_stock.py new file mode 100644 index 0000000..20ae8d4 --- /dev/null +++ b/src/leetcode/sliding_window/best_time_to_buy_and_sell_stock.py @@ -0,0 +1,52 @@ +# DIFFICULTY: EASY +# ---------------- +# +# You are given an array prices where prices[i] is the price of a given stock on the ith day. +# +# You want to maximize your profit by choosing a single day to buy one stock and choosing a different day in the future +# to sell that stock. +# +# Return the maximum profit you can achieve from this transaction. If you cannot achieve any profit, return 0. +# +# See https://leetcode.com/problems/best-time-to-buy-and-sell-stock +import math + + +class Solution: + def maxProfit(self, prices: list[int]) -> int: + """ + SOLUTION + -------- + + The question is a bit contrived, as in reality this would never happen. Here, we are assuming we can go + backwards in time to be able to buy at the low point and sell at the high point. Just keep that in mind: we + have a time machine. + + This type of problem is easily solved with a modified version of the sliding window technique. We will maintain + a minimum price and update that value whenever we see a lower value, from there we can calculate proposed profit + on any given day. + + By iterating through the array we'll find the maximum profit at the end. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of prices in the list. + + Space complexity is O(1) because we are only storing the minimum price and maximum profit. + """ + min_price = math.inf + max_profit = 0 + + for price in prices: + # If the price ends up being lower than what we've ever seen, buy it and see if we can profit. + if price < min_price: + min_price = price + continue + + # Unfortunately, it can be the case that we bought at a low point but the stock continues or fall or trades + # flat. If that is the case, we'll keep our previous value of max profit. + profit = price - min_price + max_profit = max(max_profit, profit) + + return int(max_profit) diff --git a/test/leetcode/sliding_window/best-time-to-buy-and-sell-stock-i.test.ts b/test/leetcode/sliding_window/best-time-to-buy-and-sell-stock-i.test.ts deleted file mode 100644 index 2c4da89..0000000 --- a/test/leetcode/sliding_window/best-time-to-buy-and-sell-stock-i.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { maxProfit } from '../../src/sliding-window/best-time-to-buy-and-sell-stock-i'; - -describe('best time to buy and sell stock i', () => { - test('max profit i - test case 1', async () => { - expect(maxProfit([7, 1, 5, 3, 6, 4])).toBe(5); - }); - - test('max profit i - test case 2', async () => { - expect(maxProfit([7, 6, 4, 3, 1])).toBe(0); - }); -}); diff --git a/test/leetcode/sliding_window/best_time_to_buy_and_sell_stock_test.py b/test/leetcode/sliding_window/best_time_to_buy_and_sell_stock_test.py new file mode 100644 index 0000000..9bcd357 --- /dev/null +++ b/test/leetcode/sliding_window/best_time_to_buy_and_sell_stock_test.py @@ -0,0 +1,12 @@ +from leetcode.sliding_window.best_time_to_buy_and_sell_stock import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.maxProfit([7, 1, 5, 3, 6, 4]) == 5 + + +def test_case_2(): + assert soln.maxProfit([7, 6, 4, 3, 1]) == 0 From 518751fc28889afa7fa4844790108d3761e59d78 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 17 Mar 2025 18:39:10 -0700 Subject: [PATCH 096/158] count subarrays --- ...unt-subarrays-where-max-element-appears.ts | 56 ------------------- ...unt_subarrays_where_max_element_appears.py | 50 +++++++++++++++++ ...ubarrays-where-max-element-appears.test.ts | 31 ---------- ...barrays_where_max_elements_appears_test.py | 29 ++++++++++ ...rrays_where_max_element_appears_test.json} | 0 5 files changed, 79 insertions(+), 87 deletions(-) delete mode 100644 src/leetcode/sliding_window/count-subarrays-where-max-element-appears.ts create mode 100644 src/leetcode/sliding_window/count_subarrays_where_max_element_appears.py delete mode 100644 test/leetcode/sliding_window/count-subarrays-where-max-element-appears.test.ts create mode 100644 test/leetcode/sliding_window/count_subarrays_where_max_elements_appears_test.py rename test/leetcode/sliding_window/{__data__/count-subarrays-where-max-element-appears.test.json => data/count_subarrays_where_max_element_appears_test.json} (100%) diff --git a/src/leetcode/sliding_window/count-subarrays-where-max-element-appears.ts b/src/leetcode/sliding_window/count-subarrays-where-max-element-appears.ts deleted file mode 100644 index 3b6839d..0000000 --- a/src/leetcode/sliding_window/count-subarrays-where-max-element-appears.ts +++ /dev/null @@ -1,56 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given an integer array nums and a positive integer k. -// -// Return the number of subarrays where the maximum element of nums appears at least k times in that subarray. -// -// A subarray is a contiguous sequence of elements within an array. -// -// See {@link https://leetcode.com/problems/count-subarrays-where-max-element-appears-at-least-k-times/} -export { countSubarrays }; - -// SOLUTION: -// -// Use the sliding window technique to count the number of subarrays with at least k elements. -function countSubarrays(nums: number[], k: number): number { - const max = Math.max(...nums); - - // We could use a set to store the number of unique windows that match our criterion of having at least k max - // elements. To do so, we'd need to store all combination of indices (i, j) where i <= left and j >= right. We - // have to do it this way because storing the subarrays themselves would result in 'duplicates' not being counted. - // - // For example, consider [1,9,9,9,9,9,1] and k=2 where the window [9,9] can appear multiple times. Using a set of - // subarrays would not count the subarrays resulting from 'duplicate' windows. - // - // Even worse, using the formula (a * (b + 1)) to compute additional subarrays where a is the number of elements to - // the left of the window and b is the number of elements to the right of the window does not work. Normally it - // would work, but we are trying to compute subarrays for *each* window, which will result in double counting. - // - // Even though using a set does work, you will exceed LeetCode's time limits, so you'll need a more clever solution. - let total = 0; - - // The number of max elements in our window. - let count = 0; - - for (let left = 0, right = 0; right < nums.length; right++) { - // Count elements that are equal to the max until we get to k elements. - if (nums[right] === max) { - count++; - } - - // Now that we have k elements or more, shrink the window from the left until we have fewer than k elements. - while (count >= k) { - if (nums[left] === max) { - count--; - } - - left++; - } - - // Once left is advanced after there are < k elements, every subarray ending at the right pointer is counted by - // the value at left. - total += left; - } - - return total; -} diff --git a/src/leetcode/sliding_window/count_subarrays_where_max_element_appears.py b/src/leetcode/sliding_window/count_subarrays_where_max_element_appears.py new file mode 100644 index 0000000..813626f --- /dev/null +++ b/src/leetcode/sliding_window/count_subarrays_where_max_element_appears.py @@ -0,0 +1,50 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given an integer array nums and a positive integer k. +# +# Return the number of subarrays where the maximum element of nums appears at least k times in that subarray. +# +# A subarray is a contiguous sequence of elements within an array. +# +# See https://leetcode.com/problems/count-subarrays-where-max-element-appears-at-least-k-times +class Solution: + def countSubarrays(self, nums: list[int], k: int) -> int: + """ + SOLUTION + -------- + + Use the sliding window technique to count the number of subarrays with at least k elements. Expand the window + from the right until we have k elements, counting occurrences of the max element. Shrink the window from the + left when the maximum element appears at least k times in the window. + + COMPLEXITY + ---------- + Time complexity is O(n) where n is the number of elements in the list. + + Space complexity is O(n) because we are storing the number of times each element appears in the window. + """ + max_element = max(nums) + + # The number of subarrays where the maximum element appears at least k times. + subarrays = 0 + + # The number of times max_element appears in the current window. + count = 0 + + # Use the sliding window technique to count the number of subarrays with at least k elements. + left = 0 + right = 0 + for right in range(len(nums)): + if nums[right] == max_element: + count += 1 + + while count >= k: + subarrays += len(nums) - right + + if nums[left] == max_element: + count -= 1 + + left += 1 + + return subarrays diff --git a/test/leetcode/sliding_window/count-subarrays-where-max-element-appears.test.ts b/test/leetcode/sliding_window/count-subarrays-where-max-element-appears.test.ts deleted file mode 100644 index add1b31..0000000 --- a/test/leetcode/sliding_window/count-subarrays-where-max-element-appears.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import { countSubarrays } from '../../src/sliding-window/count-subarrays-where-max-element-appears'; - -describe('count subarrays where max element appears at least k times', () => { - test('count subarrays - test case 1', async () => { - expect(countSubarrays([1, 3, 2, 3, 3], 2)).toBe(6); - }); - - test.skip('count subarrays - test case 2', async () => { - expect(countSubarrays([1, 4, 2, 1], 3)).toBe(0); - }); - - test.skip('count subarrays - test case 3', async () => { - expect( - countSubarrays( - [61, 23, 38, 23, 56, 40, 82, 56, 82, 82, 82, 70, 8, 69, 8, 7, 19, 14, 58, 42, 82, 10, 82, 78, 15, 82], - 2 - ) - ).toBe(224); - }); - - test.skip('count subarrays - test case 4', async () => { - const data = fs - .readFileSync(path.join(__dirname, '__data__', 'count-subarrays-where-max-element-appears.test.json')) - .toString(); - const nums = JSON.parse(data); - - expect(countSubarrays(nums, 13)).toBe(263559); - }); -}); diff --git a/test/leetcode/sliding_window/count_subarrays_where_max_elements_appears_test.py b/test/leetcode/sliding_window/count_subarrays_where_max_elements_appears_test.py new file mode 100644 index 0000000..ff65394 --- /dev/null +++ b/test/leetcode/sliding_window/count_subarrays_where_max_elements_appears_test.py @@ -0,0 +1,29 @@ +import json +import pathlib +from leetcode.sliding_window.count_subarrays_where_max_element_appears import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.countSubarrays([1, 3, 2, 3, 3], 3) == 2 + + +def test_case_2(): + assert soln.countSubarrays([1, 4, 2, 1], 3) == 0 + + +def test_case_3(): + xs = [61, 23, 38, 23, 56, 40, 82, 56, 82, 82, 82, 70, 8, 69, 8, 7, 19, 14, 58, 42, 82, 10, 82, 78, 15, 82] + k = 2 + + assert soln.countSubarrays(xs, k) == 224 + + +def test_case_4(): + path = pathlib.Path(__file__).parent / "data" / "count_subarrays_where_max_element_appears_test.json" + assert path.exists() + with path.open("r", encoding="utf-8") as file: + xs = json.load(file) + assert soln.countSubarrays(xs, 13) == 263559 diff --git a/test/leetcode/sliding_window/__data__/count-subarrays-where-max-element-appears.test.json b/test/leetcode/sliding_window/data/count_subarrays_where_max_element_appears_test.json similarity index 100% rename from test/leetcode/sliding_window/__data__/count-subarrays-where-max-element-appears.test.json rename to test/leetcode/sliding_window/data/count_subarrays_where_max_element_appears_test.json From 2fe3027565e0dd891fd02eec5c0c6bc1a8c27465 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 17 Mar 2025 18:47:08 -0700 Subject: [PATCH 097/158] length of longest --- .../length-of-longest-substring.ts | 43 --------------- .../length_of_longest_substring.py | 55 +++++++++++++++++++ .../length-of-longest-substring.test.ts | 15 ----- .../length_of_longest_substring_test.py | 16 ++++++ 4 files changed, 71 insertions(+), 58 deletions(-) delete mode 100644 src/leetcode/sliding_window/length-of-longest-substring.ts create mode 100644 src/leetcode/sliding_window/length_of_longest_substring.py delete mode 100644 test/leetcode/sliding_window/length-of-longest-substring.test.ts create mode 100644 test/leetcode/sliding_window/length_of_longest_substring_test.py diff --git a/src/leetcode/sliding_window/length-of-longest-substring.ts b/src/leetcode/sliding_window/length-of-longest-substring.ts deleted file mode 100644 index e191902..0000000 --- a/src/leetcode/sliding_window/length-of-longest-substring.ts +++ /dev/null @@ -1,43 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given a string s, find the length of the longest substring without repeating characters. -// -// See {@link https://leetcode.com/problems/longest-substring-without-repeating-characters/} -export { lengthOfLongestSubstring }; - -// SOLUTION: -// -// This can be solved using the sliding window technique by maintaining, always, a window where we have a substring -// without repeating characters. -// -// To ensure we can check this constraint, we'll keep track of the last time we have seen a character, so we can move -// the left pointer to the right of the left seen index and maintain our no-repeat constraint. -// -// Once we move the left pointer just one step to the right of the last seen index, we'll update that seen index to -// the rightmost position we've seen it, so we can leverage the map again next time. -function lengthOfLongestSubstring(text: string): number { - const seen = new Map(); - - // Use a sliding window approach by moving the right pointer along the string's characters, and updating the left - // pointer any time we see a repeated character. - let max = 0; - for (let left = 0, right = 0; right < text.length; ) { - const c = text[right]; - - // If we've already seen this character, update the left pointer and move it just right of the last seen - // occurrence to ensure no repeated characters. - if (seen.has(c)) { - const last = seen.get(c)!; - left = Math.max(left, last + 1); - } - - // Update our asumption about the current max. - max = Math.max(max, right - left + 1); - - // Set the current character to be seen and advance the right pointer. - seen.set(c, right); - right++; - } - - return max; -} diff --git a/src/leetcode/sliding_window/length_of_longest_substring.py b/src/leetcode/sliding_window/length_of_longest_substring.py new file mode 100644 index 0000000..b80eb3e --- /dev/null +++ b/src/leetcode/sliding_window/length_of_longest_substring.py @@ -0,0 +1,55 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given a string s, find the length of the longest substring without repeating characters. +# +# See https://leetcode.com/problems/longest-substring-without-repeating-characters +class Solution: + def lengthOfLongestSubstring(self, s: str) -> int: + """ + SOLUTION + -------- + + This can be solved using the sliding window technique by maintaining, always, a window where we have a substring + without repeating characters. + + To ensure we can check this constraint, we'll keep track of the last time we have seen a character, so we can + move the left pointer to the right of the left seen index and maintain our no-repeat constraint. + + Once we move the left pointer just one step to the right of the last seen index, we'll update that seen index + to the rightmost position we've seen it, so we can leverage the map again next time. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of characters in the string. + + Space complexity is O(1) because the number of characters in the alphabet is fixed. + """ + # Keep a map of last seen character -> index. + seen: dict[str, int] = {} + + # The length of the longest substring without repeating characters. + max_length = 0 + + # Use a sliding window approach by moving the right pointer along the string's characters, and updating the left + # pointer any time we see a repeated character. + left = 0 + right = 0 + while right < len(s): + c = s[right] + + # If we've already seen this character, update the left pointer and move it just right of the last seen + # occurrence to ensure no repeated characters. + if c in seen: + last = seen[c] + left = max(left, last + 1) + + # Update our asumption about the current max. + max_length = max(max_length, right - left + 1) + + # Set the current character to be seen and advance the right pointer. + seen[c] = right + right += 1 + + return max_length diff --git a/test/leetcode/sliding_window/length-of-longest-substring.test.ts b/test/leetcode/sliding_window/length-of-longest-substring.test.ts deleted file mode 100644 index 81dbb98..0000000 --- a/test/leetcode/sliding_window/length-of-longest-substring.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { lengthOfLongestSubstring } from '../../src/two-pointer/longest-substring-without-repeating-chars'; - -describe('length of longest substring without repeated characters', () => { - test('length of longest substring - test case 1', async () => { - expect(lengthOfLongestSubstring('abcabcbb')).toBe(3); - }); - - test('length of longest substring - test case 2', async () => { - expect(lengthOfLongestSubstring('bbbbb')).toBe(1); - }); - - test('length of longest substring - test case 3', async () => { - expect(lengthOfLongestSubstring('pwwkew')).toBe(3); - }); -}); diff --git a/test/leetcode/sliding_window/length_of_longest_substring_test.py b/test/leetcode/sliding_window/length_of_longest_substring_test.py new file mode 100644 index 0000000..9070604 --- /dev/null +++ b/test/leetcode/sliding_window/length_of_longest_substring_test.py @@ -0,0 +1,16 @@ +from leetcode.sliding_window.length_of_longest_substring import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.lengthOfLongestSubstring("abcabcbb") == 3 + + +def test_case_2(): + assert soln.lengthOfLongestSubstring("bbbbb") == 1 + + +def test_case_3(): + assert soln.lengthOfLongestSubstring("pwwkew") == 3 From 83fb64d72eae8eb8154b1700a217e0d6a995a653 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 17 Mar 2025 19:08:01 -0700 Subject: [PATCH 098/158] ongest continuous --- .../longest-continuous-subarray.ts | 94 ------------------- .../longest_continuous_subarray.py | 94 +++++++++++++++++++ .../longest-continuous-subarray.test.ts | 19 ---- .../longest_continuous_subarray_test.py | 20 ++++ 4 files changed, 114 insertions(+), 113 deletions(-) delete mode 100644 src/leetcode/sliding_window/longest-continuous-subarray.ts create mode 100644 src/leetcode/sliding_window/longest_continuous_subarray.py delete mode 100644 test/leetcode/sliding_window/longest-continuous-subarray.test.ts create mode 100644 test/leetcode/sliding_window/longest_continuous_subarray_test.py diff --git a/src/leetcode/sliding_window/longest-continuous-subarray.ts b/src/leetcode/sliding_window/longest-continuous-subarray.ts deleted file mode 100644 index f942ba9..0000000 --- a/src/leetcode/sliding_window/longest-continuous-subarray.ts +++ /dev/null @@ -1,94 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an array of integers nums and an integer limit, return the size of the longest non-empty subarray such that the -// absolute difference between any two elements of this subarray is less than or equal to limit. -// -// See {@link https://leetcode.com/problems/longest-continuous-subarray-with-absolute-diff-less-than-or-equal-to-limit/} -export { longestSubarray }; - -// Use a sliding window approach to find the max length subarray. We'll have to keep track of what the min/max -// elements in the subarray while adjusting our window size. To do so we'll use two deques. -// -// We'll have a right pointer that moves forward through the array as normal. The left pointer will move forward -// through the array only when the subarray constraint (under limit) is violated. As the pointers move, we'll need -// to keep track of the smallest and largest elements; that's what the deques are for. -// -// We can't simply store the max element or min element by itself; as the pointers move, these elements will change. -// Instead, we'll have to use our deques to maintain the max/min elements at every window size. The `minDeque` will -// have elements in increasing order, and the `maxDeque` will have elements in decreasing order. We keep the deques -// organized like this so that the largest element will always be at the front of the `maxDeque`, and the smallest -// element will always be at the front of the `minDeque`. -// -// As we advance the right pointer, we'll have to pop off elements at the back of the deque and then push the right -// pointer onto the end of the deque, while maintaining the ascending/descending order. As we advance the left -// pointer, we'll shift off the element at the front of the deque if it is less than what we just passed. -function longestSubarray(nums: number[], limit: number): number { - type Index = number; - - // Keeps track of indices whose values are in descending order. This will allow us to quickly find the index of the - // largest element as the window shifts. - const maxDeque: Index[] = []; - - // Keeps track of indices whose values are in ascending order. This will allow us to quickly find the index of the - // smallest element as the window shifts. - const minDeque: Index[] = []; - - // The current max length of a continuous subarray. - let result = 0; - - function last(deque: Index[]) { - return deque[deque.length - 1]; - } - - // Expand the right window while updating our deques. Each element we encounter from the right side will be used - // to update our deques. - for (let left = 0, right = 0; right < nums.length; right++) { - const value = nums[right]; - - // Update the max deque by removing all elements at the end of the deque that are smaller, so we can maintain a - // deque in descending order. - while (nums[last(maxDeque)] <= value) { - maxDeque.pop(); - } - maxDeque.push(right); - - // Update the min deque by removing all elements at the end of the deque that are bigger, so we can maintain a - // deque in ascending order. - while (nums[last(minDeque)] >= value) { - minDeque.pop(); - } - minDeque.push(right); - - // Check the conditions of our subarray; if we have exceeded the limit, move the left pointer until we are under - // the limit. - // - // After moving the left pointer, it could be the case that we have to update our min/max deque as it is no longer - // being considered as part of the subarray. - let min = minDeque[0]; - let max = maxDeque[0]; - while (Math.abs(nums[max] - nums[min]) > limit) { - left++; - - // If we've moved past the min index, remove the min index from the front of the deque. Note that we are - // comparing indices here, not actual values. - if (minDeque[0] < left) { - minDeque.shift(); - } - - // Alternatively, if we've moved past the max index, remove the max index from the front of the deque. Note - // that we are comparing indices here, not actual values. - if (maxDeque[0] < left) { - maxDeque.shift(); - } - - min = minDeque[0]; - max = maxDeque[0]; - } - - // Update our assumptions of the max length of the subarray. - const length = right - left + 1; - result = Math.max(result, length); - } - - return result; -} diff --git a/src/leetcode/sliding_window/longest_continuous_subarray.py b/src/leetcode/sliding_window/longest_continuous_subarray.py new file mode 100644 index 0000000..09ca1c5 --- /dev/null +++ b/src/leetcode/sliding_window/longest_continuous_subarray.py @@ -0,0 +1,94 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an array of integers nums and an integer limit, return the size of the longest non-empty subarray such that the +# absolute difference between any two elements of this subarray is less than or equal to limit. +# +# See https://leetcode.com/problems/longest-continuous-subarray-with-absolute-diff-less-than-or-equal-to-limit +from collections import deque + + +class Solution: + def longestSubarray(self, nums: list[int], limit: int) -> int: + """ + SOLUTION + -------- + + Use a sliding window approach to find the max length subarray. We'll have to keep track of what the min/max + elements in the subarray while adjusting our window size. To do so we'll use two deques. + + We'll have a right pointer that moves forward through the array as normal. The left pointer will move forward + through the array only when the subarray constraint (under limit) is violated. As the pointers move, we'll need + to keep track of the smallest and largest elements; that's what the deques are for. + + We can't simply store the max element or min element by itself; as the pointers move, these elements will + change. Instead, we'll have to use our deques to maintain the max/min elements at every window size. The + `minDeque` will have elements in increasing order, and the `maxDeque` will have elements in decreasing order. + We keep the deques organized like this so that the largest element will always be at the front of the + `maxDeque`, and the smallest element will always be at the front of the `minDeque`. + + As we advance the right pointer, we'll have to pop off elements at the back of the deque and then push the right + pointer onto the end of the deque, while maintaining the ascending/descending order. As we advance the left + pointer, we'll shift off the element at the front of the deque if it is less than what we just passed. + + COMPLEXITY + ---------- + Time complexity is O(n) where n is the number of elements in the list. + + Space complexity is O(n) because we are storing the maximum and minimum elements in the window. + """ + # Keeps track of indices whose values are in descending order. This will allow us to quickly find the index of + # the largest element as the window shifts. + max_deque: deque[int] = deque() + + # Keeps track of indices whose values are in ascending order. This will allow us to quickly find the index of + # the smallest element as the window shifts. + min_deque: deque[int] = deque() + + # The current max length of a continuous subarray. + result = 0 + + # Expand the right window while updating our deques. Each element we encounter from the right side will be used + # to update our deques. + left = 0 + for right in range(len(nums)): + value = nums[right] + + # Update the max deque by removing all elements at the end of the deque that are smaller, so we can maintain + # a deque in descending order. + while max_deque and nums[max_deque[-1]] <= value: + max_deque.pop() + max_deque.append(right) + + # Update the min deque by removing all elements at the end of the deque that are bigger, so we can maintain + # a deque in ascending order. + while min_deque and nums[min_deque[-1]] >= value: + min_deque.pop() + min_deque.append(right) + + # Check the conditions of our subarray; if we have exceeded the limit, move the left pointer until we are + # under the limit. + # + # After moving the left pointer, it could be the case that we have to update our min/max deque as it is no + # longer being considered as part of the subarray + min_index = min_deque[0] + max_index = max_deque[0] + while abs(nums[max_index] - nums[min_index]) > limit: + left += 1 + + # If we've moved past the min index, remove the min index from the front of the deque. Note that we are + # comparing indices here, not actual values. + if min_deque[0] < left: + min_deque.popleft() + + # Alternatively, if we've moved past the max index, remove the max index from the front of the deque. + # Note that we are comparing indices here, not actual values. + if max_deque[0] < left: + max_deque.popleft() + + min_index = min_deque[0] + max_index = max_deque[0] + + result = max(result, right - left + 1) + + return result diff --git a/test/leetcode/sliding_window/longest-continuous-subarray.test.ts b/test/leetcode/sliding_window/longest-continuous-subarray.test.ts deleted file mode 100644 index a15ae8a..0000000 --- a/test/leetcode/sliding_window/longest-continuous-subarray.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { longestSubarray } from '../../src/sliding-window/longest-continuous-subarray'; - -describe('longest continuous subarray with absolute diff less than or equal to limit', () => { - test('longest subarray - test case 1', async () => { - expect(longestSubarray([8, 2, 4, 7], 4)).toBe(2); - }); - - test('longest subarray - test case 2', async () => { - expect(longestSubarray([10, 1, 2, 4, 7, 2], 5)).toBe(4); - }); - - test('longest subarray - test case 3', async () => { - expect(longestSubarray([4, 2, 2, 2, 4, 4, 2, 2], 0)).toBe(3); - }); - - test('longest subarray - test case 4', async () => { - expect(longestSubarray([1, 5, 6, 7, 8, 10, 6, 5, 6], 4)).toBe(5); - }); -}); diff --git a/test/leetcode/sliding_window/longest_continuous_subarray_test.py b/test/leetcode/sliding_window/longest_continuous_subarray_test.py new file mode 100644 index 0000000..c2754e0 --- /dev/null +++ b/test/leetcode/sliding_window/longest_continuous_subarray_test.py @@ -0,0 +1,20 @@ +from leetcode.sliding_window.longest_continuous_subarray import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.longestSubarray([8, 2, 4, 7], 4) == 2 + + +def test_case_2(): + assert soln.longestSubarray([10, 1, 2, 4, 7, 2], 5) == 4 + + +def test_case_3(): + assert soln.longestSubarray([4, 2, 2, 2, 4, 4, 2, 2], 0) == 3 + + +def test_case_4(): + assert soln.longestSubarray([1, 5, 6, 7, 8, 10, 6, 5, 6], 4) == 5 From 6d43bd288861d8024318a1bb9c490be9173dfc6c Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 17 Mar 2025 19:38:37 -0700 Subject: [PATCH 099/158] max sum --- .../max-sum-distinct-subarray-of-size-k.ts | 85 ------------------ .../max_sum_distinct_subarray_of_size_k.py | 89 +++++++++++++++++++ ...ax-sum-distinct-subarray-of-size-k.test.ts | 11 --- ...ax_sum_distinct_subarray_of_size_k_test.py | 12 +++ 4 files changed, 101 insertions(+), 96 deletions(-) delete mode 100644 src/leetcode/sliding_window/max-sum-distinct-subarray-of-size-k.ts create mode 100644 src/leetcode/sliding_window/max_sum_distinct_subarray_of_size_k.py delete mode 100644 test/leetcode/sliding_window/max-sum-distinct-subarray-of-size-k.test.ts create mode 100644 test/leetcode/sliding_window/max_sum_distinct_subarray_of_size_k_test.py diff --git a/src/leetcode/sliding_window/max-sum-distinct-subarray-of-size-k.ts b/src/leetcode/sliding_window/max-sum-distinct-subarray-of-size-k.ts deleted file mode 100644 index 656e0c6..0000000 --- a/src/leetcode/sliding_window/max-sum-distinct-subarray-of-size-k.ts +++ /dev/null @@ -1,85 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given an integer array nums and an integer k. Find the maximum subarray sum of all the subarrays of nums that -// meet the following conditions: -// -// - The length of the subarray is k, and -// - All the elements of the subarray are distinct. -// -// Return the maximum subarray sum of all the subarrays that meet the conditions. If no subarray meets the conditions, -// return 0. -// -// A subarray is a contiguous non-empty sequence of elements within an array. -// -// See {@link https://leetcode.com/problems/maximum-sum-of-distinct-subarrays-with-length-k} -export { maximumSubarraySum }; - -// SOLUTION: -// -// Without the distinct requirement, we can use the sliding window technique with a window of size k, shifting the -// window as we look for the maximum sum. -// -// With the distinct element requirement, we need to maintain a set and disregard sub arrays that don't have unique -// elements. -function maximumSubarraySum(xs: number[], k: number): number { - let current = 0; - let max = 0; - let left = 0; - let right = 0; - - // Define a set to check if we have a distinct sub array, and define the left pointer of our sliding window. We - // will update the window based on uniqueness and compute a sum when we have k elements. - const uniques = new Set(); - - // Build up our window by moving the right pointer. We'll update the left pointer along the way in case we - // detect a sub array with duplicate elements. - while (right < xs.length) { - const value = xs[right]; - - // If the current element is already in the sub array, advance the left pointer until we no longer have a - // duplicate element. We'll have to decrement the current sum to account for sliding the left pointer forward. - while (uniques.has(value)) { - uniques.delete(xs[left]); - current -= xs[left]; - left++; - } - - // Now that the sub array is free of dupes, we can add the current element to the sum. Let's not update the - // right pointer just yet; if we are over k elements, we want to leave the right pointer where it is for the - // moment. - current += value; - uniques.add(value); - - // If, by adding this element, we've gone over k elements, shrink the window by moving the left pointer, which - // should put us at exactly k elements. - // - // Note that we can check if we've violated the size constraint by checking the size of the `uniques` set, which - // forms our contiguous set. By adding a single element, the most we could've gone over by is one element, so - // just check, and if we've done so, slide the left pointer forward again. - // - // Once within constraints we can fall through to the next block. - if (uniques.size > k) { - uniques.delete(xs[left]); - current -= xs[left]; - left++; - } - - // If the sub array has exactly k elements at this iteration (or after we've shrink the window), update our - // assumptions about the max sum and update the right pointer. - // - // Note that the continue statements are unnecessary but are added for clarity. - if (uniques.size === k) { - max = Math.max(current, max); - right++; - continue; - } - - // If the sub array has fewer than k elements, grow the window by increasing the right pointer. - if (uniques.size < k) { - right++; - continue; - } - } - - return max; -} diff --git a/src/leetcode/sliding_window/max_sum_distinct_subarray_of_size_k.py b/src/leetcode/sliding_window/max_sum_distinct_subarray_of_size_k.py new file mode 100644 index 0000000..9df356a --- /dev/null +++ b/src/leetcode/sliding_window/max_sum_distinct_subarray_of_size_k.py @@ -0,0 +1,89 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given an integer array nums and an integer k. Find the maximum subarray sum of all the subarrays of nums that +# meet the following conditions: +# +# - The length of the subarray is k, and +# - All the elements of the subarray are distinct. +# +# Return the maximum subarray sum of all the subarrays that meet the conditions. If no subarray meets the conditions, +# return 0. +# +# A subarray is a contiguous non-empty sequence of elements within an array. +# +# See https://leetcode.com/problems/maximum-sum-of-distinct-subarrays-with-length-k +class Solution: + def maximumSubarraySum(self, xs: list[int], k: int) -> int: + """ + SOLUTION + -------- + + Without the distinct requirement, we can use the sliding window technique with a window of size k, shifting the + window as we look for the maximum sum. + + With the distinct element requirement, we need to maintain a set and disregard sub arrays that don't have unique + elements. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of elements in the list. + + Space complexity is O(n) because we are storing the elements in the window. + """ + # Keep track of the current sum of the sub array, as well as the maximum sum we've seen so far. + current_sum = 0 + max_sum = 0 + + # Define a set to check if we have a distinct sub array. We will update the window based on uniqueness and + # compute a sum when we have k elements. + uniques = set() + + # Build up our window by moving the right pointer. We'll update the left pointer along the way in case we + # detect a sub array with duplicate elements. + left = 0 + right = 0 + while right < len(xs): + value = xs[right] + + # If the current element is already in the sub array, advance the left pointer until we no longer have a + # duplicate element. We'll have to decrement the current sum to account for sliding the left pointer + # forward. + # + # Note that xs.remove() will raise if the element is not present, so use xs.discard() instead. + while value in uniques: + uniques.discard(xs[left]) + current_sum -= xs[left] + left += 1 + + # Now that the sub array is free of dupes, we can add the current element to the sum. Let's not update the + # right pointer just yet; if we are over k elements, we want to leave the right pointer where it is for the + # moment. + current_sum += value + uniques.add(value) + + # If, by adding this element, we've gone over k elements, shrink the window by moving the left pointer, + # which should put us at exactly k elements. + # + # Note that we can check if we've violated the size constraint by checking the size of the `uniques` set, + # which forms our contiguous set. By adding a single element, the most we could've gone over by is one + # element, so just check, and if we've done so, slide the left pointer forward again. + # + # Once within constraints we can fall through to the next block. + if len(uniques) > k: + uniques.discard(xs[left]) + current_sum -= xs[left] + left += 1 + + # If the sub array has exactly k elements at this iteration (or after we've shrink the window), update our + # assumptions about the max sum and update the right pointer. + if len(uniques) == k: + max_sum = max(max_sum, current_sum) + right += 1 + + # If the sub array has fewer than k elements, grow the window by increasing the right pointer. + if len(uniques) < k: + right += 1 + + return max_sum diff --git a/test/leetcode/sliding_window/max-sum-distinct-subarray-of-size-k.test.ts b/test/leetcode/sliding_window/max-sum-distinct-subarray-of-size-k.test.ts deleted file mode 100644 index cf0a2e9..0000000 --- a/test/leetcode/sliding_window/max-sum-distinct-subarray-of-size-k.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { maximumSubarraySum } from '../../src/sliding-window/max-sum-distinct-subarray-of-size-k'; - -describe('max sum of distincts subarray with length k', () => { - test('max subarray sum - test case 1', async () => { - expect(maximumSubarraySum([1, 5, 4, 2, 9, 9, 9], 3)).toBe(15); - }); - - test('max subarray sum - test case 2', async () => { - expect(maximumSubarraySum([4, 4, 4], 3)).toBe(0); - }); -}); diff --git a/test/leetcode/sliding_window/max_sum_distinct_subarray_of_size_k_test.py b/test/leetcode/sliding_window/max_sum_distinct_subarray_of_size_k_test.py new file mode 100644 index 0000000..085d211 --- /dev/null +++ b/test/leetcode/sliding_window/max_sum_distinct_subarray_of_size_k_test.py @@ -0,0 +1,12 @@ +from leetcode.sliding_window.max_sum_distinct_subarray_of_size_k import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.maximumSubarraySum([1, 5, 4, 2, 9, 9, 9], 3) == 15 + + +def test_case_2(): + assert soln.maximumSubarraySum([4, 4, 4], 3) == 0 From 05fc4a6c9851b94883f3f42675c2ed422add986d Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 17 Mar 2025 22:18:59 -0700 Subject: [PATCH 100/158] min window substr --- .../minimum-window-substring.ts | 112 ----------------- .../minimum_window_substring.py | 113 ++++++++++++++++++ .../minimum-window-substring.test.ts | 7 -- .../minimum_window_substring_test.py | 12 ++ 4 files changed, 125 insertions(+), 119 deletions(-) delete mode 100644 src/leetcode/sliding_window/minimum-window-substring.ts create mode 100644 src/leetcode/sliding_window/minimum_window_substring.py delete mode 100644 test/leetcode/sliding_window/minimum-window-substring.test.ts create mode 100644 test/leetcode/sliding_window/minimum_window_substring_test.py diff --git a/src/leetcode/sliding_window/minimum-window-substring.ts b/src/leetcode/sliding_window/minimum-window-substring.ts deleted file mode 100644 index 295b849..0000000 --- a/src/leetcode/sliding_window/minimum-window-substring.ts +++ /dev/null @@ -1,112 +0,0 @@ -// DIFFICULTY: HARD -// -// Given two strings s and t of lengths m and n respectively, return the minimum window substring of s such that every -// character in t (including duplicates) is included in the window. If there is no such substring, return the empty -// string "". -// -// The testcases will be generated such that the answer is unique. -// -// @see {@link https://leetcode.com/problems/minimum-window-substring/} -export { minWindow }; - -// SOLUTION: -// -// This can be solved using the sliding window technique with a bunch of extra bookkeeping. The right pointer will be -// expanded until we have a valid window, then the left pointer will be shrunk to the minimum size window that still -// satisfies the requirements. -// -// COMPLEXITY: -// -// Time complexity is O(m + n) because we iterate through both the source and the target strings. -// -// The want map is O(n) space complexity because we store character frequency of the target string. The got map is -// O(m) space complexity because we store character frequency of the source string. Together it's O(m + n). -function minWindow(s: string, t: string): string { - // It is not possible to return a minimum window here; the target substring MUST be shorter than the source string. - if (t.length > s.length) { - return ''; - } - - // First we need to create a frequency map, so we can easily check if a substring has all the characters in t. This - // is required because the characters in t can be duplicated, and we require the same number of duplicates in the - // source substring. - const want = new Map(); - for (const c of t) { - const freq = want.get(c) || 0; - want.set(c, freq + 1); - } - - // Define both pointers to start at the source string, and start by expanding the right pointer. Once a valid window - // is discovered, we contract the left pointer. - let left = 0; - let right = 0; - - // Define a map to keep track of the characters we have seen so far in the window. This will let us know when we have - // a valid window. - // - // We also keep track of which characters we've 'gotten' so far. That is, if we have a requirement to contain X - // number of 'a' characters, and we have seen X characters, then we have 'gotten' that character. When we have - // 'gotten' all characters in the string, we have a valid window. - const got = new Map(); - let gotten = 0; - - // Keep track of our minimum window substring. The left pointer should point to the start, and the size will tell us - // the length of the window, allowing us to call s.substring() later. - // - // If we run through the algorithm and don't get a valid window, because minSize never got updated, then we return ''. - let minLeft = 0; - let minSize = Infinity; - - // Begin by expanding the right pointer until we have a valid window. - while (right < s.length) { - // Update the frequency of the character that we have got. - const c = s[right]; - const freq = got.get(c) || 0; - got.set(c, freq + 1); - - // If it turns out that we got exactly the frequency of character c that we wanted, then the requirement to contain - // X number of character c has been satisfied. - if (want.has(c) && want.get(c) === got.get(c)) { - gotten++; - } - - // If we've gotten all the characters at exactly the right frequency, then we have a valid window. In that case, - // we can begin shrinking the window to see if it stays valid. - while (gotten === want.size) { - const k = s[left]; - - // Calculate the size of the window. Note that left and right are INCLUSIVE indexes, so if we want the window - // size of say [0, 1, 2, 3] with left = 0 and right = 3 (a window size of 4), we should do 3 - 0 + 1 = 4. - // - // So make sure to add 1 here to get the correct size. - const size = right - left + 1; - if (size < minSize) { - minSize = size; - minLeft = left; - } - - // Now try to contract the window by moving the left pointer. When we do this, we have to update the frequency - // of characters we've gotten so far, and update the gotten count. - // - // Update the frequency of the character that we are about to lose. Then if we have got fewer characters than - // wanted, we decrement the 'gotten' count because we no longer have a valid window. - got.set(k, got.get(k)! - 1); - if (want.has(k) && got.get(k)! < want.get(k)!) { - gotten--; - } - - // After performing the bookkeeping, we can now shrink the window by moving the left pointer. - left++; - } - - // Keep expanding the right window until we have a valid window. - right++; - } - - // This means we didn't find any valid window. - if (minSize === Infinity) { - return ''; - } - - return s.substring(minLeft, minLeft + minSize); -} diff --git a/src/leetcode/sliding_window/minimum_window_substring.py b/src/leetcode/sliding_window/minimum_window_substring.py new file mode 100644 index 0000000..b249ef0 --- /dev/null +++ b/src/leetcode/sliding_window/minimum_window_substring.py @@ -0,0 +1,113 @@ +# DIFFICULTY: HARD +# ---------------- +# +# Given two strings s and t of lengths m and n respectively, return the minimum window substring of s such that every +# character in t (including duplicates) is included in the window. If there is no such substring, return the empty +# string "". +# +# The testcases will be generated such that the answer is unique. +# +# See https://leetcode.com/problems/minimum-window-substring +from collections import defaultdict +import math + + +class Solution: + def minWindow(self, s: str, t: str) -> str: + """ + SOLUTION + -------- + + This can be solved using the sliding window technique with a bunch of extra bookkeeping. The right pointer will + be expanded until we have a valid window, then the left pointer will be shrunk to the minimum size window that + still satisfies the requirements. + + COMPLEXITY + ---------- + + Time complexity is O(m + n) where m and n are the lengths of the source and target strings. + + Space complexity is O(m + n) because we store character frequency of the source and target strings. + """ + # It is not possible to return a minimum window here; the target substring MUST be shorter than the source + # string. + if len(t) > len(s): + return "" + + # First we need to create a frequency map, so we can easily check if a substring has all the characters in t. + # This is required because the characters in t can be duplicated, and we require the same number of duplicates + # in the source substring. + want: dict[str, int] = defaultdict(int) + for c in t: + want[c] += 1 + + # Define a map to keep track of the characters we have seen so far in the window. This will let us know when we + # have a valid window. + # + # We also keep track of which characters we've 'gotten' so far. That is, if we have a requirement to contain X + # number of 'a' characters, and we have seen X characters, then we have 'gotten' that character. When we have + # 'gotten' all characters in the string, we have a valid window. + got: dict[str, int] = defaultdict(int) + gotten = 0 + + # Keep track of our minimum window substring. The left pointer should point to the start, and the size will + # tell us the length of the window, allowing us to call slice the string later. + # + # If we run through the algorithm and don't get a valid window, because min_size never got updated, then we + # return ''. + min_left = 0 + min_size = math.inf + + # Define both pointers to start at the source string, and start by expanding the right pointer. Once a valid + # window is discovered, we contract the left pointer. + left = 0 + right = 0 + + # Begin by expanding the right pointer until we have a valid window. + while right < len(s): + # Update the frequency of the character that we have got. + c = s[right] + got[c] += 1 + + # If it turns out that we got exactly the frequency of character c that we wanted, then the requirement to + # contain X number of character c has been satisfied. + if c in want and got[c] == want[c]: + gotten += 1 + + # If we've gotten all the characters at exactly the right frequency, then we have a valid window. In that + # case, we can begin shrinking the window to see if it stays valid. + while gotten == len(want): + k = s[left] + + # Calculate the size of the window. Note that left and right are INCLUSIVE indexes, so if we want the + # window size of say [0, 1, 2, 3] with left = 0 and right = 3 (a window size of 4), we should do + # 3 - 0 + 1 = 4. + # + # So make sure to add 1 here to get the correct size. + size = right - left + 1 + if size < min_size: + min_size = size + min_left = left + + # Now try to contract the window by moving the left pointer. When we do this, we have to update the + # frequency of characters we've gotten so far, and update the gotten count. + # + # Update the frequency of the character that we are about to lose. Then if we have got fewer characters + # than wanted, we decrement the 'gotten' count because we no longer have a valid window. + got[k] -= 1 + if k in want and got[k] < want[k]: + gotten -= 1 + + # Move the left pointer to the right. + left += 1 + + # Keep expanding the right window until we have a valid window. + right += 1 + + # This means we didn't find any valid window. + if min_size == math.inf: + return "" + + i = min_left + j = min_left + min_size + return s[i:j] diff --git a/test/leetcode/sliding_window/minimum-window-substring.test.ts b/test/leetcode/sliding_window/minimum-window-substring.test.ts deleted file mode 100644 index 3f5fdb0..0000000 --- a/test/leetcode/sliding_window/minimum-window-substring.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { minWindow } from '../../src/sliding-window/minimum-window-substring'; - -describe('minimum window substring', () => { - test('minWindow - test case 1', async () => { - expect(minWindow('ADOBECODEBANC', 'ABC')).toStrictEqual('BANC'); - }); -}); diff --git a/test/leetcode/sliding_window/minimum_window_substring_test.py b/test/leetcode/sliding_window/minimum_window_substring_test.py new file mode 100644 index 0000000..6a24fb8 --- /dev/null +++ b/test/leetcode/sliding_window/minimum_window_substring_test.py @@ -0,0 +1,12 @@ +from leetcode.sliding_window.minimum_window_substring import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.minWindow("ADOBECODEBANC", "ABC") == "BANC" + + +def test_case_2(): + assert soln.minWindow("a", "a") == "a" From 551195ab1661b26863c936e4c3c58aa9a2bb5f0f Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 17 Mar 2025 22:22:31 -0700 Subject: [PATCH 101/158] moving avg --- .../moving-average-from-data-stream.ts | 37 ----------------- .../moving_average_from_data_stream.py | 41 +++++++++++++++++++ 2 files changed, 41 insertions(+), 37 deletions(-) delete mode 100644 src/leetcode/sliding_window/moving-average-from-data-stream.ts create mode 100644 src/leetcode/sliding_window/moving_average_from_data_stream.py diff --git a/src/leetcode/sliding_window/moving-average-from-data-stream.ts b/src/leetcode/sliding_window/moving-average-from-data-stream.ts deleted file mode 100644 index 241c026..0000000 --- a/src/leetcode/sliding_window/moving-average-from-data-stream.ts +++ /dev/null @@ -1,37 +0,0 @@ -// DIFFICULTY: EASY -// -// Given a stream of integers and a window size, calculate the moving average of all integers in the sliding window. -// -// Implement the MovingAverage class: -// -// - MovingAverage(int size) Initializes the object with the size of the window size. -// - double next(int val) Returns the moving average of the last size values of the stream. -// -// See {@link https://leetcode.com/problems/moving-average-from-data-stream/} -export { MovingAverage }; - -// SOLUTION: -// -// This can be solved with a queue or sliding window. -class MovingAverage { - private readonly size: number; - private readonly window: number[]; - private sum: number; - - constructor(size: number) { - this.size = size; - this.window = []; - this.sum = 0; - } - - next(val: number): number { - if (this.window.length === this.size) { - const out = this.window.shift(); - this.sum -= out!; - } - - this.window.push(val); - this.sum += val; - return this.sum / this.window.length; - } -} diff --git a/src/leetcode/sliding_window/moving_average_from_data_stream.py b/src/leetcode/sliding_window/moving_average_from_data_stream.py new file mode 100644 index 0000000..50d8a4b --- /dev/null +++ b/src/leetcode/sliding_window/moving_average_from_data_stream.py @@ -0,0 +1,41 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given a stream of integers and a window size, calculate the moving average of all integers in the sliding window. +# +# Implement the MovingAverage class: +# +# - MovingAverage(int size) Initializes the object with the size of the window size. +# - double next(int val) Returns the moving average of the last size values of the stream. +# +# See https://leetcode.com/problems/moving-average-from-data-stream +from collections import deque + + +class MovingAverage: + """ + SOLUTION + -------- + + This can be solved with a queue or sliding window. + + COMPLEXITY + ---------- + + Time complexity is O(1) for next. + + Space complexity is O(n) where n is the size of the window. + """ + + def __init__(self, size: int) -> None: + self.size = size + self.window: deque[int] = deque() + self.total = 0 + + def next(self, val: int) -> float: + if len(self.window) == self.size: + self.total -= self.window.popleft() + + self.window.append(val) + self.total += val + return self.total / len(self.window) From f29fb800b4c7f15914e8f4d7b3f0684e3f77ae5e Mon Sep 17 00:00:00 2001 From: Min Huang Date: Mon, 17 Mar 2025 22:26:59 -0700 Subject: [PATCH 102/158] repeated dna --- .../sliding_window/repeated-dna-sequences.ts | 49 ----------------- .../sliding_window/repeated_dna_sequences.py | 55 +++++++++++++++++++ .../repeated-dna-sequences.test.ts | 11 ---- .../repeated_dna_sequence_test.py | 12 ++++ 4 files changed, 67 insertions(+), 60 deletions(-) delete mode 100644 src/leetcode/sliding_window/repeated-dna-sequences.ts create mode 100644 src/leetcode/sliding_window/repeated_dna_sequences.py delete mode 100644 test/leetcode/sliding_window/repeated-dna-sequences.test.ts create mode 100644 test/leetcode/sliding_window/repeated_dna_sequence_test.py diff --git a/src/leetcode/sliding_window/repeated-dna-sequences.ts b/src/leetcode/sliding_window/repeated-dna-sequences.ts deleted file mode 100644 index 214527d..0000000 --- a/src/leetcode/sliding_window/repeated-dna-sequences.ts +++ /dev/null @@ -1,49 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// The DNA sequence is composed of a series of nucleotides abbreviated as 'A', 'C', 'G', and 'T'. -// -// For example, "ACGAATTCCG" is a DNA sequence. -// When studying DNA, it is useful to identify repeated sequences within the DNA. -// -// Given a string s that represents a DNA sequence, return all the 10-letter-long sequences (substrings) that occur -// more than once in a DNA molecule. You may return the answer in any order. -// -// See {@link https://leetcode.com/problems/repeated-dna-sequences/} -export { findRepeatedDnaSequences }; - -// SOLUTION: -// -// Use the sliding window technique to keep track of sequences. -function findRepeatedDnaSequences(s: string): string[] { - if (s.length < 10) { - return []; - } - - const seen = new Set(); - const repeated = new Set(); - - // We can use a sliding window to keep track of 10 character sequences, then if we have seen the sequence, add it - // to the result. - for (let left = 0, right = 9; right < s.length; right++) { - // Two options exist here, because substring takes the leftmost element (inclusive) and rightmost element - // (exclusive). - // - // i) We could calculate right - left + 1 to account for us slicing from [0, 9 + 1). - // ii) We could start right = 10, then set the loop condition to be right <= s.length instead of just <. - // - // The second approach makes it easier to avoid off by 1, but we do this here to show you have to tackle the off - // by 1 head on. - if (right - left + 1 > 10) { - left++; - } - - const seq = s.substring(left, right + 1); - if (seen.has(seq)) { - repeated.add(seq); - } else { - seen.add(seq); - } - } - - return [...repeated]; -} diff --git a/src/leetcode/sliding_window/repeated_dna_sequences.py b/src/leetcode/sliding_window/repeated_dna_sequences.py new file mode 100644 index 0000000..0c4b7aa --- /dev/null +++ b/src/leetcode/sliding_window/repeated_dna_sequences.py @@ -0,0 +1,55 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# The DNA sequence is composed of a series of nucleotides abbreviated as 'A', 'C', 'G', and 'T'. +# +# For example, "ACGAATTCCG" is a DNA sequence. +# When studying DNA, it is useful to identify repeated sequences within the DNA. +# +# Given a string s that represents a DNA sequence, return all the 10-letter-long sequences (substrings) that occur +# more than once in a DNA molecule. You may return the answer in any order. +# +# See https://leetcode.com/problems/repeated-dna-sequences +class Solution: + def findRepeatedDnaSequences(self, s: str) -> list[str]: + """ + SOLUTION + -------- + + We can use a sliding window to iterate through the string and keep track of the substrings we've seen so far. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the length of the string. + + Space complexity is O(n) because we are storing the substrings in a set. + """ + if len(s) < 10: + return [] + + seen: set[str] = set() + repeated: set[str] = set() + + # We can use a sliding window to keep track of 10 character sequences, then if we have seen the sequence, add it + # to the result. + left = 0 + for right in range(9, len(s)): + # Two options exist here, because substring takes the leftmost element (inclusive) and rightmost element + # (exclusive). + # + # i) We could calculate right - left + 1 to account for us slicing from [0, 9 + 1). + # ii) We could start right = 10, then set the loop condition to be right <= s.length instead of just <. + # + # The second approach makes it easier to avoid off by 1, but we do this here to show you have to tackle the + # off by 1 head on. + if right - left + 1 > 10: + left += 1 + + sequence = s[left : right + 1] + if sequence in seen: + repeated.add(sequence) + else: + seen.add(sequence) + + return list(repeated) diff --git a/test/leetcode/sliding_window/repeated-dna-sequences.test.ts b/test/leetcode/sliding_window/repeated-dna-sequences.test.ts deleted file mode 100644 index 9c01809..0000000 --- a/test/leetcode/sliding_window/repeated-dna-sequences.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { findRepeatedDnaSequences } from '../../src/sliding-window/repeated-dna-sequences'; - -describe('repeated dna sequences', () => { - test('repeated dna sequences - test case 1', async () => { - expect(findRepeatedDnaSequences('AAAAAAAAAAAAA')).toStrictEqual(['AAAAAAAAAA']); - }); - - test('repeated dna sequences - test case 2', async () => { - expect(findRepeatedDnaSequences('AAAAAAAAAAA')).toStrictEqual(['AAAAAAAAAA']); - }); -}); diff --git a/test/leetcode/sliding_window/repeated_dna_sequence_test.py b/test/leetcode/sliding_window/repeated_dna_sequence_test.py new file mode 100644 index 0000000..e156600 --- /dev/null +++ b/test/leetcode/sliding_window/repeated_dna_sequence_test.py @@ -0,0 +1,12 @@ +from leetcode.sliding_window.repeated_dna_sequences import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.findRepeatedDnaSequences("AAAAAAAAAAAAA") == ["AAAAAAAAAA"] + + +def test_case_2(): + assert soln.findRepeatedDnaSequences("AAAAAAAAAAA") == ["AAAAAAAAAA"] From b033ae485e06db98c0b1872f01ad1122d76be01a Mon Sep 17 00:00:00 2001 From: Min Huang Date: Tue, 18 Mar 2025 11:53:57 -0700 Subject: [PATCH 103/158] basic calc 2 --- src/leetcode/stack/README.md | 6 +- src/leetcode/stack/basic-calculator-ii.ts | 136 ------------------ src/leetcode/stack/basic_calculator_ii.py | 126 ++++++++++++++++ .../stack/basic-calculator-ii.test.ts | 11 -- .../stack/basic_calculator_ii_test.py | 16 +++ 5 files changed, 145 insertions(+), 150 deletions(-) delete mode 100644 src/leetcode/stack/basic-calculator-ii.ts create mode 100644 src/leetcode/stack/basic_calculator_ii.py delete mode 100644 test/leetcode/stack/basic-calculator-ii.test.ts create mode 100644 test/leetcode/stack/basic_calculator_ii_test.py diff --git a/src/leetcode/stack/README.md b/src/leetcode/stack/README.md index 60d47e0..3f79985 100644 --- a/src/leetcode/stack/README.md +++ b/src/leetcode/stack/README.md @@ -4,6 +4,6 @@ These phrases indicate a stack might be useful: -- 'balanced delimiter (like parentheses)', 'valid delimiters (like brackets)' -- 'next greater', 'previous smaller' -- 'undo' +- balanced delimiter (like parentheses), valid delimiters (like brackets) +- next greater, previous smaller +- undo diff --git a/src/leetcode/stack/basic-calculator-ii.ts b/src/leetcode/stack/basic-calculator-ii.ts deleted file mode 100644 index 22eb62d..0000000 --- a/src/leetcode/stack/basic-calculator-ii.ts +++ /dev/null @@ -1,136 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given a string s which represents an expression, evaluate this expression and return its value. -// -// The integer division should truncate toward zero. -// -// You may assume that the given expression is always valid. All intermediate results will be in the range of -// [-231, 231 - 1]. -// -// Note: You are not allowed to use any built-in function which evaluates strings as mathematical expressions, such as -// eval(). -// -// See {@link https://leetcode.com/problems/basic-calculator-ii} -export { calculate }; - -// SOLUTION: -// -// Assumption is that we only have numbers, +, -, *, and / in the expression. We also assume no parentheses for -// grouping expressions. -// -// To do this we iterate through the string and use a stack to store intermediate results of computation. Because the -// '*' and '/' operators have higher precedence, we'll resolve them immediately. Those intermediate results will be -// stored onto the stack and we'll resolve the '+' and '-' operators at the end. -// -// Take note that the '-' operator can be a unary operator. -// -// Here's an example evaluation. Let's suppose we have '33+2*2'. Whenever we resolve an operator, OR start the -// calculator, we'll assume the first number is 0 (no value) and the last operator is '+'. -// -// - When we see c = '3', we'll store it as the current value as '3'. -// - When we see c = '3' again, we'll set the current value as '3 *10 + 3' or '33'. -// - When we see c = '+', we'll resolve the current value '33' on the stack. In this step, the current operator c is -// '+' and the last operator is also '+'. Based on the last operator being '+', we push onto the stack. Now we set -// the last operator to '+'. -// - When we see c = '2', we'll store it as the current value as '2'. -// - When we see c = '*', we'll resolve the current value '2'. In this step, the current operator 'c' is '*' and the -// last operator is '+'. Based on the last operator being '+', we push onto the stack. Now we set the last operator -// to '*'. -// - When we see c = '2', we'll store it as the current value as '2'. -// - Because it is the last character, we resolve the last operator, which is '*'. So we pop from the stack ('2'), and -// multiply by '2', giving a result '4', which we push to the stack. -// - Finally we sum the stack. -// -// In a situation where the last operator is '-', we would push a negative value onto the stack. For example, if we -// have '3-2', when we see '2', the last operator is '-', so we push '-2' onto the stack. -// -// Similarly, if we just have '-2', we will see the '2' and push '-2' onto the stack, since the last operator is '-'. -// -// COMPLEXITY: -// -// Time and space complexity are both O(n); we do not need to traverse the string more than once, and we also do not -// need to store more than n values (where n is the length of the string) onto the stack. -function calculate(s: string): number { - function isDigit(c: string) { - return c >= '0' && c <= '9'; - } - - // Keep a stack of numbers to add up; the multiply and divide operations will be applied immediately since they have - // higher precedence. - const stack: number[] = []; - - // Keep track of the current value as we iterate through the string. Every time we see a digit, we can multiply the - // number by 10 and add the digit to it. - let value = 0; - - // Note that the action we take when we see an operator depends on the LAST operator we saw, not the current operator - // we see. - // - // Before we've seen any operators, the default behavior is to add (not subtract or something), so set the last - // operator to '+'. - let last = '+'; - - for (let i = 0; i < s.length; i++) { - const c = s[i]; - - // If we see a space, we can safely skip it. UNLESS, it is the last character in a string. In that case, we do - // actually need to resolve the last operation we saw. For example, '3 * 2 ' should resolve '3 * 2'. Here, we'll - // just skip spaces if they are not the last character. - if (c === ' ' && i !== s.length - 1) { - continue; - } - - // If we find a digit, build up the current number by multiplying the current value by 10 and adding the digit. - if (isDigit(c)) { - value = value * 10 + Number.parseInt(c, 10 /* radix */); - - // Normally, we could just continue, but if the digit is the very last character, like '3 * 2', we do actually - // need to fall through to the operation resolution section below. - if (i !== s.length - 1) { - continue; - } - } - - // Now we have a non-digit character. If it's a space, we can ignore it, but ONLY if it's not the last character. - // If it's the last character, we'll have to resolve the current number and operator. - // Okay, now we have either a space or a non-digit character that could potentially be an operation. Either way - // we need to resolve an expression. For example, reading up to the last character in both strings results in some - // evaluation: - // - // - '3 * 2 +' -> Results in resolving '3 * 2' first, then adding the result to the stack. - // - '3 * 2 ' -> Results in resolving '3 * 2' because we are at the last value, but it's a string and not an - // operator! - // - // Remember, the current operator tells us what to do LATER. What we do now depends on the last operator. If the - // last operator was '+', we delay the summation until the end. - if (last === '+') { - stack.push(value); - } - // Similarly if the operator was '-', we delay the summation until the end. - else if (last === '-') { - stack.push(-value); - } - // If the operator was '*', we do want to resolve it immediately with the current value and the value on the stack. - // Imagine we've hit a case like '2 * 2 +' and we've just read the '+'. We'll want to resolve '2 * 2' first. - else if (last === '*') { - const left = stack.pop()!; - const right = value; - stack.push(left * right); - } - // If the last operator was '/', we similarly want to resolve it immediately. Take care to do left / right where - // the left value is on the stack and the right value is what number we just read. - else if (last === '/') { - const left = stack.pop()!; - const right = value; - stack.push(Math.trunc(left / right)); - } - - // Now, after resolving an operation, reset the current value and store the current operator as the last one we have - // seen. - last = c; - value = 0; - } - - const result = stack.reduce((a, b) => a + b, 0); - return result; -} diff --git a/src/leetcode/stack/basic_calculator_ii.py b/src/leetcode/stack/basic_calculator_ii.py new file mode 100644 index 0000000..3c0d9b9 --- /dev/null +++ b/src/leetcode/stack/basic_calculator_ii.py @@ -0,0 +1,126 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given a string s which represents an expression, evaluate this expression and return its value. +# +# The integer division should truncate toward zero. +# +# You may assume that the given expression is always valid. All intermediate results will be in the range of +# [-231, 231 - 1]. +# +# Note: You are not allowed to use any built-in function which evaluates strings as mathematical expressions, such as +# eval(). +# +# See https://leetcode.com/problems/basic-calculator-ii +class Solution: + def calculate(self, s: str) -> int: + """ + SOLUTION + -------- + + Assumption is that we only have numbers, +, -, *, and / in the expression. We also assume no parentheses for + grouping expressions. + + To do this we iterate through the string and use a stack to store intermediate results of computation. Because + the '*' and '/' operators have higher precedence, we'll resolve them immediately. Those intermediate results + will be stored onto the stack and we'll resolve the '+' and '-' operators at the end. + + Take note that the '-' operator can be a unary operator. + + Here's an example evaluation. Let's suppose we have '33+2*2'. Whenever we resolve an operator, OR start the + calculator, we'll assume the first number is 0 (no value) and the last operator is '+'. + + - When we see c = '3', we'll store it as the current value as '3'. + - When we see c = '3' again, we'll set the current value as '3 *10 + 3' or '33'. + - When we see c = '+', we'll resolve the current value '33' on the stack. In this step, the current operator c + is '+' and the last operator is also '+'. Based on the last operator being '+', we push onto the stack. Now + we set the last operator to '+'. + - When we see c = '2', we'll store it as the current value as '2'. + - When we see c = '*', we'll resolve the current value '2'. In this step, the current operator 'c' is '*' and + the last operator is '+'. Based on the last operator being '+', we push onto the stack. Now we set the last + operator to '*'. + - When we see c = '2', we'll store it as the current value as '2'. + - Because it is the last character, we resolve the last operator, which is '*'. So we pop from the stack ('2'), + and multiply by '2', giving a result '4', which we push to the stack. + - Finally we sum the stack. + + In a situation where the last operator is '-', we would push a negative value onto the stack. For example, if + we have '3-2', when we see '2', the last operator is '-', so we push '-2' onto the stack. + + Similarly, if we just have '-2', we will see the '2' and push '-2' onto the stack, since the last operator is + '-'. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the length of the string. + + Space complexity is O(n) because we are storing the numbers in a stack. + """ + # Keep a stack of numbers to add up; the multiply and divide operations will be applied immediately since they + # have higher precedence. + stack: list[int] = [] + + # Keep track of the current value as we iterate through the string. Every time we see a digit, we can multiply + # the number by 10 and add the digit to it. + value = 0 + + # Note that the action we take when we see an operator depends on the LAST operator we saw, not the current + # operator we see. + # + # Before we've seen any operators, the default behavior is to add (not subtract or something), so set the last + # operator to '+'. + last = "+" + + for i, c in enumerate(s): + # If we see a space, we can safely skip it. UNLESS, it is the last character in a string. In that case, we + # do actually need to resolve the last operation we saw. For example, '3 * 2 ' should resolve '3 * 2'. + # Here, we'll just skip spaces if they are not the last character. + if c == " " and i != len(s) - 1: + continue + + # If we find a digit, build up the current number by multiplying the current value by 10 and adding the + # digit. + if c.isdigit(): + value = value * 10 + int(c) + + # Normally, we could just continue, but if the digit is the very last character, like '3 * 2', we do + # actually need to fall through to the operation resolution section below. + if i != len(s) - 1: + continue + + # Okay, now we have a non-digit character that could potentially be an operation. Either way we need to + # resolve an expression. For example, reading up to the last character in both strings results in some + # evaluation: + # + # - '3 * 2 +' -> Results in resolving '3 * 2' first, then adding the result to the stack. + # - '3 * 2 ' -> Results in resolving '3 * 2' because we are at the last value, but it's a string and not an + # operator! + # + # Remember, the current operator tells us what to do LATER. What we do now depends on the last operator. + # If the last operator was '+', we delay the summation until the end. + if last == "+": + stack.append(value) + # Similarly if the operator was '-', we delay the summation until the end. + elif last == "-": + stack.append(-value) + # If the operator was '*', we do want to resolve it immediately with the current value and the value on the + # stack. Imagine we've hit a case like '2 * 2 +' and we've just read the '+'. We'll want to resolve + # '2 * 2' first. + elif last == "*": + left = stack.pop() + right = value + stack.append(left * right) + # If the last operator was '/', we similarly want to resolve it immediately. Take care to do left / right + # where the left value is on the stack and the right value is what number we just read. + elif last == "/": + left = stack.pop() + right = value + stack.append(int(left / right)) + + # Now, after resolving an operation, reset the current value and store the current operator as the last one + # we have seen. + last = c + value = 0 + + return sum(stack) diff --git a/test/leetcode/stack/basic-calculator-ii.test.ts b/test/leetcode/stack/basic-calculator-ii.test.ts deleted file mode 100644 index af7218d..0000000 --- a/test/leetcode/stack/basic-calculator-ii.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { calculate } from '../../src/stack/basic-calculator-ii'; - -describe('basic calculator ii', () => { - test('calculate - test case 1', async () => { - expect(calculate('3+2*2')).toStrictEqual(7); - }); - - test('calculate - test case 2', async () => { - expect(calculate(' 3/2 ')).toStrictEqual(1); - }); -}); diff --git a/test/leetcode/stack/basic_calculator_ii_test.py b/test/leetcode/stack/basic_calculator_ii_test.py new file mode 100644 index 0000000..06b7256 --- /dev/null +++ b/test/leetcode/stack/basic_calculator_ii_test.py @@ -0,0 +1,16 @@ +from leetcode.stack.basic_calculator_ii import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.calculate("3+2*2") == 7 + + +def test_case_2(): + assert soln.calculate(" 3/2 ") == 1 + + +def test_case_3(): + assert soln.calculate(" 3+5 / 2 ") == 5 From 94ad5de352bad6260a4e9c092b8fdb98e045f7a1 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Tue, 18 Mar 2025 12:06:42 -0700 Subject: [PATCH 104/158] kv store --- src/leetcode/stack/basic-calculator.ts | 79 ------------------- src/leetcode/stack/basic_calculator.py | 83 ++++++++++++++++++++ test/leetcode/stack/basic-calculator.test.ts | 11 --- test/leetcode/stack/basic_calculator.py | 16 ++++ 4 files changed, 99 insertions(+), 90 deletions(-) delete mode 100644 src/leetcode/stack/basic-calculator.ts create mode 100644 src/leetcode/stack/basic_calculator.py delete mode 100644 test/leetcode/stack/basic-calculator.test.ts create mode 100644 test/leetcode/stack/basic_calculator.py diff --git a/src/leetcode/stack/basic-calculator.ts b/src/leetcode/stack/basic-calculator.ts deleted file mode 100644 index 9bb8540..0000000 --- a/src/leetcode/stack/basic-calculator.ts +++ /dev/null @@ -1,79 +0,0 @@ -// DIFFICULTY: HARD -// -// Given a string s representing a valid expression, implement a basic calculator to evaluate it, and return the result -// of the evaluation. -// -// Note: You are not allowed to use any built-in function which evaluates strings as mathematical expressions, such as -// eval(). -// -// See {@link https://leetcode.com/problems/basic-calculator} -export { calculate }; - -// SOLUTION: -// -// This is way harder than basic calculator ii, even though we don't consider multiplication and division. Here, we -// have to consider parentheses. -function calculate(s: string): number { - const stack = []; - let result = 0; - let sign = 1; - let n = 0; - - function isDigit(c: string) { - // Number(' ') gives you 0, but that's totally not a digit. - if (c === ' ') { - return false; - } - - return !Number.isNaN(Number(c)); - } - - for (let i = 0; i < s.length; i++) { - const c = s[i]; - - if (isDigit(c)) { - n = n * 10 + Number(c); - } else if (c === '+') { - // Whatever the current number is, add it to the result. The sign will account for subtractions. - result += sign * n; - - // Now we reset the sign to be POSITIVE and number to be 0. - sign = 1; - n = 0; - } else if (c === '-') { - // Whatever the current number is, add it to the result. The sign will account for subtractions. - result += sign * n; - - // Now we reset the sign to be NEGATIVE and number to be 0. - sign = -1; - n = 0; - } else if (c === '(') { - // Opening brace means we are evaluating a sub-expression. Whatever the current result and sign are, push it - // onto the stack. - stack.push(result); - stack.push(sign); - - // Now we reset the entire calculator (result, sign, current number) to evaluate the sub-expression. - result = 0; - sign = 1; - n = 0; - } else if (c === ')') { - // Closing brace means we are finished evaluating a sub-expression. Resolve the current operation and add it - // to the sub-expression result. - result += sign * n; - - // Pop off the sign. If we had a NEGATIVE sign when opening the brace, the entire sub-expression result needs - // to be negated. That is, '-(1+2)' would require us to do result *= -1. - result *= stack.pop()!; - - // Pop off the original result and add it to the sub-expression result. - result += stack.pop()!; - - // Now reset the calculator. - sign = 1; - n = 0; - } - } - - return result + sign * n; -} diff --git a/src/leetcode/stack/basic_calculator.py b/src/leetcode/stack/basic_calculator.py new file mode 100644 index 0000000..462fc4d --- /dev/null +++ b/src/leetcode/stack/basic_calculator.py @@ -0,0 +1,83 @@ +# DIFFICULTY: HARD +# ---------------- +# +# Given a string s representing a valid expression, implement a basic calculator to evaluate it, and return the result +# of the evaluation. +# +# Note: You are not allowed to use any built-in function which evaluates strings as mathematical expressions, such as +# eval(). +# +# See https://leetcode.com/problems/basic-calculator +class Solution: + def calculate(self, s: str) -> int: + """ + SOLUTION + -------- + + This is way harder than basic calculator ii, even though we don't consider multiplication and division. Here, + we have to consider parentheses. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the length of the string. + + Space complexity is O(n) because we are storing the numbers in a stack. + """ + stack: list[int] = [] + + # The sign of the current number. + sign = 1 + + # The current number. + number = 0 + + # The current sub expression value. + expr = 0 + + for c in s: + if c.isdigit(): + number = number * 10 + int(c) + elif c == "+": + # Whatever the current number is, add it to the current expression. The sign will account for + # subtractions. + expr += sign * number + + # Now we reset the sign to be POSITIVE and number to be 0. + sign = 1 + number = 0 + elif c == "-": + # Whatever the current number is, add it to the current expression. The sign will account for + # subtractions. + expr += sign * number + + # Now we reset the sign to be NEGATIVE and number to be 0. + sign = -1 + number = 0 + elif c == "(": + # Opening brace means we are evaluating an expression. Whatever the current expression and sign are, + # push it onto the stack. + stack.append(expr) + stack.append(sign) + + # Now we reset the entire calculator (result, sign, current number) to evaluate the expression. + expr = 0 + sign = 1 + number = 0 + elif c == ")": + # Closing brace means we are finished evaluating an expression. Resolve the current operation and + # add it to the current expression result. + expr += sign * number + + # Pop off the sign. If we had a NEGATIVE sign when opening the brace, the entire expression needs to be + # negated. That is, '-(1+2)' would require us to do result *= -1. + expr *= stack.pop() + + # Pop off the original result and add it to the expression. + expr += stack.pop() + + # Now reset the calculator. + sign = 1 + number = 0 + + return expr + sign * number diff --git a/test/leetcode/stack/basic-calculator.test.ts b/test/leetcode/stack/basic-calculator.test.ts deleted file mode 100644 index 0c3c8ad..0000000 --- a/test/leetcode/stack/basic-calculator.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { calculate } from '../../src/stack/basic-calculator'; - -describe('basic calculator', () => { - test('calculate - test case 1', async () => { - expect(calculate('1 + 1')).toStrictEqual(2); - }); - - test('calculate - test case 2', async () => { - expect(calculate('(1+(4+5+2)-3)+(6+8)')).toStrictEqual(23); - }); -}); diff --git a/test/leetcode/stack/basic_calculator.py b/test/leetcode/stack/basic_calculator.py new file mode 100644 index 0000000..cbc8e6c --- /dev/null +++ b/test/leetcode/stack/basic_calculator.py @@ -0,0 +1,16 @@ +from leetcode.stack.basic_calculator import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.calculate("1 + 1") == 2 + + +def test_case_2(): + assert soln.calculate(" 2-1 + 2 ") == 3 + + +def test_case_3(): + assert soln.calculate("(1+(4+5+2)-3)+(6+8)") == 23 From 0094cc73beca5c4efdf205c18db5d518b36c7c96 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Tue, 18 Mar 2025 13:11:35 -0700 Subject: [PATCH 105/158] kv store --- .../key-value-store-nested-transactions.ts | 103 ------------- .../key_value_store_nested_transactions.py | 91 ++++++++++++ ...ey-value-store-nested-transactions.test.ts | 108 -------------- ...ey_value_store_nested_transactions_test.py | 140 ++++++++++++++++++ 4 files changed, 231 insertions(+), 211 deletions(-) delete mode 100644 src/leetcode/stack/key-value-store-nested-transactions.ts create mode 100644 src/leetcode/stack/key_value_store_nested_transactions.py delete mode 100644 test/leetcode/stack/key-value-store-nested-transactions.test.ts create mode 100644 test/leetcode/stack/key_value_store_nested_transactions_test.py diff --git a/src/leetcode/stack/key-value-store-nested-transactions.ts b/src/leetcode/stack/key-value-store-nested-transactions.ts deleted file mode 100644 index 96f4013..0000000 --- a/src/leetcode/stack/key-value-store-nested-transactions.ts +++ /dev/null @@ -1,103 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Implement a key-value store with the following functionality: -// -// - get(key: string) -// - set(key: string, value: number) -// - delete(key: string) -// - begin() starts a transaction -// - commit() commits a transaction -// - rollback() rolls back a transaction -// -// This structure supports nested transactions, and the topmost transaction should be able to see the values in the -// transactions below it. -// -// NOTE: This is not an official LeetCode question, but it was asked by Cruise and Bloomberg (sometimes with nested -// transactions and sometimes without). -// -// See {@link https://leetcode.com/discuss/interview-question/279913/Bloomberg-or-Onsite-or-Key-Value-Store-with-transactions} -export { KeyValueStore }; - -// SOLUTION: -// -// The key to this problem is to maintain a stack of maps. When you start a transaction, you push a new map onto the -// stack. -// -// To improve runtime performance, we maintain a current transaction context that gets propagated to the top of the -// stack (or thrown away if we rollback). -class KeyValueStore { - private readonly stack: Map[]; - - private readonly map: Map; - - constructor() { - this.stack = []; - this.map = new Map(); - } - - public get(key: string): number | undefined { - if (this.stack.length === 0) { - return this.map.get(key); - } - - let frame = this.stack.length - 1; - while (frame >= 0) { - if (this.stack[frame].has(key)) { - return this.stack[frame].get(key) ?? undefined; - } - - frame--; - } - - return undefined; - } - - public set(key: string, value: number): void { - if (this.stack.length === 0) { - this.map.set(key, value); - return; - } - - this.stack[this.stack.length - 1].set(key, value); - } - - public delete(key: string): void { - if (this.stack.length === 0) { - this.map.delete(key); - return; - } - - this.stack[this.stack.length - 1].set(key, null); - } - - public begin(): void { - this.stack.push(new Map()); - } - - public commit() { - if (this.stack.length === 0) { - return; - } - - const target = this.stack.length === 1 ? this.map : this.stack[this.stack.length - 2]; - const source = this.stack.pop()!; - - for (const [key, value] of source) { - if (value === null) { - target.delete(key); - continue; - } - - target.set(key, value); - } - } - - public rollback() { - if (this.stack.length === 0) { - return; - } - - const map = this.stack.pop()!; - map.clear(); - } -} diff --git a/src/leetcode/stack/key_value_store_nested_transactions.py b/src/leetcode/stack/key_value_store_nested_transactions.py new file mode 100644 index 0000000..eca0786 --- /dev/null +++ b/src/leetcode/stack/key_value_store_nested_transactions.py @@ -0,0 +1,91 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Implement a key-value store with the following functionality: +# +# - get(key: string) +# - set(key: string, value: number) +# - delete(key: string) +# - begin() starts a transaction +# - commit() commits a transaction +# - rollback() rolls back a transaction +# +# This structure supports nested transactions, and the topmost transaction should be able to see the values in the +# transactions below it. +# +# See https://leetcode.com/discuss/interview-question/279913/Bloomberg-or-Onsite-or-Key-Value-Store-with-transactions +class KeyValueStore: + """ + SOLUTION + -------- + + Use a stack to store the transactions. Each transaction is a dictionary that maps keys to values. + + COMPLEXITY + ---------- + + Time complexity is O(1) for all operations. + + Space complexity is O(n) where n is the number of keys in the data structure. + """ + + def __init__(self) -> None: + self.deletions: list[set[str]] = [] + self.txs: list[dict[str, int]] = [] + self.main: dict[str, int] = {} + + def get(self, key: str) -> int | None: + if not self.txs: + return self.main.get(key, None) + + frame = len(self.txs) - 1 + while frame >= 0: + if key in self.deletions[frame]: + return None + if key in self.txs[frame]: + return self.txs[frame].get(key, None) + frame -= 1 + + return None + + def set(self, key: str, value: int) -> None: + if not self.txs: + self.main[key] = value + return + + self.txs[-1][key] = value + + def delete(self, key: str) -> None: + if not self.txs: + self.main.pop(key, None) + return + + self.txs[-1].pop(key, None) + self.deletions[-1].add(key) + + def begin(self) -> None: + self.txs.append({}) + self.deletions.append(set()) + + def commit(self) -> None: + if not self.txs: + return + + target = self.main if len(self.txs) == 1 else self.txs[-2] + source = self.txs[-1] + + for key, value in source.items(): + target[key] = value + + for key in self.deletions[-1]: + target.pop(key, None) + + self.txs.pop() + self.deletions.pop() + + def rollback(self) -> None: + if not self.txs: + return + + self.txs.pop() + self.deletions.pop() diff --git a/test/leetcode/stack/key-value-store-nested-transactions.test.ts b/test/leetcode/stack/key-value-store-nested-transactions.test.ts deleted file mode 100644 index ab10f17..0000000 --- a/test/leetcode/stack/key-value-store-nested-transactions.test.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { KeyValueStore } from '../../src/stack/key-value-store-nested-transactions'; - -describe('key value store with nested transactions', () => { - test('key value store with nested transactions - test case 1', async () => { - const store = new KeyValueStore(); - - store.set('a', 1); - store.set('b', 2); - store.set('c', 3); - - expect(store.get('a')).toBe(1); - expect(store.get('b')).toBe(2); - expect(store.get('c')).toBe(3); - - store.delete('c'); - - expect(store.get('c')).toBeUndefined(); - }); - - test('key value store with nested transactions - test case 2', async () => { - const store = new KeyValueStore(); - - store.set('a', 1); - store.set('b', 2); - store.set('c', 3); - store.begin(); - store.set('a', 10); - store.set('b', 20); - store.delete('c'); - - expect(store.get('a')).toBe(10); - expect(store.get('b')).toBe(20); - expect(store.get('c')).toBeUndefined(); - - store.commit(); - - expect(store.get('a')).toBe(10); - expect(store.get('b')).toBe(20); - }); - - test('key value store with nested transactions - test case 3', async () => { - const store = new KeyValueStore(); - - store.set('a', 1); - store.set('b', 2); - store.set('c', 3); - store.begin(); - store.set('a', 10); - store.set('b', 20); - store.delete('c'); - - expect(store.get('a')).toBe(10); - expect(store.get('b')).toBe(20); - expect(store.get('c')).toBeUndefined(); - - store.rollback(); - - expect(store.get('a')).toBe(1); - expect(store.get('b')).toBe(2); - expect(store.get('c')).toBe(3); - }); - - test('key value store with nested transactions - test case 4', async () => { - const store = new KeyValueStore(); - - store.set('a', 1); - store.begin(); - store.set('a', 10); - store.begin(); - store.set('a', 100); - - expect(store.get('a')).toBe(100); - - store.commit(); - - expect(store.get('a')).toBe(100); - - store.commit(); - - expect(store.get('a')).toBe(100); - }); - - test('key value store with nested transactions - test case 5', async () => { - const store = new KeyValueStore(); - - store.set('a', 1); - store.set('b', 2); - store.begin(); - store.delete('a'); - store.set('b', 20); - store.begin(); - store.set('a', 100); - store.delete('b'); - - expect(store.get('a')).toBe(100); - expect(store.get('b')).toBeUndefined(); - - store.rollback(); - - expect(store.get('a')).toBeUndefined(); - expect(store.get('b')).toBe(20); - - store.rollback(); - - expect(store.get('a')).toBe(1); - expect(store.get('b')).toBe(2); - }); -}); diff --git a/test/leetcode/stack/key_value_store_nested_transactions_test.py b/test/leetcode/stack/key_value_store_nested_transactions_test.py new file mode 100644 index 0000000..f62d0bd --- /dev/null +++ b/test/leetcode/stack/key_value_store_nested_transactions_test.py @@ -0,0 +1,140 @@ +from leetcode.stack.key_value_store_nested_transactions import KeyValueStore + + +def test_case_1(): + store = KeyValueStore() + + # data: {a: 1, b: 2, c: 3} + store.set("a", 1) + store.set("b", 2) + store.set("c", 3) + + assert store.get("a") == 1 + assert store.get("b") == 2 + assert store.get("c") == 3 + + # data: {a: 1, b: 2} + store.delete("c") + + assert not store.get("c") + + +def test_case_2(): + store = KeyValueStore() + + # data: {a: 1, b: 2, c: 3} + store.set("a", 1) + store.set("b", 2) + store.set("c", 3) + + # data: {a: 1, b: 2, c: 3} + # tx1: {a: 10, b: 20} + store.begin() + store.set("a", 10) + store.set("b", 20) + store.delete("c") + + assert store.get("a") == (10) + assert store.get("b") == (20) + assert not store.get("c") + + # data: {a: 10, b: 20} + store.commit() + + assert store.get("a") == 10 + assert store.get("b") == 20 + + +def test_case_3(): + store = KeyValueStore() + + # data: {a: 1, b: 2, c: 3} + store.set("a", 1) + store.set("b", 2) + store.set("c", 3) + + # data: {a: 1, b: 2, c: 3} + # tx1: {a: 10, b: 20} + store.begin() + store.set("a", 10) + store.set("b", 20) + store.delete("c") + + assert store.get("a") == 10 + assert store.get("b") == 20 + assert not store.get("c") + + # data: {a: 1, b: 2, c: 3} + store.rollback() + + assert store.get("a") == 1 + assert store.get("b") == 2 + assert store.get("c") == 3 + + +def test_case_4(): + store = KeyValueStore() + + # data: {a: 1} + store.set("a", 1) + + # data: {a: 1} + # tx1: {a: 10} + store.begin() + store.set("a", 10) + + # data: {a: 1} + # tx1: {a: 10} + # tx2: {a: 100} + store.begin() + store.set("a", 100) + + assert store.get("a") == 100 + + # data: {a: 1} + # tx1: {a: 100} + store.commit() + + assert store.get("a") == 100 + + # data: {a: 100} + store.commit() + + assert store.get("a") == 100 + + +def test_case_5(): + store = KeyValueStore() + + # data: {a: 1, b: 2} + store.set("a", 1) + store.set("b", 2) + + # data: {a: 1, b: 2} + # tx1: {b: 20} + store.begin() + store.delete("a") + store.set("b", 20) + + # data: {a: 1, b: 2} + # tx1: {b: 20} + # tx2: {a: 100} + store.begin() + store.set("a", 100) + store.delete("b") + + assert store.get("a") == 100 + assert not store.get("b") + + # data: {a: 1, b: 2} + # tx1: {b: 20} + store.rollback() + + assert not store.get("a") + assert store.get("b") == 20 + + # data: {a: 1, b: 2} + store.rollback() + + assert store.get("a") == 1 + assert store.get("b") == 2 From d65370cd0b86895474c4c8aebb1c907a7b7b2483 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Tue, 18 Mar 2025 13:44:03 -0700 Subject: [PATCH 106/158] longest abs filepath --- .../stack/longest-absolute-file-path.ts | 93 ------------------ .../stack/longest_absolute_file_path.py | 94 +++++++++++++++++++ .../stack/longest-absolute-file-path.test.ts | 7 -- .../stack/longest_absolute_file_path_test.py | 12 +++ 4 files changed, 106 insertions(+), 100 deletions(-) delete mode 100644 src/leetcode/stack/longest-absolute-file-path.ts create mode 100644 src/leetcode/stack/longest_absolute_file_path.py delete mode 100644 test/leetcode/stack/longest-absolute-file-path.test.ts create mode 100644 test/leetcode/stack/longest_absolute_file_path_test.py diff --git a/src/leetcode/stack/longest-absolute-file-path.ts b/src/leetcode/stack/longest-absolute-file-path.ts deleted file mode 100644 index ebf0716..0000000 --- a/src/leetcode/stack/longest-absolute-file-path.ts +++ /dev/null @@ -1,93 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Suppose we have a file system that stores both files and directories. An example of one system is represented in the -// following picture: -// -// Here, we have `dir` as the only directory in the root. `dir` contains two subdirectories, `subdir1` and `subdir2`. -// `subdir1` contains a file `file1.ext` and subdirectory `subsubdir1`. `subdir2` contains a subdirectory `subsubdir2`, -// which contains a file `file2.ext`. -// -// In text form, it looks like this (with ⟶ representing the tab character): -// -// dir -// ⟶ subdir1 -// ⟶ ⟶ file1.ext -// ⟶ ⟶ subsubdir1 -// ⟶ subdir2 -// ⟶ ⟶ subsubdir2 -// ⟶ ⟶ ⟶ file2.ext -// -// If we were to write this representation in code, it will look like this: -// -// - "dir\n\tsubdir1\n\t\tfile1.ext\n\t\tsubsubdir1\n\tsubdir2\n\t\tsubsubdir2\n\t\t\tfile2.ext". -// -// Note that the '\n' and '\t' are the new-line and tab characters. -// -// Every file and directory has a unique absolute path in the file system, which is the order of directories that must -// be opened to reach the file/directory itself, all concatenated by '/'s. Using the above example, the absolute path -// to file2.ext is "dir/subdir2/subsubdir2/file2.ext". Each directory name consists of letters, digits, and/or spaces. -// Each file name is of the form name.extension, where name and extension consist of letters, digits, and/or spaces. -// -// Given a string input representing the file system in the explained format, return the length of the longest absolute -// path to a file in the abstracted file system. If there is no file in the system, return 0. -// -// Note that the testcases are generated such that the file system is valid and no file or directory name has length 0. -// -// See {@link https://leetcode.com/problems/longest-absolute-file-path/} -export { lengthLongestPath }; - -// SOLUTION: -// -// A solution can be efficient by using a stack to keep track of directory depth. However, this only works if the -// input does not contain duplicate sub directories, and all of the files appear right after the subdirectory they are -// in. If it turns out the input can vary such that files and subdirectories appear out of order, or the -// subdirectories can contain duplicates, we'll have to actually build out a real filesystem structure using a graph -// of FileNode (with strings mapping to subdirectory FileNodes). -// -// In this solution, we will assume that the input is constrained. -function lengthLongestPath(input: string): number { - // This is a stack that will keep track of where we are in the directory structure. We'll assume that every - // directory has a trailing slash (no leading slash). We never calculate directory lengths, so that will never - // mess up our calculation. - const dirs: string[] = []; - - // The current and max file lengths. - let current = 0; - let max = 0; - - const parts = input.split('\n'); - for (const part of parts) { - // This indicates that the part of the text is in some subdirectory; we should locate the subdirectory by popping - // directories off of the stack. This will put us at the correct subdirectory after all the popping. - // - // The number of tabs tells us how many elements should be in the stack; if there are no tabs, the last index is - // -1. If there is 1 tab, the index is 0, and so the number of levels will be the last index + 1. - const levels = part.lastIndexOf('\t') + 1; - if (levels !== -1) { - // Pop directories off the stack until it matches levels. - while (dirs.length > levels) { - const dir = dirs.pop()!; - current -= dir.length; - } - } - - // To get the name of the file, we replace all the tabs with empty string. We can also slice the string starting - // from the original value of levels. - const name = levels === -1 ? part : part.slice(levels); - - // This indicates that the part of the text is a file, so we should calculate the file length. - if (name.includes('.')) { - max = Math.max(max, current + name.length); - continue; - } - - // If it doesn't start with a tab or contain a dot, that means we are looking at a directory, so we should push - // it onto the stack. - // - // Add 1 to the value to account for the trailing slash (there is no leading slash). - dirs.push(`${name}/`); - current += name.length + 1; - } - - return max; -} diff --git a/src/leetcode/stack/longest_absolute_file_path.py b/src/leetcode/stack/longest_absolute_file_path.py new file mode 100644 index 0000000..46122ef --- /dev/null +++ b/src/leetcode/stack/longest_absolute_file_path.py @@ -0,0 +1,94 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Suppose we have a file system that stores both files and directories. An example of one system is represented in the +# following picture: +# +# Here, we have `dir` as the only directory in the root. `dir` contains two subdirectories, `subdir1` and `subdir2`. +# `subdir1` contains a file `file1.ext` and subdirectory `subsubdir1`. `subdir2` contains a subdirectory `subsubdir2`, +# which contains a file `file2.ext`. +# +# In text form, it looks like this (with ⟶ representing the tab character): +# +# dir +# ⟶ subdir1 +# ⟶ ⟶ file1.ext +# ⟶ ⟶ subsubdir1 +# ⟶ subdir2 +# ⟶ ⟶ subsubdir2 +# ⟶ ⟶ ⟶ file2.ext +# +# If we were to write this representation in code, it will look like this: +# +# - "dir\n\tsubdir1\n\t\tfile1.ext\n\t\tsubsubdir1\n\tsubdir2\n\t\tsubsubdir2\n\t\t\tfile2.ext". +# +# Note that the '\n' and '\t' are the new-line and tab characters. +# +# Every file and directory has a unique absolute path in the file system, which is the order of directories that must +# be opened to reach the file/directory itself, all concatenated by '/'s. Using the above example, the absolute path +# to file2.ext is "dir/subdir2/subsubdir2/file2.ext". Each directory name consists of letters, digits, and/or spaces. +# Each file name is of the form name.extension, where name and extension consist of letters, digits, and/or spaces. +# +# Given a string input representing the file system in the explained format, return the length of the longest absolute +# path to a file in the abstracted file system. If there is no file in the system, return 0. +# +# Note that the testcases are generated such that the file system is valid and no file or directory name has length 0. +# +# See https://leetcode.com/problems/longest-absolute-file-path +class Solution: + def lengthLongestPath(self, input: str) -> int: + """ + SOLUTION + -------- + + A solution can be efficient by using a stack to keep track of directory depth. However, this only works if the + input does not contain duplicate sub directories, and all of the files appear right after the subdirectory they + are in. If it turns out the input can vary such that files and subdirectories appear out of order, or the + subdirectories can contain duplicates, we'll have to actually build out a real filesystem structure using a + graph of FileNode (with strings mapping to subdirectory FileNodes). + + In this solution, we will assume that the input is constrained. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the length of the input string. + + Space complexity is O(n) because we are storing the file path in a stack. + """ + # This is a stack that will keep track of where we are in the directory structure. We'll assume that every + # directory has a trailing slash (no leading slash). We never calculate directory lengths, so that won't mess + # up our calculation. + stack: list[str] = [] + + # The current and max file lengths. + current_len = 0 + max_len = 0 + + parts = input.split("\n") + for part in parts: + # If the string contents a "\t" character, that means that this part of the text is in a subdirectory. We + # can locate the subdirectory by popping directories off of the stack. + # + # We can figure out how many directories to pop by counting the number of tabs in the string. + levels = part.count("\t") + while len(stack) > levels: + # Adjust the current length by subtracting the length of the directory name; note that the directory + # name doesn't include a trailing slash but the length should account for it. + current_len -= len(stack.pop()) + 1 + + # To get the name of the file, we replace all the tabs with empty string. + name = part.replace("\t", "") + + # All directories have no extension, and all files have an extension. If we see a dot in the name, that + # means its a file. + if "." in name: + max_len = max(max_len, current_len + len(name)) + else: + # Otherwise, we are looking at a directory, and we should push it onto the stack. The stack doesn't need to + # store the directory with the trailing slash, but make sure to account for it in the current length + # calculation. + stack.append(name) + current_len += len(name) + 1 + + return max_len diff --git a/test/leetcode/stack/longest-absolute-file-path.test.ts b/test/leetcode/stack/longest-absolute-file-path.test.ts deleted file mode 100644 index 12a1d89..0000000 --- a/test/leetcode/stack/longest-absolute-file-path.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { lengthLongestPath } from '../../src/stack/longest-absolute-file-path'; - -describe('longest absolute file path', () => { - test('longest absolute file path - test case 1', async () => { - expect(lengthLongestPath('dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext')).toBe(20); - }); -}); diff --git a/test/leetcode/stack/longest_absolute_file_path_test.py b/test/leetcode/stack/longest_absolute_file_path_test.py new file mode 100644 index 0000000..764a2ec --- /dev/null +++ b/test/leetcode/stack/longest_absolute_file_path_test.py @@ -0,0 +1,12 @@ +from leetcode.stack.longest_absolute_file_path import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.lengthLongestPath("dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext") == 20 + + +def test_case_2(): + assert soln.lengthLongestPath("dir\n file.txt") == 12 From faba1238aa246126f1684d80f76a25d1d8243494 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Tue, 18 Mar 2025 14:00:18 -0700 Subject: [PATCH 107/158] max to make sorted --- .../stack/max-chunks-to-make-sorted-ii.ts | 61 ----------------- .../stack/max_chunks_to_make_sorted_ii.py | 65 +++++++++++++++++++ .../max-chunks-to-make-sorted-ii.test.ts | 7 -- .../max_chunks_to_make_sorted_ii_test.py | 12 ++++ 4 files changed, 77 insertions(+), 68 deletions(-) delete mode 100644 src/leetcode/stack/max-chunks-to-make-sorted-ii.ts create mode 100644 src/leetcode/stack/max_chunks_to_make_sorted_ii.py delete mode 100644 test/leetcode/stack/max-chunks-to-make-sorted-ii.test.ts create mode 100644 test/leetcode/stack/max_chunks_to_make_sorted_ii_test.py diff --git a/src/leetcode/stack/max-chunks-to-make-sorted-ii.ts b/src/leetcode/stack/max-chunks-to-make-sorted-ii.ts deleted file mode 100644 index 7ba4dec..0000000 --- a/src/leetcode/stack/max-chunks-to-make-sorted-ii.ts +++ /dev/null @@ -1,61 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given an integer array arr. -// -// We split arr into some number of chunks (i.e., partitions), and individually sort each chunk. After concatenating -// them, the result should equal the sorted array. -// -// Return the largest number of chunks we can make to sort the array. -// -// See {@link https://leetcode.com/problems/max-chunks-to-make-sorted-ii/} -export { maxChunksToSorted }; - -// SOLUTION: -// -// We can use a stack to keep track of chunks. Rather than store the *entire* chunk on the stack, we just store the -// largest element. That's because if we have chunks [1, 3] and [4, 5, 9], it's enough to know that [? to 3] and -// [? to 9] form chunks. -// -// When we encounter a new number, like 10, we make a new chunk (since we want as many chunks as possible). When we -// encounter a new number, like 7, we include it as part of the chunk from [? to 9]. But because we might have seen -// a 7 or 6 or some other number in between earlier, we'll have to go down the stack and prune out numbers we don't -// need anymore. -function maxChunksToSorted(xs: number[]) { - // The element stack[i] is the max element of chunk i. At the end, the length of the stack is the number of chunks - // we can create. - // - // We keep track of the max element of chunks, because the smallest element of the next chunk needs to be greater - // than the maximum element of the current chunk. - const stack = [xs[0]]; - - // Start iteration at 1 because you already pushed the first element onto the stack. - for (let i = 1; i < xs.length; i++) { - const x = xs[i]; - - // If this element is larger than top of stack, then xs[i] is part of a new chunk. Therefore, push it onto the - // stack and begin the new chunk. - // - // Conceptually, if you have chunks [1], [3], [5] then you see a [9], then this starts a new chunk. - if (x > stack[stack.length - 1]) { - stack.push(x); - continue; - } - - // Otherwise, xs[i] is part of the chunk which is made up of all chunks where stack[j] > xs[i]. Pop these chunks - // off the stack so you can "merge" them. - // - // Conceptually, if you have chunks [1], [3], [5], [9], then you see a [4], then you can't begin a new chunk. - // Instead, you know that [4, 5, 9] constitute a chunk. However, we don't need to store all numbers [4, 5, 9] in - // the stack; we only need to store the largest element in the chunk [9]. - // - // Therefore, go down the stack and pop off elements in the stack we don't want. - const max = stack[stack.length - 1]; - while (x < stack[stack.length - 1]) { - stack.pop(); - } - - stack.push(max); - } - - return stack.length; -} diff --git a/src/leetcode/stack/max_chunks_to_make_sorted_ii.py b/src/leetcode/stack/max_chunks_to_make_sorted_ii.py new file mode 100644 index 0000000..e5ea6de --- /dev/null +++ b/src/leetcode/stack/max_chunks_to_make_sorted_ii.py @@ -0,0 +1,65 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given an integer array arr. +# +# We split arr into some number of chunks (i.e., partitions), and individually sort each chunk. After concatenating +# them, the result should equal the sorted array. +# +# Return the largest number of chunks we can make to sort the array. +# +# See https://leetcode.com/problems/max-chunks-to-make-sorted-ii +class Solution: + def maxChunksToSorted(self, xs: list[int]) -> int: + """ + SOLUTION + -------- + + We can use a stack to keep track of chunks. Rather than store the *entire* chunk on the stack, we just store + the largest element. That's because if we have chunks [1, 3] and [4, 5, 9], it's enough to know that [? to 3] + and [? to 9] form chunks. + + When we encounter a new number, like 10, we make a new chunk (since we want as many chunks as possible). When + we encounter a new number, like 7, we include it as part of the chunk from [? to 9]. But because we might have + seen a 7 or 6 or some other number in between earlier, we'll have to go down the stack and prune out numbers we + don't need anymore. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of elements in arr. + + Space complexity is O(1). + """ + # The element stack[i] is the max element of chunk i. At the end, the length of the stack is the number of + # chunks we can create. + # + # We keep track of the max element of chunks, because the smallest element of the next chunk needs to be greater + # than the maximum element of the current chunk. + stack = [xs[0]] + + # Start iteration at 1 because you already pushed the first element onto the stack. + for x in xs[1:]: + # If this element is larger than top of stack, then xs[i] is part of a new chunk. Therefore, push it onto + # the stack and begin the new chunk. + # + # Conceptually, if you have chunks [1], [3], [5] then you see a [9], then this starts a new chunk. + if x > stack[-1]: + stack.append(x) + continue + + # Otherwise, xs[i] is part of the chunk which is made up of all chunks where stack[j] > xs[i]. Pop these + # chunks off the stack so you can "merge" them. + # + # Conceptually, if you have chunks [1], [3], [5], [9], then you see a [4], then you can't begin a new chunk. + # Instead, you know that [4, 5, 9] constitute a chunk. However, we don't need to store all numbers + # [4, 5, 9] in # the stack; we only need to store the largest element in the chunk [9]. + # + # Therefore, go down the stack and pop off elements in the stack we don't want. + max_value = stack[-1] + while stack and x < stack[-1]: + stack.pop() + + stack.append(max_value) + + return len(stack) diff --git a/test/leetcode/stack/max-chunks-to-make-sorted-ii.test.ts b/test/leetcode/stack/max-chunks-to-make-sorted-ii.test.ts deleted file mode 100644 index acb5f78..0000000 --- a/test/leetcode/stack/max-chunks-to-make-sorted-ii.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { maxChunksToSorted } from '../../src/stack/max-chunks-to-make-sorted-ii'; - -describe('max chunks to make sorted ii', () => { - test('max chunks to make sorted ii - test case 1', async () => { - expect(maxChunksToSorted([5, 4, 3, 2, 1])).toBe(1); - }); -}); diff --git a/test/leetcode/stack/max_chunks_to_make_sorted_ii_test.py b/test/leetcode/stack/max_chunks_to_make_sorted_ii_test.py new file mode 100644 index 0000000..b78a104 --- /dev/null +++ b/test/leetcode/stack/max_chunks_to_make_sorted_ii_test.py @@ -0,0 +1,12 @@ +from leetcode.stack.max_chunks_to_make_sorted_ii import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.maxChunksToSorted([5, 4, 3, 2, 1]) == 1 + + +def test_case_2(): + assert soln.maxChunksToSorted([2, 1, 3, 4, 4]) == 4 From 83e53bee2d35dce444aaddc2549ab14722a872f9 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Tue, 18 Mar 2025 14:12:46 -0700 Subject: [PATCH 108/158] min_stack --- src/leetcode/array/design_hit_counter.py | 23 +++---- .../dot_product_of_two_sparse_vectors.py | 31 +++++----- src/leetcode/heap/max_stack.py | 33 +++++----- .../prefix_sum/random_pick_with_weight.py | 57 +++++++++--------- .../prefix_sum/range_sum_query_immutable.py | 21 +++---- src/leetcode/stack/min-stack.ts | 60 ------------------- src/leetcode/stack/min_stack.py | 57 ++++++++++++++++++ test/leetcode/stack/max-stack.test.ts | 18 ------ test/leetcode/stack/min-stack.test.ts | 17 ------ test/leetcode/stack/min_stack_test.py | 14 +++++ 10 files changed, 156 insertions(+), 175 deletions(-) delete mode 100644 src/leetcode/stack/min-stack.ts create mode 100644 src/leetcode/stack/min_stack.py delete mode 100644 test/leetcode/stack/max-stack.test.ts delete mode 100644 test/leetcode/stack/min-stack.test.ts create mode 100644 test/leetcode/stack/min_stack_test.py diff --git a/src/leetcode/array/design_hit_counter.py b/src/leetcode/array/design_hit_counter.py index 00003d1..1bc8ac8 100644 --- a/src/leetcode/array/design_hit_counter.py +++ b/src/leetcode/array/design_hit_counter.py @@ -9,21 +9,22 @@ # # See https://leetcode.com/problems/design-hit-counter class HitCounter: - def __init__(self) -> None: - """ - SOLUTION - -------- + """ + SOLUTION + -------- + + To do this efficiently we'll have to use a circular array buffer. This is the same technique used by time + series databases. - To do this efficiently we'll have to use a circular array buffer. This is the same technique used by time - series databases. + COMPLEXITY + ---------- - COMPLEXITY - ---------- + Time complexity is O(1) for both methods since we fix the array size at 300. - Time complexity is O(1) for both methods since we fix the array size at 300. + Space complexity is O(1) for both methods because we fix the array size at 300. + """ - Space complexity is O(1) for both methods because we fix the array size at 300. - """ + def __init__(self) -> None: # Each event is a [timestamp, hit_count]. Since our granularity is in seconds, we only need to track the last # 300 seconds. self.timestamps = [0 for _ in range(300)] diff --git a/src/leetcode/array/dot_product_of_two_sparse_vectors.py b/src/leetcode/array/dot_product_of_two_sparse_vectors.py index 81e2125..30c8c1c 100644 --- a/src/leetcode/array/dot_product_of_two_sparse_vectors.py +++ b/src/leetcode/array/dot_product_of_two_sparse_vectors.py @@ -15,26 +15,27 @@ # # See https://leetcode.com/problems/dot-product-of-two-sparse-vectors class SparseVector: - def __init__(self, nums: list[int]) -> None: - """ - SOLUTION - -------- + """ + SOLUTION + -------- + + If the vector is sparse, just store the non-zero values in a map of index to value. That will compress for + storage; to compute the dot product you can either decompress and then do the dot product, or you can do the + multiplication over the non-zero values directly. - If the vector is sparse, just store the non-zero values in a map of index to value. That will compress for - storage; to compute the dot product you can either decompress and then do the dot product, or you can do the - multiplication over the non-zero values directly. + Doing it without decompression is more efficient, and we should start with the smaller vector to minimize the + number of operations. - Doing it without decompression is more efficient, and we should start with the smaller vector to minimize the - number of operations. + COMPLEXITY + ---------- - COMPLEXITY - ---------- + Time complexity is O(k) in the best case, where k is the number of non-zero elements. In the worst case, + though, it could still be O(n) if both vectors are dense. - Time complexity is O(k) in the best case, where k is the number of non-zero elements. In the worst case, - though, it could still be O(n) if both vectors are dense. + Space complexity is O(k) in the best case, where k is the number of non-zero elements. + """ - Space complexity is O(k) in the best case, where k is the number of non-zero elements. - """ + def __init__(self, nums: list[int]) -> None: # This compresses the vector so that non-zero values are mapped. self.map: dict[int, int] = {i: num for i, num in enumerate(nums) if num != 0} diff --git a/src/leetcode/heap/max_stack.py b/src/leetcode/heap/max_stack.py index 1cfecc6..7eeacea 100644 --- a/src/leetcode/heap/max_stack.py +++ b/src/leetcode/heap/max_stack.py @@ -43,28 +43,29 @@ def __init__(self, key: int, value: int): class MaxStack: - def __init__(self): - """ - SOLUTION - -------- + """ + SOLUTION + -------- + + Use a heap and a doubly linked list to represent the max stack. Most operations can be performed in O(1) or + O(log n). The only challenge is that when popping from the stack, it's easy to remove from the end of the + linked list, but it's not easy to remove from the middle of the max heap. - Use a heap and a doubly linked list to represent the max stack. Most operations can be performed in O(1) or - O(log n). The only challenge is that when popping from the stack, it's easy to remove from the end of the - linked list, but it's not easy to remove from the middle of the max heap. + Instead of removing from the max heap immediately, defer the deletion until we perform a peekMax or popMax, and + check if processing the deferred deletions are required. If so, perform them there. - Instead of removing from the max heap immediately, defer the deletion until we perform a peekMax or popMax, and - check if processing the deferred deletions are required. If so, perform them there. + For the purposes of this solution, we won't consider edge cases like popping from an empty stack. This keeps + the solution simple. - For the purposes of this solution, we won't consider edge cases like popping from an empty stack. This keeps - the solution simple. + COMPLEXITY + ---------- - COMPLEXITY - ---------- + Time complexity is O(1) for top and O(log n) for each other call. - Time complexity is O(1) for top and O(log n) for each other call. + Space complexity is O(n). + """ - Space complexity is O(n). - """ + def __init__(self): # Because the stack could have multiple duplicate elements, use a unique ID for each node. self.keys = 0 # Because deleting from the middle of a heap is hard, keep track of a set of deletions while popping and defer diff --git a/src/leetcode/prefix_sum/random_pick_with_weight.py b/src/leetcode/prefix_sum/random_pick_with_weight.py index ae8eba2..bb71d62 100644 --- a/src/leetcode/prefix_sum/random_pick_with_weight.py +++ b/src/leetcode/prefix_sum/random_pick_with_weight.py @@ -14,46 +14,47 @@ class Solution: - def __init__(self, w: list[int]) -> None: - """ - SOLUTION - -------- + """ + SOLUTION + -------- + + The naive way to do this is to take the weights and create a new array that's the size of the sum of the + weights. For example, if you have the input [1, 3], create an array [0, 1, 1, 1]. Then, generate a random + number using floor(random() * 4) to get a number between 0 and 3 inclusive. That's the index you should pick. - The naive way to do this is to take the weights and create a new array that's the size of the sum of the - weights. For example, if you have the input [1, 3], create an array [0, 1, 1, 1]. Then, generate a random - number using floor(random() * 4) to get a number between 0 and 3 inclusive. That's the index you should pick. + This will work, but once you have a large number of weights, you'll be creating quite large arrays. Probably + not a good idea. - This will work, but once you have a large number of weights, you'll be creating quite large arrays. Probably - not a good idea. + Instead, we can compute a prefix sum to avoid expanding an array. In the previous example, you can turn the + input array [1, 3] into the prefix sum array [1, 4]. The cumulative weights divide the range in this way: - Instead, we can compute a prefix sum to avoid expanding an array. In the previous example, you can turn the - input array [1, 3] into the prefix sum array [1, 4]. The cumulative weights divide the range in this way: + [0, 1) -> Index 0 + [1, 4) -> Index 1 - [0, 1) -> Index 0 - [1, 4) -> Index 1 + So if you generate a random number between [0, 4), you can do a linear scan of the prefix sum array to find the + index that you should pick. This works because each range's weight increases as you move to the right. - So if you generate a random number between [0, 4), you can do a linear scan of the prefix sum array to find the - index that you should pick. This works because each range's weight increases as you move to the right. + Likewise, if you had the input array [1, 4, 2], the prefix sum array would be [1, 5, 7]. The ranges would be: - Likewise, if you had the input array [1, 4, 2], the prefix sum array would be [1, 5, 7]. The ranges would be: + [0, 1) -> Index 0 + [1, 5) -> Index 1 + [5, 7) -> Index 2 - [0, 1) -> Index 0 - [1, 5) -> Index 1 - [5, 7) -> Index 2 + And again, picking a random number between [0, 7) would allow you to do a linear scan to find the index to pick. - And again, picking a random number between [0, 7) would allow you to do a linear scan to find the index to pick. + But wait! Instead of doing a linear scan, it's much more efficient to do a binary search, which is what this + solution does. - But wait! Instead of doing a linear scan, it's much more efficient to do a binary search, which is what this - solution does. + COMPLEXITY + ---------- - COMPLEXITY - ---------- + Time complexity in O(n). It is O(n) to create the prefix sum array, then O(n) to perform a linear scan, or + O(log n) to perform a binary search. - Time complexity in O(n). It is O(n) to create the prefix sum array, then O(n) to perform a linear scan, or - O(log n) to perform a binary search. + Space complexity is O(n) because we are storing the prefix sum array. + """ - Space complexity is O(n) because we are storing the prefix sum array. - """ + def __init__(self, w: list[int]) -> None: self.prefix_sum = [0] * len(w) self.total = 0 for i, weight in enumerate(w): diff --git a/src/leetcode/prefix_sum/range_sum_query_immutable.py b/src/leetcode/prefix_sum/range_sum_query_immutable.py index 50ba1c8..7b3f774 100644 --- a/src/leetcode/prefix_sum/range_sum_query_immutable.py +++ b/src/leetcode/prefix_sum/range_sum_query_immutable.py @@ -15,20 +15,21 @@ class NumArray: - def __init__(self, nums: list[int]) -> None: - """ - SOLUTION - -------- + """ + SOLUTION + -------- + + This can be done with a prefix sum array. - This can be done with a prefix sum array. + COMPLEXITY + ---------- - COMPLEXITY - ---------- + Time complexity is O(n) because we are iterating through the list once. - Time complexity is O(n) because we are iterating through the list once. + Space complexity is O(n) because we are storing the prefix sum array. + """ - Space complexity is O(n) because we are storing the prefix sum array. - """ + def __init__(self, nums: list[int]) -> None: self.prefix_sum = list(accumulate(nums)) def sumRange(self, left: int, right: int) -> int: diff --git a/src/leetcode/stack/min-stack.ts b/src/leetcode/stack/min-stack.ts deleted file mode 100644 index 45ea0c8..0000000 --- a/src/leetcode/stack/min-stack.ts +++ /dev/null @@ -1,60 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Design a stack that supports push, pop, top, and retrieving the minimum element in constant time. -// -// Implement the MinStack class: -// -// MinStack() initializes the stack object. -// void push(int val) pushes the element val onto the stack. -// void pop() removes the element on the top of the stack. -// int top() gets the top element of the stack. -// int getMin() retrieves the minimum element in the stack. -// -// You must implement a solution with O(1) time complexity for each function. -// -// See {@link https://leetcode.com/problems/min-stack/} -export { MinStack }; - -// SOLUTION: -class MinStack { - private readonly stack: number[]; - - private readonly min: number[]; - - constructor() { - this.stack = []; - - // This stack will contain the same number of elements as the underlying stack, but each element pushed onto it is - // the minimum value element at the time. - this.min = []; - } - - push(x: number): void { - this.stack.push(x); - - if (this.min.length === 0) { - this.min.push(x); - return; - } - - const smallest = Math.min(x, this.getMin()); - this.min.push(smallest); - } - - pop(): void { - this.stack.pop(); - this.min.pop(); - } - - top(): number { - return this.stack[this.stack.length - 1]; - } - - getMin(): number { - if (this.min.length === 0) { - return -Infinity; - } - - return this.min[this.min.length - 1]; - } -} diff --git a/src/leetcode/stack/min_stack.py b/src/leetcode/stack/min_stack.py new file mode 100644 index 0000000..10c4fa9 --- /dev/null +++ b/src/leetcode/stack/min_stack.py @@ -0,0 +1,57 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Design a stack that supports push, pop, top, and retrieving the minimum element in constant time. +# +# Implement the MinStack class: +# +# MinStack() initializes the stack object. +# void push(int val) pushes the element val onto the stack. +# void pop() removes the element on the top of the stack. +# int top() gets the top element of the stack. +# int getMin() retrieves the minimum element in the stack. +# +# You must implement a solution with O(1) time complexity for each function. +# +# See https://leetcode.com/problems/min-stack +class MinStack: + """ + SOLUTION + -------- + + Use one stack to store the values and another stack to store the minimum values. + + It appears that the problem will not call your solution with an empty stack, so we don't need to worry about that + case. + + COMPLEXITY + ---------- + + Time complexity is O(1) for all operations. + + Space complexity is O(n) where n is the number of elements in the stack. + """ + + def __init__(self): + self.stack: list[int] = [] + self.min_values: list[int] = [] + + def push(self, value: int) -> None: + self.stack.append(value) + + if not self.min_values: + self.min_values.append(value) + return + + min_value = min(value, self.getMin()) + self.min_values.append(min_value) + + def pop(self) -> None: + self.stack.pop() + self.min_values.pop() + + def top(self) -> int: + return self.stack[-1] + + def getMin(self) -> int: + return self.min_values[-1] diff --git a/test/leetcode/stack/max-stack.test.ts b/test/leetcode/stack/max-stack.test.ts deleted file mode 100644 index 4dcd9d1..0000000 --- a/test/leetcode/stack/max-stack.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MaxStack } from '../../src/heap/max-stack'; - -describe('max stack', () => { - test('max stack - test case 1', async () => { - const stack = new MaxStack(); - - stack.push(5); - stack.push(1); - stack.push(5); - - expect(stack.top()).toBe(5); - expect(stack.popMax()).toBe(5); - expect(stack.top()).toBe(1); - expect(stack.peekMax()).toBe(5); - expect(stack.pop()).toBe(1); - expect(stack.top()).toBe(5); - }); -}); diff --git a/test/leetcode/stack/min-stack.test.ts b/test/leetcode/stack/min-stack.test.ts deleted file mode 100644 index e1501db..0000000 --- a/test/leetcode/stack/min-stack.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MinStack } from '../../src/stack/min-stack'; - -describe('min stack', () => { - test('min stack - test case 1', async () => { - const stack = new MinStack(); - stack.push(-2); - stack.push(0); - stack.push(-3); - - expect(stack.getMin()).toBe(-3); - - stack.pop(); - - expect(stack.top()).toBe(0); - expect(stack.getMin()).toBe(-2); - }); -}); diff --git a/test/leetcode/stack/min_stack_test.py b/test/leetcode/stack/min_stack_test.py new file mode 100644 index 0000000..508a86d --- /dev/null +++ b/test/leetcode/stack/min_stack_test.py @@ -0,0 +1,14 @@ +from leetcode.stack.min_stack import MinStack + + +def test_case_1(): + stack = MinStack() + stack.push(0) + stack.push(-3) + + assert stack.getMin() == -3 + + stack.pop() + + assert stack.top() == 0 + assert stack.getMin() == 0 From f65d55f28fd502ab05d41ab92e5f08530a2264bc Mon Sep 17 00:00:00 2001 From: Min Huang Date: Tue, 18 Mar 2025 14:24:11 -0700 Subject: [PATCH 109/158] min add --- .../minimum-add-to-make-valid-parentheses.ts | 69 ------------------- .../minimum_add_to_make_valid_parentheses.py | 68 ++++++++++++++++++ ...imum-add-to-make-valid-parentheses.test.ts | 7 -- ...imum_add_to_make_valid_parentheses_test.py | 12 ++++ 4 files changed, 80 insertions(+), 76 deletions(-) delete mode 100644 src/leetcode/stack/minimum-add-to-make-valid-parentheses.ts create mode 100644 src/leetcode/stack/minimum_add_to_make_valid_parentheses.py delete mode 100644 test/leetcode/stack/minimum-add-to-make-valid-parentheses.test.ts create mode 100644 test/leetcode/stack/minimum_add_to_make_valid_parentheses_test.py diff --git a/src/leetcode/stack/minimum-add-to-make-valid-parentheses.ts b/src/leetcode/stack/minimum-add-to-make-valid-parentheses.ts deleted file mode 100644 index 47f21fc..0000000 --- a/src/leetcode/stack/minimum-add-to-make-valid-parentheses.ts +++ /dev/null @@ -1,69 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// A parentheses string is valid if and only if: -// -// - It is the empty string, -// - It can be written as AB (A concatenated with B), where A and B are valid strings, or -// - It can be written as (A), where A is a valid string. -// - You are given a parentheses string s. In one move, you can insert a parenthesis at any position of the string. -// -// For example, if s = "()))", you can insert an opening parenthesis to be "(()))" or a closing parenthesis to be -// "())))". -// -// Return the minimum number of moves required to make s valid. -// -// @see {@link https://leetcode.com/problems/minimum-add-to-make-parentheses-valid/} -export { minAddToMakeValid }; - -// SOLUTION: -// -// One way to do this is to use a stack and push open braces onto it. Then, when we encounter a closing brace, we can -// pop it off. If we don't have closing braces left, then increment opensRequired. At the end, count up the number of -// elements still left on the stack (the unmatched closing braces), and add that to the number of opening braces tallied -// up. -// -// However! Notice that we don't even need a stack for this. We just have to keep track of any open and close braces, -// which can be done with a simple counter for each variable. -// -// COMPLEXITY: -// -// Runs in O(n) time, where n is the length of the string. Runs in O(1) space because we are only using a few extra -// variables. -function minAddToMakeValid(s: string): number { - // Every time we see '(', increment this counter. Every time we see ')', decrement this counter, making it similar to - // our stack without using one. - // - // Likewise, at the end, if this variable isn't 0, then it's the same as the situation where the stack still has - // elements in it, which indicates some number of unmatched closing braces. - let closesRequired = 0; - - // If we encounter a ')' and we don't have a matching '(' (aka the stack is empty), then we have to insert an opening - // brace later. - let opensRequired = 0; - - for (const c of s) { - // If we encounter an opening brace, we'll need to require that we close it eventually. - if (c === '(') { - closesRequired++; - continue; - } - // If we encounter a closing brace, there are two possibilities: - // - // 1. We already have an opening brace to match it, which means we can decrement the number of closing braces we - // required. - // 2. We don't have an opening brace to match it, which means we have to insert an opening brace later. - else if (c === ')') { - // This is case 1; we encountered '(' earlier, which caused us to increment closesRequired. But we encountered - // a closing brace, so we can decrement the number of closing braces we need to insert. - if (closesRequired > 0) { - closesRequired--; - } - // This is case 2; we don't have an opening brace to match it, so we have to remember to insert one. - else { - opensRequired++; - } - } - } - - return closesRequired + opensRequired; -} diff --git a/src/leetcode/stack/minimum_add_to_make_valid_parentheses.py b/src/leetcode/stack/minimum_add_to_make_valid_parentheses.py new file mode 100644 index 0000000..91b7ce9 --- /dev/null +++ b/src/leetcode/stack/minimum_add_to_make_valid_parentheses.py @@ -0,0 +1,68 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# A parentheses string is valid if and only if: +# +# - It is the empty string, +# - It can be written as AB (A concatenated with B), where A and B are valid strings, or +# - It can be written as (A), where A is a valid string. +# - You are given a parentheses string s. In one move, you can insert a parenthesis at any position of the string. +# +# For example, if s = "()))", you can insert an opening parenthesis to be "(()))" or a closing parenthesis to be +# "())))". +# +# Return the minimum number of moves required to make s valid. +# +# See https://leetcode.com/problems/minimum-add-to-make-parentheses-valid +class Solution: + def minAddToMakeValid(self, s: str) -> int: + """ + SOLUTION + -------- + + One way to do this is to use a stack and push open braces onto it. Then, when we encounter a closing brace, we + can pop it off. If we don't have closing braces left, then increment opensRequired. At the end, count up the + number of elements still left on the stack (the unmatched closing braces), and add that to the number of opening + braces tallied up. + + However! Notice that we don't even need a stack for this. We just have to keep track of any open and close + braces, which can be done with a simple counter for each variable. + + COMPLEXITY + ---------- + + Time complexity is O(n) because we are iterating through all elements in the string. + + Space complexity is O(1). + """ + # Every time we see '(', increment this counter. Every time we see ')', decrement this counter, making it + # similar to our stack without using one. + # + # Likewise, at the end, if this variable isn't 0, then it's the same as the situation where the stack still has + # elements in it, which indicates some number of unmatched closing braces. + closes_required = 0 + + # If we encounter a ')' and we don't have a matching '(' (aka the stack is empty), then we have to insert an + # opening brace later. + opens_required = 0 + + for c in s: + # If we encounter an opening brace, we'll need to require that we close it eventually. + if c == "(": + closes_required += 1 + continue + # If we encounter a closing brace, there are two possibilities: + # + # 1. We already have an opening brace to match it, which means we can decrement the number of closing braces + # we . + # 2. We don't have an opening brace to match it, which means we have to insert an opening brace later. + elif c == ")": + # This is case 1; we encountered '(' earlier, which caused us to increment closesRequired. But we + # encountered a closing brace, so we can decrement the number of closing braces we need to insert. + if closes_required > 0: + closes_required -= 1 + # This is case 2; we don't have an opening brace to match it, so we have to remember to insert one. + else: + opens_required += 1 + + return closes_required + opens_required diff --git a/test/leetcode/stack/minimum-add-to-make-valid-parentheses.test.ts b/test/leetcode/stack/minimum-add-to-make-valid-parentheses.test.ts deleted file mode 100644 index 812067c..0000000 --- a/test/leetcode/stack/minimum-add-to-make-valid-parentheses.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { minAddToMakeValid } from '../../src/stack/minimum-add-to-make-valid-parentheses'; - -describe('minimum add to make valid parentheses', () => { - test('minAddToMakeValid - test case 1', async () => { - expect(minAddToMakeValid('())')).toBe(1); - }); -}); diff --git a/test/leetcode/stack/minimum_add_to_make_valid_parentheses_test.py b/test/leetcode/stack/minimum_add_to_make_valid_parentheses_test.py new file mode 100644 index 0000000..872462c --- /dev/null +++ b/test/leetcode/stack/minimum_add_to_make_valid_parentheses_test.py @@ -0,0 +1,12 @@ +from leetcode.stack.minimum_add_to_make_valid_parentheses import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.minAddToMakeValid("())") == 1 + + +def test_case_2(): + assert soln.minAddToMakeValid("(((") == 3 From 1cd3859131c82c103aaad99e710045d3b7ce401b Mon Sep 17 00:00:00 2001 From: Min Huang Date: Tue, 18 Mar 2025 14:36:19 -0700 Subject: [PATCH 110/158] min remove --- ...inimum-remove-to-make-valid-parentheses.ts | 97 ------------------- ...inimum_remove_to_make_valid_parentheses.py | 89 +++++++++++++++++ ...m-remove-to-make-valid-parentheses.test.ts | 7 -- ...m_remove_to_make_valid_parentheses_test.py | 12 +++ 4 files changed, 101 insertions(+), 104 deletions(-) delete mode 100644 src/leetcode/stack/minimum-remove-to-make-valid-parentheses.ts create mode 100644 src/leetcode/stack/minimum_remove_to_make_valid_parentheses.py delete mode 100644 test/leetcode/stack/minimum-remove-to-make-valid-parentheses.test.ts create mode 100644 test/leetcode/stack/minimum_remove_to_make_valid_parentheses_test.py diff --git a/src/leetcode/stack/minimum-remove-to-make-valid-parentheses.ts b/src/leetcode/stack/minimum-remove-to-make-valid-parentheses.ts deleted file mode 100644 index 9ccc35f..0000000 --- a/src/leetcode/stack/minimum-remove-to-make-valid-parentheses.ts +++ /dev/null @@ -1,97 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given a string s of '(' , ')' and lowercase English characters. -// -// Your task is to remove the minimum number of parentheses ( '(' or ')', in any positions ) so that the resulting -// parentheses string is valid and return any valid string. -// -// Formally, a parentheses string is valid if and only if: -// -// - It is the empty string, contains only lowercase characters, or -// - It can be written as AB (A concatenated with B), where A and B are valid strings, or -// - It can be written as (A), where A is a valid string. -// -// See {@link https://leetcode.com/problems/minimum-remove-to-make-valid-parentheses/} -export { minRemoveToMakeValid }; - -// SOLUTION: -// -// Imagine if we were just asked to validate the parentheses. This can be done with a stack pretty easily. In this -// problem, we can push open parens onto the stack, then pop off close parens when we encounter them. If we ever -// encounter a close parens while the stack is empty, OR if we have remaining open parens on the stack, then we know -// the parentheses are invalid. -// -// Here, though, we are asked to remove invalid parentheses. This means that instead of failing immediately, every time -// we encounter a close without an open we need to mark it for removal. In addition, any open parens left on the stack -// at the end should also be marked for removal. -// -// Once we have indices that are marked for removal, we'll go through the string again, and copy it to a new string, but -// avoid any indices that are marked for removal. -// -// COMPLEXITY: -// -// Time complexity is O(n) for each pass through the string. Two passes means O(n) overall. -// -// Space complexity is O(n) because we are using the stack (which is size of the string) and the removable set, which -// is also size of the string. -function minRemoveToMakeValid(s: string): string { - type Index = number; - - // For the validate parens problem, we'll have a stack of '(' strings. However, notice that in that case we never - // push any ')' onto the stack; as soon as ')' we pop the item off the stack or we fail. That is, in the validation - // problem, we only ever have '(' characters on the stack. - // - // Therefore we can reclaim that space by pushing the index of the '(' character onto the stack. It is not necessary - // to declare both: - // - // const stack: string[] = []; - // const opens = new Set(); - // - // One array data structure is enough to handle both. Note that having a set would cause problems because we'd at - // some point need to pop the last open paren off the stack, and we'd have to search the set for it (or convert the - // set into an array). - const stack: Index[] = []; - - // These are the indexes we are meant to remove at the end. - const removable = new Set(); - - // Do a first pass to check the string for parens that you want to remove. - for (let i = 0; i < s.length; i++) { - const c = s[i]; - - // If we encounter open paren, just push it onto the stack. - if (c === '(') { - stack.push(i); - continue; - } - - // If we encounter a non-close paren, just skip it. It's not relevant for the removal process. - if (c !== ')') { - continue; - } - - // If we have a matched close paren, pop the associated open from the stack and just continue. - if (stack.length !== 0) { - stack.pop(); - continue; - } - - // Uh oh! At this point, we have an unmatched close paren, so mark this index for removal. - removable.add(i); - } - - // It's possible we are left with some unmatched open parens, so mark them for removal as well. - stack.forEach(i => removable.add(i)); - - // Construct the result string by skipping marked indices. - let result = ''; - for (let i = 0; i < s.length; i++) { - if (removable.has(i)) { - continue; - } - - result += s[i]; - } - - return result; -} diff --git a/src/leetcode/stack/minimum_remove_to_make_valid_parentheses.py b/src/leetcode/stack/minimum_remove_to_make_valid_parentheses.py new file mode 100644 index 0000000..3083bfe --- /dev/null +++ b/src/leetcode/stack/minimum_remove_to_make_valid_parentheses.py @@ -0,0 +1,89 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given a string s of '(' , ')' and lowercase English characters. +# +# Your task is to remove the minimum number of parentheses ( '(' or ')', in any positions ) so that the resulting +# parentheses string is valid and return any valid string. +# +# Formally, a parentheses string is valid if and only if: +# +# - It is the empty string, contains only lowercase characters, or +# - It can be written as AB (A concatenated with B), where A and B are valid strings, or +# - It can be written as (A), where A is a valid string. +# +# See https://leetcode.com/problems/minimum-remove-to-make-valid-parentheses +class Solution: + def minRemoveToMakeValid(self, s: str) -> str: + """ + SOLUTION + -------- + + Imagine if we were just asked to validate the parentheses. This can be done with a stack pretty easily. In + this problem, we can push open parens onto the stack, then pop off close parens when we encounter them. If we + ever encounter a close parens while the stack is empty, OR if we have remaining open parens on the stack, then + we know the parentheses are invalid. + + Here, though, we are asked to remove invalid parentheses. This means that instead of failing immediately, every + time we encounter a close without an open we need to mark it for removal. In addition, any open parens left on + the stack at the end should also be marked for removal. + + Once we have indices that are marked for removal, we'll go through the string again, and copy it to a new + string, but avoid any indices that are marked for removal. + + COMPLEXITY + ---------- + + Time complexity is O(n) for each pass through the string. Two passes means O(n) overall. + + Space complexity is O(n) because we are using the stack (which is size of the string) and the removable set, + which is also size of the string. + """ + # For the validate parens problem, we'll have a stack of '(' strings. However, notice that in that case we + # never push any ')' onto the stack; as soon as ')' we pop the item off the stack or we fail. That is, in the + # validation # problem, we only ever have '(' characters on the stack. + # + # Therefore we can reclaim that space by pushing the index of the '(' character onto the stack. It is not + # necessary to declare both: + # + # stack: list[str] = [] + # opens: set[int] = set() + # + # One array data structure is enough to handle both. Note that having a set would cause problems because we'd + # at some point need to pop the last open paren off the stack, and we'd have to search the set for it (or + # convert the set into an array). + opens: list[int] = [] + + # Remove these indices at the end; these are unmatched close parens. + closes: set[int] = set() + + # Find indices to remove. + for i, c in enumerate(s): + # If we encounter open paren, just push it onto the stack. + if c == "(": + opens.append(i) + + # If we encounter a non-close paren, just skip it. It's not relevant for the removal process. + if c != ")": + continue + + # If we have a matched close paren, pop the associated open from the stack and just continue. + if opens: + opens.pop() + continue + + # Uh oh! At this point, we have an unmatched close paren, so mark this index for removal. + closes.add(i) + + # It's possible we are left with some unmatched open parens, so mark them for removal as well. + for open in opens: + closes.add(open) + + # Copy the string, skipping any indices that are marked for removal. + result = "" + for i, c in enumerate(s): + if i in closes: + continue + result += c + + return result diff --git a/test/leetcode/stack/minimum-remove-to-make-valid-parentheses.test.ts b/test/leetcode/stack/minimum-remove-to-make-valid-parentheses.test.ts deleted file mode 100644 index dc70ac8..0000000 --- a/test/leetcode/stack/minimum-remove-to-make-valid-parentheses.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { minRemoveToMakeValid } from '../../src/stack/minimum-remove-to-make-valid-parentheses'; - -describe('minimum remove to make valid parentheses', () => { - test('minimum remove to make valid parentheses - test case 1', async () => { - expect(minRemoveToMakeValid('lee(t(c)o)de)')).toBe('lee(t(c)o)de'); - }); -}); diff --git a/test/leetcode/stack/minimum_remove_to_make_valid_parentheses_test.py b/test/leetcode/stack/minimum_remove_to_make_valid_parentheses_test.py new file mode 100644 index 0000000..3e130ad --- /dev/null +++ b/test/leetcode/stack/minimum_remove_to_make_valid_parentheses_test.py @@ -0,0 +1,12 @@ +from leetcode.stack.minimum_remove_to_make_valid_parentheses import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.minRemoveToMakeValid("lee(t(c)o)de)") == "lee(t(c)o)de" + + +def test_case_2(): + assert soln.minRemoveToMakeValid("a)b(c)d") == "ab(c)d" From c98173208c512cf170ee24859322b80ee43912db Mon Sep 17 00:00:00 2001 From: Min Huang Date: Tue, 18 Mar 2025 14:40:13 -0700 Subject: [PATCH 111/158] valid parens --- test/leetcode/stack/valid-parentheses.test.ts | 15 --------------- .../{string => stack}/valid_parentheses_test.py | 0 2 files changed, 15 deletions(-) delete mode 100644 test/leetcode/stack/valid-parentheses.test.ts rename test/leetcode/{string => stack}/valid_parentheses_test.py (100%) diff --git a/test/leetcode/stack/valid-parentheses.test.ts b/test/leetcode/stack/valid-parentheses.test.ts deleted file mode 100644 index 566206e..0000000 --- a/test/leetcode/stack/valid-parentheses.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { isValid } from '../../src/stack/valid-parentheses'; - -describe('valid parentheses', () => { - test('valid parentheses - test case 1', async () => { - expect(isValid('()')).toBe(true); - }); - - test('valid parentheses - test case 2', async () => { - expect(isValid('()[]{}')).toBe(true); - }); - - test('valid parentheses - test case 3', async () => { - expect(isValid('(}')).toBe(false); - }); -}); diff --git a/test/leetcode/string/valid_parentheses_test.py b/test/leetcode/stack/valid_parentheses_test.py similarity index 100% rename from test/leetcode/string/valid_parentheses_test.py rename to test/leetcode/stack/valid_parentheses_test.py From 5647e7dc0a699f98320bc9c3d27f945c18662af0 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Tue, 18 Mar 2025 15:08:07 -0700 Subject: [PATCH 112/158] valid num --- src/leetcode/stack/valid-number.ts | 133 ----------------------- src/leetcode/stack/valid_number.py | 116 ++++++++++++++++++++ test/leetcode/stack/valid_number_test.py | 132 ++++++++++++++++++++++ 3 files changed, 248 insertions(+), 133 deletions(-) delete mode 100644 src/leetcode/stack/valid-number.ts create mode 100644 src/leetcode/stack/valid_number.py create mode 100644 test/leetcode/stack/valid_number_test.py diff --git a/src/leetcode/stack/valid-number.ts b/src/leetcode/stack/valid-number.ts deleted file mode 100644 index 81102dc..0000000 --- a/src/leetcode/stack/valid-number.ts +++ /dev/null @@ -1,133 +0,0 @@ -// DIFFICULTY: HARD -// -// A valid number can be split up into these components (in order): -// -// A decimal number or an integer. -// (Optional) An 'e' or 'E', followed by an integer. -// A decimal number can be split up into these components (in order): -// -// (Optional) A sign character (either '+' or '-'). -// One of the following formats: -// One or more digits, followed by a dot '.'. -// One or more digits, followed by a dot '.', followed by one or more digits. -// A dot '.', followed by one or more digits. -// An integer can be split up into these components (in order): -// -// (Optional) A sign character (either '+' or '-'). -// One or more digits. -// For example, all the following are valid numbers: ["2", "0089", "-0.1", "+3.14", "4.", "-.9", "2e10", "-90E3", "3e+7", "+6e-1", "53.5e93", "-123.456e789"], while the following are not valid numbers: ["abc", "1a", "1e", "e3", "99e2.5", "--6", "-+3", "95a54e53"]. -// -// Given a string s, return true if s is a valid number. -// -// See {@link https://leetcode.com/problems/valid-number/} -export { isNumber }; - -// SOLUTION: -// -// I don't know that this is a particularly hard problem, but it is a bit tricky to get all the edge cases right. It -// doesn't seem like a particularly fair problem to ask in a 45m interview though. -function isNumber(text: string): boolean { - // Defining this set will probably be faster than testing a regex over and over. - const set = new Set(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']); - const state = { - signed: false, - digits: false, - decimal: false, - exp: false - }; - - for (let i = 0; i < text.length; i++) { - const c = text[i]; - const p = i === 0 ? '' : text[i - 1].toLowerCase(); - - switch (c) { - case '+': - case '-': - if (state.exp) { - // After seeing the E symbol, signs are only valid immediately after the E symbol. - if (p !== 'e') { - return false; - } - - // However, signs are not valid if they are the last symbol. - if (i === text.length - 1) { - return false; - } - - state.signed = true; - break; - } - - // Before seeing the E symbol, signs are not valid if they aren't in the first position. - if (i !== 0) { - return false; - } - - // Signs are invalid after we have encountered a decimal. - if (state.decimal) { - return false; - } - - // Signs are invalid if they are the last symbol, and no digits have been seen. For example, 9. is valid, but - // . by itself is not. - if (i === text.length - 1 && !state.digits) { - return false; - } - - state.signed = true; - break; - case 'e': - case 'E': - // If the E symbol is encountered again after already seeing one, the number is automatically invalid. - if (state.exp) { - return false; - } - - // The E symbol may not appear as the first symbol or the last symbol. - if (i === 0 || i === text.length - 1) { - return false; - } - - // The E symbol may not appear just after the sign symbol. For example, 46+e3 is invalid. - if (p === '+' || p === '-') { - return false; - } - - // The E symbol may not appear after a decimal symbol, if no digits have been seen. For example, 9.e3 is - // valid, but .e3 is not valid. - if (p === '.' && !state.digits) { - return false; - } - - state.exp = true; - break; - case '.': - if (state.exp) { - // The decimal symbol may not appear after the E symbol has appeared. - return false; - } - - // The decimal symbol may not appear after the decimal symbol has appeared. - if (state.decimal) { - return false; - } - - // The decimal symbol cannot be the last symbol, unless digits have already been seen. - if (i === text.length - 1 && !state.digits) { - return false; - } - - state.decimal = true; - break; - default: - // Any other character that is not a digit does not constitute a number. - if (!set.has(c)) { - return false; - } - - state.digits = true; - } - } - - return true; -} diff --git a/src/leetcode/stack/valid_number.py b/src/leetcode/stack/valid_number.py new file mode 100644 index 0000000..d4ac25a --- /dev/null +++ b/src/leetcode/stack/valid_number.py @@ -0,0 +1,116 @@ +# DIFFICULTY: HARD +# ---------------- +# +# A valid number can be split up into these components (in order): +# - A decimal number or an integer. +# - (Optional) An 'e' or 'E', followed by an integer. + +# A decimal number can be split up into these components (in order): +# - (Optional) A sign character (either '+' or '-'). +# +# One of the following formats: +# - One or more digits, followed by a dot '.'. +# - One or more digits, followed by a dot '.', followed by one or more digits. +# - A dot '.', followed by one or more digits. +# +# An integer can be split up into these components (in order): +# - (Optional) A sign character (either '+' or '-'). +# - One or more digits. +# +# For example, all the following are valid numbers: +# ["2", "0089", "-0.1", "+3.14", "4.", "-.9", "2e10", "-90E3", "3e+7", "+6e-1", "53.5e93", "-123.456e789"] +# +# While the following are not valid numbers: +# ["abc", "1a", "1e", "e3", "99e2.5", "--6", "-+3", "95a54e53"]. +# +# Given a string s, return true if s is a valid number. +# +# See https://leetcode.com/problems/valid-number +class Solution: + def isNumber(self, s: str) -> bool: + """ + SOLUTION + -------- + + I don't know that this is a particularly hard problem, but it is a bit tricky to get all the edge cases right. + It doesn't seem like a particularly fair problem to ask in a 45m interview though. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the length of the input string. + + Space complexity is O(1). + """ + digits = False + decimal = False + exponent = False + + for i, c in enumerate(s.lower()): + # Get the previous character, if it exists. + p = "" if i == 0 else s[i - 1] + + match c: + case "+" | "-": + # After seeing the E symbol, signs are only valid immediately after the E symbol. + if exponent and p != "e": + return False + + # However, signs are not valid if they are the last symbol. + if exponent and i == len(s) - 1: + return False + + # Before seeing the E symbol, signs are not valid if they aren't in the first position. + if i != 0: + return False + + # Signs are invalid after we have encountered a decimal. + if decimal: + return False + + # Signs are invalid if they are the last symbol, and no digits have been seen. For example, "9." is + # valid, but "." by itself is not. + if i == len(s) - 1 and not digits: + return False + case "e" | "E": + # If the E symbol is encountered again after already seeing one, the number is automatically + # invalid. + if exponent: + return False + + # The E symbol may not appear as the first symbol or the last symbol. + if i == 0 or i == len(s) - 1: + return False + + # The E symbol may not appear just after the sign symbol. For example, 46+e3 is invalid. + if p == "+" or p == "-": + return False + + # The E symbol may not appear after a decimal symbol, if no digits have been seen. For example, + # 9.e3 is valid, but .e3 is not valid. + if p == "." and not digits: + return False + + exponent = True + case ".": + # The decimal symbol may not appear after the E symbol has appeared. + if exponent: + return False + + # The decimal symbol may not appear after the decimal symbol has appeared. + if decimal: + return False + + # The decimal symbol cannot be the last symbol, unless digits have already been seen. + if i == len(s) - 1 and not digits: + return False + + decimal = True + case _: + # Any other character that is not a digit does not constitute a number. + if not c.isdigit(): + return False + + digits = True + + return True diff --git a/test/leetcode/stack/valid_number_test.py b/test/leetcode/stack/valid_number_test.py new file mode 100644 index 0000000..9113abc --- /dev/null +++ b/test/leetcode/stack/valid_number_test.py @@ -0,0 +1,132 @@ +from leetcode.stack.valid_number import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.isNumber("2") + + +def test_case_2(): + assert soln.isNumber("0089") + + +def test_case_3(): + assert soln.isNumber("-0.1") + + +def test_case_4(): + assert soln.isNumber("+3.14") + + +def test_case_5(): + assert soln.isNumber("4.") + + +def test_case_6(): + assert soln.isNumber("-.9") + + +def test_case_7(): + assert soln.isNumber("2e10") + + +def test_case_8(): + assert soln.isNumber("-90E3") + + +def test_case_9(): + assert soln.isNumber("3e+7") + + +def test_case_10(): + assert soln.isNumber("+6e-1") + + +def test_case_11(): + assert soln.isNumber("53.5e93") + + +def test_case_12(): + assert soln.isNumber("-123.456e789") + + +def test_case_13(): + assert soln.isNumber("46.e3") + + +def test_case_14(): + assert soln.isNumber("1.e10") + + +def test_case_15(): + assert soln.isNumber("9.e3") + + +def test_case_16(): + assert not soln.isNumber("abc") + + +def test_case_17(): + assert not soln.isNumber("1a") + + +def test_case_18(): + assert not soln.isNumber("1e") + + +def test_case_19(): + assert not soln.isNumber("e3") + + +def test_case_20(): + assert not soln.isNumber("99e2.5") + + +def test_case_21(): + assert not soln.isNumber("--6") + + +def test_case_22(): + assert not soln.isNumber("-+3") + + +def test_case_23(): + assert not soln.isNumber("95a54e53") + + +def test_case_24(): + assert not soln.isNumber("1ee3") + + +def test_case_25(): + assert not soln.isNumber(".") + + +def test_case_26(): + assert not soln.isNumber("1e+") + + +def test_case_27(): + assert not soln.isNumber("1e+.") + + +def test_case_28(): + assert not soln.isNumber("+.") + + +def test_case_29(): + assert not soln.isNumber(".+") + + +def test_case_30(): + assert not soln.isNumber("+e3") + + +def test_case_31(): + assert not soln.isNumber("46e.3") + + +def test_case_32(): + assert not soln.isNumber(".e3") From ad736fb857e6883895c4cfff4d5a3aa3a398530d Mon Sep 17 00:00:00 2001 From: Min Huang Date: Tue, 18 Mar 2025 15:15:24 -0700 Subject: [PATCH 113/158] fixed --- src/leetcode/stack/valid_number.py | 54 ++++++++++++++++-------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/src/leetcode/stack/valid_number.py b/src/leetcode/stack/valid_number.py index d4ac25a..3d2c7fa 100644 --- a/src/leetcode/stack/valid_number.py +++ b/src/leetcode/stack/valid_number.py @@ -5,6 +5,7 @@ # - A decimal number or an integer. # - (Optional) An 'e' or 'E', followed by an integer. + # A decimal number can be split up into these components (in order): # - (Optional) A sign character (either '+' or '-'). # @@ -42,44 +43,47 @@ def isNumber(self, s: str) -> bool: Space complexity is O(1). """ + # Store some flags to keep track of what we have seen so far. digits = False decimal = False exponent = False - for i, c in enumerate(s.lower()): + t = s.strip().lower() + for i, c in enumerate(t): # Get the previous character, if it exists. - p = "" if i == 0 else s[i - 1] + p = "" if i == 0 else t[i - 1] match c: case "+" | "-": - # After seeing the E symbol, signs are only valid immediately after the E symbol. - if exponent and p != "e": - return False - - # However, signs are not valid if they are the last symbol. - if exponent and i == len(s) - 1: - return False - - # Before seeing the E symbol, signs are not valid if they aren't in the first position. - if i != 0: - return False - - # Signs are invalid after we have encountered a decimal. - if decimal: - return False - - # Signs are invalid if they are the last symbol, and no digits have been seen. For example, "9." is - # valid, but "." by itself is not. - if i == len(s) - 1 and not digits: - return False - case "e" | "E": + if exponent: + # After seeing the E symbol, signs are only valid immediately after the E symbol. + if p != "e": + return False + + # However, signs are not valid if they are the last symbol. + if i == len(t) - 1: + return False + else: + # Before seeing the E symbol, signs are not valid if they aren't in the first position. + if i != 0: + return False + + # Signs are invalid after we have encountered a decimal. + if decimal: + return False + + # Signs are invalid if they are the last symbol, and no digits have been seen. For example, "9." is + # valid, but "." by itself is not. + if i == len(t) - 1 and not digits: + return False + case "e": # If the E symbol is encountered again after already seeing one, the number is automatically # invalid. if exponent: return False # The E symbol may not appear as the first symbol or the last symbol. - if i == 0 or i == len(s) - 1: + if i == 0 or i == len(t) - 1: return False # The E symbol may not appear just after the sign symbol. For example, 46+e3 is invalid. @@ -102,7 +106,7 @@ def isNumber(self, s: str) -> bool: return False # The decimal symbol cannot be the last symbol, unless digits have already been seen. - if i == len(s) - 1 and not digits: + if i == len(t) - 1 and not digits: return False decimal = True From bfd60b2e611c993905ff420c0abbcb75e3b36e30 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Tue, 18 Mar 2025 15:22:06 -0700 Subject: [PATCH 114/158] simplify path --- src/leetcode/stack/simplify-path.ts | 67 ----------- src/leetcode/stack/simplify_path.py | 62 ++++++++++ test/leetcode/stack/simplify-path.test.ts | 11 -- test/leetcode/stack/simplify_path_test.py | 12 ++ test/leetcode/stack/valid-number.test.ts | 131 ---------------------- 5 files changed, 74 insertions(+), 209 deletions(-) delete mode 100644 src/leetcode/stack/simplify-path.ts create mode 100644 src/leetcode/stack/simplify_path.py delete mode 100644 test/leetcode/stack/simplify-path.test.ts create mode 100644 test/leetcode/stack/simplify_path_test.py delete mode 100644 test/leetcode/stack/valid-number.test.ts diff --git a/src/leetcode/stack/simplify-path.ts b/src/leetcode/stack/simplify-path.ts deleted file mode 100644 index c34c5c2..0000000 --- a/src/leetcode/stack/simplify-path.ts +++ /dev/null @@ -1,67 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given an absolute path for a Unix-style file system, which always begins with a slash '/'. Your task is to -// transform this absolute path into its simplified canonical path. -// -// The rules of a Unix-style file system are as follows: -// -// - A single period '.' represents the current directory. -// - A double period '..' represents the previous/parent directory. -// - Multiple consecutive slashes such as '//' and '///' are treated as a single slash '/'. -// - Any sequence of periods that does not match the rules above should be treated as a valid directory or file name. -// For example, '...' and '....' are valid directory or file names. -// -// The simplified canonical path should follow these rules: -// -// - The path must start with a single slash '/'. -// - Directories within the path must be separated by exactly one slash '/'. -// - The path must not end with a slash '/', unless it is the root directory. -// - The path must not have any single or double periods ('.' and '..') used to denote current or parent directories. -// -// Return the simplified canonical path. -// -// See {@link https://leetcode.com/problems/simplify-path/description} -export { simplifyPath }; - -// SOLUTION: -// -// Just iterate through the path and use a stack to keep track of directories. The only one we need to be careful with -// is the '..' name; this means we need to pop the last directory from the stack to "go up" one level. -// -// COMPLEXITY: -// -// Splitting the path, traversing segments, and joining the path are all O(n) time complexity. Where n is the length of -// the input string. -// -// The space complexity is O(n) to store the string segments on the stack. Worst case we have to store the length of -// the string. -function simplifyPath(path: string): string { - const stack: string[] = []; - const names = path.split('/'); - - for (const name of names) { - // Presumably these can just get ignored and thrown away. - if (name === '' || name === '.') { - continue; - } - - // If we are going up a level, we need to pop the last directory from the stack. - // - // Wait, what happens if we are already at the root directory and we can't go up a directory anymore? The - // instructions are not clear, but we definitely do get inputs like '/../' and the result should be '/', indicating - // that the desired behavior is to do nothing. - if (name === '..') { - // Technically we don't actually need this check; stack.pop() does nothing if the array is empty. - if (stack.length > 0) { - stack.pop(); - } - - continue; - } - - // Otherwise we should just push the directory onto the stack as normal. - stack.push(name); - } - - return '/' + stack.join('/'); -} diff --git a/src/leetcode/stack/simplify_path.py b/src/leetcode/stack/simplify_path.py new file mode 100644 index 0000000..32c7b5b --- /dev/null +++ b/src/leetcode/stack/simplify_path.py @@ -0,0 +1,62 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given an absolute path for a Unix-style file system, which always begins with a slash '/'. Your task is to +# transform this absolute path into its simplified canonical path. +# +# The rules of a Unix-style file system are as follows: +# +# - A single period '.' represents the current directory. +# - A double period '..' represents the previous/parent directory. +# - Multiple consecutive slashes such as '//' and '///' are treated as a single slash '/'. +# - Any sequence of periods that does not match the rules above should be treated as a valid directory or file name. +# For example, '...' and '....' are valid directory or file names. +# +# The simplified canonical path should follow these rules: +# +# - The path must start with a single slash '/'. +# - Directories within the path must be separated by exactly one slash '/'. +# - The path must not end with a slash '/', unless it is the root directory. +# - The path must not have any single or double periods ('.' and '..') used to denote current or parent directories. +# +# Return the simplified canonical path. +# +# See https://leetcode.com/problems/simplify-path +class Solution: + def simplifyPath(self, path: str) -> str: + """ + SOLUTION + -------- + + Just iterate through the path and use a stack to keep track of directories. The only one we need to be careful + with is the '..' name; this means we need to pop the last directory from the stack to "go up" one level. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the length of the input string. + + Space complexity is O(n) to store the string segments on the stack. + """ + stack: list[str] = [] + names = path.split("/") + + for name in names: + # Presumably these can just get ignored and thrown away. + if name == "" or name == ".": + continue + + # If we are going up a level, we need to pop the last directory from the stack. + # + # Wait, what happens if we are already at the root directory and we can't go up a directory anymore? The + # instructions are not clear, but we definitely do get inputs like '/../' and the result should be '/', + # indicating that the desired behavior is to do nothing. + if name == "..": + if stack: + stack.pop() + continue + + # Otherwise we should just push the directory onto the stack as normal. + stack.append(name) + + return "/" + "/".join(stack) diff --git a/test/leetcode/stack/simplify-path.test.ts b/test/leetcode/stack/simplify-path.test.ts deleted file mode 100644 index 7f63648..0000000 --- a/test/leetcode/stack/simplify-path.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { simplifyPath } from '../../src/stack/simplify-path'; - -describe('simplify path', () => { - test('simplify path - test case 1', () => { - expect(simplifyPath('/home/')).toEqual('/home'); - }); - - test('simplify path - test case 2', () => { - expect(simplifyPath('/../')).toEqual('/'); - }); -}); diff --git a/test/leetcode/stack/simplify_path_test.py b/test/leetcode/stack/simplify_path_test.py new file mode 100644 index 0000000..7348b1d --- /dev/null +++ b/test/leetcode/stack/simplify_path_test.py @@ -0,0 +1,12 @@ +from leetcode.stack.simplify_path import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.simplifyPath("/home/") == "/home" + + +def test_case_2(): + assert soln.simplifyPath("/../") == "/" diff --git a/test/leetcode/stack/valid-number.test.ts b/test/leetcode/stack/valid-number.test.ts deleted file mode 100644 index b1231ef..0000000 --- a/test/leetcode/stack/valid-number.test.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { isNumber } from '../../src/stack/valid-number'; - -describe('valid number', () => { - test('valid number - test case 1', async () => { - expect(isNumber('2')).toBe(true); - }); - - test('valid number - test case 2', async () => { - expect(isNumber('0089')).toBe(true); - }); - - test('valid number - test case 3', async () => { - expect(isNumber('-0.1')).toBe(true); - }); - - test('valid number - test case 4', async () => { - expect(isNumber('+3.14')).toBe(true); - }); - - test('valid number - test case 5', async () => { - expect(isNumber('4.')).toBe(true); - }); - - test('valid number - test case 6', async () => { - expect(isNumber('-.9')).toBe(true); - }); - - test('valid number - test case 7', async () => { - expect(isNumber('2e10')).toBe(true); - }); - - test('valid number - test case 8', async () => { - expect(isNumber('-90E3')).toBe(true); - }); - - test('valid number - test case 9', async () => { - expect(isNumber('3e+7')).toBe(true); - }); - - test('valid number - test case 10', async () => { - expect(isNumber('+6e-1')).toBe(true); - }); - - test('valid number - test case 11', async () => { - expect(isNumber('53.5e93')).toBe(true); - }); - - test('valid number - test case 12', async () => { - expect(isNumber('-123.456e789')).toBe(true); - }); - - test('valid number - test case 13', async () => { - expect(isNumber('46.e3')).toBe(true); - }); - - test('valid number - test case 14', async () => { - expect(isNumber('1.e10')).toBe(true); - }); - - test('valid number - test case 15', async () => { - expect(isNumber('9.e3')).toBe(true); - }); - - test('valid number - test case 16', async () => { - expect(isNumber('abc')).toBe(false); - }); - - test('valid number - test case 17', async () => { - expect(isNumber('1a')).toBe(false); - }); - - test('valid number - test case 18', async () => { - expect(isNumber('1e')).toBe(false); - }); - - test('valid number - test case 19', async () => { - expect(isNumber('e3')).toBe(false); - }); - - test('valid number - test case 20', async () => { - expect(isNumber('99e2.5')).toBe(false); - }); - - test('valid number - test case 21', async () => { - expect(isNumber('--6')).toBe(false); - }); - - test('valid number - test case 22', async () => { - expect(isNumber('-+3')).toBe(false); - }); - - test('valid number - test case 23', async () => { - expect(isNumber('95a54e53')).toBe(false); - }); - - test('valid number - test case 24', async () => { - expect(isNumber('1ee3')).toBe(false); - }); - - test('valid number - test case 25', async () => { - expect(isNumber('.')).toBe(false); - }); - - test('valid number - test case 26', async () => { - expect(isNumber('1e+')).toBe(false); - }); - - test('valid number - test case 27', async () => { - expect(isNumber('1e+.')).toBe(false); - }); - - test('valid number - test case 28', async () => { - expect(isNumber('+.')).toBe(false); - }); - - test('valid number - test case 29', async () => { - expect(isNumber('.+')).toBe(false); - }); - - test('valid number - test case 30', async () => { - expect(isNumber('+e3')).toBe(false); - }); - - test('valid number - test case 31', async () => { - expect(isNumber('46e.3')).toBe(false); - }); - - test('valid number - test case 32', async () => { - expect(isNumber('.e3')).toBe(false); - }); -}); From b0329d64d7735a816365b2bc83a77fd5d2f7de8b Mon Sep 17 00:00:00 2001 From: Min Huang Date: Tue, 18 Mar 2025 17:41:51 -0700 Subject: [PATCH 115/158] tmp --- .../string/find_the_closest_palindrome.py | 147 ++++++++++++++++++ .../find_the_closest_palindrome_test.py | 40 +++++ 2 files changed, 187 insertions(+) create mode 100644 src/leetcode/string/find_the_closest_palindrome.py create mode 100644 test/leetcode/string/find_the_closest_palindrome_test.py diff --git a/src/leetcode/string/find_the_closest_palindrome.py b/src/leetcode/string/find_the_closest_palindrome.py new file mode 100644 index 0000000..99c9f51 --- /dev/null +++ b/src/leetcode/string/find_the_closest_palindrome.py @@ -0,0 +1,147 @@ +# DIFFICULTY: HARD +# ---------------- +# +# Given a string n representing an integer, return the closest integer (not including itself), which is a palindrome. +# If there is a tie, return the smaller one. +# +# The closest is defined as the absolute difference minimized between two integers. +# +# Constraints: 1 <= n.length <= 18 +# +# See https://leetcode.com/problems/find-the-closest-palindrome +from collections import defaultdict +import math + + +class Solution: + def nearestPalindromic(self, text: str) -> str: + """ + SOLUTION + -------- + + To devise a strategy, first consider a few examples: + + "123" => "121" + "1234" => "1221" + "1000" => "1001" + "1" => "0" <-- It's not "1" because we can't return the number itself. + "999" => "1001" <-- It's not "888" because "1001" is "closer" to "999" by 2. + "1221" => "1111" <-- It's not "1221" because we can't return the number itself. + + Generally, we can either mirror the left side of the number (since that should result in the "closer" number) + and use that, or we may have to do some incrementing/decrementing to get a part of the number we can mirror. We + also need to handle edge cases like "1" or "0". + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the length of the input string. + + Space complexity is O(n). + """ + n = int(text) + + # If it's just a single digit, return that digit minus one (since we can't return the original digit). + if len(text) == 1: + return str(n - 1) + + # Split up the string so we have the left prefix (and middle) in case we need it. + (left, mid) = self.__findPrefix(text) + + # Find possible candidates for the closest palindrome. + candidates = self.__findCandidates(text, left, mid) + + # Find the actual closest. + closest = self.__findClosest(candidates, n) + + return closest + + def __findPrefix(self, text: str) -> tuple[str, str]: + # Find the left half of the number, which we may need to mirror. If the number has an odd number of digits, we + # don't take the midpoint because we won't want to mirror it anyways. + k = len(text) // 2 + left = text[:k] + mid = text[k] if len(text) % 2 == 1 else "" + return (left, mid) + + def __findCandidates(self, text: str, left: str, mid: str) -> list[str]: + # Generate candidate palindromes; if the original number is already a palindrome, this won't work and we'll have + # to increment or decrement the left side to find the nearest palindrome. + # + # In some cases, we may also have to increment or decrement the middle digit. + uniques: set[str] = set() + for i in [-1, 0, 1]: + u = int(left) + v = u + i + prefix = str(v) + suffix = prefix[::-1] + uniques.add(prefix + mid + suffix) + + # There are cases where the middle digit needs to be incremented or decremented as well. For example, take + # the following cases: + # + # "11911" => "11811" + # "10001" => "11111" + # + # To handle these cases we'll vary the middle digit as well. + for j in [-1, 0, 1]: + if mid == "": + continue + + updated = int(mid) + j + + # Only add this middle digit if we have a positive value, so we don't end up with "10-11" or something. + if updated < 0: + continue + + uniques.add(prefix + str(updated) + suffix) + + # These candidates will work for the vast majority of numbers, but sometimes we'll get edge cases where simply + # incrementing or decrementing won't work. For example: + # + # "101" => "99" + # "99" => "101" + # + # In these situations, just special case them by building special numbers like all 9's or 100...001. + for length in [len(text) - 1, len(text), len(text) + 1]: + xs = [9] * length + + # Add all 9s. + value = "".join(map(str, xs)) + uniques.add(value) + + # Add 100..001. + xs = [0] * length + xs[0] = 1 + xs[-1] = 1 + value = "".join(map(str, xs)) + uniques.add(value) + + # Finally prune the candidate list of numbers that don't make sense. + candidates: list[str] = [] + for candidate in uniques: + if candidate.lstrip("0") == "": + continue + + if candidate == text: + continue + + candidates.append(candidate) + + return candidates + + def __findClosest(self, candidates: list[str], n: int) -> str: + # Find the closest elements to the original number. Since we need to find the smallest to break a tie, just map + # deltas to their candidates. + deltas: dict[int, list[int]] = defaultdict(list) + lowest = math.inf + for candidate in candidates: + c = int(candidate) + delta = abs(n - c) + lowest = min(lowest, delta) + deltas[delta].append(c) + + # If there are multiple closest candidates, return the smallest one. + xs = deltas[int(lowest)] + xs.sort() + return str(xs[0]) diff --git a/test/leetcode/string/find_the_closest_palindrome_test.py b/test/leetcode/string/find_the_closest_palindrome_test.py new file mode 100644 index 0000000..c325cac --- /dev/null +++ b/test/leetcode/string/find_the_closest_palindrome_test.py @@ -0,0 +1,40 @@ +from leetcode.string.find_the_closest_palindrome import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.nearestPalindromic("1234") == "1221" + + +def test_case_2(): + assert soln.nearestPalindromic("123") == "121" + + +def test_case_3(): + assert soln.nearestPalindromic("1") == "0" + + +def test_case_4(): + assert soln.nearestPalindromic("10") == "9" + + +def test_case_5(): + assert soln.nearestPalindromic("11911") == "11811" + + +def test_case_6(): + assert soln.nearestPalindromic("100") == "99" + + +def test_case_7(): + assert soln.nearestPalindromic("11011") == "11111" + + +def test_case_8(): + assert soln.nearestPalindromic("111111111") == "111101111" + + +def test_case_9(): + assert soln.nearestPalindromic("19991") == "20002" From 9f0237b11c51cee030038fe129feb7e605f27b49 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Tue, 18 Mar 2025 18:06:31 -0700 Subject: [PATCH 116/158] closest palindrome --- .../string/find-the-closest-palindrome.ts | 159 ------------------ .../string/find_the_closest_palindrome.py | 29 ++-- .../find-the-closest-palindrome.test.ts | 35 ---- 3 files changed, 14 insertions(+), 209 deletions(-) delete mode 100644 src/leetcode/string/find-the-closest-palindrome.ts delete mode 100644 test/leetcode/string/find-the-closest-palindrome.test.ts diff --git a/src/leetcode/string/find-the-closest-palindrome.ts b/src/leetcode/string/find-the-closest-palindrome.ts deleted file mode 100644 index 2eff520..0000000 --- a/src/leetcode/string/find-the-closest-palindrome.ts +++ /dev/null @@ -1,159 +0,0 @@ -// DIFFICULTY: HARD -// -// Given a string n representing an integer, return the closest integer (not including itself), which is a palindrome. -// If there is a tie, return the smaller one. -// -// The closest is defined as the absolute difference minimized between two integers. -// -// Constraints: 1 <= n.length <= 18 -// -// See {@link https://leetcode.com/problems/find-the-closest-palindrome/} -export { nearestPalindromic }; - -// SOLUTION: -// -// To devise a strategy, first consider a few examples: -// -// "123" => "121" -// "1234" => "1221" -// "1000" => "1001" -// "1" => "0" <-- It's not "1" because we can't return the number itself. -// "999" => "1001" <-- It's not "888" because "1001" is "closer" to "999" by 2. -// "1221" => "1111" <-- It's not "1221" because we can't return the number itself. -// -// Generally, we can either mirror the left side of the number (since that should result in the "closer" number) and -// use that, or we may have to do some incrementing/decrementing to get a part of the number we can mirror. We also -// need to handle edge cases like "1" or "0". -function nearestPalindromic(text: string): string { - // Because the problem states that we could have 18 digit numbers, we may lose precision if we do not use BigInt. - const n = BigInt(text); - - // If it's just a single digit, return that digit minus one (since we can't return the original digit). - if (text.length === 1) { - return String(n - BigInt(1)); - } - - // Split up the string so we have the left prefix (and middle) in case we need it. - const { left, mid } = findPrefix(text); - - // Find possible candidates for the closest palindrome. - const candidates = findCandidates(text, left, mid); - - // Find the actual closest. - const closest = findClosest(candidates, n); - - return closest; -} - -function findPrefix(text: string) { - // Find the left half of the number, which we may need to mirror. If the number has an odd number of digits, we - // don't take the midpoint because we won't want to mirror it anyways. - const left = text.slice(0, text.length / 2); - const mid = text.length % 2 === 0 ? '' : text.charAt(text.length / 2); - return { left, mid }; -} - -function findCandidates(text: string, left: string, mid: string) { - // Generate candidate palindromes; if the original number is already a palindrome, this won't work and we'll have - // to increment or decrement the left side to find the nearest palindrome. - // - // In some cases, we may also have to increment or decrement the middle digit. - const set = new Set(); - [-1, 0, 1].forEach(i => { - const u = BigInt(left); - const v = u + BigInt(i); - const prefix = v.toString(); - const suffix = prefix.split('').reverse().join(''); - set.add(prefix + mid + suffix); - - // There are cases where the middle digit needs to be incremented or decremented as well. For example, take the - // following cases: - // - // "11911" => "11811" - // "10001" => "11111" - // - // To handle these cases we'll vary the middle digit as well. - [-1, 0, 1].forEach(j => { - const updated = BigInt(mid) + BigInt(j); - - // Only add this middle digit if we have a positive value, so we don't end up with "10-11" or something. - if (updated < 0) { - return; - } - - set.add(prefix + updated + suffix); - }); - }); - - // These candidates will work for the vast majority of numbers, but sometimes we'll get edge cases where simply - // incrementing or decrementing won't work. For example: - // - // "101" => "99" - // "99" => "101" - // - // In these situations, just special case them by building special numbers like all 9's or 100...001. - [text.length - 1, text.length, text.length + 1].forEach(size => { - const array = Array(size); - - // Add all 9s. - set.add(array.fill(9).join('')); - - // Add 100..001. - array.fill(0); - array[0] = 1; - array[array.length - 1] = 1; - set.add(array.join('')); - }); - - // Finally prune the candidate list of numbers that don't make sense. - return [...set].filter(candidate => { - if (candidate.startsWith('0')) { - return false; - } - - if (candidate === text) { - return false; - } - - return true; - }); -} - -function findClosest(candidates: string[], n: bigint) { - // Find the closest elements to the original number. Since we need to find the smallest to break a tie, just map - // deltas to their candidates. Note that BigInt is the constructor and bigint is the type. Don't set the type to - // be BigInt or else you won't be able to store stuff in your map. - type Delta = bigint; - type Candidate = bigint; - const map = new Map(); - - // Note that BigInt(Infinity) doesn't work; we'll just set the max value to the number itself. - let lowest = n; - for (let i = 0; i < candidates.length; i++) { - const candidate = BigInt(candidates[i]); - - // Math.abs() is not available for BigInt; we'll have to implement it ourselves. - let delta = n - candidate; - delta = delta > 0 ? delta : -delta; - if (delta < lowest) { - lowest = delta; - } - - const list = map.get(delta) ?? []; - list.push(candidate); - map.set(delta, list); - } - - // If there are multiple closests, then return the smallest. - const list = map.get(lowest) ?? []; - - // BigInt sorting seems to be broken; using .sort() puts 11n in front of 9n. Just do this explicitly instead. - list.sort((a, b) => { - if (a === b) { - return 0; - } - - return a < b ? -1 : 1; - }); - return list[0].toString(); -} diff --git a/src/leetcode/string/find_the_closest_palindrome.py b/src/leetcode/string/find_the_closest_palindrome.py index 99c9f51..2920a2f 100644 --- a/src/leetcode/string/find_the_closest_palindrome.py +++ b/src/leetcode/string/find_the_closest_palindrome.py @@ -71,30 +71,29 @@ def __findCandidates(self, text: str, left: str, mid: str) -> list[str]: # In some cases, we may also have to increment or decrement the middle digit. uniques: set[str] = set() for i in [-1, 0, 1]: - u = int(left) - v = u + i - prefix = str(v) + prefix = str(int(left) + i) suffix = prefix[::-1] uniques.add(prefix + mid + suffix) + # Skip middle digit variation if there is no middle digit. + if not mid: + continue + # There are cases where the middle digit needs to be incremented or decremented as well. For example, take # the following cases: # # "11911" => "11811" # "10001" => "11111" # - # To handle these cases we'll vary the middle digit as well. + # To handle these cases we'll vary the middle digit as well. However, we need to be careful about the + # middle digit going out of bounds, so we'll wrap around if it does. for j in [-1, 0, 1]: - if mid == "": - continue - - updated = int(mid) + j - - # Only add this middle digit if we have a positive value, so we don't end up with "10-11" or something. - if updated < 0: - continue - - uniques.add(prefix + str(updated) + suffix) + m = int(mid) + j + if m == 10: + m = 0 + if m == -1: + m = 9 + uniques.add(prefix + str(m) + suffix) # These candidates will work for the vast majority of numbers, but sometimes we'll get edge cases where simply # incrementing or decrementing won't work. For example: @@ -120,7 +119,7 @@ def __findCandidates(self, text: str, left: str, mid: str) -> list[str]: # Finally prune the candidate list of numbers that don't make sense. candidates: list[str] = [] for candidate in uniques: - if candidate.lstrip("0") == "": + if candidate.startswith("0"): continue if candidate == text: diff --git a/test/leetcode/string/find-the-closest-palindrome.test.ts b/test/leetcode/string/find-the-closest-palindrome.test.ts deleted file mode 100644 index 756d4b8..0000000 --- a/test/leetcode/string/find-the-closest-palindrome.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { nearestPalindromic } from '../../src/string/find-the-closest-palindrome'; - -describe('find the closest palindrome', () => { - test('nearest palindromic - test case 1', async () => { - expect(nearestPalindromic('1234')).toBe('1221'); - }); - - test('nearest palindromic - test case 2', async () => { - expect(nearestPalindromic('123')).toBe('121'); - }); - - test('nearest palindromic - test case 3', async () => { - expect(nearestPalindromic('1')).toBe('0'); - }); - - test('nearest palindromic - test case 4', async () => { - expect(nearestPalindromic('10')).toBe('9'); - }); - - test('nearest palindromic - test case 5', async () => { - expect(nearestPalindromic('11911')).toBe('11811'); - }); - - test('nearest palindromic - test case 6', async () => { - expect(nearestPalindromic('100')).toBe('99'); - }); - - test('nearest palindromic - test case 7', async () => { - expect(nearestPalindromic('11011')).toBe('11111'); - }); - - test('nearest palindromic - test case 8', async () => { - expect(nearestPalindromic('111111111')).toBe('111101111'); - }); -}); From 5168cbbfa5fa4ab879ddf6740cd09931381c663d Mon Sep 17 00:00:00 2001 From: Min Huang Date: Tue, 18 Mar 2025 21:20:14 -0700 Subject: [PATCH 117/158] int2roman --- .../string/find_the_closest_palindrome.py | 2 +- src/leetcode/string/int-to-roman.ts | 93 ------------------ src/leetcode/string/int_to_roman.py | 97 +++++++++++++++++++ test/leetcode/string/int-to-roman.test.ts | 43 -------- test/leetcode/string/int_to_roman_test.py | 16 +++ 5 files changed, 114 insertions(+), 137 deletions(-) delete mode 100644 src/leetcode/string/int-to-roman.ts create mode 100644 src/leetcode/string/int_to_roman.py delete mode 100644 test/leetcode/string/int-to-roman.test.ts create mode 100644 test/leetcode/string/int_to_roman_test.py diff --git a/src/leetcode/string/find_the_closest_palindrome.py b/src/leetcode/string/find_the_closest_palindrome.py index 2920a2f..4583c14 100644 --- a/src/leetcode/string/find_the_closest_palindrome.py +++ b/src/leetcode/string/find_the_closest_palindrome.py @@ -6,7 +6,7 @@ # # The closest is defined as the absolute difference minimized between two integers. # -# Constraints: 1 <= n.length <= 18 +# Constraints: 1 <= len(str(n)) <= 18 # # See https://leetcode.com/problems/find-the-closest-palindrome from collections import defaultdict diff --git a/src/leetcode/string/int-to-roman.ts b/src/leetcode/string/int-to-roman.ts deleted file mode 100644 index a33c5f7..0000000 --- a/src/leetcode/string/int-to-roman.ts +++ /dev/null @@ -1,93 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M. -// -// Symbol Value -// I 1 -// V 5 -// X 10 -// L 50 -// C 100 -// D 500 -// M 1000 -// For example, 2 is written as II in Roman numeral, just two one's added together. 12 is written as XII, which is -// simply X + II. The number 27 is written as XXVII, which is XX + V + II. -// -// Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. -// Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same -// principle applies to the number nine, which is written as IX. There are six instances where subtraction is used: -// -// I can be placed before V (5) and X (10) to make 4 and 9. -// X can be placed before L (50) and C (100) to make 40 and 90. -// C can be placed before D (500) and M (1000) to make 400 and 900. -// Given an integer, convert it to a roman numeral. -// -// See {@link https://leetcode.com/problems/integer-to-roman/} -export { intToRoman }; - -// SOLUTION: -function intToRoman(n: number): string { - function convert(digit: number, ones: string, fives: string, tens: string) { - switch (digit) { - case 1: - case 2: - case 3: - return ones.repeat(digit); - case 4: - return `${ones}${fives}`; - case 5: - return fives; - case 6: - case 7: - case 8: - return `${fives}${ones.repeat(digit - 5)}`; - case 9: - return `${ones}${tens}`; - case 0: - return ''; - default: - throw new Error('not a digit'); - } - } - - let cs = n.toString(); - // If we have a number like 1, we don't know how many digits remain. We could run two pointers through the array, - // converting the least significant digits first, or we could just pad the number so that the hundreds and thousands - // logic will skip over the few digits with zeroes. Choosing the latter here. - if (cs.length < 4) { - cs = '0'.repeat(4 - cs.length) + cs; - } - - const result = []; - for (let i = 0; i < cs.length; i++) { - const digit = Number.parseInt(cs[i], 10 /* radix */); - if (i === 0) { - // We can't have any numbers over 3999, so we don't need to specify any numerals for fives or tens. - const roman = convert(digit, 'M', '', ''); - result.push(roman); - continue; - } - - if (i === 1) { - const roman = convert(digit, 'C', 'D', 'M'); - result.push(roman); - continue; - } - - if (i === 2) { - const roman = convert(digit, 'X', 'L', 'C'); - result.push(roman); - continue; - } - - if (i === 3) { - const roman = convert(digit, 'I', 'V', 'X'); - result.push(roman); - continue; - } - - throw new Error('too many digits'); - } - - return result.join(''); -} diff --git a/src/leetcode/string/int_to_roman.py b/src/leetcode/string/int_to_roman.py new file mode 100644 index 0000000..41165ac --- /dev/null +++ b/src/leetcode/string/int_to_roman.py @@ -0,0 +1,97 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M. +# +# Symbol Value +# I 1 +# V 5 +# X 10 +# L 50 +# C 100 +# D 500 +# M 1000 +# For example, 2 is written as II in Roman numeral, just two one's added together. 12 is written as XII, which is +# simply X + II. The number 27 is written as XXVII, which is XX + V + II. +# +# Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. +# Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same +# principle applies to the number nine, which is written as IX. There are six instances where subtraction is used: +# +# I can be placed before V (5) and X (10) to make 4 and 9. +# X can be placed before L (50) and C (100) to make 40 and 90. +# C can be placed before D (500) and M (1000) to make 400 and 900. +# Given an integer, convert it to a roman numeral. +# +# See https://leetcode.com/problems/integer-to-roman +class Solution: + def intToRoman(self, n: int) -> str: + """ + SOLUTION + -------- + + A straightforward solution works. + + COMPLEXITY + ---------- + + Time complexity is O(n). + + Space complexity is O(1) because we are only storing a fixed number of values. + """ + + def convert(digit: int, ones: str, fives: str, tens: str) -> str: + match digit: + case 1 | 2 | 3: + return ones * digit + case 4: + return ones + fives + case 5: + return fives + case 6 | 7 | 8: + times = digit - 5 + return fives + (ones * times) + case 9: + return ones + tens + case 0: + return "" + case _: + raise ValueError("not a digit") + + cs = str(n) + + # If we have a number like 1, we don't know how many digits remain. We could run two pointers through the + # array, converting the least significant digits first, or we could just pad the number so that the hundreds and + # thousands logic will skip over the few digits with zeroes. Choosing the latter here. + if len(cs) < 4: + times = 4 - len(cs) + cs = ("0" * times) + cs + + result: list[str] = [] + for i, c in enumerate(cs): + digit = int(c) + + if i == 0: + # We can't have any numbers over 3999, so we don't need to specify any numerals for fives or tens. + roman = convert(digit, "M", "", "") + result.append(roman) + continue + + if i == 1: + roman = convert(digit, "C", "D", "M") + result.append(roman) + continue + + if i == 2: + roman = convert(digit, "X", "L", "C") + result.append(roman) + continue + + if i == 3: + roman = convert(digit, "I", "V", "X") + result.append(roman) + continue + + raise ValueError("too many digits") + + return "".join(result) diff --git a/test/leetcode/string/int-to-roman.test.ts b/test/leetcode/string/int-to-roman.test.ts deleted file mode 100644 index 4c23b3f..0000000 --- a/test/leetcode/string/int-to-roman.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { intToRoman } from '../../src/string/int-to-roman'; - -describe('roman to integer', () => { - test('int to roman - test case 1', async () => { - expect(intToRoman(0)).toBe(''); - }); - - test('int to roman - test case 2', async () => { - expect(intToRoman(1)).toBe('I'); - }); - - test('int to roman - test case 3', async () => { - expect(intToRoman(5)).toBe('V'); - }); - - test('int to roman - test case 4', async () => { - expect(intToRoman(10)).toBe('X'); - }); - - test('int to roman - test case 5', async () => { - expect(intToRoman(40)).toBe('XL'); - }); - - test('int to roman - test case 6', async () => { - expect(intToRoman(50)).toBe('L'); - }); - - test('int to roman - test case 7', async () => { - expect(intToRoman(58)).toBe('LVIII'); - }); - - test('int to roman - test case 8', async () => { - expect(intToRoman(90)).toBe('XC'); - }); - - test('int to roman - test case 9', async () => { - expect(intToRoman(100)).toBe('C'); - }); - - test('int to roman - test case 10', async () => { - expect(intToRoman(1000)).toBe('M'); - }); -}); diff --git a/test/leetcode/string/int_to_roman_test.py b/test/leetcode/string/int_to_roman_test.py new file mode 100644 index 0000000..190e6c6 --- /dev/null +++ b/test/leetcode/string/int_to_roman_test.py @@ -0,0 +1,16 @@ +from leetcode.string.int_to_roman import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.intToRoman(3) == "III" + + +def test_case_2(): + assert soln.intToRoman(4) == "IV" + + +def test_case_3(): + assert soln.intToRoman(9) == "IX" From eb46c952f36d29867951ed2e2e507e57d0a2a4be Mon Sep 17 00:00:00 2001 From: Min Huang Date: Tue, 18 Mar 2025 21:35:14 -0700 Subject: [PATCH 118/158] roman2int --- src/leetcode/string/roman-to-int.ts | 59 ---------------------- src/leetcode/string/roman_to_int.py | 60 +++++++++++++++++++++++ test/leetcode/string/roman-to-int.test.ts | 7 --- test/leetcode/string/roman_to_int_test.py | 8 +++ 4 files changed, 68 insertions(+), 66 deletions(-) delete mode 100644 src/leetcode/string/roman-to-int.ts create mode 100644 src/leetcode/string/roman_to_int.py delete mode 100644 test/leetcode/string/roman-to-int.test.ts create mode 100644 test/leetcode/string/roman_to_int_test.py diff --git a/src/leetcode/string/roman-to-int.ts b/src/leetcode/string/roman-to-int.ts deleted file mode 100644 index 0170e77..0000000 --- a/src/leetcode/string/roman-to-int.ts +++ /dev/null @@ -1,59 +0,0 @@ -// DIFFICULTY: EASY -// -// Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M. -// -// Symbol Value -// I 1 -// V 5 -// X 10 -// L 50 -// C 100 -// D 500 -// M 1000 -// -// For example, 2 is written as II in Roman numeral, just two ones added together. 12 is written as XII, which is -// simply X + II. The number 27 is written as XXVII, which is XX + V + II. -// -// Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. -// Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same -// principle applies to the number nine, which is written as IX. There are six instances where subtraction is used: -// -// - I can be placed before V (5) and X (10) to make 4 and 9. -// - X can be placed before L (50) and C (100) to make 40 and 90. -// - C can be placed before D (500) and M (1000) to make 400 and 900. -// -// Given a roman numeral, convert it to an integer. -// -// See {@link https://leetcode.com/problems/roman-to-integer/} -export { romanToInt }; - -// SOLUTION: -function romanToInt(s: string): number { - const map = new Map([ - ['I', 1], - ['V', 5], - ['X', 10], - ['L', 50], - ['C', 100], - ['D', 500], - ['M', 1000] - ]); - - let total = 0; - for (let i = 0; i < s.length; i++) { - const current = map.get(s[i])!; - const next = i + 1 < s.length ? map.get(s[i + 1]) : undefined; - - // In this case, we have a situation like IV (4) or IX (9), where the current number is less than the next number. - // That means we want to do a subtraction. - if (next !== undefined && current < next) { - total -= current; - } - // Other cases like VI (6) or XI (11), we just want to add. - else { - total += current; - } - } - - return total; -} diff --git a/src/leetcode/string/roman_to_int.py b/src/leetcode/string/roman_to_int.py new file mode 100644 index 0000000..d31657c --- /dev/null +++ b/src/leetcode/string/roman_to_int.py @@ -0,0 +1,60 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M. +# +# Symbol Value +# I 1 +# V 5 +# X 10 +# L 50 +# C 100 +# D 500 +# M 1000 +# +# For example, 2 is written as II in Roman numeral, just two ones added together. 12 is written as XII, which is +# simply X + II. The number 27 is written as XXVII, which is XX + V + II. +# +# Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. +# Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same +# principle applies to the number nine, which is written as IX. There are six instances where subtraction is used: +# +# - I can be placed before V (5) and X (10) to make 4 and 9. +# - X can be placed before L (50) and C (100) to make 40 and 90. +# - C can be placed before D (500) and M (1000) to make 400 and 900. +# +# Given a roman numeral, convert it to an integer. +# +# See https://leetcode.com/problems/roman-to-integer +class Solution: + def romanToInt(self, s: str) -> int: + """ + SOLUTION + -------- + + + COMPLEXITY + ---------- + """ + numerals: dict[str, int] = {"I": 1, "V": 5, "X": 10, "L": 50, "C": 100, "D": 500, "M": 1000} + + result = 0 + for i, c in enumerate(s): + # Get the current roman numeral value. + current_val = numerals[c] + + # Get the next roman numeral value, if it exists. + next_val = None + if i + 1 < len(s): + next_numeral = s[i + 1] + next_val = numerals[next_numeral] + + # In this case, we have a situation like IV (4) or IX (9), where the current number is less than the next + # number. That means we want to do a subtraction. + if next_val and current_val < next_val: + result -= current_val + # Other cases like VI (6) or XI (11), we just want to add. + else: + result += current_val + + return result diff --git a/test/leetcode/string/roman-to-int.test.ts b/test/leetcode/string/roman-to-int.test.ts deleted file mode 100644 index ad294b6..0000000 --- a/test/leetcode/string/roman-to-int.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { romanToInt } from '../../src/string/roman-to-int'; - -describe('roman to int', () => { - test('roman to int - test case 1', () => { - expect(romanToInt('III')).toBe(3); - }); -}); diff --git a/test/leetcode/string/roman_to_int_test.py b/test/leetcode/string/roman_to_int_test.py new file mode 100644 index 0000000..3786583 --- /dev/null +++ b/test/leetcode/string/roman_to_int_test.py @@ -0,0 +1,8 @@ +from leetcode.string.roman_to_int import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.romanToInt("III") == 3 From f0a0c088ef3a5bab2f860843cbe178b6e7d31c4f Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 19 Mar 2025 12:30:11 -0700 Subject: [PATCH 119/158] why make me type pycache windows? --- src/leetcode/string/int_to_roman.py | 5 +++-- src/leetcode/string/roman_to_int.py | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/leetcode/string/int_to_roman.py b/src/leetcode/string/int_to_roman.py index 41165ac..e0c573d 100644 --- a/src/leetcode/string/int_to_roman.py +++ b/src/leetcode/string/int_to_roman.py @@ -35,9 +35,10 @@ def intToRoman(self, n: int) -> str: COMPLEXITY ---------- - Time complexity is O(n). + Time complexity is O(n). However, there is an upper limit of what roman numerals can represent, so you can also + argue that the time complexity is O(1). - Space complexity is O(1) because we are only storing a fixed number of values. + Space complexity is O(1). """ def convert(digit: int, ones: str, fives: str, tens: str) -> str: diff --git a/src/leetcode/string/roman_to_int.py b/src/leetcode/string/roman_to_int.py index d31657c..7df4e52 100644 --- a/src/leetcode/string/roman_to_int.py +++ b/src/leetcode/string/roman_to_int.py @@ -32,9 +32,17 @@ def romanToInt(self, s: str) -> int: SOLUTION -------- + A straightforward solution works. + COMPLEXITY ---------- + + Time complexity is O(n). However, there is an upper limit of what roman numerals can represent, so you can also + argue that the time complexity is O(1). + + Space complexity is O(1). + """ numerals: dict[str, int] = {"I": 1, "V": 5, "X": 10, "L": 50, "C": 100, "D": 500, "M": 1000} From 070468e4f2d99946f9a91f670794124b0b77b410 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 19 Mar 2025 13:21:42 -0700 Subject: [PATCH 120/158] english --- pyproject.toml | 2 +- .../string/integer_to_english_words.py | 153 ++++++++++++++++++ .../string/integer_to_english_words_test.py | 12 ++ 3 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 src/leetcode/string/integer_to_english_words.py create mode 100644 test/leetcode/string/integer_to_english_words_test.py diff --git a/pyproject.toml b/pyproject.toml index b9fbecf..979b066 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ python = "^3.13" [tool.poetry.group.dev.dependencies] black = "^25.1.0" isort = "^6.0.1" -pyright = "^1.1.396" +pyright = "^1.1.397" pytest = "^8.3.4" ruff = "^0.9.10" snapshottest = "^1.0.0a0" diff --git a/src/leetcode/string/integer_to_english_words.py b/src/leetcode/string/integer_to_english_words.py new file mode 100644 index 0000000..d90d072 --- /dev/null +++ b/src/leetcode/string/integer_to_english_words.py @@ -0,0 +1,153 @@ +# DIFFICULTY: HARD +# ---------------- +# +# Convert a non-negative integer num to its English words representation. +# +# See https://leetcode.com/problems/integer-to-english-words +class Solution: + def numberToWords(self, num: int) -> str: + """ + SOLUTION + -------- + + To get an idea of how to approach this problem, first start with a few examples, as there are going to be a lot + of edges cases: + + 1 => One + 10 => Ten + 11 => Eleven + 19 => Nineteen + 100 => One Hundred + 101 => One Hundred One + 1_000 => One Thousand + 1_001 => One Thousand One + 100_000 => One Hundred Thousand + 1_000_000 => One Million + 1_000_000_000 => One Billion + + A few notes: + + - 2^32 is the limit, and it is approximately 4 billion, so we don't need to describe numbers over 4 billion. + - The word "And" isn't required between words, so "One Hundred One", not "One Hundred And One". + - The numbers 1-20 need to be handled in a special way due to how English works. + - It's easier to deal with numbers 3 digits at a time. + + When looking at a number XYZ_123_UWV, the 123 part will always be translated into "One Hundred Twenty Three", + and after that, we will append "Thousand", "Million", or "Billion". For this reason, it's best to split the + number into segments of 3, translate that part directly, and then append the correct word afterwards. + + COMPLEXITY + ---------- + + """ + if num == 0: + return "Zero" + + # Each index represents how to say a number in English, if it applies. + under_20 = [ + # Zero is missing; we will never say it in a multi word number phrase. + "", + "One", + "Two", + "Three", + "Four", + "Five", + "Six", + "Seven", + "Eight", + "Nine", + "Ten", + "Eleven", + "Twelve", + "Thirteen", + "Fourteen", + "Fifteen", + "Sixteen", + "Seventeen", + "Eighteen", + "Nineteen", + ] + ones = under_20 + + # Each index represents how we'd say a tens digit in English. + over_20 = [ + # We don't say Zero. + "", + # We don't say Ten One; we say Eleven. This is covered by the under_20 array. + "", + "Twenty", + "Thirty", + "Forty", + "Fifty", + "Sixty", + "Seventy", + "Eighty", + "Ninety", + ] + tens = over_20 + + # Each index represents how we'd say increasingly larger 3 segment chunks of numbers. For example: + # + # 111 => One Hundred Eleven + # 111_000 => One Hundred Eleven Thousand + # 111_000_000 => One Hundred Eleven Million + # 111_000_000_000 => One Hundred Eleven Billion + thousands = [ + # For numbers under 1000, we don't say anything. + "", + "Thousand", + "Million", + "Billion", + ] + + # Converts a 3 digit number into a phrase. For example, 123 => One Hundred Twenty Three. We can then append + # the word Million, Billion, or Thousand afterwards. + def toWordInternal(n: int) -> str: + nonlocal ones, tens, thousands + + # If we have a number like 100_000_001, then the middle segment of 000 does not get translated to "Zero", + # but instead remains blank. + if n == 0: + return "" + # Handle numbers under 20; English has a special way of handling these numbers. + elif n < 20: + return under_20[n] + # Handle 2 digit numbers over 20. + elif n < 100: + first_digit = n // 10 + second_digit = n % 10 + phrase = tens[first_digit] + " " + ones[second_digit] + return phrase.strip() + # Finally, handle 3 digit numbers by figuring how to say the hundreds digit, then reducing the problem to + # the 2 digit case. + else: + first_digit = n // 100 + last_2_digits = n % 100 + first_part = ones[first_digit] + " Hundred" + second_part = toWordInternal(last_2_digits) + phrase = first_part + " " + second_part + return phrase.strip() + + result = "" + + # This will keep track of how many 3 digit segments we've seen so we can add Thousands, Millions, or Billions. + i = 0 + + # Handle the number 3 digits at a time; each 3 digit segment represents an increase to Thousands, Millions, or + # Billions. + while num > 0: + # Get the last 3 digits and figure out how to say them. Once we do that, figure out if we should append + # nothing, Thousand, Million, or Billion. + if num % 1000 != 0: + last_3_digits = num % 1000 + phrase = toWordInternal(last_3_digits) + " " + thousands[i] + + # Add the created phrase to the front of the result, since we are dealing with the last 3 (least + # significant) digits each time. + result = phrase + " " + result + + # Now truncate the number by 3 digits and repeat. + num //= 1000 + i += 1 + + return result.strip() diff --git a/test/leetcode/string/integer_to_english_words_test.py b/test/leetcode/string/integer_to_english_words_test.py new file mode 100644 index 0000000..0477a84 --- /dev/null +++ b/test/leetcode/string/integer_to_english_words_test.py @@ -0,0 +1,12 @@ +from leetcode.string.integer_to_english_words import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.numberToWords(123) == "One Hundred Twenty Three" + + +def test_case_2(): + assert soln.numberToWords(12345) == "Twelve Thousand Three Hundred Forty Five" From 5b934b8e199b88bd579c369c5fe9dd6c2ddf904d Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 19 Mar 2025 13:22:11 -0700 Subject: [PATCH 121/158] update lock --- poetry.lock | 47 +++++++++++++---------------------------------- 1 file changed, 13 insertions(+), 34 deletions(-) diff --git a/poetry.lock b/poetry.lock index 3bf3bc1..7788c6f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. [[package]] name = "black" @@ -6,7 +6,6 @@ version = "25.1.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"}, {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"}, @@ -51,7 +50,6 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" -groups = ["dev"] files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -66,8 +64,6 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["dev"] -markers = "platform_system == \"Windows\" or sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -79,7 +75,6 @@ version = "0.3.0" description = "A fast native implementation of diff algorithm with a pure python fallback" optional = false python-versions = "*" -groups = ["dev"] files = [ {file = "fastdiff-0.3.0-py2.py3-none-any.whl", hash = "sha256:ca5f61f6ddf5a1564ddfd98132ad28e7abe4a88a638a8b014a2214f71e5918ec"}, {file = "fastdiff-0.3.0.tar.gz", hash = "sha256:4dfa09c47832a8c040acda3f1f55fc0ab4d666f0e14e6951e6da78d59acd945a"}, @@ -91,14 +86,13 @@ wasmer-compiler-cranelift = ">=1.0.0" [[package]] name = "iniconfig" -version = "2.0.0" +version = "2.1.0" description = "brain-dead simple config-ini parsing" optional = false -python-versions = ">=3.7" -groups = ["dev"] +python-versions = ">=3.8" files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] [[package]] @@ -107,7 +101,6 @@ version = "6.0.1" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.9.0" -groups = ["dev"] files = [ {file = "isort-6.0.1-py3-none-any.whl", hash = "sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615"}, {file = "isort-6.0.1.tar.gz", hash = "sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450"}, @@ -123,7 +116,6 @@ version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" -groups = ["dev"] files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, @@ -135,7 +127,6 @@ version = "1.9.1" description = "Node.js virtual environment builder" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["dev"] files = [ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, @@ -147,7 +138,6 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -159,7 +149,6 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -171,7 +160,6 @@ version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, @@ -188,7 +176,6 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -200,14 +187,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pyright" -version = "1.1.396" +version = "1.1.397" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" -groups = ["dev"] files = [ - {file = "pyright-1.1.396-py3-none-any.whl", hash = "sha256:c635e473095b9138c471abccca22b9fedbe63858e0b40d4fc4b67da041891844"}, - {file = "pyright-1.1.396.tar.gz", hash = "sha256:142901f5908f5a0895be3d3befcc18bedcdb8cc1798deecaec86ef7233a29b03"}, + {file = "pyright-1.1.397-py3-none-any.whl", hash = "sha256:2e93fba776e714a82b085d68f8345b01f91ba43e1ab9d513e79b70fc85906257"}, + {file = "pyright-1.1.397.tar.gz", hash = "sha256:07530fd65a449e4b0b28dceef14be0d8e0995a7a5b1bb2f3f897c3e548451ce3"}, ] [package.dependencies] @@ -221,14 +207,13 @@ nodejs = ["nodejs-wheel-binaries"] [[package]] name = "pytest" -version = "8.3.4" +version = "8.3.5" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ - {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, - {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, + {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, + {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, ] [package.dependencies] @@ -246,7 +231,6 @@ version = "0.9.10" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" -groups = ["dev"] files = [ {file = "ruff-0.9.10-py3-none-linux_armv6l.whl", hash = "sha256:eb4d25532cfd9fe461acc83498361ec2e2252795b4f40b17e80692814329e42d"}, {file = "ruff-0.9.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:188a6638dab1aa9bb6228a7302387b2c9954e455fb25d6b4470cb0641d16759d"}, @@ -274,7 +258,6 @@ version = "1.0.0a1" description = "Snapshot testing for pytest, unittest, Django, and Nose" optional = false python-versions = "*" -groups = ["dev"] files = [ {file = "snapshottest-1.0.0a1-py3-none-any.whl", hash = "sha256:fff0e1da3825c32d001018777c3b56d1eae1c850fc5b3418618da3d7f2cd152f"}, {file = "snapshottest-1.0.0a1.tar.gz", hash = "sha256:6ef848ee4d6621baff79df6a36bb1da4d7eddf5013dc6b9ca9c361bc42c605b9"}, @@ -295,7 +278,6 @@ version = "2.5.0" description = "ANSI color formatting for output in terminal" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "termcolor-2.5.0-py3-none-any.whl", hash = "sha256:37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8"}, {file = "termcolor-2.5.0.tar.gz", hash = "sha256:998d8d27da6d48442e8e1f016119076b690d962507531df4890fcd2db2ef8a6f"}, @@ -310,7 +292,6 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -322,7 +303,6 @@ version = "1.1.0" description = "Python extension to run WebAssembly binaries" optional = false python-versions = "*" -groups = ["dev"] files = [ {file = "wasmer-1.1.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:c2af4b907ae2dabcac41e316e811d5937c93adf1f8b05c5d49427f8ce0f37630"}, {file = "wasmer-1.1.0-cp310-cp310-manylinux_2_24_x86_64.whl", hash = "sha256:ab1ae980021e5ec0bf0c6cdd3b979b1d15a5f3eb2b8a32da8dcb1156e4a1e484"}, @@ -346,7 +326,6 @@ version = "1.1.0" description = "The Cranelift compiler for the `wasmer` package (to compile WebAssembly module)" optional = false python-versions = "*" -groups = ["dev"] files = [ {file = "wasmer_compiler_cranelift-1.1.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:9869910179f39696a020edc5689f7759257ac1cce569a7a0fcf340c59788baad"}, {file = "wasmer_compiler_cranelift-1.1.0-cp310-cp310-manylinux_2_24_x86_64.whl", hash = "sha256:405546ee864ac158a4107f374dfbb1c8d6cfb189829bdcd13050143a4bd98f28"}, @@ -365,6 +344,6 @@ files = [ ] [metadata] -lock-version = "2.1" +lock-version = "2.0" python-versions = "^3.13" -content-hash = "a23e446554f21e15c723222e3bc375121ed1ce083e24ccbe625f13333687b9e7" +content-hash = "bc8b700268d3ea6269eb75076c073dc104fe50d562ad155f36d18018842f6aa7" From 175079349ea1f16c54d5bbef4d2c9642a9330a7e Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 19 Mar 2025 13:45:47 -0700 Subject: [PATCH 122/158] letters --- .../dot_product_of_two_sparse_vectors.py | 6 +- src/leetcode/array/group_anagrams.py | 6 +- .../letter_combinations_phone_number.py | 61 +++++++++++++++++++ .../letter_combinations_phone_number_test.py | 0 4 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 src/leetcode/string/letter_combinations_phone_number.py create mode 100644 test/leetcode/string/letter_combinations_phone_number_test.py diff --git a/src/leetcode/array/dot_product_of_two_sparse_vectors.py b/src/leetcode/array/dot_product_of_two_sparse_vectors.py index 30c8c1c..246ed0b 100644 --- a/src/leetcode/array/dot_product_of_two_sparse_vectors.py +++ b/src/leetcode/array/dot_product_of_two_sparse_vectors.py @@ -37,11 +37,13 @@ class SparseVector: def __init__(self, nums: list[int]) -> None: # This compresses the vector so that non-zero values are mapped. - self.map: dict[int, int] = {i: num for i, num in enumerate(nums) if num != 0} + self.mapping: dict[int, int] = {i: num for i, num in enumerate(nums) if num != 0} def dotProduct(self, vec: "SparseVector") -> int: # Get the vectors in order of size; we'll iterate over the smaller vector. - (smaller, larger) = (self.map, vec.map) if len(self.map) < len(vec.map) else (vec.map, self.map) + (smaller, larger) = (self.mapping, vec.mapping) + if len(self.mapping) >= len(vec.mapping): + (smaller, larger) = (vec.mapping, self.mapping) result = 0 for i, value in smaller.items(): diff --git a/src/leetcode/array/group_anagrams.py b/src/leetcode/array/group_anagrams.py index d0f654b..6a945d8 100644 --- a/src/leetcode/array/group_anagrams.py +++ b/src/leetcode/array/group_anagrams.py @@ -28,10 +28,10 @@ def groupAnagrams(self, texts: list[str]) -> list[list[str]]: Space complexity is O(m * n). """ # Define a map of canonical representation -> list of anagrams. - map: dict[str, list[str]] = defaultdict(list) + mapping: dict[str, list[str]] = defaultdict(list) for text in texts: canonical = "".join(sorted(text)) - map[canonical].append(text) + mapping[canonical].append(text) - return list(map.values()) + return list(mapping.values()) diff --git a/src/leetcode/string/letter_combinations_phone_number.py b/src/leetcode/string/letter_combinations_phone_number.py new file mode 100644 index 0000000..496892c --- /dev/null +++ b/src/leetcode/string/letter_combinations_phone_number.py @@ -0,0 +1,61 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could +# represent. Return the answer in any order. +# +# A mapping of digits to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any +# letters. +# +# See https://leetcode.com/problems/letter-combinations-of-a-phone-number +class Solution: + def letterCombinations(self, s: str) -> list[str]: + """ + SOLUTION + -------- + + This can be done recursively; first generate all combinations for string of digits minus the head digit. Then + add letters to each combination of the rest. + + COMPLEXITY + ---------- + """ + table: dict[str, str] = { + "2": "abc", + "3": "def", + "4": "ghi", + "5": "jkl", + "6": "mno", + "7": "pqrs", + "8": "tuv", + "9": "wxyz", + } + + def generate(t: str) -> list[str]: + nonlocal table + + if len(t) == 0: + return [] + + if len(t) == 1: + return list(table[t]) + + # Separate the first digit and the rest of the digits. + first = t[0] + rest = t[1:] + + # Find all combinations of words possible just from the rest of the digits. + combinations = generate(rest) + + # Get the letters possible from the first digit. + letters = table[first] + + # Now insert the letters possible from the first digit in front of each combination. + result = [] + for combination in combinations: + for letter in letters: + result.append(letter + combination) + + return result + + return generate(s) diff --git a/test/leetcode/string/letter_combinations_phone_number_test.py b/test/leetcode/string/letter_combinations_phone_number_test.py new file mode 100644 index 0000000..e69de29 From 3e23573c28ba1155832a64c0f5287cf44195f298 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 19 Mar 2025 13:49:33 -0700 Subject: [PATCH 123/158] rename to mapping --- src/leetcode/array/top_k_frequent_elements.py | 8 ++-- src/leetcode/array/two_sum.py | 8 ++-- .../dynamic_programming/word_break_i.py | 10 ++--- .../graph/add_edges_to_make_degrees_even.py | 40 ++++++++++--------- .../graph/nested_list_weighted_sum_ii.py | 6 +-- src/leetcode/heap/task_scheduler.py | 6 +-- .../copy_list_with_random_pointers.py | 12 +++--- src/leetcode/linked_list/lru_cache.py | 16 ++++---- .../prefix_sum/subarray_sum_equals_k.py | 8 ++-- .../recursion/optimal_account_balancing.py | 8 ++-- 10 files changed, 63 insertions(+), 59 deletions(-) diff --git a/src/leetcode/array/top_k_frequent_elements.py b/src/leetcode/array/top_k_frequent_elements.py index fd04095..05fdce0 100644 --- a/src/leetcode/array/top_k_frequent_elements.py +++ b/src/leetcode/array/top_k_frequent_elements.py @@ -24,18 +24,18 @@ def topKFrequent(self, xs: list[int], k: int) -> list[int]: Space complexity is O(n) because we are using a map to store frequency. """ # Create a map of number -> frequency. - map: dict[int, int] = defaultdict(int) + mapping: dict[int, int] = defaultdict(int) # Now populate the map by mapping each number to its frequency. for x in xs: - map[x] += 1 + mapping[x] += 1 # Now get all the unique elements from the list. - uniques = list(map.keys()) + uniques = list(mapping.keys()) # Sort the unique values by their frequency. Since we want the most frequent elements, we sort in decreasing # order (aka reverse order). - uniques = sorted(map.keys(), key=lambda x: map[x], reverse=True) + uniques = sorted(mapping.keys(), key=lambda x: mapping[x], reverse=True) # Return the first k elements return uniques[:k] diff --git a/src/leetcode/array/two_sum.py b/src/leetcode/array/two_sum.py index fc80933..8003e20 100644 --- a/src/leetcode/array/two_sum.py +++ b/src/leetcode/array/two_sum.py @@ -29,17 +29,17 @@ def twoSum(self, xs: list[int], target: int) -> list[int]: Space complexity is O(n). """ # Map number complement -> index where it appears. - map: dict[int, int] = {} + mapping: dict[int, int] = {} for i, x in enumerate(xs): complement = target - x # If our map has the complementary value that would make up the target, we can return it immediately. - if complement in map: - return [i, map[complement]] + if complement in mapping: + return [i, mapping[complement]] # Otherwise, store the number and its index in the map. If we find the complement later, then this index # will be the complement's complement and we can return the indices as normal. - map[x] = i + mapping[x] = i return [] diff --git a/src/leetcode/dynamic_programming/word_break_i.py b/src/leetcode/dynamic_programming/word_break_i.py index 852b82c..b061ca0 100644 --- a/src/leetcode/dynamic_programming/word_break_i.py +++ b/src/leetcode/dynamic_programming/word_break_i.py @@ -29,10 +29,10 @@ def wordBreak(self, s: str, wordDict: list[str]) -> bool: # We'll use a map to indicate if a slice (0,i) can make a sentence. This can just be an array, because the # first index is always going to be 0. However, we make a map here because it's a little clearer what we are # doing. - map: dict[tuple[int, int], bool] = {} + mapping: dict[tuple[int, int], bool] = {} # The slice [0,0) is always going to form a sentence; the base sentence. - map[(0, 0)] = True + mapping[(0, 0)] = True # We want to see if we can successfully parse a sentence from [0,s.length), but to do so, we'll incrementally # build up whether or not we can parse a sentence from [0,j) for all j <= len(s). @@ -45,8 +45,8 @@ def wordBreak(self, s: str, wordDict: list[str]) -> bool: # If the slice (0,i) formed a sentence, then (0,j) must form a sentence because (i,j) is a word. For # example, if (0,i) was 'hello' and (i,j) was 'there', then (0,j) slice of 'hellothere' is a sentence. - if (0, i) in map: - map[(0, j)] = True + if (0, i) in mapping: + mapping[(0, j)] = True # If we've formed a sentence using all the characters, we've got a winner here. - return (0, len(s)) in map + return (0, len(s)) in mapping diff --git a/src/leetcode/graph/add_edges_to_make_degrees_even.py b/src/leetcode/graph/add_edges_to_make_degrees_even.py index 8f2fda0..6178e69 100644 --- a/src/leetcode/graph/add_edges_to_make_degrees_even.py +++ b/src/leetcode/graph/add_edges_to_make_degrees_even.py @@ -33,43 +33,47 @@ def isPossible(self, n: int, edges: list[list[int]]) -> bool: Space complexity is O(m + n). We are storing a set of edges and a map of each node to degree. """ - edgeSet: set[tuple[int, int]] = set() + edge_set: set[tuple[int, int]] = set() def normalizeEdge(edge: tuple[int, int]) -> tuple[int, int]: (a, b) = edge return (a, b) if a < b else (b, a) def hasEdge(edge: tuple[int, int]) -> bool: + nonlocal edge_set + e = normalizeEdge(edge) - return e in edgeSet + return e in edge_set def addEdge(edge: tuple[int, int]) -> None: + nonlocal edge_set + e = normalizeEdge(edge) - edgeSet.add(e) + edge_set.add(e) # Create a map of node -> degrees. - degreeMap: dict[int, int] = defaultdict(int) + degree_mapping: dict[int, int] = defaultdict(int) for edge in edges: [a, b] = edge - if a not in degreeMap: - degreeMap[a] = 0 - if b not in degreeMap: - degreeMap[b] = 0 + if a not in degree_mapping: + degree_mapping[a] = 0 + if b not in degree_mapping: + degree_mapping[b] = 0 - degreeMap[a] += 1 - degreeMap[b] += 1 + degree_mapping[a] += 1 + degree_mapping[b] += 1 addEdge((a, b)) # Find all odd degree nodes. Note that nodes are number 1 through n inclusive. - oddNodes: list[int] = [] + odd_nodes: list[int] = [] for i in range(1, n + 1): - degrees = degreeMap[i] + degrees = degree_mapping[i] if degrees % 2 == 1: - oddNodes.append(i) + odd_nodes.append(i) # Only 0, 2, or 4 odd degree node graphs can have edges added to create a valid all even degree path. So if we # have 0 odd degree nodes, we are golden. - if len(oddNodes) == 0: + if len(odd_nodes) == 0: return True # If we have 2 odd degree nodes, we have to check if we can create an all even degree graph by doing one of the @@ -77,8 +81,8 @@ def addEdge(edge: tuple[int, int]) -> None: # # 1. Add an edge between the two nodes, as long as it's not a duplicate. # 2. Add two edges connecting the nodes, with another node in between. - if len(oddNodes) == 2: - [a, b] = oddNodes + if len(odd_nodes) == 2: + [a, b] = odd_nodes # Check if we can create an all even degree graph by connecting the nodes directly. if not hasEdge((a, b)): return True @@ -98,8 +102,8 @@ def addEdge(edge: tuple[int, int]) -> None: # If we have 4 odd degree nodes, we can check if we can create an all even degree graph by connecting the nodes # together. The nodes must be connected directly though; we can't connect them through some unrelated node as # that requires two edges already, leaving the other two odd degree nodes stranded. - if len(oddNodes) == 4: - [a, b, c, d] = oddNodes + if len(odd_nodes) == 4: + [a, b, c, d] = odd_nodes # With 4 nodes, we can only connect them in 3 distinct ways: pairs = [((a, b), (c, d)), ((a, c), (b, d)), ((a, d), (c, b))] diff --git a/src/leetcode/graph/nested_list_weighted_sum_ii.py b/src/leetcode/graph/nested_list_weighted_sum_ii.py index 24a5ef6..dc8925d 100644 --- a/src/leetcode/graph/nested_list_weighted_sum_ii.py +++ b/src/leetcode/graph/nested_list_weighted_sum_ii.py @@ -38,7 +38,7 @@ def depthSumInverse(self, nestedList: list[NestedInteger]) -> int: """ # Define a list of depth -> integers; each integer at that depth will have the same weight. We'll compute the # max depth as we traverse. - map: dict[int, list[int]] = defaultdict(list[int]) + mapping: dict[int, list[int]] = defaultdict(list[int]) max_depth = 0 def compute(x: NestedInteger, depth: int) -> None: @@ -47,7 +47,7 @@ def compute(x: NestedInteger, depth: int) -> None: if x.isInteger(): value = x.getInteger() or 0 - map[depth].append(value) + mapping[depth].append(value) else: values = x.getList() for value in values: @@ -58,7 +58,7 @@ def compute(x: NestedInteger, depth: int) -> None: # Now that we have the map, just multiply each list of integers at each depth by the computed weight, and sum. result = 0 - for depth, values in map.items(): + for depth, values in mapping.items(): weight = max_depth - depth + 1 partial = sum(values) * weight result += partial diff --git a/src/leetcode/heap/task_scheduler.py b/src/leetcode/heap/task_scheduler.py index 958820e..ca12fe5 100644 --- a/src/leetcode/heap/task_scheduler.py +++ b/src/leetcode/heap/task_scheduler.py @@ -53,13 +53,13 @@ def leastInterval(self, tasks: list[str], n: int) -> int: Space complexity is O(n). """ # Create a map of task to frequency. - map: dict[str, int] = defaultdict(int) + mapping: dict[str, int] = defaultdict(int) for task in tasks: - map[task] += 1 + mapping[task] += 1 # Create a max heap of frequency. max_heap: list[int] = [] - for freq in map.values(): + for freq in mapping.values(): heappush(max_heap, freq) # Create a queue of [freq, cycle] for cooldown tasks. diff --git a/src/leetcode/linked_list/copy_list_with_random_pointers.py b/src/leetcode/linked_list/copy_list_with_random_pointers.py index 51f1d03..f38a997 100644 --- a/src/leetcode/linked_list/copy_list_with_random_pointers.py +++ b/src/leetcode/linked_list/copy_list_with_random_pointers.py @@ -50,21 +50,21 @@ def copyRandomList(self, head: "Node | None") -> "Node | None": return None # Create a map of original nodes to new nodes. - map: dict[Node | None, Node] = {} + mapping: dict[Node | None, Node] = {} # First, create a map of each node to its copy. current = head while current: - map[current] = Node(current.val) + mapping[current] = Node(current.val) current = current.next # Second, assign the pointers. current = head while current: - copy = map[current] - copy.next = map.get(current.next, None) - copy.random = map.get(current.random, None) + copy = mapping[current] + copy.next = mapping.get(current.next, None) + copy.random = mapping.get(current.random, None) current = current.next # Return the head of the copied list. - return map[head] + return mapping[head] diff --git a/src/leetcode/linked_list/lru_cache.py b/src/leetcode/linked_list/lru_cache.py index 28308ab..38d6c21 100644 --- a/src/leetcode/linked_list/lru_cache.py +++ b/src/leetcode/linked_list/lru_cache.py @@ -43,7 +43,7 @@ class LRUCache: """ def __init__(self, capacity: int) -> None: - self.map: dict[int, Node] = {} + self.mapping: dict[int, Node] = {} self.capacity = capacity # Create two sentinel nodes to simplify the logic. @@ -53,29 +53,29 @@ def __init__(self, capacity: int) -> None: self.tail.previous = self.head def get(self, key: int) -> int: - if key not in self.map: + if key not in self.mapping: return -1 - node = self.map[key] + node = self.mapping[key] self.__updateTimestamp(node) return node.value def put(self, key: int, value: int) -> None: - if key in self.map: - node = self.map[key] + if key in self.mapping: + node = self.mapping[key] node.value = value self.__updateTimestamp(node) return # If the key isn't in the map, we need to add it. node = Node(key, value) - self.map[key] = node + self.mapping[key] = node self.__addLeftNode(node) # If we've exceeded the capacity, remove the least recently used key. - if len(self.map) > self.capacity: + if len(self.mapping) > self.capacity: node = self.tail.previous - self.map.pop(node.key) + self.mapping.pop(node.key) self.__removeNode(node) def __removeNode(self, node: Node) -> None: diff --git a/src/leetcode/prefix_sum/subarray_sum_equals_k.py b/src/leetcode/prefix_sum/subarray_sum_equals_k.py index 2f24f06..4c2c9b8 100644 --- a/src/leetcode/prefix_sum/subarray_sum_equals_k.py +++ b/src/leetcode/prefix_sum/subarray_sum_equals_k.py @@ -48,7 +48,7 @@ def subarraySum(self, nums: list[int], k: int) -> int: # # Don't forget to seed the map with a prefix sum of 0 with frequency 1. Without this seed, we'd miss subarrays # that start at the beginning of the array. - map: dict[int, int] = {0: 1} + mapping: dict[int, int] = {0: 1} for num in nums: pj += num @@ -59,10 +59,10 @@ def subarraySum(self, nums: list[int], k: int) -> int: # If prefix[j] appears in the frequency map, we have at LEAST one subarray that sums to k. Add the total # frequency to the result. - result += map.get(pi, 0) + result += mapping.get(pi, 0) # Now update the frequency map to account for this new prefix sum up to j. - freq = map.get(pj, 0) - map[pj] = freq + 1 + freq = mapping.get(pj, 0) + mapping[pj] = freq + 1 return result diff --git a/src/leetcode/recursion/optimal_account_balancing.py b/src/leetcode/recursion/optimal_account_balancing.py index 1b3fcbb..9f7ee91 100644 --- a/src/leetcode/recursion/optimal_account_balancing.py +++ b/src/leetcode/recursion/optimal_account_balancing.py @@ -31,13 +31,13 @@ def minTransfers(self, transactions: list[list[int]]) -> int: Space complexity is O(n) because we are using recursion to solve the problem. """ # Create a map of person -> amount. - map: dict[int, int] = {} + mapping: dict[int, int] = {} # First, figure out the balance that each person holds. for transaction in transactions: [from_person, to_person, amount] = transaction - map[from_person] = map.get(from_person, 0) - amount - map[to_person] = map.get(to_person, 0) + amount + mapping[from_person] = mapping.get(from_person, 0) - amount + mapping[to_person] = mapping.get(to_person, 0) + amount # Second, figure out all non-zero balances. If we needed to know who is paying whom to achieve the minimum # number of transactions, we will need to have an array indexed by person. However, we don't need to know that. @@ -45,7 +45,7 @@ def minTransfers(self, transactions: list[list[int]]) -> int: # We just need to know the number of transactions, so we can just get a list of non-zero balances and try to # zero them out. balances: list[int] = [] - for value in map.values(): + for value in mapping.values(): if value != 0: balances.append(value) From 6dcf3e77fde350a84dcfc142746e25e9ea7f0103 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 19 Mar 2025 13:59:59 -0700 Subject: [PATCH 124/158] letter combs --- .../letter_combinations_phone_number.py | 0 .../string/integer-to-english-words.ts | 152 ------------------ .../letter-combinations-phone-number.ts | 59 ------- .../letter_combinations_phone_number_test.py | 16 ++ .../string/integer-to-english-words.test.ts | 19 --- .../letter-combinations-phone-number.test.ts | 21 --- .../letter_combinations_phone_number_test.py | 0 7 files changed, 16 insertions(+), 251 deletions(-) rename src/leetcode/{string => recursion}/letter_combinations_phone_number.py (100%) delete mode 100644 src/leetcode/string/integer-to-english-words.ts delete mode 100644 src/leetcode/string/letter-combinations-phone-number.ts create mode 100644 test/leetcode/recursion/letter_combinations_phone_number_test.py delete mode 100644 test/leetcode/string/integer-to-english-words.test.ts delete mode 100644 test/leetcode/string/letter-combinations-phone-number.test.ts delete mode 100644 test/leetcode/string/letter_combinations_phone_number_test.py diff --git a/src/leetcode/string/letter_combinations_phone_number.py b/src/leetcode/recursion/letter_combinations_phone_number.py similarity index 100% rename from src/leetcode/string/letter_combinations_phone_number.py rename to src/leetcode/recursion/letter_combinations_phone_number.py diff --git a/src/leetcode/string/integer-to-english-words.ts b/src/leetcode/string/integer-to-english-words.ts deleted file mode 100644 index 6db349c..0000000 --- a/src/leetcode/string/integer-to-english-words.ts +++ /dev/null @@ -1,152 +0,0 @@ -// DIFFICULTY: HARD -// -// Convert a non-negative integer num to its English words representation. -// -// See {@link https://leetcode.com/problems/integer-to-english-words/} -export { numberToWords }; - -// SOLUTION: -// -// To get an idea of how to approach this problem, first start with a few examples, as there are going to be a lot of -// edges cases: -// -// 1 => One -// 10 => Ten -// 11 => Eleven -// 19 => Nineteen -// 100 => One Hundred -// 101 => One Hundred One -// 1_000 => One Thousand -// 1_001 => One Thousand One -// 100_000 => One Hundred Thousand -// 1_000_000 => One Million -// 1_000_000_000 => One Billion -// -// A few notes: -// -// - 2^32 is the limit, and it is approximately 4 billion, so we don't need to describe numbers over 4 billion. -// - The word "And" isn't required between words, so "One Hundred One", not "One Hundred And One". -// - The numbers 1-20 need to be handled in a special way due to how English works. -// - It's easier to deal with numbers 3 digits at a time. -// -// When looking at a number XYZ_123_UWV, the 123 part will always be translated into "One Hundred Twenty Three", and -// after that, we will append "Thousand", "Million", or "Billion". For this reason, it's best to split the number -// into segments of 3, translate that part directly, and then append the correct word afterwards. -function numberToWords(num: number): string { - if (num === 0) { - return 'Zero'; - } - - // Each index represents how to say a number in English, if it applies. - const under20 = [ - '', // Zero missing as a special case; we will never say it in a multi word number phrase. - 'One', - 'Two', - 'Three', - 'Four', - 'Five', - 'Six', - 'Seven', - 'Eight', - 'Nine', - 'Ten', - 'Eleven', - 'Twelve', - 'Thirteen', - 'Fourteen', - 'Fifteen', - 'Sixteen', - 'Seventeen', - 'Eighteen', - 'Nineteen' - ]; - - // Each index represents how we'd say a tens digit in English. - const over20 = [ - '', // We don't say Zero, - '', // We don't say Ten One; we say Eleven. This is covered by the under20 array. - 'Twenty', - 'Thirty', - 'Forty', - 'Fifty', - 'Sixty', - 'Seventy', - 'Eighty', - 'Ninety' - ]; - - // Each index represents how we'd say increasingly larger 3 segment chunks of numbers. For example: - // - // 111 => One Hundred Eleven - // 111_000 => One Hundred Eleven Thousand - // 111_000_000 => One Hundred Eleven Million - // 111_000_000_000 => One Hundred Eleven Billion - const thousands = [ - '', // For numbers under 1000, we don't say anything. - 'Thousand', - 'Million', - 'Billion' - ]; - - // A few aliases for convenience. - const ones = under20; - const tens = over20; - - // Converts a 3 digit number into a phrase. For example, 123 => One Hundred Twenty Three. We can then append the - // word Million, Billion, or Thousand afterwards. - function toWordInternal(n: number): string { - // If we have a number like 100_000_001, then the middle segment of 000 does not get translated to "Zero", but - // instead remains blank. - if (n === 0) { - return ''; - } - - // The numbers under twenty need to be handled in a special way due to how English works. - if (n < 20) { - return under20[n]; - } - - // For a number like 11, we take the tens digit and find out how to say it, then append - if (n < 100) { - const firstDigit = Math.floor(n / 10); - const secondDigit = n % 10; - const phrase = `${tens[firstDigit]} ${ones[secondDigit]}`; - return phrase.trim(); - } - - // Finally, for a three digit number, take the first digit and append "Hundred", then figure out how to say the - // 2 digit number. - const firstDigit = Math.floor(n / 100); - const last2Digits = n % 100; - const firstPart = `${ones[firstDigit]} Hundred`; - const secondPart = toWordInternal(last2Digits); - const phrase = `${firstPart} ${secondPart}`; - return phrase.trim(); - } - - let result = ''; - - // This will keep track of how many 3 digit segments we've seen so we can add Thousands, Millions, or Billions. - let i = 0; - - // Handle the number 3 digits at a time; each 3 digit segment represents an increase to Thousands, Millions, or - // Billions. - while (num > 0) { - // Get the last 3 digits and figure out how to say them. Once we do that, figure out if we should append nothing, - // Thousand, Million, or Billion. - if (num % 1000 !== 0) { - const last3 = num % 1000; - const phrase = `${toWordInternal(last3)} ${thousands[i]}`; - - // Add the created phrase to the front of the result, since we are dealing with the last 3 (least significant) - // digits each time. - result = `${phrase} ${result}`; - } - - // Now truncate the number by 3 digits and repeat. - num = Math.floor(num / 1000); - i++; - } - - return result.trim(); -} diff --git a/src/leetcode/string/letter-combinations-phone-number.ts b/src/leetcode/string/letter-combinations-phone-number.ts deleted file mode 100644 index 713156a..0000000 --- a/src/leetcode/string/letter-combinations-phone-number.ts +++ /dev/null @@ -1,59 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could -// represent. Return the answer in any order. -// -// A mapping of digits to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any -// letters. -// -// See {@link https://leetcode.com/problems/letter-combinations-of-a-phone-number/} -export { letterCombinations }; - -// SOLUTION: -function letterCombinations(digits: string): string[] { - const map = new Map(); - map.set('1', []); - map.set('2', ['a', 'b', 'c']); - map.set('3', ['d', 'e', 'f']); - map.set('4', ['g', 'h', 'i']); - map.set('5', ['j', 'k', 'l']); - map.set('6', ['m', 'n', 'o']); - map.set('7', ['p', 'q', 'r', 's']); - map.set('8', ['t', 'u', 'v']); - map.set('9', ['w', 'x', 'y', 'z']); - map.set('0', [' ']); - - // This can be done recursively; first generate all combinations for string of digits minus the head digit. Then add - // letters to each combination of the rest. - function generate(text: string): string[] { - if (text.length === 0) { - return []; - } - - if (text.length === 1) { - return map.get(text)!; - } - - // Separate the first digit and the rest of the digits. - const first = text[0]; - const rest = text.slice(1); - - // Find all combinations of words possible just from the rest of the digits. - const combinations = generate(rest); - - // Now insert the letters possible from the first digit in front of each combination. - const letters = map.get(first)!; - const result = []; - for (let i = 0; i < combinations.length; i++) { - const combination = combinations[i]; - for (let j = 0; j < letters.length; j++) { - const letter = letters[j]; - result.push(letter + combination); - } - } - - return result; - } - - return generate(digits); -} diff --git a/test/leetcode/recursion/letter_combinations_phone_number_test.py b/test/leetcode/recursion/letter_combinations_phone_number_test.py new file mode 100644 index 0000000..50bbf2b --- /dev/null +++ b/test/leetcode/recursion/letter_combinations_phone_number_test.py @@ -0,0 +1,16 @@ +from leetcode.recursion.letter_combinations_phone_number import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.letterCombinations("") == [] + + +def test_case_2(): + assert sorted(soln.letterCombinations("2")) == ["a", "b", "c"] + + +def test_case_3(): + assert sorted(soln.letterCombinations("23")) == ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"] diff --git a/test/leetcode/string/integer-to-english-words.test.ts b/test/leetcode/string/integer-to-english-words.test.ts deleted file mode 100644 index aac5959..0000000 --- a/test/leetcode/string/integer-to-english-words.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { numberToWords } from '../../src/string/integer-to-english-words'; - -describe('integer to english words', () => { - test('integer to english words - test case 1', async () => { - expect(numberToWords(123)).toBe('One Hundred Twenty Three'); - }); - - test('integer to english words - test case 2', async () => { - expect(numberToWords(12345)).toBe('Twelve Thousand Three Hundred Forty Five'); - }); - - test('integer to english words - test case 3', async () => { - expect(numberToWords(1234567)).toBe('One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven'); - }); - - test('integer to english words - test case 4', async () => { - expect(numberToWords(50868)).toBe('Fifty Thousand Eight Hundred Sixty Eight'); - }); -}); diff --git a/test/leetcode/string/letter-combinations-phone-number.test.ts b/test/leetcode/string/letter-combinations-phone-number.test.ts deleted file mode 100644 index 877c733..0000000 --- a/test/leetcode/string/letter-combinations-phone-number.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { letterCombinations } from '../../src/string/letter-combinations-phone-number'; - -describe('letter combinations of a phone number', () => { - test('letter combinations - test case 1', async () => { - const letters = new Set(letterCombinations('')); - - expect(letters).toStrictEqual(new Set()); - }); - - test('letter combinations - test case 2', async () => { - const letters = new Set(letterCombinations('2')); - - expect(letters).toStrictEqual(new Set(['a', 'b', 'c'])); - }); - - test('letter combinations - test case 3', async () => { - const letters = new Set(letterCombinations('23')); - - expect(letters).toStrictEqual(new Set(['ad', 'ae', 'af', 'bd', 'be', 'bf', 'cd', 'ce', 'cf'])); - }); -}); diff --git a/test/leetcode/string/letter_combinations_phone_number_test.py b/test/leetcode/string/letter_combinations_phone_number_test.py deleted file mode 100644 index e69de29..0000000 From 985a20a82b8c466d4840769493ab5470803906cf Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 19 Mar 2025 14:04:00 -0700 Subject: [PATCH 125/158] hybrid --- src/leetcode/{linked_list => hybrid}/lru_cache.py | 0 src/leetcode/{heap => hybrid}/max_stack.py | 0 test/leetcode/{linked_list => hybrid}/lru_cache_test.py | 2 +- test/leetcode/{heap => hybrid}/max_stack_test.py | 2 +- test/leetcode/linked_list/snapshots/__init__.py | 0 5 files changed, 2 insertions(+), 2 deletions(-) rename src/leetcode/{linked_list => hybrid}/lru_cache.py (100%) rename src/leetcode/{heap => hybrid}/max_stack.py (100%) rename test/leetcode/{linked_list => hybrid}/lru_cache_test.py (94%) rename test/leetcode/{heap => hybrid}/max_stack_test.py (85%) delete mode 100644 test/leetcode/linked_list/snapshots/__init__.py diff --git a/src/leetcode/linked_list/lru_cache.py b/src/leetcode/hybrid/lru_cache.py similarity index 100% rename from src/leetcode/linked_list/lru_cache.py rename to src/leetcode/hybrid/lru_cache.py diff --git a/src/leetcode/heap/max_stack.py b/src/leetcode/hybrid/max_stack.py similarity index 100% rename from src/leetcode/heap/max_stack.py rename to src/leetcode/hybrid/max_stack.py diff --git a/test/leetcode/linked_list/lru_cache_test.py b/test/leetcode/hybrid/lru_cache_test.py similarity index 94% rename from test/leetcode/linked_list/lru_cache_test.py rename to test/leetcode/hybrid/lru_cache_test.py index fc960f7..be93654 100644 --- a/test/leetcode/linked_list/lru_cache_test.py +++ b/test/leetcode/hybrid/lru_cache_test.py @@ -1,4 +1,4 @@ -from leetcode.linked_list.lru_cache import LRUCache +from leetcode.hybrid.lru_cache import LRUCache def test_case_1(): diff --git a/test/leetcode/heap/max_stack_test.py b/test/leetcode/hybrid/max_stack_test.py similarity index 85% rename from test/leetcode/heap/max_stack_test.py rename to test/leetcode/hybrid/max_stack_test.py index 66851ad..77fa93f 100644 --- a/test/leetcode/heap/max_stack_test.py +++ b/test/leetcode/hybrid/max_stack_test.py @@ -1,4 +1,4 @@ -from leetcode.heap.max_stack import MaxStack +from leetcode.hybrid.max_stack import MaxStack stack = MaxStack() diff --git a/test/leetcode/linked_list/snapshots/__init__.py b/test/leetcode/linked_list/snapshots/__init__.py deleted file mode 100644 index e69de29..0000000 From 51f12827aab165d4ccc35c2d0676e75050d3a57e Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 19 Mar 2025 14:09:42 -0700 Subject: [PATCH 126/158] graph --- src/leetcode/array/design_hit_counter.py | 23 +++++++------- .../dot_product_of_two_sparse_vectors.py | 30 +++++++++---------- .../common/nested_integer.py | 0 .../nested_list_weighted_sum.py | 2 +- .../nested_list_weighted_sum_ii.py | 2 +- .../nested_list_weighted_sum_ii_test.py | 4 +-- .../nested_list_weighted_sum_test.py | 4 +-- 7 files changed, 32 insertions(+), 33 deletions(-) rename src/leetcode/{graph => linked_list}/common/nested_integer.py (100%) rename src/leetcode/{graph => linked_list}/nested_list_weighted_sum.py (96%) rename src/leetcode/{graph => linked_list}/nested_list_weighted_sum_ii.py (97%) rename test/leetcode/{graph => linked_list}/nested_list_weighted_sum_ii_test.py (58%) rename test/leetcode/{graph => linked_list}/nested_list_weighted_sum_test.py (57%) diff --git a/src/leetcode/array/design_hit_counter.py b/src/leetcode/array/design_hit_counter.py index 1bc8ac8..00003d1 100644 --- a/src/leetcode/array/design_hit_counter.py +++ b/src/leetcode/array/design_hit_counter.py @@ -9,22 +9,21 @@ # # See https://leetcode.com/problems/design-hit-counter class HitCounter: - """ - SOLUTION - -------- - - To do this efficiently we'll have to use a circular array buffer. This is the same technique used by time - series databases. + def __init__(self) -> None: + """ + SOLUTION + -------- - COMPLEXITY - ---------- + To do this efficiently we'll have to use a circular array buffer. This is the same technique used by time + series databases. - Time complexity is O(1) for both methods since we fix the array size at 300. + COMPLEXITY + ---------- - Space complexity is O(1) for both methods because we fix the array size at 300. - """ + Time complexity is O(1) for both methods since we fix the array size at 300. - def __init__(self) -> None: + Space complexity is O(1) for both methods because we fix the array size at 300. + """ # Each event is a [timestamp, hit_count]. Since our granularity is in seconds, we only need to track the last # 300 seconds. self.timestamps = [0 for _ in range(300)] diff --git a/src/leetcode/array/dot_product_of_two_sparse_vectors.py b/src/leetcode/array/dot_product_of_two_sparse_vectors.py index 246ed0b..5310df6 100644 --- a/src/leetcode/array/dot_product_of_two_sparse_vectors.py +++ b/src/leetcode/array/dot_product_of_two_sparse_vectors.py @@ -15,27 +15,27 @@ # # See https://leetcode.com/problems/dot-product-of-two-sparse-vectors class SparseVector: - """ - SOLUTION - -------- + def __init__(self, nums: list[int]) -> None: + """ + SOLUTION + -------- - If the vector is sparse, just store the non-zero values in a map of index to value. That will compress for - storage; to compute the dot product you can either decompress and then do the dot product, or you can do the - multiplication over the non-zero values directly. + If the vector is sparse, just store the non-zero values in a map of index to value. That will compress for + storage; to compute the dot product you can either decompress and then do the dot product, or you can do the + multiplication over the non-zero values directly. - Doing it without decompression is more efficient, and we should start with the smaller vector to minimize the - number of operations. + Doing it without decompression is more efficient, and we should start with the smaller vector to minimize the + number of operations. - COMPLEXITY - ---------- + COMPLEXITY + ---------- - Time complexity is O(k) in the best case, where k is the number of non-zero elements. In the worst case, - though, it could still be O(n) if both vectors are dense. + Time complexity is O(k) in the best case, where k is the number of non-zero elements. In the worst case, + though, it could still be O(n) if both vectors are dense. - Space complexity is O(k) in the best case, where k is the number of non-zero elements. - """ + Space complexity is O(k) in the best case, where k is the number of non-zero elements. + """ - def __init__(self, nums: list[int]) -> None: # This compresses the vector so that non-zero values are mapped. self.mapping: dict[int, int] = {i: num for i, num in enumerate(nums) if num != 0} diff --git a/src/leetcode/graph/common/nested_integer.py b/src/leetcode/linked_list/common/nested_integer.py similarity index 100% rename from src/leetcode/graph/common/nested_integer.py rename to src/leetcode/linked_list/common/nested_integer.py diff --git a/src/leetcode/graph/nested_list_weighted_sum.py b/src/leetcode/linked_list/nested_list_weighted_sum.py similarity index 96% rename from src/leetcode/graph/nested_list_weighted_sum.py rename to src/leetcode/linked_list/nested_list_weighted_sum.py index b3d9a1e..b6283a4 100644 --- a/src/leetcode/graph/nested_list_weighted_sum.py +++ b/src/leetcode/linked_list/nested_list_weighted_sum.py @@ -10,7 +10,7 @@ # Return the sum of each integer in nestedList multiplied by its depth. # # See https://leetcode.com/problems/nested-list-weight-sum -from leetcode.graph.common.nested_integer import NestedInteger +from leetcode.linked_list.common.nested_integer import NestedInteger class Solution: diff --git a/src/leetcode/graph/nested_list_weighted_sum_ii.py b/src/leetcode/linked_list/nested_list_weighted_sum_ii.py similarity index 97% rename from src/leetcode/graph/nested_list_weighted_sum_ii.py rename to src/leetcode/linked_list/nested_list_weighted_sum_ii.py index dc8925d..b8a9769 100644 --- a/src/leetcode/graph/nested_list_weighted_sum_ii.py +++ b/src/leetcode/linked_list/nested_list_weighted_sum_ii.py @@ -13,7 +13,7 @@ # # See https://leetcode.com/problems/nested-list-weight-sum-ii from collections import defaultdict -from leetcode.graph.common.nested_integer import NestedInteger +from leetcode.linked_list.common.nested_integer import NestedInteger class Solution: diff --git a/test/leetcode/graph/nested_list_weighted_sum_ii_test.py b/test/leetcode/linked_list/nested_list_weighted_sum_ii_test.py similarity index 58% rename from test/leetcode/graph/nested_list_weighted_sum_ii_test.py rename to test/leetcode/linked_list/nested_list_weighted_sum_ii_test.py index 4df6b05..65ea204 100644 --- a/test/leetcode/graph/nested_list_weighted_sum_ii_test.py +++ b/test/leetcode/linked_list/nested_list_weighted_sum_ii_test.py @@ -1,5 +1,5 @@ -from leetcode.graph.common.nested_integer import NestedInteger -from leetcode.graph.nested_list_weighted_sum_ii import Solution +from leetcode.linked_list.common.nested_integer import NestedInteger +from leetcode.linked_list.nested_list_weighted_sum_ii import Solution soln = Solution() diff --git a/test/leetcode/graph/nested_list_weighted_sum_test.py b/test/leetcode/linked_list/nested_list_weighted_sum_test.py similarity index 57% rename from test/leetcode/graph/nested_list_weighted_sum_test.py rename to test/leetcode/linked_list/nested_list_weighted_sum_test.py index f3aa899..1f3ef57 100644 --- a/test/leetcode/graph/nested_list_weighted_sum_test.py +++ b/test/leetcode/linked_list/nested_list_weighted_sum_test.py @@ -1,5 +1,5 @@ -from leetcode.graph.common.nested_integer import NestedInteger -from leetcode.graph.nested_list_weighted_sum import Solution +from leetcode.linked_list.common.nested_integer import NestedInteger +from leetcode.linked_list.nested_list_weighted_sum import Solution soln = Solution() From aa160b1cf536b425515836e39c7e22f62f20ed3f Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 19 Mar 2025 14:18:49 -0700 Subject: [PATCH 127/158] comments --- src/leetcode/hybrid/lru_cache.py | 21 ++++++------ src/leetcode/hybrid/max_stack.py | 32 +++++++++---------- src/leetcode/linked_list/all_one.py | 26 +++++++-------- .../copy_list_with_random_pointers.py | 7 ++-- .../letter_combinations_phone_number.py | 5 +++ .../moving_average_from_data_stream.py | 21 ++++++------ .../key_value_store_nested_transactions.py | 21 ++++++------ src/leetcode/stack/min_stack.py | 25 +++++++-------- 8 files changed, 80 insertions(+), 78 deletions(-) diff --git a/src/leetcode/hybrid/lru_cache.py b/src/leetcode/hybrid/lru_cache.py index 38d6c21..7118fa3 100644 --- a/src/leetcode/hybrid/lru_cache.py +++ b/src/leetcode/hybrid/lru_cache.py @@ -28,21 +28,20 @@ def __init__(self, key: int, value: int) -> None: class LRUCache: - """ - SOLUTION - -------- - - This problem can be solved using a combination of a doubly linked list and a hashmap. + def __init__(self, capacity: int) -> None: + """ + SOLUTION + -------- - COMPLEXITY - ---------- + This problem can be solved using a combination of a doubly linked list and a hashmap. - Time complexity is O(1) for all operations. + COMPLEXITY + ---------- - Space complexity is O(n) where n is the number of keys in the data structure. - """ + Time complexity is O(1) for all operations. - def __init__(self, capacity: int) -> None: + Space complexity is O(n) where n is the number of keys in the data structure. + """ self.mapping: dict[int, Node] = {} self.capacity = capacity diff --git a/src/leetcode/hybrid/max_stack.py b/src/leetcode/hybrid/max_stack.py index 7eeacea..2002979 100644 --- a/src/leetcode/hybrid/max_stack.py +++ b/src/leetcode/hybrid/max_stack.py @@ -43,29 +43,29 @@ def __init__(self, key: int, value: int): class MaxStack: - """ - SOLUTION - -------- + def __init__(self): + """ + SOLUTION + -------- - Use a heap and a doubly linked list to represent the max stack. Most operations can be performed in O(1) or - O(log n). The only challenge is that when popping from the stack, it's easy to remove from the end of the - linked list, but it's not easy to remove from the middle of the max heap. + Use a heap and a doubly linked list to represent the max stack. Most operations can be performed in O(1) or + O(log n). The only challenge is that when popping from the stack, it's easy to remove from the end of the + linked list, but it's not easy to remove from the middle of the max heap. - Instead of removing from the max heap immediately, defer the deletion until we perform a peekMax or popMax, and - check if processing the deferred deletions are required. If so, perform them there. + Instead of removing from the max heap immediately, defer the deletion until we perform a peekMax or popMax, and + check if processing the deferred deletions are required. If so, perform them there. - For the purposes of this solution, we won't consider edge cases like popping from an empty stack. This keeps - the solution simple. + For the purposes of this solution, we won't consider edge cases like popping from an empty stack. This keeps + the solution simple. - COMPLEXITY - ---------- + COMPLEXITY + ---------- - Time complexity is O(1) for top and O(log n) for each other call. + Time complexity is O(1) for top and O(log n) for each other call. - Space complexity is O(n). - """ + Space complexity is O(n). + """ - def __init__(self): # Because the stack could have multiple duplicate elements, use a unique ID for each node. self.keys = 0 # Because deleting from the middle of a heap is hard, keep track of a set of deletions while popping and defer diff --git a/src/leetcode/linked_list/all_one.py b/src/leetcode/linked_list/all_one.py index cc9743f..30e3cd3 100644 --- a/src/leetcode/linked_list/all_one.py +++ b/src/leetcode/linked_list/all_one.py @@ -34,25 +34,25 @@ def __init__(self, count: float) -> None: class AllOne: - """ - SOLUTION - -------- + def __init__(self) -> None: + """ + SOLUTION + -------- - Use a doubly linked list and hashmap to store the keys and their counts. Each node in the linked list will - represent a specific count, and the keys in that node have the count. + Use a doubly linked list and hashmap to store the keys and their counts. Each node in the linked list will + represent a specific count, and the keys in that node have the count. - The doubly linked list will let us quickly find the min and max counts, and the hashmap will let us quickly find any - key for inc/dec. + The doubly linked list will let us quickly find the min and max counts, and the hashmap will let us quickly find + any key for inc/dec. - COMPLEXITY - ---------- + COMPLEXITY + ---------- - Time complexity is O(1) for all operations. + Time complexity is O(1) for all operations. - Space complexity is O(n) where n is the number of keys in the data structure. - """ + Space complexity is O(n) where n is the number of keys in the data structure. + """ - def __init__(self) -> None: # Map of key -> node. self.nodes: dict[str, Node] = {} # We'll use this linked list to quickly find the min and max counts, which will lead us to the min and max keys. diff --git a/src/leetcode/linked_list/copy_list_with_random_pointers.py b/src/leetcode/linked_list/copy_list_with_random_pointers.py index f38a997..abdf439 100644 --- a/src/leetcode/linked_list/copy_list_with_random_pointers.py +++ b/src/leetcode/linked_list/copy_list_with_random_pointers.py @@ -34,10 +34,11 @@ def copyRandomList(self, head: "Node | None") -> "Node | None": -------- If there's no random pointer you can iterate through the nodes and create a copy of each node. With the random - pointer you'll have to maintain a map of original node to copied node so you can assign the random pointers later. + pointer you'll have to maintain a map of original node to copied node so you can assign the random pointers + later. - First iterate through the list once to make copies of each node in a map. Then iterate again and assign the next - and random pointers. + First iterate through the list once to make copies of each node in a map. Then iterate again and assign the + next and random pointers. COMPLEXITY ---------- diff --git a/src/leetcode/recursion/letter_combinations_phone_number.py b/src/leetcode/recursion/letter_combinations_phone_number.py index 496892c..48a90ae 100644 --- a/src/leetcode/recursion/letter_combinations_phone_number.py +++ b/src/leetcode/recursion/letter_combinations_phone_number.py @@ -19,6 +19,11 @@ def letterCombinations(self, s: str) -> list[str]: COMPLEXITY ---------- + + Time complexity is O(4^n) where n is the number of digits in the input string. We are branching max 4 times per + digit. + + Space complexity is O(n) where n is the number of digits in the input string. """ table: dict[str, str] = { "2": "abc", diff --git a/src/leetcode/sliding_window/moving_average_from_data_stream.py b/src/leetcode/sliding_window/moving_average_from_data_stream.py index 50d8a4b..65a2066 100644 --- a/src/leetcode/sliding_window/moving_average_from_data_stream.py +++ b/src/leetcode/sliding_window/moving_average_from_data_stream.py @@ -13,21 +13,20 @@ class MovingAverage: - """ - SOLUTION - -------- - - This can be solved with a queue or sliding window. + def __init__(self, size: int) -> None: + """ + SOLUTION + -------- - COMPLEXITY - ---------- + This can be solved with a queue or sliding window. - Time complexity is O(1) for next. + COMPLEXITY + ---------- - Space complexity is O(n) where n is the size of the window. - """ + Time complexity is O(1) for next. - def __init__(self, size: int) -> None: + Space complexity is O(n) where n is the size of the window. + """ self.size = size self.window: deque[int] = deque() self.total = 0 diff --git a/src/leetcode/stack/key_value_store_nested_transactions.py b/src/leetcode/stack/key_value_store_nested_transactions.py index eca0786..15b0444 100644 --- a/src/leetcode/stack/key_value_store_nested_transactions.py +++ b/src/leetcode/stack/key_value_store_nested_transactions.py @@ -15,21 +15,20 @@ # # See https://leetcode.com/discuss/interview-question/279913/Bloomberg-or-Onsite-or-Key-Value-Store-with-transactions class KeyValueStore: - """ - SOLUTION - -------- - - Use a stack to store the transactions. Each transaction is a dictionary that maps keys to values. + def __init__(self) -> None: + """ + SOLUTION + -------- - COMPLEXITY - ---------- + Use a stack to store the transactions. Each transaction is a dictionary that maps keys to values. - Time complexity is O(1) for all operations. + COMPLEXITY + ---------- - Space complexity is O(n) where n is the number of keys in the data structure. - """ + Time complexity is O(1) for all operations. - def __init__(self) -> None: + Space complexity is O(n) where n is the number of keys in the data structure. + """ self.deletions: list[set[str]] = [] self.txs: list[dict[str, int]] = [] self.main: dict[str, int] = {} diff --git a/src/leetcode/stack/min_stack.py b/src/leetcode/stack/min_stack.py index 10c4fa9..33b67e9 100644 --- a/src/leetcode/stack/min_stack.py +++ b/src/leetcode/stack/min_stack.py @@ -15,24 +15,23 @@ # # See https://leetcode.com/problems/min-stack class MinStack: - """ - SOLUTION - -------- - - Use one stack to store the values and another stack to store the minimum values. + def __init__(self): + """ + SOLUTION + -------- - It appears that the problem will not call your solution with an empty stack, so we don't need to worry about that - case. + Use one stack to store the values and another stack to store the minimum values. - COMPLEXITY - ---------- + It appears that the problem will not call your solution with an empty stack, so we don't need to worry about that + case. - Time complexity is O(1) for all operations. + COMPLEXITY + ---------- - Space complexity is O(n) where n is the number of elements in the stack. - """ + Time complexity is O(1) for all operations. - def __init__(self): + Space complexity is O(n) where n is the number of elements in the stack. + """ self.stack: list[int] = [] self.min_values: list[int] = [] From b69cae11a3b506ebe3cd12d56bea4ab29500c46e Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 19 Mar 2025 14:45:30 -0700 Subject: [PATCH 128/158] longest --- .../string/longest-palindromic-substring.ts | 81 ------------------- .../string/longest_palindromic_substring.py | 77 ++++++++++++++++++ .../longest_palindromic_substring_test.py | 12 +++ 3 files changed, 89 insertions(+), 81 deletions(-) delete mode 100644 src/leetcode/string/longest-palindromic-substring.ts create mode 100644 src/leetcode/string/longest_palindromic_substring.py create mode 100644 test/leetcode/string/longest_palindromic_substring_test.py diff --git a/src/leetcode/string/longest-palindromic-substring.ts b/src/leetcode/string/longest-palindromic-substring.ts deleted file mode 100644 index 82924fe..0000000 --- a/src/leetcode/string/longest-palindromic-substring.ts +++ /dev/null @@ -1,81 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given a string s, return the longest palindromic substring in s. -// -// See {@link https://leetcode.com/problems/longest-palindromic-substring/} -export { longestPalindrome }; - -// SOLUTION: -// -// The naive solution is to iterate through each character, then treat that character as the center of a palindrome, -// expanding outwards to check if the characters match. -// -// Note that for each character, we have to do two checks: -// -// 1. Check if there's a palindrome centered at i, for odd length palindromes. -// 2. Check if there's a palindrome centered at i and i + 1, for even length palindromes. -// -// There is a dynamic programming solution that can solve this problem in O(n^2) time and O(n^2) space. -// -// There is a more sophisticated algorithm called Manacher's algorithm that can solve this problem in O(n) time, but -// it's pretty complicated. -// -// COMPLEXITY: -// -// Each checkPalindrome() call is O(n) where n is the length of the string. We call this function twice for each -// character in the string, so the total time complexity is O(n^2). -// -// The space complexity is O(1) because we only use a constant amount of space. -function longestPalindrome(s: string): string { - let start = 0; - let maxLength = 0; - - function checkPalindrome(left: number, right: number) { - let matched = false; - - // Expand the left and right pointers outwards from the center. - while (left >= 0 && right < s.length) { - // If the characters match, expand the pointers. - if (s[left] === s[right]) { - left--; - right++; - matched = true; - } - // Otherwise, break out of the loop. - else { - break; - } - } - - // If we didn't match any characters, then there's no need to update the length of the longest palindrome. - if (!matched) { - return; - } - - // When the while loop ends, s[left] !== s[right], which means they don't match. Adjust them back to a valid - // position. If they didn't match, then there's no need to - left++; - right--; - - // The length of the palindrome we have is bounded by [left, right] inclusive. To get the length of this range, - // we need to do right - left + 1. - const length = right - left + 1; - if (right - left + 1 > maxLength) { - start = left; - maxLength = length; - } - } - - if (s.length === 0) { - return ''; - } - - for (let i = 0; i < s.length; i++) { - // Check if there's a palindrome centered at i. - checkPalindrome(i, i); - // Check if there's a palindrome centered at i and i + 1. - checkPalindrome(i, i + 1); // Even-length palindrome - } - - return s.substring(start, start + maxLength); -} diff --git a/src/leetcode/string/longest_palindromic_substring.py b/src/leetcode/string/longest_palindromic_substring.py new file mode 100644 index 0000000..0992403 --- /dev/null +++ b/src/leetcode/string/longest_palindromic_substring.py @@ -0,0 +1,77 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given a string s, return the longest palindromic substring in s. +# +# See https://leetcode.com/problems/longest-palindromic-substring +class Solution: + def longestPalindrome(self, s: str) -> str: + """ + SOLUTION + -------- + + The naive solution is to iterate through each character, then treat that character as the center of a + palindrome, expanding outwards to check if the characters match. + + Note that for each character, we have to do two checks: + + 1. Check if there's a palindrome centered at i, for odd length palindromes. + 2. Check if there's a palindrome centered at i and i + 1, for even length palindromes. + + There is a dynamic programming solution that can solve this problem in O(n^2) time and O(n^2) space. + + There is a more sophisticated algorithm called Manacher's algorithm that can solve this problem in O(n) time, + but it's pretty complicated. + + COMPLEXITY + ---------- + + Each checkPalindrome() call is O(n) where n is the length of the string. We call this function twice for each + character in the string, so the total time complexity is O(n^2). + + The space complexity is O(1) because we only use a constant amount of space. + """ + start = 0 + max_length = 0 + + def checkPalindrome(left: int, right: int) -> None: + nonlocal start, max_length + + matched = False + + # Expand the left and right pointers outwards from the center. + while 0 <= left and right < len(s): + # If the characters match, expand the window, otherwise, break. + if s[left] == s[right]: + left -= 1 + right += 1 + matched = True + else: + break + + # If we didn't match any characters, then there's no need to update the length of the longest palindrome. + if not matched: + return + + # When the while loop ends, s[left] !== s[right], which means they don't match. Adjust them back to a valid + # position. If they didn't match, then there's no need to. + left += 1 + right -= 1 + + # The length of the palindrome we have is bounded by [left, right] inclusive. To get the length of this + # range, we need to do right - left + 1. + current_length = right - left + 1 + if current_length > max_length: + start = left + max_length = current_length + + if len(s) == 0: + return "" + + for i in range(len(s)): + # Check if there's a palindrome centered at i. + checkPalindrome(i, i) + # Check if there's a palindrome centered at i and i + 1. + checkPalindrome(i, i + 1) + + return s[start : start + max_length] diff --git a/test/leetcode/string/longest_palindromic_substring_test.py b/test/leetcode/string/longest_palindromic_substring_test.py new file mode 100644 index 0000000..0c0c17e --- /dev/null +++ b/test/leetcode/string/longest_palindromic_substring_test.py @@ -0,0 +1,12 @@ +from leetcode.string.longest_palindromic_substring import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.longestPalindrome("babad") == "bab" + + +def test_case_2(): + assert soln.longestPalindrome("cbbd") == "bb" From b80cd0357ba2c52015250d049d5b98ff34f7dc37 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 19 Mar 2025 14:47:24 -0700 Subject: [PATCH 129/158] palindrome num --- src/leetcode/string/palindrome-number.ts | 13 ----------- src/leetcode/string/palindrome_number.py | 22 +++++++++++++++++++ .../leetcode/string/palindrome-number.test.ts | 7 ------ .../leetcode/string/palindrome_number_test.py | 8 +++++++ 4 files changed, 30 insertions(+), 20 deletions(-) delete mode 100644 src/leetcode/string/palindrome-number.ts create mode 100644 src/leetcode/string/palindrome_number.py delete mode 100644 test/leetcode/string/palindrome-number.test.ts create mode 100644 test/leetcode/string/palindrome_number_test.py diff --git a/src/leetcode/string/palindrome-number.ts b/src/leetcode/string/palindrome-number.ts deleted file mode 100644 index c60635f..0000000 --- a/src/leetcode/string/palindrome-number.ts +++ /dev/null @@ -1,13 +0,0 @@ -// DIFFICULTY: EASY -// -// Given an integer x, return true if x is a palindrome, and false otherwise. -// -// See {@link https://leetcode.com/problems/palindrome-number/} -export { isPalindrome }; - -// SOLUTION: -function isPalindrome(x: number): boolean { - const s = x.toString(); - const r = s.split('').reverse().join(''); - return s === r; -} diff --git a/src/leetcode/string/palindrome_number.py b/src/leetcode/string/palindrome_number.py new file mode 100644 index 0000000..bd72184 --- /dev/null +++ b/src/leetcode/string/palindrome_number.py @@ -0,0 +1,22 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given an integer x, return true if x is a palindrome, and false otherwise. +# +# See https://leetcode.com/problems/palindrome-number +class Solution: + def isPalindrome(self, x: int) -> bool: + """ + SOLUTION + -------- + + Convert the integer to a string and compare the string to its reverse. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of digits in x. + + Space complexity is O(n). + """ + return str(x) == str(x)[::-1] diff --git a/test/leetcode/string/palindrome-number.test.ts b/test/leetcode/string/palindrome-number.test.ts deleted file mode 100644 index 065c97a..0000000 --- a/test/leetcode/string/palindrome-number.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { isPalindrome } from '../../src/string/palindrome-number'; - -describe('palindrome-number', () => { - test('palindrome number - test case 1', () => { - expect(isPalindrome(121)).toBe(true); - }); -}); diff --git a/test/leetcode/string/palindrome_number_test.py b/test/leetcode/string/palindrome_number_test.py new file mode 100644 index 0000000..9424696 --- /dev/null +++ b/test/leetcode/string/palindrome_number_test.py @@ -0,0 +1,8 @@ +from leetcode.string.palindrome_number import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.isPalindrome(121) From 3d4f8fe02a750009d2eeb0867c268f6a8cb8655d Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 19 Mar 2025 14:49:35 -0700 Subject: [PATCH 130/158] remove duples --- .../remove-all-adjacent-duplicates-i.ts | 30 ---------------- .../string/remove_all_adjacent_duplicates.py | 35 +++++++++++++++++++ .../remove_all_adjacent_duplicates_test.py | 8 +++++ 3 files changed, 43 insertions(+), 30 deletions(-) delete mode 100644 src/leetcode/string/remove-all-adjacent-duplicates-i.ts create mode 100644 src/leetcode/string/remove_all_adjacent_duplicates.py create mode 100644 test/leetcode/string/remove_all_adjacent_duplicates_test.py diff --git a/src/leetcode/string/remove-all-adjacent-duplicates-i.ts b/src/leetcode/string/remove-all-adjacent-duplicates-i.ts deleted file mode 100644 index a5d4832..0000000 --- a/src/leetcode/string/remove-all-adjacent-duplicates-i.ts +++ /dev/null @@ -1,30 +0,0 @@ -// DIFFICULTY: EASY -// -// You are given a string s consisting of lowercase English letters. A duplicate removal consists of choosing two -// adjacent and equal letters and removing them. -// -// We repeatedly make duplicate removals on s until we no longer can. -// -// Return the final string after all such duplicate removals have been made. It can be proven that the answer is unique. -// -// See {@link https://leetcode.com/problems/remove-all-adjacent-duplicates-in-string/} -export { removeDuplicates }; - -// SOLUTION: -function removeDuplicates(s: string) { - const stack: string[] = []; - - for (const c of s) { - // If the top of the stack has a dupe, pop it off and ignore this character. - if (stack.length !== 0 && c === stack[stack.length - 1]) { - stack.pop(); - continue; - } - - // Otherwise push the character on the stack. - stack.push(c); - } - - // We'll end up with all the non-dupes. - return stack.join(''); -} diff --git a/src/leetcode/string/remove_all_adjacent_duplicates.py b/src/leetcode/string/remove_all_adjacent_duplicates.py new file mode 100644 index 0000000..e6ad060 --- /dev/null +++ b/src/leetcode/string/remove_all_adjacent_duplicates.py @@ -0,0 +1,35 @@ +# DIFFICULTY: EASY +# +# You are given a string s consisting of lowercase English letters. A duplicate removal consists of choosing two +# adjacent and equal letters and removing them. +# +# We repeatedly make duplicate removals on s until we no longer can. +# +# Return the final string after all such duplicate removals have been made. It can be proven that the answer is unique. +# +# See https://leetcode.com/problems/remove-all-adjacent-duplicates-in-string +class Solution: + def removeDuplicates(self, s: str) -> str: + """ + SOLUTION + -------- + + Use a stack to keep track of the characters. If the next character is the same as the top of the stack, pop the + top of the stack. Otherwise, push the character onto the stack. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the length of s. + + Space complexity is O(n). + """ + stack: list[str] = [] + + for c in s: + if stack and c == stack[-1]: + stack.pop() + else: + stack.append(c) + + return "".join(stack) diff --git a/test/leetcode/string/remove_all_adjacent_duplicates_test.py b/test/leetcode/string/remove_all_adjacent_duplicates_test.py new file mode 100644 index 0000000..27a0091 --- /dev/null +++ b/test/leetcode/string/remove_all_adjacent_duplicates_test.py @@ -0,0 +1,8 @@ +from leetcode.string.remove_all_adjacent_duplicates import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.removeDuplicates("abbaca") == "ca" From d073c59edb63db434cee5d7ebd9ee94d29bbb3b7 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 19 Mar 2025 15:04:40 -0700 Subject: [PATCH 131/158] remove duples --- .../remove-all-adjacent-duplicates-ii.ts | 63 ------------------- .../string/remove_all_adjacent_duplicates.py | 4 -- .../remove_all_adjacent_duplicates_ii.py | 58 +++++++++++++++++ .../remove-all-adjacent-duplicates-i.test.ts | 7 --- .../remove_all_adjacent_duplicates_ii_test.py | 12 ++++ 5 files changed, 70 insertions(+), 74 deletions(-) delete mode 100644 src/leetcode/string/remove-all-adjacent-duplicates-ii.ts create mode 100644 src/leetcode/string/remove_all_adjacent_duplicates_ii.py delete mode 100644 test/leetcode/string/remove-all-adjacent-duplicates-i.test.ts create mode 100644 test/leetcode/string/remove_all_adjacent_duplicates_ii_test.py diff --git a/src/leetcode/string/remove-all-adjacent-duplicates-ii.ts b/src/leetcode/string/remove-all-adjacent-duplicates-ii.ts deleted file mode 100644 index 60769df..0000000 --- a/src/leetcode/string/remove-all-adjacent-duplicates-ii.ts +++ /dev/null @@ -1,63 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given a string s and an integer k, a k duplicate removal consists of choosing k adjacent and equal letters -// from s and removing them, causing the left and the right side of the deleted substring to concatenate together. -// -// We repeatedly make k duplicate removals on s until we no longer can. -// -// Return the final string after all such duplicate removals have been made. It is guaranteed that the answer is unique. -// -// See {@link https://leetcode.com/problems/remove-all-adjacent-duplicates-in-string-ii/} -export { removeDuplicates }; - -// SOLUTION: -// -// In contrast with remove duplicates i, we need to keep track of not just the top element of the stack, but rather if -// the top k elements constitute dupes. Once we remove k elements, it's possible the new stack now has k more -// elements to remove. -// -// To account for this, we cannot simply count elements and remove when we see k, resetting out counter to 1. Instead -// we will have to store, at each stack frame, the elements *and* what the counter was. -function removeDuplicates(s: string, k: number) { - interface Character { - value: string; - count: number; - } - - const stack: Character[] = []; - let count = 1; - - for (const c of s) { - // If the top of the stack and the current character match, increase our count. If they don't match, reset our - // count so that it is 1 (since we always at least see 1 match; the character itself). - if (stack.length !== 0 && c === stack[stack.length - 1].value) { - count++; - } else { - count = 1; - } - - stack.push({ - value: c, - count - }); - - // Only if we've seen k dupes, pop them all. Here, we cannot reset our counter back to 1; we have to reset the - // counter back to what it was when we first pushed the top element onto the stack. - if (count === k) { - for (let i = 0; i < k; i++) { - stack.pop(); - } - - // Reset our count. If we have no more stack elements, we can reset to 1. However, if we have stack elements, - // we have to reset to what it was previously. - if (stack.length === 0) { - count = 1; - } else { - count = stack[stack.length - 1].count; - } - } - } - - // We'll end up with all the non-dupes. - return stack.map(c => c.value).join(''); -} diff --git a/src/leetcode/string/remove_all_adjacent_duplicates.py b/src/leetcode/string/remove_all_adjacent_duplicates.py index e6ad060..550bce5 100644 --- a/src/leetcode/string/remove_all_adjacent_duplicates.py +++ b/src/leetcode/string/remove_all_adjacent_duplicates.py @@ -13,15 +13,11 @@ def removeDuplicates(self, s: str) -> str: """ SOLUTION -------- - Use a stack to keep track of the characters. If the next character is the same as the top of the stack, pop the top of the stack. Otherwise, push the character onto the stack. - COMPLEXITY ---------- - Time complexity is O(n) where n is the length of s. - Space complexity is O(n). """ stack: list[str] = [] diff --git a/src/leetcode/string/remove_all_adjacent_duplicates_ii.py b/src/leetcode/string/remove_all_adjacent_duplicates_ii.py new file mode 100644 index 0000000..9538277 --- /dev/null +++ b/src/leetcode/string/remove_all_adjacent_duplicates_ii.py @@ -0,0 +1,58 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given a string s and an integer k, a k duplicate removal consists of choosing k adjacent and equal letters +# from s and removing them, causing the left and the right side of the deleted substring to concatenate together. +# +# We repeatedly make k duplicate removals on s until we no longer can. +# +# Return the final string after all such duplicate removals have been made. It is guaranteed that the answer is unique. +# +# See https://leetcode.com/problems/remove-all-adjacent-duplicates-in-string-ii +class Solution: + def removeDuplicates(self, s: str, k: int) -> str: + """ + SOLUTION + -------- + + In contrast with remove duplicates i, we need to keep track of not just the top element of the stack, but rather + if the top k elements constitute dupes. Once we remove k elements, it's possible the new stack now has k more + elements to remove. + + To account for this, we cannot simply count elements and remove when we see k, resetting out counter to 1. + Instead we will have to store, at each stack frame, the elements *and* what the counter was. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the length of s. + + Space complexity is O(n). + """ + stack: list[tuple[str, int]] = [] + freq = 1 + + for c in s: + # If the top of the stack and the current character match, increase our count. If they don't match, reset + # our freq so that it is 1 (since we always at least see 1 match; the character itself). + if stack and c == stack[-1][0]: + freq += 1 + else: + freq = 1 + + stack.append((c, freq)) + + # Only if we've seen k dupes, pop them all. Here, we cannot reset our counter back to 1; we have to + # reset the counter back to what it was when we first pushed the top element onto the stack. + if freq == k: + for _ in range(k): + stack.pop() + + # Reset our count. If we have no more stack elements, we can reset to 1. However, if we have stack + # elements, we have to reset to what it was previously. + if not stack: + freq = 1 + else: + freq = stack[-1][1] + + return "".join([c for c, _ in stack]) diff --git a/test/leetcode/string/remove-all-adjacent-duplicates-i.test.ts b/test/leetcode/string/remove-all-adjacent-duplicates-i.test.ts deleted file mode 100644 index 06c0f66..0000000 --- a/test/leetcode/string/remove-all-adjacent-duplicates-i.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { removeDuplicates } from '../../src/string/remove-all-adjacent-duplicates-i'; - -describe('remove all adjacent duplicates i', () => { - test('remove all adjacent duplicates i - test case 1', async () => { - expect(removeDuplicates('abbaca')).toBe('ca'); - }); -}); diff --git a/test/leetcode/string/remove_all_adjacent_duplicates_ii_test.py b/test/leetcode/string/remove_all_adjacent_duplicates_ii_test.py new file mode 100644 index 0000000..0257c39 --- /dev/null +++ b/test/leetcode/string/remove_all_adjacent_duplicates_ii_test.py @@ -0,0 +1,12 @@ +from leetcode.string.remove_all_adjacent_duplicates_ii import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.removeDuplicates("abcd", 2) == "abcd" + + +def test_case_2(): + assert soln.removeDuplicates("deeedbbcccbdaa", 3) == "aa" From ab090c395a55feb9033984a75f5c4c2f35da188a Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 19 Mar 2025 15:04:52 -0700 Subject: [PATCH 132/158] remove --- .../string/remove-all-adjacent-duplicates-ii.test.ts | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 test/leetcode/string/remove-all-adjacent-duplicates-ii.test.ts diff --git a/test/leetcode/string/remove-all-adjacent-duplicates-ii.test.ts b/test/leetcode/string/remove-all-adjacent-duplicates-ii.test.ts deleted file mode 100644 index 4df4847..0000000 --- a/test/leetcode/string/remove-all-adjacent-duplicates-ii.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { removeDuplicates } from '../../src/string/remove-all-adjacent-duplicates-ii'; - -describe('remove all adjacent duplicates ii', () => { - test('remove all adjacent duplicates ii - test case 1', async () => { - expect(removeDuplicates('abcd', 2)).toBe('abcd'); - }); - - test('remove all adjacent duplicates ii - test case 2', async () => { - expect(removeDuplicates('deeedbbcccbdaa', 3)).toBe('aa'); - }); -}); From 09a1300245b9fb4217dc0c9069ee7c070bf5b544 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 19 Mar 2025 16:31:24 -0700 Subject: [PATCH 133/158] reverse words --- .../string/reverse-words-in-a-string.ts | 61 ------------------- .../string/reverse_words_in_a_string.py | 29 +++++++++ .../string/reverse-words-in-a-string.test.ts | 7 --- .../string/reverse_words_in_a_string_test.py | 8 +++ 4 files changed, 37 insertions(+), 68 deletions(-) delete mode 100644 src/leetcode/string/reverse-words-in-a-string.ts create mode 100644 src/leetcode/string/reverse_words_in_a_string.py delete mode 100644 test/leetcode/string/reverse-words-in-a-string.test.ts create mode 100644 test/leetcode/string/reverse_words_in_a_string_test.py diff --git a/src/leetcode/string/reverse-words-in-a-string.ts b/src/leetcode/string/reverse-words-in-a-string.ts deleted file mode 100644 index 66aa6d2..0000000 --- a/src/leetcode/string/reverse-words-in-a-string.ts +++ /dev/null @@ -1,61 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an input string s, reverse the order of the words. -// -// A word is defined as a sequence of non-space characters. The words in s will be separated by at least one space. -// -// Return a string of the words in reverse order concatenated by a single space. -// -// Note that s may contain leading or trailing spaces or multiple spaces between two words. The returned string should -// only have a single space separating the words. Do not include any extra spaces. -// -// See {@link https://leetcode.com/problems/reverse-words-in-a-string/} -export { reverseWords }; - -// SOLUTION: -function reverseWords(s: string): string { - let reversed = ''; - let word = ''; - let i = s.length - 1; - - while (i >= 0) { - // If we see a non space character, we are going to continue forming our word. - if (s[i] !== ' ') { - // Don't add it to the end; we are looping backwards and getting 'eulb' instead of 'blue'. Instead build the - // string in reverse. - word = s[i] + word; - i--; - continue; - } - - // If we see a space character, we will add our word to the reversed result, but only if we actually have a word - // to form. - if (word.length !== 0) { - // If we've already added words to the reversed sentence, separate this one with a space. - if (reversed.length > 0) { - reversed += ' '; - } - - // Otherwise just add our word. - reversed += word; - - // Reset our word and continue; - word = ''; - i--; - continue; - } - - // Otherwise if there's a space and we have no word, just continue. - i--; - } - - // Now add the last word to the list, if there is one. - if (word.length !== 0) { - if (reversed.length > 0) { - reversed += ' '; - } - reversed += word; - } - - return reversed; -} diff --git a/src/leetcode/string/reverse_words_in_a_string.py b/src/leetcode/string/reverse_words_in_a_string.py new file mode 100644 index 0000000..cbe3743 --- /dev/null +++ b/src/leetcode/string/reverse_words_in_a_string.py @@ -0,0 +1,29 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an input string s, reverse the order of the words. +# +# A word is defined as a sequence of non-space characters. The words in s will be separated by at least one space. +# +# Return a string of the words in reverse order concatenated by a single space. +# +# Note that s may contain leading or trailing spaces or multiple spaces between two words. The returned string should +# only have a single space separating the words. Do not include any extra spaces. +# +# See https://leetcode.com/problems/reverse-words-in-a-string +class Solution: + def reverseWords(self, s: str) -> str: + """ + SOLUTION + -------- + + Split the string into words, reverse the list of words, and join them back together. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the length of s. + + Space complexity is O(n). + """ + return " ".join(reversed(s.split())) \ No newline at end of file diff --git a/test/leetcode/string/reverse-words-in-a-string.test.ts b/test/leetcode/string/reverse-words-in-a-string.test.ts deleted file mode 100644 index b0d0d68..0000000 --- a/test/leetcode/string/reverse-words-in-a-string.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { reverseWords } from '../../src/string/reverse-words-in-a-string'; - -describe('reverse words in a string', () => { - test('reverse words in a string - test case 1', async () => { - expect(reverseWords('the sky is blue')).toBe('blue is sky the'); - }); -}); diff --git a/test/leetcode/string/reverse_words_in_a_string_test.py b/test/leetcode/string/reverse_words_in_a_string_test.py new file mode 100644 index 0000000..03e2acc --- /dev/null +++ b/test/leetcode/string/reverse_words_in_a_string_test.py @@ -0,0 +1,8 @@ +from leetcode.string.reverse_words_in_a_string import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.reverseWords("the sky is blue") == "blue is sky the" \ No newline at end of file From 4be13ca299f8f1eb5718ea391c9f763bcb8f57ee Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 19 Mar 2025 16:41:56 -0700 Subject: [PATCH 134/158] time to rearrange --- .../string/reverse_words_in_a_string.py | 2 +- .../time-needed-to-rearrange-binary-string.ts | 68 ----------------- .../time_needed_to_rearrange_binary_string.py | 76 +++++++++++++++++++ .../string/reverse_words_in_a_string_test.py | 2 +- ...-needed-to-rearrange-binary-string.test.ts | 7 -- ..._needed_to_rearrange_binary_string_test.py | 8 ++ 6 files changed, 86 insertions(+), 77 deletions(-) delete mode 100644 src/leetcode/string/time-needed-to-rearrange-binary-string.ts create mode 100644 src/leetcode/string/time_needed_to_rearrange_binary_string.py delete mode 100644 test/leetcode/string/time-needed-to-rearrange-binary-string.test.ts create mode 100644 test/leetcode/string/time_needed_to_rearrange_binary_string_test.py diff --git a/src/leetcode/string/reverse_words_in_a_string.py b/src/leetcode/string/reverse_words_in_a_string.py index cbe3743..7121627 100644 --- a/src/leetcode/string/reverse_words_in_a_string.py +++ b/src/leetcode/string/reverse_words_in_a_string.py @@ -26,4 +26,4 @@ def reverseWords(self, s: str) -> str: Space complexity is O(n). """ - return " ".join(reversed(s.split())) \ No newline at end of file + return " ".join(reversed(s.split())) diff --git a/src/leetcode/string/time-needed-to-rearrange-binary-string.ts b/src/leetcode/string/time-needed-to-rearrange-binary-string.ts deleted file mode 100644 index deaec15..0000000 --- a/src/leetcode/string/time-needed-to-rearrange-binary-string.ts +++ /dev/null @@ -1,68 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given a binary string s. In one second, all occurrences of "01" are simultaneously replaced with "10". This -// process repeats until no occurrences of "01" exist. -// -// Return the number of seconds needed to complete this process. -// -// See {@link https://leetcode.com/problems/time-needed-to-rearrange-a-binary-string/} -export { secondsToRemoveOccurrences }; - -// SOLUTION: -// -// Rather than simulate the replacement process, we can think about how the replacement affects a string. In a simple -// case, "01" -> "10". However, a newly created "10" can create more replacements. Here's an example: -// -// "0001" -> "0010" -> "0100" -> "1000" (three 0's and three seconds to flip) -// -// Here' the "10" ripples to the left. The number of times it will "ripple" is determined by the number of 0's to -// the left of the 1. Here there are three 0's before the 1, so it will take 3 seconds to complete the replacement. -// -// Here's a more complicated example: -// -// "001001" -> "010010" -> "100100" -> "101000" -> "110000" (four 0's and four seconds to flip) -// -// Notice that the first "001" only takes two seconds to transform into "100", but we are not done yet. The second -// "001" took 4 seconds to replace, which is the two 0's in front of "001", but also the two 0's that were in front -// of the first "001". In essence, the first "001" -> "100" transferred the 0's to the right, which the second "001" -// has to deal with. -// -// This means the number of seconds it takes to replace the rightmost "01" is equal to the number of 0's that appeared -// to the left of it, in most cases. However, this won't work for a case like this: -// -// "011" -> "101" -> "110" (one 0 and two seconds to flip) -// -// Here, there is one 0 but two seconds needed to elapse. This is because the second 1 cannot flip until the first -// "01" has flipped. We don't have this issue if we space out 0's and 1's: -// -// "101" -> "110" (one 0 and one second to flip) -// "0101" -> "1010" -> "1100" (two 0's and two seconds to flip) -// -// This means that each 1 we see requires at LEAST one second to flip. -function secondsToRemoveOccurrences(s: string): number { - let count = 0; - let seconds = 0; - - for (let i = 0; i < s.length; i++) { - // This counts how many 0's we have seen so far. - if (s[i] === '0') { - count++; - continue; - } - - // If we see a 1 and we haven't seen any 0's before this, we don't need to update the number of seconds. That is, - // "1111..." will not result in increasing the number of seconds. - if (count === 0) { - continue; - } - - // Here we have seen a 1, and there are some number of zeroes before it. Keep in mind that in most cases, we can - // calculate that count of zeroes = count of seconds. However, each 1 requires at LEAST one second to flip. - // - // To handle a case of "011" -> "101" -> "110" which has one 0, but requires TWO seconds to flip, we should - // compare: seconds + 1 (at LEAST one second to flip) versus the count of zeroes. - seconds = Math.max(seconds + 1, count); - } - - return seconds; -} diff --git a/src/leetcode/string/time_needed_to_rearrange_binary_string.py b/src/leetcode/string/time_needed_to_rearrange_binary_string.py new file mode 100644 index 0000000..78a76e0 --- /dev/null +++ b/src/leetcode/string/time_needed_to_rearrange_binary_string.py @@ -0,0 +1,76 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given a binary string s. In one second, all occurrences of "01" are simultaneously replaced with "10". This +# process repeats until no occurrences of "01" exist. +# +# Return the number of seconds needed to complete this process. +# +# See https://leetcode.com/problems/time-needed-to-rearrange-a-binary-string +class Solution: + def secondsToRemoveOccurrences(self, s: str) -> int: + """ + SOLUTION + -------- + + Rather than simulate the replacement process, we can think about how the replacement affects a string. In a + simple case, "01" -> "10". However, a newly created "10" can create more replacements. Here's an example: + + "0001" -> "0010" -> "0100" -> "1000" (three 0's and three seconds to flip) + + Here' the "10" ripples to the left. The number of times it will "ripple" is determined by the number of 0's to + the left of the 1. Here there are three 0's before the 1, so it will take 3 seconds to complete the + replacement. + + Here's a more complicated example: + + "001001" -> "010010" -> "100100" -> "101000" -> "110000" (four 0's and four seconds to flip) + + Notice that the first "001" only takes two seconds to transform into "100", but we are not done yet. The second + "001" took 4 seconds to replace, which is the two 0's in front of "001", but also the two 0's that were in front + of the first "001". In essence, the first "001" -> "100" transferred the 0's to the right, which the second + "001" has to deal with. + + This means the number of seconds it takes to replace the rightmost "01" is equal to the number of 0's that + appeared to the left of it, in most cases. However, this won't work for a case like this: + + "011" -> "101" -> "110" (one 0 and two seconds to flip) + + Here, there is one 0 but two seconds needed to elapse. This is because the second 1 cannot flip until the first + "01" has flipped. We don't have this issue if we space out 0's and 1's: + + "101" -> "110" (one 0 and one second to flip) + "0101" -> "1010" -> "1100" (two 0's and two seconds to flip) + + This means that each 1 we see requires at LEAST one second to flip. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the length of s. + + Space complexity is O(1). + """ + count = 0 + seconds = 0 + + for c in s: + # This counts how many "0"'s we have seen so far. + if c == "0": + count += 1 + continue + + # If we see a 1 and we haven't seen any 0's before this, we don't need to update the number of seconds. + # That is, "1111..." will not result in increasing the number of seconds. + if count == 0: + continue + + # Here we have seen a 1, and there are some number of zeroes before it. Keep in mind that in most cases, we + # can calculate that count of zeroes = count of seconds. However, each 1 requires at LEAST one second to + # flip. + # + # To handle a case of "011" -> "101" -> "110" which has one 0, but requires TWO seconds to flip, we should + # compare: seconds + 1 (at LEAST one second to flip) versus the count of zeroes. + seconds = max(seconds + 1, count) + + return seconds diff --git a/test/leetcode/string/reverse_words_in_a_string_test.py b/test/leetcode/string/reverse_words_in_a_string_test.py index 03e2acc..5bca394 100644 --- a/test/leetcode/string/reverse_words_in_a_string_test.py +++ b/test/leetcode/string/reverse_words_in_a_string_test.py @@ -5,4 +5,4 @@ def test_case_1(): - assert soln.reverseWords("the sky is blue") == "blue is sky the" \ No newline at end of file + assert soln.reverseWords("the sky is blue") == "blue is sky the" diff --git a/test/leetcode/string/time-needed-to-rearrange-binary-string.test.ts b/test/leetcode/string/time-needed-to-rearrange-binary-string.test.ts deleted file mode 100644 index 4ec8a59..0000000 --- a/test/leetcode/string/time-needed-to-rearrange-binary-string.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { secondsToRemoveOccurrences } from '../../src/string/time-needed-to-rearrange-binary-string'; - -describe('time needed to rearrange a binary string', () => { - test('time needed to rearrange a binary string - test case 1', async () => { - expect(secondsToRemoveOccurrences('0110101')).toBe(4); - }); -}); diff --git a/test/leetcode/string/time_needed_to_rearrange_binary_string_test.py b/test/leetcode/string/time_needed_to_rearrange_binary_string_test.py new file mode 100644 index 0000000..cf4b653 --- /dev/null +++ b/test/leetcode/string/time_needed_to_rearrange_binary_string_test.py @@ -0,0 +1,8 @@ +from leetcode.string.time_needed_to_rearrange_binary_string import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.secondsToRemoveOccurrences("0110101") == 4 From 3508ea933250805619e9a0351a84849a997a9a47 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 19 Mar 2025 16:54:34 -0700 Subject: [PATCH 135/158] vowel checker --- src/leetcode/string/vowel-spellchecker.ts | 80 ----------------- src/leetcode/string/vowel_spellchecker.py | 90 +++++++++++++++++++ .../string/vowel-spellchecker.test.ts | 21 ----- .../string/vowel_spellchecker_test.py | 22 +++++ 4 files changed, 112 insertions(+), 101 deletions(-) delete mode 100644 src/leetcode/string/vowel-spellchecker.ts create mode 100644 src/leetcode/string/vowel_spellchecker.py delete mode 100644 test/leetcode/string/vowel-spellchecker.test.ts create mode 100644 test/leetcode/string/vowel_spellchecker_test.py diff --git a/src/leetcode/string/vowel-spellchecker.ts b/src/leetcode/string/vowel-spellchecker.ts deleted file mode 100644 index 6befa50..0000000 --- a/src/leetcode/string/vowel-spellchecker.ts +++ /dev/null @@ -1,80 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given a wordlist, we want to implement a spellchecker that converts a query word into a correct word. -// -// For a given query word, the spell checker handles two categories of spelling mistakes: -// -// Capitalization: If the query matches a word in the wordlist (case-insensitive), then the query word is returned with -// the same case as the case in the wordlist. -// -// - Example: wordlist = ["yellow"], query = "YellOw": correct = "yellow" -// - Example: wordlist = ["Yellow"], query = "yellow": correct = "Yellow" -// - Example: wordlist = ["yellow"], query = "yellow": correct = "yellow" -// - Vowel Errors: If after replacing the vowels ('a', 'e', 'i', 'o', 'u') of the query word with any vowel -// individually, it matches a word in the wordlist (case-insensitive), then the query word is returned with the same -// case as the match in the wordlist. -// - Example: wordlist = ["YellOw"], query = "yollow": correct = "YellOw" -// - Example: wordlist = ["YellOw"], query = "yeellow": correct = "" (no match) -// - Example: wordlist = ["YellOw"], query = "yllw": correct = "" (no match) -// -// In addition, the spell checker operates under the following precedence rules: -// -// - When the query exactly matches a word in the wordlist (case-sensitive), you should return the same word back. -// - When the query matches a word up to capitlization, you should return the first such match in the wordlist. -// - When the query matches a word up to vowel errors, you should return the first such match in the wordlist. -// - If the query has no matches in the wordlist, you should return the empty string. -// - Given some queries, return a list of words answer, where answer[i] is the correct word for query = queries[i]. -// -// See {@link https://leetcode.com/problems/vowel-spellchecker/} -export { spellchecker }; - -// SOLUTION: -function spellchecker(wordlist: string[], queries: string[]): string[] { - const set = new Set(wordlist); - const caps = new Map(); - const vowels = new Map(); - - // We can normalize all inputs and words to a canonical form, then check if the inputs match the canonical form. - for (const word of wordlist) { - const lowered = word.toLowerCase(); - - // Problem says to only use the first mapping; multiple may be given. So if we have a mapping, ignore the rest. - if (!caps.has(lowered)) { - caps.set(lowered, word); - } - - // Problem is not 100% clear, but it seems we are allowed to change any number of vowels so that the input matches - // the word (not just a single vowel). That means we can just normalize all the vowel inputs to a canonical form - // with vowels replaced. - const voweled = lowered.replace(/[aeiou]/g, '#'); - if (!vowels.has(voweled)) { - vowels.set(voweled, word); - } - } - - const result: string[] = []; - - // Now just canonicalize all the words and check if they match a rule. - for (const query of queries) { - if (set.has(query)) { - result.push(query); - continue; - } - - const lowered = query.toLowerCase(); - if (caps.has(lowered)) { - result.push(caps.get(lowered)!); - continue; - } - - const voweled = lowered.replace(/[aeiou]/g, '#'); - if (vowels.has(voweled)) { - result.push(vowels.get(voweled)!); - continue; - } - - result.push(''); - } - - return result; -} diff --git a/src/leetcode/string/vowel_spellchecker.py b/src/leetcode/string/vowel_spellchecker.py new file mode 100644 index 0000000..3acbce6 --- /dev/null +++ b/src/leetcode/string/vowel_spellchecker.py @@ -0,0 +1,90 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given a wordlist, we want to implement a spellchecker that converts a query word into a correct word. +# +# For a given query word, the spell checker handles two categories of spelling mistakes: +# +# Capitalization: If the query matches a word in the wordlist (case-insensitive), then the query word is returned with +# the same case as the case in the wordlist. +# +# - Example: wordlist = ["yellow"], query = "YellOw": correct = "yellow" +# - Example: wordlist = ["Yellow"], query = "yellow": correct = "Yellow" +# - Example: wordlist = ["yellow"], query = "yellow": correct = "yellow" +# - Vowel Errors: If after replacing the vowels ('a', 'e', 'i', 'o', 'u') of the query word with any vowel +# individually, it matches a word in the wordlist (case-insensitive), then the query word is returned with the same +# case as the match in the wordlist. +# - Example: wordlist = ["YellOw"], query = "yollow": correct = "YellOw" +# - Example: wordlist = ["YellOw"], query = "yeellow": correct = "" (no match) +# - Example: wordlist = ["YellOw"], query = "yllw": correct = "" (no match) +# +# In addition, the spell checker operates under the following precedence rules: +# +# - When the query exactly matches a word in the wordlist (case-sensitive), you should return the same word back. +# - When the query matches a word up to capitlization, you should return the first such match in the wordlist. +# - When the query matches a word up to vowel errors, you should return the first such match in the wordlist. +# - If the query has no matches in the wordlist, you should return the empty string. +# - Given some queries, return a list of words answer, where answer[i] is the correct word for query = queries[i]. +# +# See https://leetcode.com/problems/vowel-spellchecker +import re + + +class Solution: + def spellchecker(self, wordlist: list[str], queries: list[str]) -> list[str]: + """ + SOLUTION + -------- + + We will use a set to store the wordlist and two dictionaries to store the case-insensitive and vowel-error + mappings. We will then iterate through the queries and check for matches in the hash set, case-insensitive + dictionary, and vowel-error dictionary in that order. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of queries. + + Space complexity is O(n). + """ + uniques: set[str] = set(wordlist) + lowers: dict[str, str] = {} + vowels: dict[str, str] = {} + + # We can normalize all inputs and words to a canonical form, then check if the inputs match the canonical form. + for word in wordlist: + lowered = word.lower() + + # Problem says to only use the first mapping; multiple may be given. So if we have a mapping, ignore the + # rest. + if lowered not in lowers: + lowers[lowered] = word + + # Problem is not 100% clear, but it seems we are allowed to change any number of vowels so that the input + # matches the word (not just a single vowel). That means we can just normalize all the vowel inputs to a + # canonical form with vowels replaced. + voweled = re.sub(r"[aeiou]", "#", lowered) + if voweled not in vowels: + vowels[voweled] = word + + result: list[str] = [] + + # Now just canonicalize all the words and check if they match a rule. + for query in queries: + if query in uniques: + result.append(query) + continue + + lowered = query.lower() + if lowered in lowers: + result.append(lowers[lowered]) + continue + + voweled = re.sub(r"[aeiou]", "#", lowered) + if voweled in vowels: + result.append(vowels[voweled]) + continue + + result.append("") + + return result diff --git a/test/leetcode/string/vowel-spellchecker.test.ts b/test/leetcode/string/vowel-spellchecker.test.ts deleted file mode 100644 index 94cbe57..0000000 --- a/test/leetcode/string/vowel-spellchecker.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { spellchecker } from '../../src/string/vowel-spellchecker'; - -describe('vowel spellchecker', () => { - test('vowel spellchecker - test case 1', async () => { - const wordlist = ['KiTe', 'kite', 'hare', 'Hare']; - const queries = ['kite', 'Kite', 'KiTe', 'Hare', 'HARE', 'Hear', 'hear', 'keti', 'keet', 'keto']; - - expect(spellchecker(wordlist, queries)).toStrictEqual([ - 'kite', - 'KiTe', - 'KiTe', - 'Hare', - 'hare', - '', - '', - 'KiTe', - '', - 'KiTe' - ]); - }); -}); diff --git a/test/leetcode/string/vowel_spellchecker_test.py b/test/leetcode/string/vowel_spellchecker_test.py new file mode 100644 index 0000000..94ae396 --- /dev/null +++ b/test/leetcode/string/vowel_spellchecker_test.py @@ -0,0 +1,22 @@ +from leetcode.string.vowel_spellchecker import Solution + + +soln = Solution() + + +def test_case_1(): + wordlist = ["KiTe", "kite", "hare", "Hare"] + queries = ["kite", "Kite", "KiTe", "Hare", "HARE", "Hear", "hear", "keti", "keet", "keto"] + + assert soln.spellchecker(wordlist, queries) == [ + "kite", + "KiTe", + "KiTe", + "Hare", + "hare", + "", + "", + "KiTe", + "", + "KiTe", + ] From 0fbbd28358e601a6476c8012e1198e2104c02066 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 19 Mar 2025 17:08:58 -0700 Subject: [PATCH 136/158] level order traversal --- .../tree/binary-tree-level-order-traversal.ts | 55 ------------------ .../tree/binary_tree_level_order_traversal.py | 54 +++++++++++++++++ src/leetcode/tree/common/tree-node.ts | 58 ------------------- src/leetcode/tree/common/tree_node.py | 44 ++++++++++++++ .../binary-tree-level-order-traversal.test.ts | 22 ------- .../binary_tree_level_order_traversal_test.py | 23 ++++++++ 6 files changed, 121 insertions(+), 135 deletions(-) delete mode 100644 src/leetcode/tree/binary-tree-level-order-traversal.ts create mode 100644 src/leetcode/tree/binary_tree_level_order_traversal.py delete mode 100644 src/leetcode/tree/common/tree-node.ts create mode 100644 src/leetcode/tree/common/tree_node.py delete mode 100644 test/leetcode/tree/binary-tree-level-order-traversal.test.ts create mode 100644 test/leetcode/tree/binary_tree_level_order_traversal_test.py diff --git a/src/leetcode/tree/binary-tree-level-order-traversal.ts b/src/leetcode/tree/binary-tree-level-order-traversal.ts deleted file mode 100644 index 5cbdcab..0000000 --- a/src/leetcode/tree/binary-tree-level-order-traversal.ts +++ /dev/null @@ -1,55 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given the root of a binary tree, return the level order traversal of its nodes' values. (i.e., from left to right, -// level by level). -// -// See {@link https://leetcode.com/problems/binary-tree-level-order-traversal/} -import { TreeNode } from './common/tree-node'; -export { levelOrder }; - -// SOLUTION: -// -// This is essentially a BFS algorithm from the root node. -// -// COMPLEXITY: -// -// Each node is visited once, so the time complexity is O(n). -function levelOrder(root: TreeNode | null): number[][] { - if (root === null) { - return []; - } - - const result: number[][] = []; - const queue: TreeNode[] = [root]; - - // Consume the nodes in the current level while noting the frontier nodes. The nodes at the current level will be - // recorded, then the frontier nodes are added to the queue. - while (queue.length > 0) { - const level: number[] = []; - const frontier: TreeNode[] = []; - - // Consume all the nodes from the current level and record them. Don't use the queue.length in the for loop as - // we are going to be modifying the queue during the loop. - const size = queue.length; - for (let i = 0; i < size; i++) { - const node = queue.shift()!; - level.push(node.val); - - if (node.left !== null) { - frontier.push(node.left); - } - - if (node.right !== null) { - frontier.push(node.right); - } - } - - // Add the recorded nodes at this level to the result. - result.push(level); - - // Continue processing frontier nodes. - queue.push(...frontier); - } - - return result; -} diff --git a/src/leetcode/tree/binary_tree_level_order_traversal.py b/src/leetcode/tree/binary_tree_level_order_traversal.py new file mode 100644 index 0000000..c31658e --- /dev/null +++ b/src/leetcode/tree/binary_tree_level_order_traversal.py @@ -0,0 +1,54 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given the root of a binary tree, return the level order traversal of its nodes' values. (i.e., from left to right, +# level by level). +# +# See https://leetcode.com/problems/binary-tree-level-order-traversal +from collections import deque +from leetcode.tree.common.tree_node import TreeNode + + +class Solution: + def levelOrder(self, root: TreeNode | None) -> list[list[int]]: + """ + SOLUTION + -------- + + This is essentially a BFS algorithm from the root node. + + COMPLEXITY + ---------- + + Each node is visited once, so the time complexity is O(n). + """ + if not root: + return [] + + result: list[list[int]] = [] + queue: deque[TreeNode] = deque([root]) + + # Consume the nodes in the current level while noting the frontier nodes. The nodes at the current level will + # be recorded, then the frontier nodes are added to the queue. + while queue: + level: list[int] = [] + frontier: list[TreeNode] = [] + + # Consume all the nodes from the current level and record them. Don't use the queue.length in the for loop + # as we are going to be modifying the queue during the loop. + for _ in range(len(queue)): + node = queue.popleft() + level.append(node.val) + + if node.left: + frontier.append(node.left) + if node.right: + frontier.append(node.right) + + # Add the recorded nodes at this level to the result. + result.append(level) + + # Continue processing frontier nodes. + queue.extend(frontier) + + return result diff --git a/src/leetcode/tree/common/tree-node.ts b/src/leetcode/tree/common/tree-node.ts deleted file mode 100644 index 9d9625a..0000000 --- a/src/leetcode/tree/common/tree-node.ts +++ /dev/null @@ -1,58 +0,0 @@ -// This class definition comes from the problem itself and we cannot change it, or else our submission will not be -// accepted. -export class TreeNode { - val: number; - left: TreeNode | null; - right: TreeNode | null; - - constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { - this.val = val === undefined ? 0 : val; - this.left = left === undefined ? null : left; - this.right = right === undefined ? null : right; - } -} - -// The LeetCode test input is given as an array, but the exact code to construct the tree is not given. This is likely -// how they've implemented it. -export function array2tree(array: (number | null)[]): TreeNode | null { - if (array.length === 0) { - return null; - } - - const root = new TreeNode(array[0]!); - const queue = [root]; - let i = 1; - - while (queue.length > 0 && i < array.length) { - const node = queue.shift()!; - if (node === null) { - continue; - } - - // Add the left node. - if (i < array.length) { - const val = array[i]; - if (val !== null) { - node.left = new TreeNode(val); - queue.push(node.left); - } else { - node.left = null; - } - i++; - } - - // Add the right node. - if (i < array.length) { - const val = array[i]; - if (val !== null) { - node.right = new TreeNode(val); - queue.push(node.right); - } else { - node.right = null; - } - i++; - } - } - - return root; -} diff --git a/src/leetcode/tree/common/tree_node.py b/src/leetcode/tree/common/tree_node.py new file mode 100644 index 0000000..4550282 --- /dev/null +++ b/src/leetcode/tree/common/tree_node.py @@ -0,0 +1,44 @@ +from collections import deque + + +# This class definition comes from the problem itself and we cannot change it, or else our submission will not be +# accepted. +class TreeNode: + def __init__(self, val=0, left: "TreeNode | None" = None, right: "TreeNode | None" = None): + self.val = val + self.left = left + self.right = right + + +def array2tree(array: list[int | None]) -> TreeNode | None: + if not array: + return None + + if not array[0]: + return None + + root = TreeNode(array[0]) + queue = deque([root]) + i = 1 + + while queue and i < len(array): + node = queue.popleft() + + if not node: + continue + + if i < len(array): + val = array[i] + if val is not None: + node.left = TreeNode(val) + queue.append(node.left) + i += 1 + + if i < len(array): + val = array[i] + if val is not None: + node.right = TreeNode(val) + queue.append(node.right) + i += 1 + + return root diff --git a/test/leetcode/tree/binary-tree-level-order-traversal.test.ts b/test/leetcode/tree/binary-tree-level-order-traversal.test.ts deleted file mode 100644 index 4ea83e7..0000000 --- a/test/leetcode/tree/binary-tree-level-order-traversal.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { levelOrder } from '../../src/tree/binary-tree-level-order-traversal'; -import { array2tree } from '../../src/tree/common/tree-node'; - -describe('binary tree level order traversal', () => { - test('level order - test case 1', async () => { - const tree = array2tree([3, 9, 20, null, null, 15, 7]); - - expect(levelOrder(tree)).toStrictEqual([[3], [9, 20], [15, 7]]); - }); - - test('level order - test case 2', async () => { - const tree = array2tree([1]); - - expect(levelOrder(tree)).toStrictEqual([[1]]); - }); - - test('level order - test case 3', async () => { - const tree = array2tree([]); - - expect(levelOrder(tree)).toStrictEqual([]); - }); -}); diff --git a/test/leetcode/tree/binary_tree_level_order_traversal_test.py b/test/leetcode/tree/binary_tree_level_order_traversal_test.py new file mode 100644 index 0000000..b66a126 --- /dev/null +++ b/test/leetcode/tree/binary_tree_level_order_traversal_test.py @@ -0,0 +1,23 @@ +from leetcode.tree.binary_tree_level_order_traversal import Solution +from leetcode.tree.common.tree_node import array2tree + + +soln = Solution() + + +def test_case_1(): + tree = array2tree([3, 9, 20, None, None, 15, 7]) + + assert soln.levelOrder(tree) == [[3], [9, 20], [15, 7]] + + +def test_case_2(): + tree = array2tree([1]) + + assert soln.levelOrder(tree) == [[1]] + + +def test_case_3(): + tree = array2tree([]) + + assert soln.levelOrder(tree) == [] From 69665b541778780ad9cf165d249be1785649accb Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 19 Mar 2025 17:13:49 -0700 Subject: [PATCH 137/158] right side view --- .../tree/binary-tree-right-side-view.ts | 38 ---------------- .../tree/binary_tree_right_side_view.py | 44 +++++++++++++++++++ .../tree/binary-tree-right-side-view.test.ts | 10 ----- .../tree/binary_tree_right_side_view_test.py | 11 +++++ 4 files changed, 55 insertions(+), 48 deletions(-) delete mode 100644 src/leetcode/tree/binary-tree-right-side-view.ts create mode 100644 src/leetcode/tree/binary_tree_right_side_view.py delete mode 100644 test/leetcode/tree/binary-tree-right-side-view.test.ts create mode 100644 test/leetcode/tree/binary_tree_right_side_view_test.py diff --git a/src/leetcode/tree/binary-tree-right-side-view.ts b/src/leetcode/tree/binary-tree-right-side-view.ts deleted file mode 100644 index 340852f..0000000 --- a/src/leetcode/tree/binary-tree-right-side-view.ts +++ /dev/null @@ -1,38 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given the root of a binary tree, imagine yourself standing on the right side of it, return the values of the nodes -// you can see ordered from top to bottom. -// - -import { TreeNode } from './common/tree-node'; - -// See {@link https://leetcode.com/problems/binary-tree-right-side-view} -export { rightSideView }; - -// SOLUTION: -// -// We can use a simple DFS to traverse the tree, and prioritize the right child over the left child. At each depth, -// we can add the first rightmost node to the result array, since that's the node we'll see when viewing it from the -// right. -function rightSideView(root: TreeNode | null): number[] { - const depths: number[] = []; - - function dfs(node: TreeNode | null, depth: number) { - if (node === null) { - return; - } - - // If the current depth is the lowest depth we've seen, then it's the rightmost node at this depth, because we will - // always prioritize going down the right node. - if (depths.length === depth) { - depths.push(node.val); - } - - // Prioritize going down the right subtree first! - dfs(node.right, depth + 1); - dfs(node.left, depth + 1); - } - - dfs(root, 0); - return depths; -} diff --git a/src/leetcode/tree/binary_tree_right_side_view.py b/src/leetcode/tree/binary_tree_right_side_view.py new file mode 100644 index 0000000..d8eb633 --- /dev/null +++ b/src/leetcode/tree/binary_tree_right_side_view.py @@ -0,0 +1,44 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given the root of a binary tree, imagine yourself standing on the right side of it, return the values of the nodes +# you can see ordered from top to bottom. +# +# See https://leetcode.com/problems/binary-tree-right-side-view +from leetcode.tree.common.tree_node import TreeNode + + +class Solution: + def rightSideView(self, root: TreeNode | None) -> list[int]: + """ + SOLUTION + -------- + + We can use a simple DFS to traverse the tree, and prioritize the right child over the left child. At each + depth, we can add the first rightmost node to the result array, since that's the node we'll see when viewing it + from the right. + + COMPLEXITY + ---------- + + Time complexity is O(n) because we are visiting each node once. + + Space complexity is O(n) because we are storing the nodes in a list. + """ + depths: list[int] = [] + + def dfs(node: TreeNode | None, depth: int) -> None: + if not node: + return + + # If the current depth is the lowest depth we've seen, then it's the rightmost node at this depth, because + # we will always prioritize going down the right node. + if depth == len(depths): + depths.append(node.val) + + # Prioritize going down the right subtree first! + dfs(node.right, depth + 1) + dfs(node.left, depth + 1) + + dfs(root, 0) + return depths diff --git a/test/leetcode/tree/binary-tree-right-side-view.test.ts b/test/leetcode/tree/binary-tree-right-side-view.test.ts deleted file mode 100644 index c268847..0000000 --- a/test/leetcode/tree/binary-tree-right-side-view.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { array2tree } from '../../src/tree/common/tree-node'; -import { rightSideView } from '../../src/tree/binary-tree-right-side-view'; - -describe('binary tree right side view', () => { - test('binary tree right side view', async () => { - const root = array2tree([1, 2, 3, null, 5, null, 4]); - - expect(rightSideView(root)).toStrictEqual([1, 3, 4]); - }); -}); diff --git a/test/leetcode/tree/binary_tree_right_side_view_test.py b/test/leetcode/tree/binary_tree_right_side_view_test.py new file mode 100644 index 0000000..0f66153 --- /dev/null +++ b/test/leetcode/tree/binary_tree_right_side_view_test.py @@ -0,0 +1,11 @@ +from leetcode.tree.binary_tree_right_side_view import Solution +from leetcode.tree.common.tree_node import array2tree + + +soln = Solution() + + +def test_case_1(): + root = array2tree([1, 2, 3, None, 5, None, 4]) + + assert soln.rightSideView(root) == [1, 3, 4] From 1ad57a5a554cc6fc08457c199058782773f3fb22 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 19 Mar 2025 17:31:17 -0700 Subject: [PATCH 138/158] binary tree vertical --- .../binary-tree-vertical-order-traversal.ts | 88 ------------------- .../binary_tree_vertical_order_traversal.py | 81 +++++++++++++++++ ...nary_tree_vertical_order_traversal_test.py | 11 +++ 3 files changed, 92 insertions(+), 88 deletions(-) delete mode 100644 src/leetcode/tree/binary-tree-vertical-order-traversal.ts create mode 100644 src/leetcode/tree/binary_tree_vertical_order_traversal.py create mode 100644 test/leetcode/tree/binary_tree_vertical_order_traversal_test.py diff --git a/src/leetcode/tree/binary-tree-vertical-order-traversal.ts b/src/leetcode/tree/binary-tree-vertical-order-traversal.ts deleted file mode 100644 index eb57d3e..0000000 --- a/src/leetcode/tree/binary-tree-vertical-order-traversal.ts +++ /dev/null @@ -1,88 +0,0 @@ -// DIFFICULTY: MEDIUM -// Given the root of a binary tree, return the vertical order traversal of its nodes' values. (i.e., from top to bottom, -// column by column). -// -// If two nodes are in the same row and column, the order should be from left to right. -// -// See {@link https://leetcode.com/problems/binary-tree-vertical-order-traversal/}. -import { TreeNode } from './common/tree-node'; -export { verticalOrder }; - -// SOLUTION: -// -// There is no straightforward vertical order traversal. However, a standard level order traversal can be used to get -// part of the way there. If we assume that the root is at row 0 and column 0, we can do some bookkeeping while we -// traverse the nodes to assign a coordinate to every single node. -// -// While doing the traversal, we can keep track of a Map where each column is mapped to an array of -// nodes in the order they were encountered. By doing a level order traversal (or BFS), we can ensure that nodes in -// the same "row" are encountered from left to right. -// -// COMPLEXITY: -// -// The BFS will run in O(n) time where n is the number of nodes. The result computation at the end will run in O(k) -// time where k is the number of columns. However, you're always guaranteed to have more nodes than columns (or the -// same) so overall time complexity is still O(n). -// -// Space complexity is O(n) because we are storing k columns mapping to lists of nodes, but the overall number of nodes -// stored in the map is at most n. We are also using a queue, which has at most n nodes in it. -function verticalOrder(root: TreeNode | null): number[][] { - type Column = number; - - if (root === null) { - return []; - } - - // Keep a map of column to nodes. Note that we don't actually need to store their row values, because if we visit - // the nodes in the right order, the array will already be sorted by row. The key is to push the left child before - // the right child. - const map = new Map(); - - // We DO need to store the column values in our BFS queue though. That's because as we shift items off the queue, we - // will need to know which column it's in, so we can add it to the TreeNode array in the map above. - type ExtendedNode = [TreeNode, Column]; - const queue: ExtendedNode[] = [[root, 0]]; - - // Finally, we have to keep track of the min and max columns so we know where to start with the vertical order list. - let min = 0; - let max = 0; - - // The rest of the algorithm is a standard BFS. We don't need to maintain a visited set because the nodes are - // guaranteed to be organized as a tree, so we can't visit the same node twice. - while (queue.length > 0) { - const [node, column] = queue.shift()!; - - // Add the node to this map for bookkeeping. - if (!map.has(column)) { - map.set(column, []); - } - map.get(column)!.push(node); - - // Update min/max columns. - min = Math.min(min, column); - max = Math.max(max, column); - - // Enqueue the children as usual in BFS, but make sure to enqueue the LEFT child first to keep our proper row order. - if (node.left !== null) { - queue.push([node.left, column - 1]); - } - - // Enqueue the children as usual in BFS, but make sure to enqueue the RIGHT child last. - if (node.right !== null) { - queue.push([node.right, column + 1]); - } - } - - // Now we have a map of Column -> TreeNode[], and for each column, we just print out the values in the row. - const result: number[][] = []; - for (let column = min; column <= max; column++) { - // These nodes are already in row order because we visited the left child before the right. - const rows = map.get(column)!; - - // Now map each extended node to its value for the final result. - const values = rows.map(node => node.val); - result.push(values); - } - - return result; -} diff --git a/src/leetcode/tree/binary_tree_vertical_order_traversal.py b/src/leetcode/tree/binary_tree_vertical_order_traversal.py new file mode 100644 index 0000000..8188411 --- /dev/null +++ b/src/leetcode/tree/binary_tree_vertical_order_traversal.py @@ -0,0 +1,81 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given the root of a binary tree, return the vertical order traversal of its nodes' values. (i.e., from top to bottom, +# column by column). +# +# If two nodes are in the same row and column, the order should be from left to right. +# +# See https://leetcode.com/problems/binary-tree-vertical-order-traversal +from collections import defaultdict, deque +from leetcode.tree.common.tree_node import TreeNode + + +class Solution: + def verticalOrder(self, root: TreeNode | None) -> list[list[int]]: + """ + SOLUTION + -------- + + There is no straightforward vertical order traversal. However, a standard level order traversal can be used to + get part of the way there. If we assume that the root is at row 0 and column 0, we can do some bookkeeping + while we traverse the nodes to assign a coordinate to every single node. + + While doing the traversal, we can keep track of a Map where each column is mapped to an + array of nodes in the order they were encountered. By doing a level order traversal (or BFS), we can ensure + that nodes in the same "row" are encountered from left to right. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of nodes. The result computation at the end will run in O(k) + time where k is the number of columns. However, you're always guaranteed to have more nodes than columns (or + the same) so overall time complexity is still O(n). + + Space complexity is O(n) because we are storing k columns mapping to lists of nodes, but the overall number of + nodes stored in the map is at most n. We are also using a queue, which has at most n nodes in it. + """ + if not root: + return [] + + # Keep a map of column to nodes values. Note that we don't actually need to store their row values, because if + # we visit the nodes in the right order, the array will already be sorted by row. The key is to push the left + # child before the right child. + mapping: dict[int, list[int]] = defaultdict(list) + + # We DO need to store the column values in our BFS queue though. That's because as we shift items off the + # queue, we will need to know which column it's in, so we can add it to the TreeNode array in the map above. + queue: deque[tuple[TreeNode, int]] = deque([(root, 0)]) + + # Finally, we have to keep track of the min and max columns so we know where to start with the vertical order + # list. + min_col = 0 + max_col = 0 + + # The rest of the algorithm is a standard BFS. We don't need to maintain a visited set because the nodes are + # guaranteed to be organized as a tree, so we can't visit the same node twice. + while queue: + (node, col) = queue.popleft() + + # Add the node to this map for bookkeeping. + mapping[col].append(node.val) + + # Update min/max columns. + min_col = min(min_col, col) + max_col = max(max_col, col) + + # Enqueue the children as usual in BFS, but make sure to enqueue the LEFT child first to keep our proper row + # order. + if node.left: + queue.append((node.left, col - 1)) + if node.right: + queue.append((node.right, col + 1)) + + # Now we have a map of Column -> TreeNode[], and for each column, we just print out the values in the row. + result: list[list[int]] = [] + for col in range(min_col, max_col + 1): + # These node values are already in row order because we visited the left child before the right. + values = mapping[col] + result.append(values) + + return result diff --git a/test/leetcode/tree/binary_tree_vertical_order_traversal_test.py b/test/leetcode/tree/binary_tree_vertical_order_traversal_test.py new file mode 100644 index 0000000..942fd5d --- /dev/null +++ b/test/leetcode/tree/binary_tree_vertical_order_traversal_test.py @@ -0,0 +1,11 @@ +from leetcode.tree.binary_tree_vertical_order_traversal import Solution +from leetcode.tree.common.tree_node import array2tree + + +soln = Solution() + + +def test_case_1(): + tree = array2tree([3, 9, 20, None, None, 15, 7]) + + assert soln.verticalOrder(tree) == [[9], [3, 15], [20], [7]] From 5a78c387e0fb11fb43fa3497baa809bf3143fb3d Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 19 Mar 2025 18:27:19 -0700 Subject: [PATCH 139/158] construct quad tree --- src/leetcode/tree/common/quad-tree.ts | 26 ----- src/leetcode/tree/common/quad_tree.py | 18 +++ src/leetcode/tree/construct-quad-tree.ts | 59 ---------- src/leetcode/tree/construct_quad_tree.py | 60 ++++++++++ .../construct-quad-tree.test.ts.snap | 107 ------------------ ...nary-tree-vertical-order-traversal.test.ts | 10 -- .../leetcode/tree/construct-quad-tree.test.ts | 27 ----- .../leetcode/tree/construct_quad_tree_test.py | 25 ++++ .../snap_construct_quad_tree_test.py | 12 ++ 9 files changed, 115 insertions(+), 229 deletions(-) delete mode 100644 src/leetcode/tree/common/quad-tree.ts create mode 100644 src/leetcode/tree/common/quad_tree.py delete mode 100644 src/leetcode/tree/construct-quad-tree.ts create mode 100644 src/leetcode/tree/construct_quad_tree.py delete mode 100644 test/leetcode/tree/__snapshots__/construct-quad-tree.test.ts.snap delete mode 100644 test/leetcode/tree/binary-tree-vertical-order-traversal.test.ts delete mode 100644 test/leetcode/tree/construct-quad-tree.test.ts create mode 100644 test/leetcode/tree/construct_quad_tree_test.py create mode 100644 test/leetcode/tree/snapshots/snap_construct_quad_tree_test.py diff --git a/src/leetcode/tree/common/quad-tree.ts b/src/leetcode/tree/common/quad-tree.ts deleted file mode 100644 index 0015b7d..0000000 --- a/src/leetcode/tree/common/quad-tree.ts +++ /dev/null @@ -1,26 +0,0 @@ -// This class definition comes from the problem itself and we cannot change it, or else our submission will not be -// accepted. -export class _Node { - val: boolean; - isLeaf: boolean; - topLeft: _Node | null; - topRight: _Node | null; - bottomLeft: _Node | null; - bottomRight: _Node | null; - - constructor( - val?: boolean, - isLeaf?: boolean, - topLeft?: _Node, - topRight?: _Node, - bottomLeft?: _Node, - bottomRight?: _Node - ) { - this.val = val === undefined ? false : val; - this.isLeaf = isLeaf === undefined ? false : isLeaf; - this.topLeft = topLeft === undefined ? null : topLeft; - this.topRight = topRight === undefined ? null : topRight; - this.bottomLeft = bottomLeft === undefined ? null : bottomLeft; - this.bottomRight = bottomRight === undefined ? null : bottomRight; - } -} diff --git a/src/leetcode/tree/common/quad_tree.py b/src/leetcode/tree/common/quad_tree.py new file mode 100644 index 0000000..de19de4 --- /dev/null +++ b/src/leetcode/tree/common/quad_tree.py @@ -0,0 +1,18 @@ +# This class definition comes from the problem itself and we cannot change it, or else our submission will not be +# accepted. +class Node: + def __init__( + self, + val: bool = False, + isLeaf: bool = False, + topLeft: "Node | None" = None, + topRight: "Node | None" = None, + bottomLeft: "Node | None" = None, + bottomRight: "Node | None" = None, + ) -> None: + self.val = val + self.isLeaf = isLeaf + self.topLeft = topLeft + self.topRight = topRight + self.bottomLeft = bottomLeft + self.bottomRight = bottomRight diff --git a/src/leetcode/tree/construct-quad-tree.ts b/src/leetcode/tree/construct-quad-tree.ts deleted file mode 100644 index 3bfcb9f..0000000 --- a/src/leetcode/tree/construct-quad-tree.ts +++ /dev/null @@ -1,59 +0,0 @@ -// DIFFICULTY: HARD -// -// Given a n * n matrix grid of 0's and 1's only. We want to represent grid with a Quad-Tree. -// -// Return the root of the Quad-Tree representing grid. -// -// See {@link https://en.wikipedia.org/wiki/Quadtree} -// See {@link https://leetcode.com/problems/construct-quad-tree/} -import { _Node } from './common/quad-tree'; -export { construct }; - -// SOLUTION: -// -// To make a quad tree, recursively divide the matrix into quadrants until each quadrant is all 1's or all 0's. -function construct(_grid: number[][]): _Node | null { - if (_grid.length === 0 || _grid[0].length === 0) { - return null; - } - - return constructInternal(_grid, 0, 0, _grid.length); -} - -function constructInternal(grid: number[][], row: number, column: number, size: number): _Node { - const node = new _Node(); - - // If the grid segment is uniform, we can create a single quad tree node and set the value to all 1's or 0's. - if (isUniform(grid, row, column, size)) { - node.val = grid[row][column] === 1; - node.isLeaf = true; - return node; - } - - // If the grid segment here isn't uniform, we'll need to create 4 quadrants and construct nodes out of all 4 of - // them. - // - // Setting the `node.val` of this node is irrelevant as it's not a leaf. We can just use the default value. - const half = size / 2; - node.isLeaf = false; - node.topLeft = constructInternal(grid, row, column, half); - node.topRight = constructInternal(grid, row, column + half, half); - node.bottomLeft = constructInternal(grid, row + half, column, half); - node.bottomRight = constructInternal(grid, row + half, column + half, half); - - return node; -} - -// Checks if a quadrant of the grid starting at [row, column] is uniformly all 1's or all 0's. -function isUniform(grid: number[][], row: number, column: number, size: number) { - const value = grid[row][column]; - for (let i = row; i < row + size; i++) { - for (let j = column; j < column + size; j++) { - if (grid[i][j] !== value) { - return false; - } - } - } - - return true; -} diff --git a/src/leetcode/tree/construct_quad_tree.py b/src/leetcode/tree/construct_quad_tree.py new file mode 100644 index 0000000..8e0a91c --- /dev/null +++ b/src/leetcode/tree/construct_quad_tree.py @@ -0,0 +1,60 @@ +# DIFFICULTY: HARD +# ---------------- +# +# Given a n * n matrix grid of 0's and 1's only. We want to represent grid with a Quad-Tree. +# +# Return the root of the Quad-Tree representing grid. +# +# See https://leetcode.com/problems/construct-quad-tree +from leetcode.tree.common.quad_tree import Node + + +class Solution: + def construct(self, grid: list[list[int]]) -> Node | None: + """ + SOLUTION + -------- + + To make a quad tree, recursively divide the matrix into quadrants until each quadrant is all 1's or all 0's. + + COMPLEXITY + ---------- + + Time complexity is O(n^2) because we are visiting each cell in the grid. + + Space complexity is O(log n) because the recursion depth is limited by the size of the grid. + """ + if not grid or not grid[0]: + return None + + return self.__construct(grid, 0, 0, len(grid)) + + def __construct(self, grid: list[list[int]], row: int, column: int, size: int) -> Node | None: + node = Node() + + if self.__isUniform(grid, row, column, size): + node.val = bool(grid[row][column]) + node.isLeaf = True + return node + + # If the grid segment here isn't uniform, we'll need to create 4 quadrants and construct nodes out of all 4 + # of them. + # + # Setting the `node.val` of this node is irrelevant as it's not a leaf. We can just use the default value. + half = size // 2 + node.isLeaf = False + node.topLeft = self.__construct(grid, row, column, half) + node.topRight = self.__construct(grid, row, column + half, half) + node.bottomLeft = self.__construct(grid, row + half, column, half) + node.bottomRight = self.__construct(grid, row + half, column + half, half) + return node + + # Checks if a quadrant of the grid starting at [row, column] is uniformly all 1's or all 0's. + def __isUniform(self, grid: list[list[int]], row: int, column: int, size: int) -> bool: + value = grid[row][column] + for i in range(row, row + size): + for j in range(column, column + size): + if grid[i][j] != value: + return False + + return True diff --git a/test/leetcode/tree/__snapshots__/construct-quad-tree.test.ts.snap b/test/leetcode/tree/__snapshots__/construct-quad-tree.test.ts.snap deleted file mode 100644 index 47cd81c..0000000 --- a/test/leetcode/tree/__snapshots__/construct-quad-tree.test.ts.snap +++ /dev/null @@ -1,107 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`construct quad tree quad tree - test case 1 1`] = ` -_Node { - "bottomLeft": _Node { - "bottomLeft": null, - "bottomRight": null, - "isLeaf": true, - "topLeft": null, - "topRight": null, - "val": true, - }, - "bottomRight": _Node { - "bottomLeft": null, - "bottomRight": null, - "isLeaf": true, - "topLeft": null, - "topRight": null, - "val": false, - }, - "isLeaf": false, - "topLeft": _Node { - "bottomLeft": null, - "bottomRight": null, - "isLeaf": true, - "topLeft": null, - "topRight": null, - "val": false, - }, - "topRight": _Node { - "bottomLeft": null, - "bottomRight": null, - "isLeaf": true, - "topLeft": null, - "topRight": null, - "val": true, - }, - "val": false, -} -`; - -exports[`construct quad tree quad tree - test case 2 1`] = ` -_Node { - "bottomLeft": _Node { - "bottomLeft": null, - "bottomRight": null, - "isLeaf": true, - "topLeft": null, - "topRight": null, - "val": true, - }, - "bottomRight": _Node { - "bottomLeft": null, - "bottomRight": null, - "isLeaf": true, - "topLeft": null, - "topRight": null, - "val": false, - }, - "isLeaf": false, - "topLeft": _Node { - "bottomLeft": null, - "bottomRight": null, - "isLeaf": true, - "topLeft": null, - "topRight": null, - "val": true, - }, - "topRight": _Node { - "bottomLeft": _Node { - "bottomLeft": null, - "bottomRight": null, - "isLeaf": true, - "topLeft": null, - "topRight": null, - "val": true, - }, - "bottomRight": _Node { - "bottomLeft": null, - "bottomRight": null, - "isLeaf": true, - "topLeft": null, - "topRight": null, - "val": true, - }, - "isLeaf": false, - "topLeft": _Node { - "bottomLeft": null, - "bottomRight": null, - "isLeaf": true, - "topLeft": null, - "topRight": null, - "val": false, - }, - "topRight": _Node { - "bottomLeft": null, - "bottomRight": null, - "isLeaf": true, - "topLeft": null, - "topRight": null, - "val": false, - }, - "val": false, - }, - "val": false, -} -`; diff --git a/test/leetcode/tree/binary-tree-vertical-order-traversal.test.ts b/test/leetcode/tree/binary-tree-vertical-order-traversal.test.ts deleted file mode 100644 index d790d02..0000000 --- a/test/leetcode/tree/binary-tree-vertical-order-traversal.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { array2tree } from '../../src/tree/common/tree-node'; -import { verticalOrder } from '../../src/tree/binary-tree-vertical-order-traversal'; - -describe('binary tree vertical order traversal', () => { - test('binary tree vertical order traversal - test case 1', async () => { - const tree = array2tree([3, 9, 20, null, null, 15, 7]); - - expect(verticalOrder(tree)).toStrictEqual([[9], [3, 15], [20], [7]]); - }); -}); diff --git a/test/leetcode/tree/construct-quad-tree.test.ts b/test/leetcode/tree/construct-quad-tree.test.ts deleted file mode 100644 index 59e6761..0000000 --- a/test/leetcode/tree/construct-quad-tree.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { construct } from '../../src/tree/construct-quad-tree'; - -describe('construct quad tree', () => { - test('quad tree - test case 1', async () => { - const grid = [ - [0, 1], - [1, 0] - ]; - - expect(construct(grid)).toMatchSnapshot(); - }); - - test('quad tree - test case 2', async () => { - const grid = [ - [1, 1, 1, 1, 0, 0, 0, 0], - [1, 1, 1, 1, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 0, 0, 0, 0], - [1, 1, 1, 1, 0, 0, 0, 0], - [1, 1, 1, 1, 0, 0, 0, 0], - [1, 1, 1, 1, 0, 0, 0, 0] - ]; - - expect(construct(grid)).toMatchSnapshot(); - }); -}); diff --git a/test/leetcode/tree/construct_quad_tree_test.py b/test/leetcode/tree/construct_quad_tree_test.py new file mode 100644 index 0000000..d7b7085 --- /dev/null +++ b/test/leetcode/tree/construct_quad_tree_test.py @@ -0,0 +1,25 @@ +from leetcode.tree.construct_quad_tree import Solution + + +soln = Solution() + + +def test_case_1(snapshot): + grid = [[0, 1], [1, 0]] + + snapshot.assert_match(soln.construct(grid)) + + +def test_case_2(snapshot): + grid = [ + [1, 1, 1, 1, 0, 0, 0, 0], + [1, 1, 1, 1, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 0, 0, 0, 0], + [1, 1, 1, 1, 0, 0, 0, 0], + [1, 1, 1, 1, 0, 0, 0, 0], + [1, 1, 1, 1, 0, 0, 0, 0], + ] + + snapshot.assert_match(soln.construct(grid)) diff --git a/test/leetcode/tree/snapshots/snap_construct_quad_tree_test.py b/test/leetcode/tree/snapshots/snap_construct_quad_tree_test.py new file mode 100644 index 0000000..7511e39 --- /dev/null +++ b/test/leetcode/tree/snapshots/snap_construct_quad_tree_test.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# snapshottest: v1 - https://goo.gl/zC4yUc +from __future__ import unicode_literals + +from snapshottest import GenericRepr, Snapshot + + +snapshots = Snapshot() + +snapshots['test_case_1 1'] = GenericRepr('') + +snapshots['test_case_2 1'] = GenericRepr('') From ea6cb0f968b78c5cba4ceaf6356090d50dc7b4a6 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 19 Mar 2025 18:43:24 -0700 Subject: [PATCH 140/158] in mem fs --- .../tree/design-in-memory-file-system.ts | 118 ------------------ .../tree/design_in_memory_file_system.py | 107 ++++++++++++++++ .../leetcode/tree/construct_quad_tree_test.py | 25 ---- .../tree/design-in-memory-file-system.test.ts | 15 --- .../tree/design_in_memory_file_system_test.py | 13 ++ .../snap_construct_quad_tree_test.py | 12 -- 6 files changed, 120 insertions(+), 170 deletions(-) delete mode 100644 src/leetcode/tree/design-in-memory-file-system.ts create mode 100644 src/leetcode/tree/design_in_memory_file_system.py delete mode 100644 test/leetcode/tree/construct_quad_tree_test.py delete mode 100644 test/leetcode/tree/design-in-memory-file-system.test.ts create mode 100644 test/leetcode/tree/design_in_memory_file_system_test.py delete mode 100644 test/leetcode/tree/snapshots/snap_construct_quad_tree_test.py diff --git a/src/leetcode/tree/design-in-memory-file-system.ts b/src/leetcode/tree/design-in-memory-file-system.ts deleted file mode 100644 index bbe15de..0000000 --- a/src/leetcode/tree/design-in-memory-file-system.ts +++ /dev/null @@ -1,118 +0,0 @@ -// DIFFICULTY: HARD -// -// Design a data structure that simulates an in-memory file system. -// -// Implement the FileSystem class: -// -// - FileSystem() -// Initializes the object of the system. -// - List ls(String path) -// If path is a file path, returns a list that only contains this file's name. -// If path is a directory path, returns the list of file and directory names in this directory. -// The answer should in lexicographic order. -// - void mkdir(String path) -// Makes a new directory according to the given path. The given directory path does not exist. -// If the middle directories in the path do not exist, you should create them as well. -// - void addContentToFile(String filePath, String content) -// If filePath does not exist, creates that file containing given content. -// If filePath already exists, appends the given content to original content. -// - String readContentFromFile(String filePath) -// Returns the content in the file at filePath. -// -// See {@link https://leetcode.com/problems/design-in-memory-file-system/} -export { FileSystem }; - -// SOLUTION: -// -// The problem states that we can assume all inputs are valid, so we don't need to check if we try to mkdir on path -// parts that contain files, or otherwise list children of files. We can also assume that all listed directories -// and files exist. -// -// Also, note that FileSystem shadows a built-in, so we'll have to suppress warnings about it. -class FileNode { - public isFile = false; - - public content: string | undefined = undefined; - - public children = new Map(); -} - -class FileSystem { - private readonly root = new FileNode(); - - ls(path: string): string[] { - let node = this.root; - - const parts = this.split(path); - for (const part of parts) { - if (!node.children.has(part)) { - return []; - } - - node = node.children.get(part)!; - - // Note that the ls function only wants to know the name of the file, not the absolute path, so don't bother - // prefixing the parent path. - if (node.isFile) { - return [part]; - } - } - - const children = [...node.children.keys()]; - children.sort((a, b) => a.localeCompare(b)); - return children; - } - - mkdir(path: string): void { - let node = this.root; - - const parts = this.split(path); - for (const part of parts) { - if (!node.children.has(part)) { - node.children.set(part, new FileNode()); - } - - node = node.children.get(part)!; - } - } - - addContentToFile(filePath: string, content: string): void { - let node = this.root; - - const parts = this.split(filePath); - for (let i = 0; i < parts.length; i++) { - const part = parts[i]; - - // The problem isn't exactly clear here, but the assumption is that we also mkdir the paths if they do not - // exist. - if (!node.children.has(part)) { - node.children.set(part, new FileNode()); - } - - node = node.children.get(part)!; - - // The very last part is the filename, so append the content. - if (i === parts.length - 1) { - const value = node.content === undefined ? '' : node.content; - node.content = value + content; - node.isFile = true; - } - } - } - - readContentFromFile(filePath: string): string { - let node = this.root; - - const parts = this.split(filePath); - for (const part of parts) { - node = node.children.get(part)!; - } - - return node.content ?? ''; - } - - private split(path: string) { - const normalized = path.endsWith('/') ? path.slice(0, path.length - 1) : path; - return normalized.split('/'); - } -} diff --git a/src/leetcode/tree/design_in_memory_file_system.py b/src/leetcode/tree/design_in_memory_file_system.py new file mode 100644 index 0000000..6b9d30a --- /dev/null +++ b/src/leetcode/tree/design_in_memory_file_system.py @@ -0,0 +1,107 @@ +# DIFFICULTY: HARD +# ---------------- +# +# Design a data structure that simulates an in-memory file system. +# +# Implement the FileSystem class: +# +# - FileSystem() +# Initializes the object of the system. +# - List ls(String path) +# If path is a file path, returns a list that only contains this file's name. +# If path is a directory path, returns the list of file and directory names in this directory. +# The answer should in lexicographic order. +# - void mkdir(String path) +# Makes a new directory according to the given path. The given directory path does not exist. +# If the middle directories in the path do not exist, you should create them as well. +# - void addContentToFile(String filePath, String content) +# If filePath does not exist, creates that file containing given content. +# If filePath already exists, appends the given content to original content. +# - String readContentFromFile(String filePath) +# Returns the content in the file at filePath. +# +# See https://leetcode.com/problems/design-in-memory-file-system +class FileNode: + def __init__(self): + self.is_file = False + self.content = "" + self.children: "dict[str, FileNode]" = {} + + +class FileSystem: + def __init__(self): + """ + SOLUTION + -------- + + The problem states that we can assume all inputs are valid, so we don't need to check if we try to mkdir on path + parts that contain files, or otherwise list children of files. We can also assume that all listed directories + and files exist. + + COMPLEXITY + ---------- + + Time complexity is O(n) for all operations. + + Space complexity is O(n) where n is the number of directories and files in the file system. + """ + self.root = FileNode() + + def ls(self, path: str) -> list[str]: + node = self.root + + normalized = self.__normalize(path) + for part in normalized.split("/"): + if part not in node.children: + return [] + + node = node.children[part] + + # Note that the ls function only wants to know the name of the file, not the absolute path, so don't bother + # prefixing the parent path. + if node.is_file: + return [part] + + children = list(node.children.keys()) + children.sort() + return children + + def mkdir(self, path: str) -> None: + node = self.root + + normalized = self.__normalize(path) + for part in normalized.split("/"): + if part not in node.children: + node.children[part] = FileNode() + + node = node.children[part] + + def addContentToFile(self, filePath: str, content: str) -> None: + node = self.root + + normalized = self.__normalize(filePath) + parts = normalized.split("/") + for i, part in enumerate(parts): + # The problem isn't exactly clear here, but the assumption is that we also mkdir the paths if they do not + # exist. + if part not in node.children: + node.children[part] = FileNode() + + node = node.children[part] + + # The very last part is the filename, so append the content. + if i == len(parts) - 1: + node.content = node.content + content if node.content else content + node.is_file = True + + def readContentFromFile(self, filePath: str) -> str: + node = self.root + + normalized = self.__normalize(filePath) + for part in normalized.split("/"): + node = node.children[part] + + return node.content if node.content else "" + + def __normalize(self, path: str) -> str: + return path[0:-1] if path[-1] == "/" else path diff --git a/test/leetcode/tree/construct_quad_tree_test.py b/test/leetcode/tree/construct_quad_tree_test.py deleted file mode 100644 index d7b7085..0000000 --- a/test/leetcode/tree/construct_quad_tree_test.py +++ /dev/null @@ -1,25 +0,0 @@ -from leetcode.tree.construct_quad_tree import Solution - - -soln = Solution() - - -def test_case_1(snapshot): - grid = [[0, 1], [1, 0]] - - snapshot.assert_match(soln.construct(grid)) - - -def test_case_2(snapshot): - grid = [ - [1, 1, 1, 1, 0, 0, 0, 0], - [1, 1, 1, 1, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 0, 0, 0, 0], - [1, 1, 1, 1, 0, 0, 0, 0], - [1, 1, 1, 1, 0, 0, 0, 0], - [1, 1, 1, 1, 0, 0, 0, 0], - ] - - snapshot.assert_match(soln.construct(grid)) diff --git a/test/leetcode/tree/design-in-memory-file-system.test.ts b/test/leetcode/tree/design-in-memory-file-system.test.ts deleted file mode 100644 index f1379bc..0000000 --- a/test/leetcode/tree/design-in-memory-file-system.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { FileSystem } from '../../src/tree/design-in-memory-file-system'; - -describe('design in memory file system', () => { - test('design in memory file system - test case 1', async () => { - const fs = new FileSystem(); - - expect(fs.ls('/')).toStrictEqual([]); - - fs.mkdir('/a/b/c'); - fs.addContentToFile('/a/b/c/d', 'hello'); - - expect(fs.ls('/')).toStrictEqual(['a']); - expect(fs.readContentFromFile('/a/b/c/d')).toBe('hello'); - }); -}); diff --git a/test/leetcode/tree/design_in_memory_file_system_test.py b/test/leetcode/tree/design_in_memory_file_system_test.py new file mode 100644 index 0000000..de9f01e --- /dev/null +++ b/test/leetcode/tree/design_in_memory_file_system_test.py @@ -0,0 +1,13 @@ +from leetcode.tree.design_in_memory_file_system import FileSystem + + +def test_case_1(): + fs = FileSystem() + + assert fs.ls("/") == [] + + fs.mkdir("/a/b/c") + fs.addContentToFile("/a/b/c/d", "hello") + + assert fs.ls("/") == ["a"] + assert fs.readContentFromFile("/a/b/c/d") == "hello" diff --git a/test/leetcode/tree/snapshots/snap_construct_quad_tree_test.py b/test/leetcode/tree/snapshots/snap_construct_quad_tree_test.py deleted file mode 100644 index 7511e39..0000000 --- a/test/leetcode/tree/snapshots/snap_construct_quad_tree_test.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -# snapshottest: v1 - https://goo.gl/zC4yUc -from __future__ import unicode_literals - -from snapshottest import GenericRepr, Snapshot - - -snapshots = Snapshot() - -snapshots['test_case_1 1'] = GenericRepr('') - -snapshots['test_case_2 1'] = GenericRepr('') From 1ee9897bff1585c7deab2e7387cced62eef31f2d Mon Sep 17 00:00:00 2001 From: Min Huang Date: Wed, 19 Mar 2025 18:52:07 -0700 Subject: [PATCH 141/158] fs --- .../tree/design_in_memory_file_system.py | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/leetcode/tree/design_in_memory_file_system.py b/src/leetcode/tree/design_in_memory_file_system.py index 6b9d30a..614764e 100644 --- a/src/leetcode/tree/design_in_memory_file_system.py +++ b/src/leetcode/tree/design_in_memory_file_system.py @@ -24,7 +24,7 @@ class FileNode: def __init__(self): self.is_file = False - self.content = "" + self.content: str | None = None self.children: "dict[str, FileNode]" = {} @@ -50,8 +50,8 @@ def __init__(self): def ls(self, path: str) -> list[str]: node = self.root - normalized = self.__normalize(path) - for part in normalized.split("/"): + parts = self.__normalize(path).split("/") + for part in parts: if part not in node.children: return [] @@ -69,8 +69,8 @@ def ls(self, path: str) -> list[str]: def mkdir(self, path: str) -> None: node = self.root - normalized = self.__normalize(path) - for part in normalized.split("/"): + parts = self.__normalize(path).split("/") + for part in parts: if part not in node.children: node.children[part] = FileNode() @@ -79,8 +79,7 @@ def mkdir(self, path: str) -> None: def addContentToFile(self, filePath: str, content: str) -> None: node = self.root - normalized = self.__normalize(filePath) - parts = normalized.split("/") + parts = self.__normalize(filePath).split("/") for i, part in enumerate(parts): # The problem isn't exactly clear here, but the assumption is that we also mkdir the paths if they do not # exist. @@ -91,17 +90,20 @@ def addContentToFile(self, filePath: str, content: str) -> None: # The very last part is the filename, so append the content. if i == len(parts) - 1: - node.content = node.content + content if node.content else content + value = node.content if node.content else "" + node.content = value + content node.is_file = True def readContentFromFile(self, filePath: str) -> str: node = self.root - normalized = self.__normalize(filePath) - for part in normalized.split("/"): + parts = self.__normalize(filePath).split("/") + for part in parts: node = node.children[part] return node.content if node.content else "" def __normalize(self, path: str) -> str: + if path == "/": + return "" return path[0:-1] if path[-1] == "/" else path From 0b8b4d4535bd68e450cea572a3e751675620dd36 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 20 Mar 2025 13:19:16 -0700 Subject: [PATCH 142/158] diameter --- src/leetcode/tree/diameter-of-binary-tree.ts | 46 --------------- src/leetcode/tree/diameter_of_binary_tree.py | 59 +++++++++++++++++++ .../tree/diameter-of-binary-tree.test.ts | 16 ----- .../tree/diameter_of_binary_tree_test.py | 17 ++++++ 4 files changed, 76 insertions(+), 62 deletions(-) delete mode 100644 src/leetcode/tree/diameter-of-binary-tree.ts create mode 100644 src/leetcode/tree/diameter_of_binary_tree.py delete mode 100644 test/leetcode/tree/diameter-of-binary-tree.test.ts create mode 100644 test/leetcode/tree/diameter_of_binary_tree_test.py diff --git a/src/leetcode/tree/diameter-of-binary-tree.ts b/src/leetcode/tree/diameter-of-binary-tree.ts deleted file mode 100644 index 88491e7..0000000 --- a/src/leetcode/tree/diameter-of-binary-tree.ts +++ /dev/null @@ -1,46 +0,0 @@ -// DIFFICULTY: EASY -// -// Given the root of a binary tree, return the length of the diameter of the tree. -// -// The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or -// may not pass through the root. -// -// The length of a path between two nodes is represented by the number of edges between them. -// -// See {@link https://leetcode.com/problems/diameter-of-binary-tree} -import { TreeNode } from './common/tree-node'; -export { diameterOfBinaryTree }; - -// SOLUTION: -// -// For any node, the longest path might: -// -// - Pass through the node, in which case the longest path is the longest path of the left and right subtrees, plus one. -// - Not pass through the node, in which case the longest path is the max of the longest path of the left and right. -// -// We can use DFS to traverse the tree and keep track of the longest path we've seen so far. -function diameterOfBinaryTree(root: TreeNode | null): number { - let max = 0; - - function dfs(node: TreeNode | null): number { - if (node === null) { - return 0; - } - - // Figure out the max depth of the left and right subtrees. - const left = dfs(node.left); - const right = dfs(node.right); - - // The left and right depths represent the current diameter of the tree rooted at this node. We'll compare it - // with the global max diameter. - const current = left + right; - max = Math.max(current, max); - - // Return the max depth starting from the current node. Whichever path (left or right) is longer will be the max - // depth, but we'll add one to account for the current node. - return Math.max(left, right) + 1; - } - - dfs(root); - return max; -} diff --git a/src/leetcode/tree/diameter_of_binary_tree.py b/src/leetcode/tree/diameter_of_binary_tree.py new file mode 100644 index 0000000..72a2532 --- /dev/null +++ b/src/leetcode/tree/diameter_of_binary_tree.py @@ -0,0 +1,59 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given the root of a binary tree, return the length of the diameter of the tree. +# +# The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or +# may not pass through the root. +# +# The length of a path between two nodes is represented by the number of edges between them. +# +# See https://leetcode.com/problems/diameter-of-binary-tree +from leetcode.tree.common.tree_node import TreeNode + + +class Solution: + def diameterOfBinaryTree(self, root: TreeNode | None) -> int: + """ + SOLUTION + -------- + + For any node, the longest path might: + + - Pass through the node, in which case the longest path is the longest path of the left and right subtrees, plus + one. + - Not pass through the node, in which case the longest path is the max of the longest path of the left and + right. + + We can use DFS to traverse the tree and keep track of the longest path we've seen so far. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of nodes in the tree. + + Space complexity is O(n) because of the recursive stack. + """ + max_path = 0 + + def dfs(node: TreeNode | None) -> int: + nonlocal max_path + + if not node: + return 0 + + # Figure out the max depth of the left and right subtrees. + left = dfs(node.left) + right = dfs(node.right) + + # The left and right depths represent the current diameter of the tree rooted at this node. We'll compare + # it with the global max diameter. + current_path = left + right + max_path = max(max_path, current_path) + + # Return the max depth starting from the current node. Whichever path (left or right) is longer will be the + # max depth, but we'll add one to account for the current node. + return max(left, right) + 1 + + dfs(root) + return max_path diff --git a/test/leetcode/tree/diameter-of-binary-tree.test.ts b/test/leetcode/tree/diameter-of-binary-tree.test.ts deleted file mode 100644 index 62ae07e..0000000 --- a/test/leetcode/tree/diameter-of-binary-tree.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { array2tree } from '../../src/tree/common/tree-node'; -import { diameterOfBinaryTree } from '../../src/tree/diameter-of-binary-tree'; - -describe('diameter of binary tree', () => { - test('diameter of binary tree - test case 1', async () => { - const tree = array2tree([1, 2, 3]); - - expect(diameterOfBinaryTree(tree)).toBe(2); - }); - - test('diameter of binary tree - test case 2', async () => { - const tree = array2tree([1, 2, 3, 4, 5]); - - expect(diameterOfBinaryTree(tree)).toBe(3); - }); -}); diff --git a/test/leetcode/tree/diameter_of_binary_tree_test.py b/test/leetcode/tree/diameter_of_binary_tree_test.py new file mode 100644 index 0000000..582a008 --- /dev/null +++ b/test/leetcode/tree/diameter_of_binary_tree_test.py @@ -0,0 +1,17 @@ +from leetcode.tree.common.tree_node import array2tree +from leetcode.tree.diameter_of_binary_tree import Solution + + +soln = Solution() + + +def test_case_1(): + tree = array2tree([1, 2, 3]) + + assert soln.diameterOfBinaryTree(tree) == 2 + + +def test_case_2(): + tree = array2tree([1, 2, 3, 4, 5]) + + assert soln.diameterOfBinaryTree(tree) == 3 From 0c9ff35fac686fd31e0be116e85456b82f753807 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 20 Mar 2025 13:38:14 -0700 Subject: [PATCH 143/158] lca --- ...lowest-common-ancestor-of-a-binary-tree.ts | 80 ----------------- ...lowest_common_ancestor_of_a_binary_tree.py | 87 +++++++++++++++++++ .../longest-palindromic-substring.test.ts | 7 -- ...mmon-ancestor-of-a-binary-tree-iii.test.ts | 10 --- ...t-common-ancestor-of-a-binary-tree.test.ts | 10 --- ...t_common_ancestor_of_a_binary_tree_test.py | 17 ++++ 6 files changed, 104 insertions(+), 107 deletions(-) delete mode 100644 src/leetcode/tree/lowest-common-ancestor-of-a-binary-tree.ts create mode 100644 src/leetcode/tree/lowest_common_ancestor_of_a_binary_tree.py delete mode 100644 test/leetcode/tree/longest-palindromic-substring.test.ts delete mode 100644 test/leetcode/tree/lowest-common-ancestor-of-a-binary-tree-iii.test.ts delete mode 100644 test/leetcode/tree/lowest-common-ancestor-of-a-binary-tree.test.ts create mode 100644 test/leetcode/tree/lowest_common_ancestor_of_a_binary_tree_test.py diff --git a/src/leetcode/tree/lowest-common-ancestor-of-a-binary-tree.ts b/src/leetcode/tree/lowest-common-ancestor-of-a-binary-tree.ts deleted file mode 100644 index eef3189..0000000 --- a/src/leetcode/tree/lowest-common-ancestor-of-a-binary-tree.ts +++ /dev/null @@ -1,80 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given two nodes of a binary tree p and q, return their lowest common ancestor (LCA). -// -// According to the definition of LCA on Wikipedia: "The lowest common ancestor of two nodes p and q in a tree T is the -// lowest node that has both p and q as descendants (where we allow a node to be a descendant of itself)." -// -// See {@link https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree-iii} -import { TreeNode } from './common/tree-node'; -export { lowestCommonAncestor }; - -// SOLUTION: -// -// In this problem, we are not given the parent nodes. So we have to solve this problem using a recursive divide and -// conquer approach. -// -// The idea is to recursively check the left and right subtrees for nodes p and q. There are three cases: -// -// 1. We find p and q in DIFFERENT subtrees. -// 2. We find p and q BOTH in the left subtree. -// 3. We find p and q BOTH in the right subtree. -// -// Well, in case 1, if we began this process from the root, that's it! The current node has to be the LCA. If instead -// we discover that p and q are BOTH in the left or right subtree, make that subtree the new root and continue the -// search. -// -// Note that this solution requires that both p and q exist in the tree. If one of them doesn't exist we could return -// a false LCA. To guard against this we'd have to check the tree beforehand to ensure that both p and q exist. -// -// COMPLEXITY: -// -// The recursive calls visit every node once, so the time complexity is O(n) in number of nodes. We don't use extra -// space but the call stack uses O(h) space where h is the height of the tree. -function lowestCommonAncestor(node: TreeNode | null, p: TreeNode | null, q: TreeNode | null): TreeNode | null { - // Recursively find p or q in the tree rooted at node. - function findNode(node: TreeNode | null, p: TreeNode | null, q: TreeNode | null): TreeNode | null { - // We DID NOT find p or q in either of the subtrees. - if (node === null) { - return null; - } - - // We DID find p or q in either of the subtrees, so return it. - if (node === p || node === q) { - return node; - } - - // Otherwise, continue by checking for p and q in the left and right subtrees. - // - // If left is non-null, then we found one of p or q in the left subtree. - // If right is non-null, then we found one of p or q in the right subtree. - // If both were non-null, then we found p and q in different subtrees. - const left = findNode(node.left, p, q); - const right = findNode(node.right, p, q); - - // If BOTH left and right are non-null, then we found p and q in different subtrees. If that is the case, the - // current node must be the LCA. - if (left !== null && right !== null) { - return node; - } - - // We found ONE of nodes p or q in the LEFT subtree. However, we didn't find the other node in the right subtree - // so we assume that they were both in the left subtree. Note that we ASSUME that both p and q exist in the tree, - // or else we could return a false LCA. - if (left !== null) { - return left; - } - - // We found ONE of nodes p or q in the RIGHT subtree. However, we didn't find the other node in the left subtree - // so we assume that they were both in the right subtree. Note that we ASSUME that both p and q exist in the tree, - // or else we could return a false LCA. - return right; - } - - // Technically, lowestCommonAncestor has the same signature as findNode, so we could've just implemented the LCA - // logic directly. - // - // However, lowestCommonAncestor will return to you the LCA always. In findNode, sometimes the intermediate result - // is not the LCA (e.g. when we are just looking for the node). - return findNode(node, p, q); -} diff --git a/src/leetcode/tree/lowest_common_ancestor_of_a_binary_tree.py b/src/leetcode/tree/lowest_common_ancestor_of_a_binary_tree.py new file mode 100644 index 0000000..cd07ae9 --- /dev/null +++ b/src/leetcode/tree/lowest_common_ancestor_of_a_binary_tree.py @@ -0,0 +1,87 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given two nodes of a binary tree p and q, return their lowest common ancestor (LCA). +# +# According to the definition of LCA on Wikipedia: "The lowest common ancestor of two nodes p and q in a tree T is the +# lowest node that has both p and q as descendants (where we allow a node to be a descendant of itself)." +# +# See https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree +from leetcode.tree.common.tree_node import TreeNode + + +class Solution: + def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode: + """ + SOLUTION + -------- + + In this problem, we are not given the parent nodes. So we have to solve this problem using a recursive divide + and conquer approach. + + The idea is to recursively check the left and right subtrees for nodes p and q. There are three cases: + + 1. We find p and q in DIFFERENT subtrees. + 2. We find p and q BOTH in the left subtree. + 3. We find p and q BOTH in the right subtree. + + Well, in case 1, if we began this process from the root, that's it! The current node has to be the LCA. If + instead we discover that p and q are BOTH in the left or right subtree, make that subtree the new root and + continue the search. + + Note that this solution requires that both p and q exist in the tree. If one of them doesn't exist we could + return a false LCA. To guard against this we'd have to check the tree beforehand to ensure that both p and q + exist. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of nodes in the tree. + + Space complexity is O(n) because of the recursive stack. + """ + + # Recursively find p or q in the tree rooted at node. + def findNode(node: TreeNode | None, p: TreeNode | None, q: TreeNode | None) -> TreeNode | None: + # We DID NOT find p or q in either of the subtrees. + if not node: + return None + + # We DID find p or q in either of the subtrees, so return it. + if node == p or node == q: + return node + + # Otherwise, continue by checking for p and q in the left and right subtrees. + # + # - If left is non-null, then we found one of p or q in the left subtree. + # - If right is non-null, then we found one of p or q in the right subtree. + # - If both were non-null, then we found p and q in different subtrees. + left = findNode(node.left, p, q) + right = findNode(node.right, p, q) + + # If BOTH left and right are non-null, then we found p and q in different subtrees. If that is the case, + # the current node must be the LCA. + if left and right: + return node + + # We found ONE of nodes p or q in the LEFT subtree. However, we didn't find the other node in the right + # subtree so we assume that they were both in the left subtree. Note that we ASSUME that both p and q exist + # in the tree, or else we could return a false LCA. + if left: + return left + # We found ONE of nodes p or q in the RIGHT subtree. However, we didn't find the other node in the left + # subtree so we assume that they were both in the right subtree. Note that we ASSUME that both p and q + # exist in the tree, or else we could return a false LCA. + else: + return right + + # Technically, lowestCommonAncestor has the same signature as findNode, so we could've just implemented the LCA + # logic directly. + # + # However, lowestCommonAncestor will return to you the LCA always. In findNode, sometimes the intermediate + # result is not the LCA (e.g. when we are just looking for the node). + lca = findNode(root, p, q) + + # The problem appears to guarantee that p and q exist in the tree, and will be non-null. + assert lca + return lca diff --git a/test/leetcode/tree/longest-palindromic-substring.test.ts b/test/leetcode/tree/longest-palindromic-substring.test.ts deleted file mode 100644 index b42863f..0000000 --- a/test/leetcode/tree/longest-palindromic-substring.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { longestPalindrome } from '../../src/string/longest-palindromic-substring'; - -describe('longest palindromic substring', () => { - test('longestPalindrome - test case 1', () => { - expect(longestPalindrome('babad')).toEqual('bab'); - }); -}); diff --git a/test/leetcode/tree/lowest-common-ancestor-of-a-binary-tree-iii.test.ts b/test/leetcode/tree/lowest-common-ancestor-of-a-binary-tree-iii.test.ts deleted file mode 100644 index f5d0ef9..0000000 --- a/test/leetcode/tree/lowest-common-ancestor-of-a-binary-tree-iii.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { array2tree } from '../../src/tree/common/parent-node'; -import { lowestCommonAncestor } from '../../src/tree/lowest-common-ancestor-of-a-binary-tree-iii'; - -describe('lowest common ancestor of a binary tree iii', () => { - test('lowest common ancestor - test case 1', async () => { - const tree = array2tree([3, 5, 1, 6, 2, 0, 8, null, null, 7, 4]); - - expect(lowestCommonAncestor(tree!.left, tree!.right)?.val).toStrictEqual(3); - }); -}); diff --git a/test/leetcode/tree/lowest-common-ancestor-of-a-binary-tree.test.ts b/test/leetcode/tree/lowest-common-ancestor-of-a-binary-tree.test.ts deleted file mode 100644 index a3e1187..0000000 --- a/test/leetcode/tree/lowest-common-ancestor-of-a-binary-tree.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { array2tree } from '../../src/tree/common/tree-node'; -import { lowestCommonAncestor } from '../../src/tree/lowest-common-ancestor-of-a-binary-tree'; - -describe('lowest common ancestor of a binary tree i', () => { - test('lowest common ancestor - test case 1', async () => { - const tree = array2tree([3, 5, 1, 6, 2, 0, 8, null, null, 7, 4]); - - expect(lowestCommonAncestor(tree, tree!.left, tree!.right)?.val).toStrictEqual(3); - }); -}); diff --git a/test/leetcode/tree/lowest_common_ancestor_of_a_binary_tree_test.py b/test/leetcode/tree/lowest_common_ancestor_of_a_binary_tree_test.py new file mode 100644 index 0000000..39af7fb --- /dev/null +++ b/test/leetcode/tree/lowest_common_ancestor_of_a_binary_tree_test.py @@ -0,0 +1,17 @@ +from leetcode.tree.common.tree_node import array2tree +from leetcode.tree.lowest_common_ancestor_of_a_binary_tree import Solution + + +soln = Solution() + + +def test_case_1(): + tree = array2tree([3, 5, 1, 6, 2, 0, 8, None, None, 7, 4]) + assert tree + assert tree.left + assert tree.right + + lca = soln.lowestCommonAncestor(tree, tree.left, tree.right) + assert lca + + assert lca.val == 3 From ba4407adeb75af9390d5f9fff949e7d581975583 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 20 Mar 2025 13:59:59 -0700 Subject: [PATCH 144/158] lca 3 --- src/leetcode/tree/common/parent-node.ts | 62 ------------------ src/leetcode/tree/common/parent_node.py | 44 +++++++++++++ src/leetcode/tree/construct_quad_tree.py | 2 + ...st-common-ancestor-of-a-binary-tree-iii.ts | 58 ----------------- ...st_common_ancestor_of_a_binary_tree_iii.py | 63 +++++++++++++++++++ ...mmon_ancestor_of_a_binary_tree_iii_test.py | 17 +++++ 6 files changed, 126 insertions(+), 120 deletions(-) delete mode 100644 src/leetcode/tree/common/parent-node.ts create mode 100644 src/leetcode/tree/common/parent_node.py delete mode 100644 src/leetcode/tree/lowest-common-ancestor-of-a-binary-tree-iii.ts create mode 100644 src/leetcode/tree/lowest_common_ancestor_of_a_binary_tree_iii.py create mode 100644 test/leetcode/tree/lowest_common_ancestor_of_a_binary_tree_iii_test.py diff --git a/src/leetcode/tree/common/parent-node.ts b/src/leetcode/tree/common/parent-node.ts deleted file mode 100644 index a264c9b..0000000 --- a/src/leetcode/tree/common/parent-node.ts +++ /dev/null @@ -1,62 +0,0 @@ -// This class definition comes from the problem itself and we cannot change it, or else our submission will not be -// accepted. -export class _Node { - val: number; - left: _Node | null; - right: _Node | null; - parent: _Node | null; - - constructor(v: number) { - this.val = v; - this.left = null; - this.right = null; - this.parent = null; - } -} - -// The LeetCode test input is given as an array, but the exact code to construct the tree is not given. This is likely -// how they've implemented it. -export function array2tree(array: (number | null)[]): _Node | null { - if (array.length === 0) { - return null; - } - - const root = new _Node(array[0]!); - const queue = [root]; - let i = 1; - - while (queue.length > 0 && i < array.length) { - const node = queue.shift()!; - if (node === null) { - continue; - } - - // Add the left node. - if (i < array.length) { - const val = array[i]; - if (val !== null) { - node.left = new _Node(val); - node.left.parent = node; - queue.push(node.left); - } else { - node.left = null; - } - i++; - } - - // Add the right node. - if (i < array.length) { - const val = array[i]; - if (val !== null) { - node.right = new _Node(val); - node.right.parent = node; - queue.push(node.right); - } else { - node.right = null; - } - i++; - } - } - - return root; -} diff --git a/src/leetcode/tree/common/parent_node.py b/src/leetcode/tree/common/parent_node.py new file mode 100644 index 0000000..e255925 --- /dev/null +++ b/src/leetcode/tree/common/parent_node.py @@ -0,0 +1,44 @@ +from collections import deque + + +# This class definition comes from the problem itself and we cannot change it, or else our submission will not be +# accepted. +class Node: + def __init__(self, val=0): + self.val = val + self.left: "Node | None" = None + self.right: "Node | None" = None + self.parent: "Node | None" = None + + +def array2tree(array: list[int | None]) -> Node | None: + if not array or array[0] is None: + return None + + root = Node(array[0]) + queue = deque([root]) + i = 1 + + while queue and i < len(array): + node = queue.popleft() + + if node is None: + continue + + if i < len(array): + val = array[i] + if val is not None: + node.left = Node(val) + node.left.parent = node + queue.append(node.left) + i += 1 + + if i < len(array): + val = array[i] + if val is not None: + node.right = Node(val) + node.right.parent = node + queue.append(node.right) + i += 1 + + return root diff --git a/src/leetcode/tree/construct_quad_tree.py b/src/leetcode/tree/construct_quad_tree.py index 8e0a91c..096ebc4 100644 --- a/src/leetcode/tree/construct_quad_tree.py +++ b/src/leetcode/tree/construct_quad_tree.py @@ -17,6 +17,8 @@ def construct(self, grid: list[list[int]]) -> Node | None: To make a quad tree, recursively divide the matrix into quadrants until each quadrant is all 1's or all 0's. + It's not clear why, but in LeetCode, the Node class must be implement and pasted in the solution. + COMPLEXITY ---------- diff --git a/src/leetcode/tree/lowest-common-ancestor-of-a-binary-tree-iii.ts b/src/leetcode/tree/lowest-common-ancestor-of-a-binary-tree-iii.ts deleted file mode 100644 index 829a7eb..0000000 --- a/src/leetcode/tree/lowest-common-ancestor-of-a-binary-tree-iii.ts +++ /dev/null @@ -1,58 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given two nodes of a binary tree p and q, return their lowest common ancestor (LCA). -// -// According to the definition of LCA on Wikipedia: "The lowest common ancestor of two nodes p and q in a tree T is the -// lowest node that has both p and q as descendants (where we allow a node to be a descendant of itself)." -// -// See {@link https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree-iii} -import { _Node } from './common/parent-node'; -export { lowestCommonAncestor }; - -// SOLUTION: -// -// Note that this problem is quite simple since we are given parent nodes. All we have to do is traverse up to the root -// for one node and collect ancestors. Then we traverse up to the root for the other node and check if any parent is in -// the ancestor set. If it is, then we have found the lowest common ancestor. -// -// However, that solution involves the use of a set. We can optimize this by using the two pointer technique in a -// clever way. Let's consider paths from p and q to the LCA. -// -// If path[p] === path[q], then all we have to do is advance both pointers one step at a time and they will converge at -// the LCA eventually. -// -// If path[p] !== path[q], then one of the pointers will reach past the root and become null first. -// -// The goal, then is to make BOTH pointers reach the LCA at the same time. So let's say path[p].length === 5, and that -// path[q].length === 10. If we could craft an extended path for both p and q so that they both travel the same -// distance, and end up at the LCA, then that would work. -// -// To create this extended path, envision an extended path2[p] that goes from p to the root then to q then to the root -// (yes, imagine there's a direct path from root -> q). Likewise, envision an extended path2[q] that goes from q to the -// root then from p to the root. -// -// Now if you make both the p and q pointers follow this path, they will both advance PAST the root, but then eventually -// converge at the LCA before they hit the root again. -// -// COMPLEXITY: -// -// For the solution involving a set, the time complexity is O(h + h) where h is the depth of the deeper node. The -// space complexity is O(h) to store the ancestors. -// -// For the solution not involving a set, the time complexity is O(h + h) where h is the depth of the deeper node. -function lowestCommonAncestor(p: _Node | null, q: _Node | null): _Node | null { - // Create new pointers to advance; we need the original starting points p and q. - let [a, b] = [p, q]; - - // Eventually a and b will converge at the LCA. - while (a !== b) { - // Advance a = p -> root -> q -> root. Pointers converge before hitting the root again. - a = a !== null ? a.parent : q; - - // Advance b = q -> root -> p -> root. Pointers converge before hitting the root again. - b = b !== null ? b.parent : p; - } - - // Return either pointer; they are both at the LCA. - return a; -} diff --git a/src/leetcode/tree/lowest_common_ancestor_of_a_binary_tree_iii.py b/src/leetcode/tree/lowest_common_ancestor_of_a_binary_tree_iii.py new file mode 100644 index 0000000..dc3aa74 --- /dev/null +++ b/src/leetcode/tree/lowest_common_ancestor_of_a_binary_tree_iii.py @@ -0,0 +1,63 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given two nodes of a binary tree p and q, return their lowest common ancestor (LCA). +# +# According to the definition of LCA on Wikipedia: "The lowest common ancestor of two nodes p and q in a tree T is the +# lowest node that has both p and q as descendants (where we allow a node to be a descendant of itself)." +# +# See https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree-iii +from leetcode.tree.common.parent_node import Node + + +class Solution: + def lowestCommonAncestor(self, p: Node, q: Node) -> Node: + """ + SOLUTION + -------- + + Note that this problem is quite simple since we are given parent nodes. All we have to do is traverse up to the + root for one node and collect ancestors. Then we traverse up to the root for the other node and check if any + parent is in the ancestor set. If it is, then we have found the lowest common ancestor. + + However, that solution involves the use of a set. We can optimize this by using the two pointer technique in a + clever way. Let's consider paths from p and q to the LCA. + + If path[p] == path[q], then all we have to do is advance both pointers one step at a time and they will + converge at the LCA eventually. + + If path[p] != path[q], then one of the pointers will reach past the root and become null first. + + The goal, then is to make BOTH pointers reach the LCA at the same time. So let's say len(path[p]) == 5, and + that len(path[q]) == 10. If we could craft an extended path for both p and q so that they both travel the same + distance, and end up at the LCA, then that would work. + + To create this extended path, envision an extended path2[p] that goes from p to the root then to q then to the + root (yes, imagine there's a direct path from root -> q). Likewise, envision an extended path2[q] that goes + from q to the root then from p to the root. + + Now if you make both the p and q pointers follow this path, they will both advance PAST the root, but then + eventually converge at the LCA before they hit the root again. + + It's not clear why, but in LeetCode, the Node class must be implement and pasted in the solution. + + COMPLEXITY + ---------- + + Time complexity is O(h) where h is the height of tree. + + Space complexity is O(1). + """ + # Create new pointers to advance; we need the original starting points p and q. + a, b = p, q + + # Eventually a and b will converge at the LCA. + while a != b: + # Advance a = p -> root -> q -> root. Pointers converge before hitting the root again. + a = a.parent if a else q + # Advance b = q -> root -> p -> root. Pointers converge before hitting the root again. + b = b.parent if b else p + + # Return either pointer; they are both at the LCA. + assert a + return a diff --git a/test/leetcode/tree/lowest_common_ancestor_of_a_binary_tree_iii_test.py b/test/leetcode/tree/lowest_common_ancestor_of_a_binary_tree_iii_test.py new file mode 100644 index 0000000..4d081f9 --- /dev/null +++ b/test/leetcode/tree/lowest_common_ancestor_of_a_binary_tree_iii_test.py @@ -0,0 +1,17 @@ +from leetcode.tree.common.parent_node import array2tree +from leetcode.tree.lowest_common_ancestor_of_a_binary_tree_iii import Solution + + +soln = Solution() + + +def test_case_1(): + tree = array2tree([3, 5, 1, 6, 2, 0, 8, None, None, 7, 4]) + assert tree + assert tree.left + assert tree.right + + lca = soln.lowestCommonAncestor(tree.left, tree.right) + assert lca + + assert lca.val == 3 From f8bbebc61b53abfe8f707d6c9aa587d941e6df74 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 20 Mar 2025 14:05:05 -0700 Subject: [PATCH 145/158] range sum --- src/leetcode/tree/range-sum-of-bst.ts | 35 ------------------ src/leetcode/tree/range_sum_of_bst.py | 39 +++++++++++++++++++++ test/leetcode/tree/range-sum-of-bst.test.ts | 9 ----- test/leetcode/tree/range_sum_of_bst_test.py | 11 ++++++ 4 files changed, 50 insertions(+), 44 deletions(-) delete mode 100644 src/leetcode/tree/range-sum-of-bst.ts create mode 100644 src/leetcode/tree/range_sum_of_bst.py delete mode 100644 test/leetcode/tree/range-sum-of-bst.test.ts create mode 100644 test/leetcode/tree/range_sum_of_bst_test.py diff --git a/src/leetcode/tree/range-sum-of-bst.ts b/src/leetcode/tree/range-sum-of-bst.ts deleted file mode 100644 index 3ae0f18..0000000 --- a/src/leetcode/tree/range-sum-of-bst.ts +++ /dev/null @@ -1,35 +0,0 @@ -// DIFFICULTY: EASY -// -// Given the root node of a binary search tree and two integers low and high, return the sum of values of all nodes -// with a value in the inclusive range [low, high]. -// -// See {@link https://leetcode.com/problems/range-sum-of-bst/} -import { TreeNode } from './common/tree-node'; -export { rangeSumBST }; - -// SOLUTION: -// -// A simple recursive solution will work. -// -// COMPLEXITY: -// -// The time complexity is O(n) where n is the number of nodes in the tree. -function rangeSumBST(root: TreeNode | null, low: number, high: number): number { - let sum = 0; - - function traverse(node: TreeNode | null) { - if (node === null) { - return; - } - - if (node.val >= low && node.val <= high) { - sum += node.val; - } - - traverse(node.left); - traverse(node.right); - } - - traverse(root); - return sum; -} diff --git a/src/leetcode/tree/range_sum_of_bst.py b/src/leetcode/tree/range_sum_of_bst.py new file mode 100644 index 0000000..a3cbc1e --- /dev/null +++ b/src/leetcode/tree/range_sum_of_bst.py @@ -0,0 +1,39 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given the root node of a binary search tree and two integers low and high, return the sum of values of all nodes +# with a value in the inclusive range [low, high]. +# +# See https://leetcode.com/problems/range-sum-of-bst +from leetcode.tree.common.tree_node import TreeNode + + +class Solution: + def rangeSumBST(self, root: TreeNode | None, low: int, high: int) -> int: + """ + SOLUTION + -------- + + A simple recursive solution will work. + + COMPLEXITY + ---------- + + The time complexity is O(n) where n is the number of nodes in the tree. + """ + range_sum = 0 + + def traverse(node: TreeNode | None): + nonlocal range_sum + + if not node: + return + + if low <= node.val <= high: + range_sum += node.val + + traverse(node.left) + traverse(node.right) + + traverse(root) + return range_sum diff --git a/test/leetcode/tree/range-sum-of-bst.test.ts b/test/leetcode/tree/range-sum-of-bst.test.ts deleted file mode 100644 index 2be267f..0000000 --- a/test/leetcode/tree/range-sum-of-bst.test.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { array2tree } from '../../src/tree/common/tree-node'; -import { rangeSumBST } from '../../src/tree/range-sum-of-bst'; - -describe('range sum of bst', () => { - test('range sum of bst - test case 1', async () => { - const tree = array2tree([10, 5, 15, 3, 7, null, 18]); - expect(rangeSumBST(tree, 7, 15)).toBe(32); - }); -}); diff --git a/test/leetcode/tree/range_sum_of_bst_test.py b/test/leetcode/tree/range_sum_of_bst_test.py new file mode 100644 index 0000000..19edf3d --- /dev/null +++ b/test/leetcode/tree/range_sum_of_bst_test.py @@ -0,0 +1,11 @@ +from leetcode.tree.common.tree_node import array2tree +from leetcode.tree.range_sum_of_bst import Solution + + +soln = Solution() + + +def test_case_1(): + tree = array2tree([10, 5, 15, 3, 7, None, 18]) + + assert soln.rangeSumBST(tree, 7, 15) == 32 From ab9276ab591cf537a0164964890b10db856e2720 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 20 Mar 2025 14:14:04 -0700 Subject: [PATCH 146/158] tree problems --- src/leetcode/tree/same-tree.ts | 32 ----------- src/leetcode/tree/same_tree.py | 34 ++++++++++++ src/leetcode/tree/sum-root-to-leaf-numbers.ts | 45 ---------------- src/leetcode/tree/sum_root_to_leaf_numbers.py | 53 +++++++++++++++++++ test/leetcode/tree/same-tree.test.ts | 12 ----- test/leetcode/tree/same_tree_test.py | 16 ++++++ .../tree/sum-root-to-leaf-numbers.test.ts | 12 ----- .../tree/sum_root_to_leaf_number_test.py | 13 +++++ 8 files changed, 116 insertions(+), 101 deletions(-) delete mode 100644 src/leetcode/tree/same-tree.ts create mode 100644 src/leetcode/tree/same_tree.py delete mode 100644 src/leetcode/tree/sum-root-to-leaf-numbers.ts create mode 100644 src/leetcode/tree/sum_root_to_leaf_numbers.py delete mode 100644 test/leetcode/tree/same-tree.test.ts create mode 100644 test/leetcode/tree/same_tree_test.py delete mode 100644 test/leetcode/tree/sum-root-to-leaf-numbers.test.ts create mode 100644 test/leetcode/tree/sum_root_to_leaf_number_test.py diff --git a/src/leetcode/tree/same-tree.ts b/src/leetcode/tree/same-tree.ts deleted file mode 100644 index 8a60848..0000000 --- a/src/leetcode/tree/same-tree.ts +++ /dev/null @@ -1,32 +0,0 @@ -// DIFFICULTY: EASY -// -// Given the roots of two binary trees p and q, write a function to check if they are the same or not. -// -// Two binary trees are considered the same if they are structurally identical, and the nodes have the same value. -// -// See {@link https://leetcode.com/problems/same-tree/} -import { TreeNode } from './common/tree-node'; -export { isSameTree }; - -// SOLUTION: -// -// A simple recursive solution will work. -// -// COMPLEXITY: -// -// The time complexity is O(n) where n is the number of nodes in the tree. -function isSameTree(p: TreeNode | null, q: TreeNode | null): boolean { - if (p === null) { - return q === null; - } - - if (q === null) { - return p === null; - } - - if (p.val !== q.val) { - return false; - } - - return isSameTree(p.left, q.left) && isSameTree(p.right, q.right); -} diff --git a/src/leetcode/tree/same_tree.py b/src/leetcode/tree/same_tree.py new file mode 100644 index 0000000..14157d9 --- /dev/null +++ b/src/leetcode/tree/same_tree.py @@ -0,0 +1,34 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given the roots of two binary trees p and q, write a function to check if they are the same or not. +# +# Two binary trees are considered the same if they are structurally identical, and the nodes have the same value. +# +# See https://leetcode.com/problems/same-tree +from leetcode.tree.common.tree_node import TreeNode + + +class Solution: + def isSameTree(self, p: TreeNode | None, q: TreeNode | None) -> bool: + """ + SOLUTION + -------- + + A simple recursive solution will work. + + COMPLEXITY + ---------- + + The time complexity is O(n) where n is the number of nodes in the tree. + """ + if not p: + return not q + + if not q: + return not p + + if p.val != q.val: + return False + + return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right) diff --git a/src/leetcode/tree/sum-root-to-leaf-numbers.ts b/src/leetcode/tree/sum-root-to-leaf-numbers.ts deleted file mode 100644 index 7808097..0000000 --- a/src/leetcode/tree/sum-root-to-leaf-numbers.ts +++ /dev/null @@ -1,45 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given the root of a binary tree containing digits from 0 to 9 only. -// -// Each root-to-leaf path in the tree represents a number. -// -// For example, the root-to-leaf path 1 -> 2 -> 3 represents the number 123. -// -// Return the total sum of all root-to-leaf numbers. Test cases are generated so that the answer will fit in a 32-bit integer. -// -// A leaf node is a node with no children. -// -// See {@link https://leetcode.com/problems/sum-root-to-leaf-numbers} -import { TreeNode } from './common/tree-node'; -export { sumNumbers }; - -// SOLUTION: -// -// This is a simple DFS problem. Just keep track of the current number as you traverse the tree. If you reach a leaf, -// add it to the running tally and sum up all the numbers at the end. -function sumNumbers(root: TreeNode | null): number { - const xs: number[] = []; - if (root === null) { - return 0; - } - - function dfs(node: TreeNode | null, sum: number) { - if (node === null) { - return; - } - - // Only add to the running list if we're at a leaf. - const x = sum * 10 + node.val; - if (node.left === null && node.right === null) { - xs.push(x); - return; - } - - dfs(node.left, x); - dfs(node.right, x); - } - - dfs(root, 0); - return xs.reduce((a, b) => a + b, 0); -} diff --git a/src/leetcode/tree/sum_root_to_leaf_numbers.py b/src/leetcode/tree/sum_root_to_leaf_numbers.py new file mode 100644 index 0000000..cbf6ed9 --- /dev/null +++ b/src/leetcode/tree/sum_root_to_leaf_numbers.py @@ -0,0 +1,53 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given the root of a binary tree containing digits from 0 to 9 only. +# +# Each root-to-leaf path in the tree represents a number. +# +# For example, the root-to-leaf path 1 -> 2 -> 3 represents the number 123. +# +# Return the total sum of all root-to-leaf numbers. Test cases are generated so that the answer will fit in a 32-bit +# integer. +# +# A leaf node is a node with no children. +# +# See https://leetcode.com/problems/sum-root-to-leaf-numbers +from leetcode.tree.common.tree_node import TreeNode + + +class Solution: + def sumNumbers(self, root: TreeNode | None) -> int: + """ + SOLUTION + -------- + + This is a simple DFS problem. Just keep track of the current number as you traverse the tree. If you reach a + leaf, add it to the running tally and sum up all the numbers at the end. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of nodes in the tree. + + Space complexity is O(n) for the call stack. + """ + xs: list[int] = [] + + def dfs(node: TreeNode | None, sum: int) -> None: + nonlocal xs + + if not node: + return + + # Only add to the running list if we're at a leaf. + x = sum * 10 + node.val + if not node.left and not node.right: + xs.append(x) + return + + dfs(node.left, x) + dfs(node.right, x) + + dfs(root, 0) + return sum(xs) diff --git a/test/leetcode/tree/same-tree.test.ts b/test/leetcode/tree/same-tree.test.ts deleted file mode 100644 index b9d1ef8..0000000 --- a/test/leetcode/tree/same-tree.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { TreeNode } from '../../src/tree/common/tree-node'; -import { isSameTree } from '../../src/tree/same-tree'; - -describe('same tree', () => { - test('same tree - test case 1', async () => { - expect(isSameTree(null, null)).toBe(true); - }); - - test('same tree - test case 2', async () => { - expect(isSameTree(new TreeNode(10, null, null), new TreeNode(10, null, null))); - }); -}); diff --git a/test/leetcode/tree/same_tree_test.py b/test/leetcode/tree/same_tree_test.py new file mode 100644 index 0000000..9d5ac0e --- /dev/null +++ b/test/leetcode/tree/same_tree_test.py @@ -0,0 +1,16 @@ +from leetcode.tree.common.tree_node import array2tree +from leetcode.tree.same_tree import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.isSameTree(None, None) + + +def test_case_2(): + p = array2tree([10, None, None]) + q = array2tree([10, None, None]) + + assert soln.isSameTree(p, q) diff --git a/test/leetcode/tree/sum-root-to-leaf-numbers.test.ts b/test/leetcode/tree/sum-root-to-leaf-numbers.test.ts deleted file mode 100644 index f25329e..0000000 --- a/test/leetcode/tree/sum-root-to-leaf-numbers.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { TreeNode } from '../../src/tree/common/tree-node'; -import { sumNumbers } from '../../src/tree/sum-root-to-leaf-numbers'; - -describe('sum root to leaf numbers', () => { - test('sum root to leaf numbers - test case 1', async () => { - const tree = new TreeNode(1); - tree.left = new TreeNode(2); - tree.right = new TreeNode(3); - - expect(sumNumbers(tree)).toBe(25); - }); -}); diff --git a/test/leetcode/tree/sum_root_to_leaf_number_test.py b/test/leetcode/tree/sum_root_to_leaf_number_test.py new file mode 100644 index 0000000..f847869 --- /dev/null +++ b/test/leetcode/tree/sum_root_to_leaf_number_test.py @@ -0,0 +1,13 @@ +from leetcode.tree.common.tree_node import TreeNode +from leetcode.tree.sum_root_to_leaf_numbers import Solution + + +soln = Solution() + + +def test_case_1(): + tree = TreeNode(1) + tree.left = TreeNode(2) + tree.right = TreeNode(3) + + assert soln.sumNumbers(tree) == 25 From e3dbb90e5c306ec1992edc21ab34ba6955eabaee Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 20 Mar 2025 14:18:44 -0700 Subject: [PATCH 147/158] most water --- src/leetcode/two_pointers/README.md | 14 ++-- .../two_pointers/container-with-most-water.ts | 54 --------------- .../two_pointers/container_with_most_water.py | 66 +++++++++++++++++++ .../container-with-most-water.test.ts | 7 -- .../container_with_most_water_test.py | 12 ++++ 5 files changed, 85 insertions(+), 68 deletions(-) delete mode 100644 src/leetcode/two_pointers/container-with-most-water.ts create mode 100644 src/leetcode/two_pointers/container_with_most_water.py delete mode 100644 test/leetcode/two_pointers/container-with-most-water.test.ts create mode 100644 test/leetcode/two_pointers/container_with_most_water_test.py diff --git a/src/leetcode/two_pointers/README.md b/src/leetcode/two_pointers/README.md index e7e8f0a..c8b8018 100644 --- a/src/leetcode/two_pointers/README.md +++ b/src/leetcode/two_pointers/README.md @@ -4,20 +4,20 @@ These phrases indicate the two pointer technique might be useful: -- 'non-fixed size subarray with condition' -- 'sorted array', 'sorted list' -- 'find pairs', 'find triplets' -- 'maximize delta', 'minimize delta' +- non-fixed size subarray with condition +- sorted array, sorted list +- find pairs, find triplets +- maximize delta, minimize delta ## Boundaries A note on boundary conditions when you have two pointers that move towards each other. -Use the `left < right` condition if it's not necessary to process every element. For example: +Use the `left < right` condition if its not necessary to process every element. For example: - palindromes checking, because the middle element is guaranteed to match itself. -- container with most water, because the when `left === right`, you have a container that does not hold water. +- container with most water, because the when `left == right`, you have a container that does not hold water. Use the `left <= right` condition when it is necessary to process every element. For example: -- bag of tokens, because you must consider the ramifications of processing the token at `left === right`. +- bag of tokens, because you must consider the ramifications of processing the token at `left == right`. diff --git a/src/leetcode/two_pointers/container-with-most-water.ts b/src/leetcode/two_pointers/container-with-most-water.ts deleted file mode 100644 index 165c4c7..0000000 --- a/src/leetcode/two_pointers/container-with-most-water.ts +++ /dev/null @@ -1,54 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given an integer array height of length n. There are n vertical lines drawn such that the two endpoints of -// the ith line are (i, 0) and (i, height[i]). -// -// Find two lines that together with the x-axis form a container, such that the container contains the most water. -// -// Return the maximum amount of water a container can store. -// -// Notice that you may not slant the container. -// -// See {@link https://leetcode.com/problems/container-with-most-water/} -export { maxArea }; - -// SOLUTION: -// -// Use the two pointers technique to maximize the area. We'll initialize at the left and right sides of the array, -// then advance the pointers inward and update our knowledge of the maximum area. -// -// We'll want to go from outside to inside, because if we have the highest columns on the outside, it's trivial to -// calculate the max area container. The problem is if we have shorter columns over a long width, but we can adjust -// the pointers and max area as we move them inwards. -function maxArea(xs: number[]): number { - let max = 0; - let left = 0; - let right = xs.length - 1; - - // Use left < right because there's no need to process left === right (that's a container with no area). - while (left < right) { - // Set the width to be the difference between left and right pointers. The height should be the smaller of the - // two heights referenced by the pointers. - const width = right - left; - const height = Math.min(xs[left], xs[right]); - - // Update the max area. - max = Math.max(max, width * height); - - // Now advance the pointers. No matter which pointer we choose to advance, the width will decrease. - // - // To attempt to maximize the area, we should advance the pointer that points to the lowest height, in some hopes - // that we will reach a higher height area and get a bigger container. - // - // Note that if we advance a pointer and the height ends up lower, we'll end up with an area calculation that does - // not correspond to an actual container. However, that "phantom container" will have a smaller max area than - // what was previously calculated, and it won't affect the actual result. - if (xs[left] < xs[right]) { - left++; - } else { - right--; - } - } - - return max; -} diff --git a/src/leetcode/two_pointers/container_with_most_water.py b/src/leetcode/two_pointers/container_with_most_water.py new file mode 100644 index 0000000..9b79648 --- /dev/null +++ b/src/leetcode/two_pointers/container_with_most_water.py @@ -0,0 +1,66 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given an integer array height of length n. There are n vertical lines drawn such that the two endpoints of +# the ith line are (i, 0) and (i, height[i]). +# +# Find two lines that together with the x-axis form a container, such that the container contains the most water. +# +# Return the maximum amount of water a container can store. +# +# Notice that you may not slant the container. +# +# See https://leetcode.com/problems/container-with-most-water +class Solution: + def maxArea(self, height: list[int]) -> int: + """ + SOLUTION + -------- + + Use the two pointers technique to maximize the area. We'll initialize at the left and right sides of the array, + then advance the pointers inward and update our knowledge of the maximum area. + + We'll want to go from outside to inside, because if we have the highest columns on the outside, it's trivial to + calculate the max area container. The problem is if we have shorter columns over a long width, but we can adjust + the pointers and max area as we move them inwards. + + To maximize the area, we should advance the pointer that points to the lowest height, in some hopes that we will + reach a higher height area and get a bigger container. + + Note that if we advance a pointer and the height ends up lower, we'll end up with an area calculation that does + not correspond to an actual container. However, that "phantom container" will have a smaller max area than + what was previously calculated, and it won't affect the actual result. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of heights in the array. + + Space complexity is O(1). + """ + max_area = 0 + left = 0 + right = len(height) - 1 + + # Use left < right because there's no need to process left === right (that's a container with no area). + while left < right: + # Set the width to be the difference between left and right pointers. The height should be the smaller of + # the two heights referenced by the pointers. + width = right - left + h = min(height[left], height[right]) + max_area = max(max_area, width * h) + + # Now advance the pointers. No matter which pointer we choose to advance, the width will decrease. + # + # To attempt to maximize the area, we should advance the pointer that points to the lowest height, in some + # hopes that we will reach a higher height area and get a bigger container. + # + # Note that if we advance a pointer and the height ends up lower, we'll end up with an area calculation that + # does not correspond to an actual container. However, that "phantom container" will have a smaller max + # area than what was previously calculated, and it won't affect the actual result. + if height[left] < height[right]: + left += 1 + else: + right -= 1 + + return max_area diff --git a/test/leetcode/two_pointers/container-with-most-water.test.ts b/test/leetcode/two_pointers/container-with-most-water.test.ts deleted file mode 100644 index 8e0866c..0000000 --- a/test/leetcode/two_pointers/container-with-most-water.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { maxArea } from '../../src/two-pointer/container-with-most-water'; - -describe('container with most water', () => { - test('container with most water - test case 1', async () => { - expect(maxArea([1, 8, 6, 2, 5, 4, 8, 3, 7])).toBe(49); - }); -}); diff --git a/test/leetcode/two_pointers/container_with_most_water_test.py b/test/leetcode/two_pointers/container_with_most_water_test.py new file mode 100644 index 0000000..74dda0a --- /dev/null +++ b/test/leetcode/two_pointers/container_with_most_water_test.py @@ -0,0 +1,12 @@ +from leetcode.two_pointers.container_with_most_water import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.maxArea([1, 8, 6, 2, 5, 4, 8, 3, 7]) == 49 + + +def test_case_2(): + assert soln.maxArea([1, 1]) == 1 From 9c7f1385b6002e1c5a3722768553330c380cf3e3 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 20 Mar 2025 14:31:55 -0700 Subject: [PATCH 148/158] longest --- .../two_pointers/longest-common-prefix.ts | 46 --------------- ...ngest-substring-without-repeating-chars.ts | 57 ------------------- .../two_pointers/longest_common_prefix.py | 55 ++++++++++++++++++ .../longest-common-prefix.test.ts | 23 -------- ...-substring-without-repeating-chars.test.ts | 15 ----- .../longest_common_prefix_test.py | 24 ++++++++ 6 files changed, 79 insertions(+), 141 deletions(-) delete mode 100644 src/leetcode/two_pointers/longest-common-prefix.ts delete mode 100644 src/leetcode/two_pointers/longest-substring-without-repeating-chars.ts create mode 100644 src/leetcode/two_pointers/longest_common_prefix.py delete mode 100644 test/leetcode/two_pointers/longest-common-prefix.test.ts delete mode 100644 test/leetcode/two_pointers/longest-substring-without-repeating-chars.test.ts create mode 100644 test/leetcode/two_pointers/longest_common_prefix_test.py diff --git a/src/leetcode/two_pointers/longest-common-prefix.ts b/src/leetcode/two_pointers/longest-common-prefix.ts deleted file mode 100644 index ecf2d82..0000000 --- a/src/leetcode/two_pointers/longest-common-prefix.ts +++ /dev/null @@ -1,46 +0,0 @@ -// DIFFICULTY: EASY -// -// Write a function to find the longest common prefix string amongst an array of strings. -// -// If there is no common prefix, return an empty string "". -// -// See {@link https://leetcode.com/problems/longest-common-prefix/} -export { longestCommonPrefix }; - -// SOLUTION: -// -// Note that the longest prefix can only be, at most, the length of the shortest string. We can start with the first -// string as a guess for the longest prefix, then tighten the bounds of that guess as we look the other strings. -function longestCommonPrefix(all: string[]): string { - if (all.length === 0) { - return ''; - } - - // The longest prefix can be at most the length of the shortest string. Therefore, we can set our best guess of - // the longest prefix to the first string. From there, we can tighten the bounds of the prefix and come up with an - // even shorter string as a result. - let longest = all[0]; - for (let i = 1; i < all.length; i++) { - const current = all[i]; - - // Compare the current string, character by character, against the longest prefix we've found so far. The length - // of matching characters will be used to update our assumption about the longest prefix afterwards. - let j = 0; - while ( - // If the characters at the current position DO NOT match, stop. The longest prefix will become where we have - // stopped. - longest[j] === current[j] && - // If the characters at the current position DO match, but we exceeded the length of either the current string - // or the longest prefix, we have to stop. There are no more characters to compare against each other. - j < Math.min(current.length, longest.length) - ) { - j += 1; - } - - // The length j represents how many characters match between the current prefix and the longest string, so update - // that value. - longest = current.substring(0, j); - } - - return longest; -} diff --git a/src/leetcode/two_pointers/longest-substring-without-repeating-chars.ts b/src/leetcode/two_pointers/longest-substring-without-repeating-chars.ts deleted file mode 100644 index dfc8215..0000000 --- a/src/leetcode/two_pointers/longest-substring-without-repeating-chars.ts +++ /dev/null @@ -1,57 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given a string s, find the length of the longest substring without repeating characters. -// -// See {@link https://leetcode.com/problems/longest-substring-without-repeating-characters/} -export { lengthOfLongestSubstring }; - -// SOLUTION: -// -// The naive way of doing this problem would be to generate all substrings, then check if each of the substrings has -// unique characters, updating a max length as we go along. -// -// However, we can do better than that using the two pointer technique. -// -// Since we only need to know the length of the substring, and not exactly what it is, we don't need to build up the -// string as we loop through the characters. -// -// Instead, we'll maintain a map of character -> index, where the index is the time where we most recently encountered -// that character. We'll use that index, along with the current index, to update our knowledge of what the max length -// substring is. -function lengthOfLongestSubstring(text: string): number { - // Use start as the index of the current substring under consideration. - let start = 0; - let max = 0; - const map = new Map(); - - for (let i = 0; i < text.length; i++) { - const c = text.charAt(i); - - // If we've seen this character before, we'll have to update our start index. This is because our substring can't - // have repeated characters any repeat character forces us to update start value. - if (map.has(c)) { - const j = map.get(c)!; - // It's possible we saw this character a while ago, and our current start index is more accurate. In that case - // the case, leave start alone. - if (start <= j) { - // In the case that we need to refresh the start index, set it past the index of the last seen instance of - // this character (since we are seeing this character now). - start = j + 1; - } - } - - // Consider the current index as the possible end of a substring. If this substring has a longer length than the - // max, update the max. - const end = i; - // The new max should be the difference between start and end, inclusive. - const len = end - start + 1; - if (len > max) { - max = len; - } - - // Finally mark this character's last seen index in our map. - map.set(c, i); - } - - return max; -} diff --git a/src/leetcode/two_pointers/longest_common_prefix.py b/src/leetcode/two_pointers/longest_common_prefix.py new file mode 100644 index 0000000..d632c8c --- /dev/null +++ b/src/leetcode/two_pointers/longest_common_prefix.py @@ -0,0 +1,55 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Write a function to find the longest common prefix string amongst an array of strings. +# +# If there is no common prefix, return an empty string "". +# +# See https://leetcode.com/problems/longest-common-prefix +class Solution: + def longestCommonPrefix(self, all: list[str]) -> str: + """ + SOLUTION + -------- + + Note that the longest prefix can only be, at most, the length of the shortest string. We can start with the + first string as a guess for the longest prefix, then tighten the bounds of that guess as we look the other + strings. + + COMPLEXITY + ---------- + + Time complexity is O(n * m) where n is the number of strings and m is the length of the shortest string. + + Space complexity is O(1). + """ + if not all: + return "" + + # The longest prefix can be at most the length of the shortest string. Therefore, we can set our best guess of + # the longest prefix to the first string. From there, we can tighten the bounds of the prefix and come up with + # an even shorter string as a result. + longest = all[0] + for i in range(1, len(all)): + current = all[i] + + # Compare the current string, character by character, against the longest prefix we've found so far. The + # length of matching characters will be used to update our assumption about the longest prefix afterwards. + j = 0 + while ( + # If the characters at the current position DO match, but we exceeded the length of either the current + # string or the longest prefix, we have to stop. There are no more characters to compare against each + # other. + j < min(len(current), len(longest)) + and + # If the characters at the current position DO NOT match, stop. The longest prefix will become where we + # have stopped. + longest[j] == current[j] + ): + j += 1 + + # The length j represents how many characters match between the current prefix and the longest string, so + # update that value. + longest = current[:j] + + return longest diff --git a/test/leetcode/two_pointers/longest-common-prefix.test.ts b/test/leetcode/two_pointers/longest-common-prefix.test.ts deleted file mode 100644 index 6ce7161..0000000 --- a/test/leetcode/two_pointers/longest-common-prefix.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { longestCommonPrefix } from '../../src/two-pointer/longest-common-prefix'; - -describe('longest common prefix', () => { - test('longest common prefix - test case 1', async () => { - expect(longestCommonPrefix(['ab', 'a'])).toBe('a'); - }); - - test('longest common prefix - test case 2', async () => { - expect(longestCommonPrefix(['a'])).toBe('a'); - }); - - test('longest common prefix - test case 3', async () => { - expect(longestCommonPrefix(['flower', 'flow', 'flight'])).toBe('fl'); - }); - - test('longest common prefix - test case 4', async () => { - expect(longestCommonPrefix(['dog', 'racecar', 'car'])).toBe(''); - }); - - test('longest common prefix - test case 5', async () => { - expect(longestCommonPrefix(['', 'car'])).toBe(''); - }); -}); diff --git a/test/leetcode/two_pointers/longest-substring-without-repeating-chars.test.ts b/test/leetcode/two_pointers/longest-substring-without-repeating-chars.test.ts deleted file mode 100644 index bcbfcf3..0000000 --- a/test/leetcode/two_pointers/longest-substring-without-repeating-chars.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { lengthOfLongestSubstring } from '../../src/two-pointer/longest-substring-without-repeating-chars'; - -describe('longest substring without repeating characters', () => { - test('length of longest substring - test case 4', async () => { - expect(lengthOfLongestSubstring('abcabcbb')).toBe(3); - }); - - test('length of longest substring - test case 5', async () => { - expect(lengthOfLongestSubstring('bbbbb')).toBe(1); - }); - - test('length of longest substring - test case 6', async () => { - expect(lengthOfLongestSubstring('pwwkew')).toBe(3); - }); -}); diff --git a/test/leetcode/two_pointers/longest_common_prefix_test.py b/test/leetcode/two_pointers/longest_common_prefix_test.py new file mode 100644 index 0000000..146b818 --- /dev/null +++ b/test/leetcode/two_pointers/longest_common_prefix_test.py @@ -0,0 +1,24 @@ +from leetcode.two_pointers.longest_common_prefix import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.longestCommonPrefix(["ab", "a"]) == "a" + + +def test_case_2(): + assert soln.longestCommonPrefix(["a"]) == "a" + + +def test_case_3(): + assert soln.longestCommonPrefix(["flower", "flow", "flight"]) == "fl" + + +def test_case_4(): + assert soln.longestCommonPrefix(["dog", "racecar", "car"]) == "" + + +def test_case_5(): + assert soln.longestCommonPrefix(["", "car"]) == "" From a4cf86bdd0a48982dbac3866d8b4b1d96d23d7e1 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 20 Mar 2025 14:34:45 -0700 Subject: [PATCH 149/158] remove dupes --- .../remove-duplicates-from-sorted-array.ts | 51 ---------------- .../remove_duplicates_from_sorted_array.py | 58 +++++++++++++++++++ ...emove-duplicates-from-sorted-array.test.ts | 7 --- ...emove_duplicates_from_sorted_array_test.py | 8 +++ 4 files changed, 66 insertions(+), 58 deletions(-) delete mode 100644 src/leetcode/two_pointers/remove-duplicates-from-sorted-array.ts create mode 100644 src/leetcode/two_pointers/remove_duplicates_from_sorted_array.py delete mode 100644 test/leetcode/two_pointers/remove-duplicates-from-sorted-array.test.ts create mode 100644 test/leetcode/two_pointers/remove_duplicates_from_sorted_array_test.py diff --git a/src/leetcode/two_pointers/remove-duplicates-from-sorted-array.ts b/src/leetcode/two_pointers/remove-duplicates-from-sorted-array.ts deleted file mode 100644 index 4ad62f5..0000000 --- a/src/leetcode/two_pointers/remove-duplicates-from-sorted-array.ts +++ /dev/null @@ -1,51 +0,0 @@ -// DIFFICULTY: EASY -// -// Given an integer array nums sorted in non-decreasing order, remove the duplicates in-place such that each unique -// element appears only once. The relative order of the elements should be kept the same. Then return the number of -// unique elements in nums. -// -// Consider the number of unique elements of nums to be k, to get accepted, you need to do the following things: -// -// Change the array nums such that the first k elements of nums contain the unique elements in the order they were -// present in nums initially. The remaining elements of nums are not important as well as the size of nums. -// -// Return k. -// -// See {@link https://leetcode.com/problems/remove-duplicates-from-sorted-array/} -export { removeDuplicates }; - -// SOLUTION: -// -// Use the two pointer technique to iterate through the array at different speeds. The first pointer moves through the -// array as number. The second pointer, j, will point to the last unique element. -function removeDuplicates(nums: number[]): number { - if (nums.length === 0) { - return 0; - } - - // Initialize the first pointer that moves through the array at normal speed. The first element is always unique. - let i = 1; - - // Initialize the second pointer to move only if we found a unique element. - let j = 0; - - while (i < nums.length) { - const current = nums[i]; - const lastUnique = nums[j]; - - // If the current element is different from the last unique element, then we have a new unique element. So advance - // the unique pointer, then copy the current element to the unique pointer. - if (current !== lastUnique) { - j++; - nums[j] = current; - } - - // Now advance the other pointer to continue through the array. - i++; - } - - // The index j tracks the last unique element, so the number of unique elements is j + 1. Contrast this to the remove - // element problem. - const k = j + 1; - return k; -} diff --git a/src/leetcode/two_pointers/remove_duplicates_from_sorted_array.py b/src/leetcode/two_pointers/remove_duplicates_from_sorted_array.py new file mode 100644 index 0000000..1b9e926 --- /dev/null +++ b/src/leetcode/two_pointers/remove_duplicates_from_sorted_array.py @@ -0,0 +1,58 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given an integer array nums sorted in non-decreasing order, remove the duplicates in-place such that each unique +# element appears only once. The relative order of the elements should be kept the same. Then return the number of +# unique elements in nums. +# +# Consider the number of unique elements of nums to be k, to get accepted, you need to do the following things: +# +# Change the array nums such that the first k elements of nums contain the unique elements in the order they were +# present in nums initially. The remaining elements of nums are not important as well as the size of nums. +# +# Return k. +# +# See https://leetcode.com/problems/remove-duplicates-from-sorted-array +class Solution: + def removeDuplicates(self, nums: list[int]) -> int: + """ + SOLUTION + -------- + + Use the two pointer technique to iterate through the array at different speeds. The first pointer moves through + the array as number. The second pointer, j, will point to the last unique element. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of elements in the array. + + Space complexity is O(1). + """ + if not nums: + return 0 + + # Initialize the first pointer that moves through the array at normal speed. The first element is always + # unique. + i = 1 + + # Initialize the second pointer to move only if we found a unique element. + j = 0 + + while i < len(nums): + current = nums[i] + last_unique = nums[j] + + # If the current element is different from the last unique element, then we have a new unique element. So + # advance the unique pointer, then copy the current element to the unique pointer. + if current != last_unique: + j += 1 + nums[j] = current + + # Now advance the other pointer to continue through the array. + i += 1 + + # The index j tracks the last unique element, so the number of unique elements is j + 1. Contrast this to the + # remove element problem. + k = j + 1 + return k diff --git a/test/leetcode/two_pointers/remove-duplicates-from-sorted-array.test.ts b/test/leetcode/two_pointers/remove-duplicates-from-sorted-array.test.ts deleted file mode 100644 index 520acf3..0000000 --- a/test/leetcode/two_pointers/remove-duplicates-from-sorted-array.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { removeDuplicates } from '../../src/two-pointer/remove-duplicates-from-sorted-array'; - -describe('remove duplicates from sorted array', () => { - test('remove duplicates from sorted array - test case 1', () => { - expect(removeDuplicates([1, 1, 2])).toBe(2); - }); -}); diff --git a/test/leetcode/two_pointers/remove_duplicates_from_sorted_array_test.py b/test/leetcode/two_pointers/remove_duplicates_from_sorted_array_test.py new file mode 100644 index 0000000..7a9a7e7 --- /dev/null +++ b/test/leetcode/two_pointers/remove_duplicates_from_sorted_array_test.py @@ -0,0 +1,8 @@ +from leetcode.two_pointers.remove_duplicates_from_sorted_array import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.removeDuplicates([1, 1, 2]) == 2 From 92b5f658c92dd49095b049abff1fdff00373b2df Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 20 Mar 2025 14:38:21 -0700 Subject: [PATCH 150/158] remove elements --- src/leetcode/two_pointers/remove-element.ts | 44 ---------------- src/leetcode/two_pointers/remove_element.py | 52 +++++++++++++++++++ .../two_pointers/remove-element.test.ts | 11 ---- 3 files changed, 52 insertions(+), 55 deletions(-) delete mode 100644 src/leetcode/two_pointers/remove-element.ts create mode 100644 src/leetcode/two_pointers/remove_element.py delete mode 100644 test/leetcode/two_pointers/remove-element.test.ts diff --git a/src/leetcode/two_pointers/remove-element.ts b/src/leetcode/two_pointers/remove-element.ts deleted file mode 100644 index 1d4f11c..0000000 --- a/src/leetcode/two_pointers/remove-element.ts +++ /dev/null @@ -1,44 +0,0 @@ -// DIFFICULTY: EASY -// -// Given an integer array nums and an integer val, remove all occurrences of val in nums in-place. The order of the -// elements may be changed. Then return the number of elements in nums which are not equal to val. -// -// Consider the number of elements in nums which are not equal to val be k, to get accepted, you need to do the -// following things: -// -// - Change the array nums such that the first k elements of nums contain the elements which are not equal to val. -// The remaining elements of nums are not important as well as the size of nums. -// - Return k. -// -// See {@link https://leetcode.com/problems/remove-element} -export { removeElement }; - -// SOLUTION: -// -// The solution is very similar to the remove duplicates from sorted array problem. We just need to maintain two -// pointers; one that moves through the array at normal speed and another that moves only when we find an element that -// is not equal to val. -function removeElement(nums: number[], val: number): number { - // Initialize the first pointer that moves through the array at normal speed. The first element is always unique. - let i = 0; - - // Initialize the second pointer to move only if we found an element not equal to val. - let j = 0; - - while (i < nums.length) { - const current = nums[i]; - - // If we found an element that's not equal to val, we should keep it. So copy it to the j pointer. - if (current !== val) { - nums[j] = current; - j++; - } - - // Now advance the other pointer to continue through the array. - i++; - } - - // The index j represents the number of elements that are not equal to val. That means j === k, so we can return it - // directly. Contrast this with remove duplicates from sorted array. - return j; -} diff --git a/src/leetcode/two_pointers/remove_element.py b/src/leetcode/two_pointers/remove_element.py new file mode 100644 index 0000000..d437b67 --- /dev/null +++ b/src/leetcode/two_pointers/remove_element.py @@ -0,0 +1,52 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given an integer array nums and an integer val, remove all occurrences of val in nums in-place. The order of the +# elements may be changed. Then return the number of elements in nums which are not equal to val. +# +# Consider the number of elements in nums which are not equal to val be k, to get accepted, you need to do the +# following things: +# +# - Change the array nums such that the first k elements of nums contain the elements which are not equal to val. +# The remaining elements of nums are not important as well as the size of nums. +# - Return k. +# +# See https://leetcode.com/problems/remove-element +class Solution: + def removeElement(self, nums: list[int], val: int) -> int: + """ + SOLUTION + -------- + + The solution is very similar to the remove duplicates from sorted array problem. We just need to maintain two + pointers; one that moves through the array at normal speed and another that moves only when we find an element + that is not equal to val. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of elements in the array. + + Space complexity is O(1). + """ + # Initialize the first pointer that moves through the array at normal speed. The first element is always + # unique. + i = 0 + + # Initialize the second pointer to move only if we found an element not equal to val. + j = 0 + + while i < len(nums): + current = nums[i] + + # If we found an element that's not equal to val, we should keep it. So copy it to the j pointer. + if current != val: + nums[j] = current + j += 1 + + # Now advance the other pointer to continue through the array. + i += 1 + + # The index j represents the number of elements that are not equal to val. That means j === k, so we can return + # it directly. Contrast this with remove duplicates from sorted array. + return j diff --git a/test/leetcode/two_pointers/remove-element.test.ts b/test/leetcode/two_pointers/remove-element.test.ts deleted file mode 100644 index fdec913..0000000 --- a/test/leetcode/two_pointers/remove-element.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { removeElement } from '../../src/two-pointer/remove-element'; - -describe('remove element', () => { - test('remove element - test case 1', () => { - expect(removeElement([3, 2, 2, 3], 3)).toBe(2); - }); - - test('remove element - test case 2', () => { - expect(removeElement([0, 1, 2, 2, 3, 0, 4, 2], 2)).toBe(5); - }); -}); From 7b198a202fb900f67948c5a810fd1ff8259a132c Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 20 Mar 2025 14:42:09 -0700 Subject: [PATCH 151/158] str comp --- .../two_pointers/string-compression.ts | 54 ----------------- .../two_pointers/string_compression.py | 59 +++++++++++++++++++ .../two_pointers/string-compression.test.ts | 11 ---- .../two_pointers/string_compression_test.py | 12 ++++ 4 files changed, 71 insertions(+), 65 deletions(-) delete mode 100644 src/leetcode/two_pointers/string-compression.ts create mode 100644 src/leetcode/two_pointers/string_compression.py delete mode 100644 test/leetcode/two_pointers/string-compression.test.ts create mode 100644 test/leetcode/two_pointers/string_compression_test.py diff --git a/src/leetcode/two_pointers/string-compression.ts b/src/leetcode/two_pointers/string-compression.ts deleted file mode 100644 index e8c4c3f..0000000 --- a/src/leetcode/two_pointers/string-compression.ts +++ /dev/null @@ -1,54 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an array of characters chars, compress it using the following algorithm: -// -// Begin with an empty string s. For each group of consecutive repeating characters in chars: -// -// - If the group's length is 1, append the character to s. -// - Otherwise, append the character followed by the group's length. -// -// The compressed string s should not be returned separately, but instead, be stored in the input character array chars. -// Note that group lengths that are 10 or longer will be split into multiple characters in chars. -// -// After you are done modifying the input array, return the new length of the array. -// -// You must write an algorithm that uses only constant extra space. -// -// See {@link https://leetcode.com/problems/string-compression/} -export { compress }; - -// SOLUTION: -// -// Because we cannot use extra space, we will need to read and write to the array at the same time. We can use the -// two pointer approach for doing so. -function compress(cs: string[]): number { - let read = 0; - let write = 0; - - while (read < cs.length) { - // Keep track of the current character and the number of times it has appeared. - const c = cs[read]; - let n = 0; - - // Consume the characters until we reach a point where the character differs. - while (read < cs.length && cs[read] === c) { - read++; - n++; - } - - // Write the current character and the number of times it appears. We'll always have enough room because we'll - // only need to write more than one digit if we have 10 or more characters. - cs[write] = c; - write++; - - // Only write digits if the count > 1; otherwise just leave the character as is. - if (n > 1) { - for (const digit of n.toString()) { - cs[write] = digit; - write++; - } - } - } - - return write; -} diff --git a/src/leetcode/two_pointers/string_compression.py b/src/leetcode/two_pointers/string_compression.py new file mode 100644 index 0000000..ab14ba5 --- /dev/null +++ b/src/leetcode/two_pointers/string_compression.py @@ -0,0 +1,59 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an array of characters chars, compress it using the following algorithm: +# +# Begin with an empty string s. For each group of consecutive repeating characters in chars: +# +# - If the group's length is 1, append the character to s. +# - Otherwise, append the character followed by the group's length. +# +# The compressed string s should not be returned separately, but instead, be stored in the input character array chars. +# Note that group lengths that are 10 or longer will be split into multiple characters in chars. +# +# After you are done modifying the input array, return the new length of the array. +# +# You must write an algorithm that uses only constant extra space. +# +# See https://leetcode.com/problems/string-compression +class Solution: + def compress(self, cs: list[str]) -> int: + """ + SOLUTION + -------- + + Because we cannot use extra space, we will need to read and write to the array at the same time. We can use the + two pointer approach for doing so. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of characters in the array. + + Space complexity is O(1). + """ + read = 0 + write = 0 + + while read < len(cs): + # Keep track of the current character and the number of times it has appeared. + c = cs[read] + n = 0 + + # Consume the characters until we reach a point where the character differs. + while read < len(cs) and cs[read] == c: + read += 1 + n += 1 + + # Write the current character and the number of times it appears. We'll always have enough room because + # we'll only need to write more than one digit if we have 10 or more characters. + cs[write] = c + write += 1 + + # Only write digits if the count > 1; otherwise just leave the character as is. + if n > 1: + for digit in str(n): + cs[write] = digit + write += 1 + + return write diff --git a/test/leetcode/two_pointers/string-compression.test.ts b/test/leetcode/two_pointers/string-compression.test.ts deleted file mode 100644 index 4813c75..0000000 --- a/test/leetcode/two_pointers/string-compression.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { compress } from '../../src/two-pointer/string-compression'; - -describe('string compression', () => { - test('string compression - test case 1', async () => { - const cs = ['a', 'a', 'b', 'b', 'c', 'c', 'c']; - - const count = compress(cs); - - expect(cs.slice(0, count)).toStrictEqual(['a', '2', 'b', '2', 'c', '3']); - }); -}); diff --git a/test/leetcode/two_pointers/string_compression_test.py b/test/leetcode/two_pointers/string_compression_test.py new file mode 100644 index 0000000..6404364 --- /dev/null +++ b/test/leetcode/two_pointers/string_compression_test.py @@ -0,0 +1,12 @@ +from leetcode.two_pointers.string_compression import Solution + + +soln = Solution() + + +def test_case_1(): + cs = ["a", "a", "b", "b", "c", "c", "c"] + + count = soln.compress(cs) + + assert cs[0:count] == ["a", "2", "b", "2", "c", "3"] From 47f95735a9d51293c75599be7b2e051b2c66b46a Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 20 Mar 2025 14:45:07 -0700 Subject: [PATCH 152/158] 3sum closest --- .../two_pointers/three-sum-closest.ts | 59 ----------------- .../two_pointers/three_sum_closest.py | 66 +++++++++++++++++++ .../two_pointers/three-sum-closest.test.ts | 11 ---- .../two_pointers/three_sum_closest_test.py | 12 ++++ 4 files changed, 78 insertions(+), 70 deletions(-) delete mode 100644 src/leetcode/two_pointers/three-sum-closest.ts create mode 100644 src/leetcode/two_pointers/three_sum_closest.py delete mode 100644 test/leetcode/two_pointers/three-sum-closest.test.ts create mode 100644 test/leetcode/two_pointers/three_sum_closest_test.py diff --git a/src/leetcode/two_pointers/three-sum-closest.ts b/src/leetcode/two_pointers/three-sum-closest.ts deleted file mode 100644 index 8d3fb58..0000000 --- a/src/leetcode/two_pointers/three-sum-closest.ts +++ /dev/null @@ -1,59 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an integer array nums of length n and an integer target, find three integers in nums such that the sum is -// closest to target. -// -// Return the sum of the three integers. -// -// You may assume that each input would have exactly one solution. -// -// See {@link https://leetcode.com/problems/3sum-closest/} -export { threeSumClosest }; - -// SOLUTION: -// -// We'll use the two pointer technique to zero in on the target sum while iterating through the array. To make -// calculations easier, we have to sort the array. -function threeSumClosest(xs: number[], target: number): number { - xs.sort((a, b) => a - b); - - let closestSum = Infinity; - let closestValues: number[] = []; - - for (let i = 0; i < xs.length - 2; i++) { - // Use the two pointer technique to find the closest triple. We will move the left pointer forward, or move the - // right pointer backwards as we attempt to get close to our target sum. - // - // Initialize the left pointer to the right of i, and initialize the right pointer at the end of the array. We - // will begin moving the left and right pointers closer to each other as we attempt to compute the triple. - let left = i + 1; - let right = xs.length - 1; - - while (left < right) { - // If the current sum is closer than what we have, update the closest sum and the triple. - const currentSum = xs[i] + xs[left] + xs[right]; - const currentDistance = Math.abs(currentSum - target); - const closestDistance = Math.abs(closestSum - target); - if (currentDistance < closestDistance) { - closestSum = currentSum; - closestValues = [xs[i], xs[left], xs[right]]; - } - - // If we've matched the target sum, just return right away. - if (closestSum === target) { - return closestValues.reduce((a, b) => a + b); - } - - // Update the left and right pointers, and try again. Because we've sorted the array, we can move the pointers - // accordingly. If the sum is too small, move the left pointer forwards. If the sum is too big, move the - // the right pointer backwards. - if (currentSum < target) { - left++; - } else { - right--; - } - } - } - - return closestValues.reduce((a, b) => a + b); -} diff --git a/src/leetcode/two_pointers/three_sum_closest.py b/src/leetcode/two_pointers/three_sum_closest.py new file mode 100644 index 0000000..1bc0485 --- /dev/null +++ b/src/leetcode/two_pointers/three_sum_closest.py @@ -0,0 +1,66 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an integer array nums of length n and an integer target, find three integers in nums such that the sum is +# closest to target. +# +# Return the sum of the three integers. +# +# You may assume that each input would have exactly one solution. +# +# See https://leetcode.com/problems/3sum-closest +import math + + +class Solution: + def threeSumClosest(self, xs: list[int], target: int) -> int: + """ + SOLUTION + -------- + + We'll use the two pointer technique to zero in on the target sum while iterating through the array. To make + calculations easier, we have to sort the array. + + COMPLEXITY + ---------- + + Time complexity is O(n^2) where n is the number of elements in the array. + + Space complexity is O(1). + """ + xs.sort() + + closest_sum = math.inf + closest_values = [] + + for i in range(len(xs) - 2): + # Use the two pointer technique to find the closest triple. We will move the left pointer forward, or move + # the right pointer backwards as we attempt to get close to our target sum. + # + # Initialize the left pointer to the right of i, and initialize the right pointer at the end of the array. + # We will begin moving the left and right pointers closer to each other as we attempt to compute the triple. + left = i + 1 + right = len(xs) - 1 + + while left < right: + # If the current sum is closer than what we have, update the closest sum and the triple. + current_sum = xs[i] + xs[left] + xs[right] + current_distance = abs(current_sum - target) + closest_distance = abs(closest_sum - target) + if current_distance < closest_distance: + closest_sum = current_sum + closest_values = [xs[i], xs[left], xs[right]] + + # If we've matched the target sum, just return right away. + if closest_sum == target: + return sum(closest_values) + + # Update the left and right pointers, and try again. Because we've sorted the array, we can move the + # pointers accordingly. If the sum is too small, move the left pointer forwards. If the sum is too + # big, move the the right pointer backwards. + if current_sum < target: + left += 1 + else: + right -= 1 + + return sum(closest_values) diff --git a/test/leetcode/two_pointers/three-sum-closest.test.ts b/test/leetcode/two_pointers/three-sum-closest.test.ts deleted file mode 100644 index 6422080..0000000 --- a/test/leetcode/two_pointers/three-sum-closest.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { threeSumClosest } from '../../src/two-pointer/three-sum-closest'; - -describe('three sum closest', () => { - test('three sum closest - test case 1', async () => { - expect(threeSumClosest([-1, 2, 1, -4], 1)).toStrictEqual(2); - }); - - test('three sum closest - test case 2', async () => { - expect(threeSumClosest([0, 0, 0], 1)).toStrictEqual(0); - }); -}); diff --git a/test/leetcode/two_pointers/three_sum_closest_test.py b/test/leetcode/two_pointers/three_sum_closest_test.py new file mode 100644 index 0000000..5dc8efe --- /dev/null +++ b/test/leetcode/two_pointers/three_sum_closest_test.py @@ -0,0 +1,12 @@ +from leetcode.two_pointers.three_sum_closest import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.threeSumClosest([-1, 2, 1, -4], 1) == 2 + + +def test_case_2(): + assert soln.threeSumClosest([1, 1, 1, 0], -100) == 2 From 113e5ccb957c4c2b69896e43618b590aaa5810cb Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 20 Mar 2025 14:51:21 -0700 Subject: [PATCH 153/158] 3sum --- src/leetcode/two_pointers/three-sum.ts | 92 ------------------- src/leetcode/two_pointers/three_sum.py | 93 ++++++++++++++++++++ test/leetcode/two_pointers/three-sum.test.ts | 11 --- test/leetcode/two_pointers/three_sum_test.py | 12 +++ 4 files changed, 105 insertions(+), 103 deletions(-) delete mode 100644 src/leetcode/two_pointers/three-sum.ts create mode 100644 src/leetcode/two_pointers/three_sum.py delete mode 100644 test/leetcode/two_pointers/three-sum.test.ts create mode 100644 test/leetcode/two_pointers/three_sum_test.py diff --git a/src/leetcode/two_pointers/three-sum.ts b/src/leetcode/two_pointers/three-sum.ts deleted file mode 100644 index de87e95..0000000 --- a/src/leetcode/two_pointers/three-sum.ts +++ /dev/null @@ -1,92 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that -// i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0. -// -// Notice that the solution set must not contain duplicate triplets. -// -// See {@link https://leetcode.com/problems/3sum/} -export { threeSum }; - -// SOLUTION: -// -// The two sum problem can be solved with a two pointer approach. This can be solved with a similar approach, but -// you'll need to apply the two pointer approach to every element in the array. -function threeSum(xs: number[]) { - const result: number[][] = []; - - // Sorting will help us more efficiently use the sliding window approach to find the triplets that sum to 0. - // - // Note that xs.sort() will sort by string value. So if you have negative numbers, something like [-1, -2, 0] is - // considered "sorted". To fix this, make sure to explicitly provide a compare function. - xs.sort((a, b) => a - b); - - // For each of the elements we will use a sliding window approach to find the other two elements that will sum up - // to 0. - // - // There are two ways we can think of this: we can think of our current element as element b, then set the left - // point to 0 and the right pointer to the length of the array - 1. However, if we do this, we will reconsider - // elements we've already seen as the pointer advances. - // - // Instead, we'll consider the current element as a, then b starts at left = i + 1, and c starts at - // right = length - 1. - for (let i = 0; i < xs.length - 2; i++) { - // Because we can't have duplicate triples in the result, we should just skip over any duplicates. - if (i > 0 && xs[i] === xs[i - 1]) { - continue; - } - - // For each element a, use the two pointers technique to find (b, c) such that a + b + c = 0. - const a = xs[i]; - - // We cannot have duplicate triples in the result. We can do this by setting the left pointer to 0, and the right - // pointer to the last element, tightening the bounds as we consider sums. However, this will reconsider - // duplicate triples as i advances. We would need a set to dedupe the triples. - // - // Starting the left pointer at i + 1 avoids this problem. - let left = i + 1; - let right = xs.length - 1; - while (left < right) { - const b = xs[left]; - const c = xs[right]; - const sum = a + b + c; - - // If the sum is less than 0, that means we need to increase the sum, so advance the left pointer. - if (sum < 0) { - left++; - continue; - } - - // If the sum is greater than 0, that means we need to decrease the sum, so advance the right pointer. - if (sum > 0) { - right--; - continue; - } - - // If the sum is the target of 0, add the triple to the result array. However, there may yet still be more - // triplets we haven't found with our pointers! - // - // To find them, we should keep advancing the pointers past any dupes (as long as they are in range). - if (sum === 0) { - result.push([a, b, c]); - - // Advance the left pointer to skip any dupes of b. - while (xs[left + 1] === xs[left] && left < right) { - left++; - } - - // Advance the right pointer to skip any dupes of c. - while (xs[right - 1] === xs[right] && left < right) { - right--; - } - - // Advance both pointers to consider the next potential sum. We need to advance here and not before doing - // the while loops because advancing could have put us into a dupe! - left++; - right--; - } - } - } - - return result; -} diff --git a/src/leetcode/two_pointers/three_sum.py b/src/leetcode/two_pointers/three_sum.py new file mode 100644 index 0000000..9ad48b2 --- /dev/null +++ b/src/leetcode/two_pointers/three_sum.py @@ -0,0 +1,93 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that +# i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0. +# +# Notice that the solution set must not contain duplicate triplets. +# +# See https://leetcode.com/problems/3sum +class Solution: + def threeSum(self, xs: list[int]) -> list[list[int]]: + """ + SOLUTION + -------- + + The two sum problem can be solved with a two pointer approach. This can be solved with a similar approach, but + you'll need to apply the two pointer approach to every element in the array. + + COMPLEXITY + ---------- + + Time complexity is O(n^2) where n is the number of elements in the array. + + Space complexity is O(1). + """ + result: list[list[int]] = [] + + # Sorting will help us more efficiently use the sliding window approach to find the triplets that sum to 0. + # + # Note that xs.sort() will sort by string value. So if you have negative numbers, something like [-1, -2, 0] is + # considered "sorted". To fix this, make sure to explicitly provide a compare function. + xs.sort() + + # For each of the elements we will use a sliding window approach to find the other two elements that will sum up + # to 0. + # + # There are two ways we can think of this: we can think of our current element as element b, then set the left + # point to 0 and the right pointer to the length of the array - 1. However, if we do this, we will reconsider + # elements we've already seen as the pointer advances. + # + # Instead, we'll consider the current element as a, then b starts at left = i + 1, and c starts at + # right = length - 1. + for i in range(len(xs) - 2): + # Because we can't have duplicate triples in the result, we should just skip over any duplicates. + if i > 0 and xs[i] == xs[i - 1]: + continue + + # For each element a, use the two pointers technique to find (b, c) such that a + b + c = 0. + a = xs[i] + + # We cannot have duplicate triples in the result. We can do this by setting the left pointer to 0, and the + # right pointer to the last element, tightening the bounds as we consider sums. However, this will + # reconsider duplicate triples as i advances. We would need a set to dedupe the triples. + # + # Starting the left pointer at i + 1 avoids this problem. + left = i + 1 + right = len(xs) - 1 + while left < right: + b = xs[left] + c = xs[right] + total = a + b + c + + # If the sum is less than 0, that means we need to increase the sum, so advance the left pointer. + if total < 0: + left += 1 + continue + + # If the sum is greater than 0, that means we need to decrease the sum, so advance the right pointer. + if total > 0: + right -= 1 + continue + + # If the sum is the target of 0, add the triple to the result array. However, there may yet still be + # more triplets we haven't found with our pointers! + # + # To find them, we should keep advancing the pointers past any dupes (as long as they are in range). + if total == 0: + result.append([a, b, c]) + + # Advance the left pointer to skip any dupes of b. + while left < right and xs[left + 1] == xs[left]: + left += 1 + + # Advance the right pointer to skip any dupes of c. + while left < right and xs[right - 1] == xs[right]: + right -= 1 + + # Advance both pointers to consider the next potential sum. We need to advance here and not before + # doing the while loops because advancing could have put us into a dupe! + left += 1 + right -= 1 + + return result diff --git a/test/leetcode/two_pointers/three-sum.test.ts b/test/leetcode/two_pointers/three-sum.test.ts deleted file mode 100644 index 2c529a2..0000000 --- a/test/leetcode/two_pointers/three-sum.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { threeSum } from '../../src/two-pointer/three-sum'; - -describe('three sum', () => { - test('three sum - test case 1', async () => { - expect(threeSum([-1, 0, 1, 2, -1, -4])).toMatchSnapshot(); - }); - - test('three sum - test case 2', async () => { - expect(threeSum([-1, 0, 1, 2, -1, -4, -2, -3, 3, 0, 4])).toMatchSnapshot(); - }); -}); diff --git a/test/leetcode/two_pointers/three_sum_test.py b/test/leetcode/two_pointers/three_sum_test.py new file mode 100644 index 0000000..5ebd7a5 --- /dev/null +++ b/test/leetcode/two_pointers/three_sum_test.py @@ -0,0 +1,12 @@ +from leetcode.two_pointers.three_sum import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.threeSum([-1, 0, 1, 2, -1, -4]) == [[-1, -1, 2], [-1, 0, 1]] + + +def test_case_2(): + assert soln.threeSum([]) == [] From d594a04c605e9d23766778108f8961b6d2edbef0 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 20 Mar 2025 14:55:17 -0700 Subject: [PATCH 154/158] trapping --- .../two_pointers/trapping-rain-water.ts | 63 ---------------- .../two_pointers/trapping_rain_water.py | 71 +++++++++++++++++++ .../two_pointers/trapping-rain-water.test.ts | 15 ---- .../two_pointers/trapping_rain_water_test.py | 16 +++++ 4 files changed, 87 insertions(+), 78 deletions(-) delete mode 100644 src/leetcode/two_pointers/trapping-rain-water.ts create mode 100644 src/leetcode/two_pointers/trapping_rain_water.py delete mode 100644 test/leetcode/two_pointers/trapping-rain-water.test.ts create mode 100644 test/leetcode/two_pointers/trapping_rain_water_test.py diff --git a/src/leetcode/two_pointers/trapping-rain-water.ts b/src/leetcode/two_pointers/trapping-rain-water.ts deleted file mode 100644 index a24fcf6..0000000 --- a/src/leetcode/two_pointers/trapping-rain-water.ts +++ /dev/null @@ -1,63 +0,0 @@ -// DIFFICULTY: HARD -// -// Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water -// it can trap after raining. -// -// See {@link https://leetcode.com/problems/trapping-rain-water/} -export { trap }; - -// SOLUTION: -// -// Note that for this question, the leftmost element of the elevation map doesn't trap any water, because there is no -// land/bar to the left of the leftmost element. -// -// Similarly, for the rightmost element, any depression of the land/bar doesn't trap any water because there isn't -// any elevation to the right of the rightmost element. -// -// To solve this we can use the two pointers technique to compute trapped rainwater on a pointer by pointer basis, -// advancing the pointers -function trap(elevationMap: number[]): number { - // The difference between the lowest height and the smaller of the max height from the left and right sides - // contributes to that many units of trapped rainwater AT THE CURRENT INDEX. - // - // For example, a current elevation of zero with a max elevation of 4 and 6 on the left and right sides respectively - // will hold 4 units of trapped rainwater. We'll use this information to compute rainwater at each index. - let trapped = 0; - - // Track the maximum height on the left side and right side, based on where our pointers land. The smaller of these - // two values will be the height we will use to compute trapped rainwater (since any rainwater over the smaller - // value will spill out). - let lmax = 0; - let rmax = 0; - let max = 0; - - // Use the two pointers technique to compute the trapped rainwater. - let left = 0; - let right = elevationMap.length - 1; - while (left < right) { - // Update the left and right max values, then compute the smaller of these two values to figure out the amount - // of rainwater we can actually trap. - lmax = Math.max(lmax, elevationMap[left]); - rmax = Math.max(rmax, elevationMap[right]); - - // This is the max amount we can trap with an elevation of zero. We compute the elevation next to figure out how - // much is really trapped. - max = Math.min(lmax, rmax); - - // Compute the trapped rainwater from one of the pointers, then advance it. - // - // Choose to advance the pointer associated with the smaller height inward, so that we have the opportunity - // to move to a higher elevation and trap more water. - if (elevationMap[left] < elevationMap[right]) { - const elevation = elevationMap[left]; - trapped += max - elevation; - left++; - } else { - const elevation = elevationMap[right]; - trapped += max - elevation; - right--; - } - } - - return trapped; -} diff --git a/src/leetcode/two_pointers/trapping_rain_water.py b/src/leetcode/two_pointers/trapping_rain_water.py new file mode 100644 index 0000000..8352f93 --- /dev/null +++ b/src/leetcode/two_pointers/trapping_rain_water.py @@ -0,0 +1,71 @@ +# DIFFICULTY: HARD +# ---------------- +# +# Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water +# it can trap after raining. +# +# See https://leetcode.com/problems/trapping-rain-water +class Solution: + def trap(self, elevation_map: list[int]) -> int: + """ + SOLUTION + -------- + + Note that for this question, the leftmost element of the elevation map doesn't trap any water, because there is + no land/bar to the left of the leftmost element. + + Similarly, for the rightmost element, any depression of the land/bar doesn't trap any water because there isn't + any elevation to the right of the rightmost element. + + To solve this we can use the two pointers technique to compute trapped rainwater on a pointer by pointer basis, + advancing the pointers. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of elements in the elevation map. + + Space complexity is O(1). + """ + # The difference between the lowest height and the smaller of the max height from the left and right sides + # contributes to that many units of trapped rainwater AT THE CURRENT INDEX. + # + # For example, a current elevation of zero with a max elevation of 4 and 6 on the left and right sides + # respectively will hold 4 units of trapped rainwater. We'll use this information to compute rainwater at each + # index. + trapped = 0 + + # Track the maximum height on the left side and right side, based on where our pointers land. The smaller of + # these two values will be the height we will use to compute trapped rainwater (since any rainwater over the + # smaller value will spill out). + left_max = 0 + right_max = 0 + max_value = 0 + + # Use the two pointers technique to compute the trapped rainwater. + left = 0 + right = len(elevation_map) - 1 + while left < right: + # Update the left and right max values, then compute the smaller of these two values to figure out the + # amount of rainwater we can actually trap. + left_max = max(left_max, elevation_map[left]) + right_max = max(right_max, elevation_map[right]) + + # This is the max amount we can trap with an elevation of zero. We compute the elevation next to figure out + # how much is really trapped. + max_value = min(left_max, right_max) + + # Compute the trapped rainwater from one of the pointers, then advance it. + # + # Choose to advance the pointer associated with the smaller height inward, so that we have the opportunity + # to move to a higher elevation and trap more water. + if elevation_map[left] < elevation_map[right]: + elevation = elevation_map[left] + trapped += max_value - elevation + left += 1 + else: + elevation = elevation_map[right] + trapped += max_value - elevation + right -= 1 + + return trapped diff --git a/test/leetcode/two_pointers/trapping-rain-water.test.ts b/test/leetcode/two_pointers/trapping-rain-water.test.ts deleted file mode 100644 index dfd60ed..0000000 --- a/test/leetcode/two_pointers/trapping-rain-water.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { trap } from '../../src/two-pointer/trapping-rain-water'; - -describe('trapping rain water', () => { - test('trapping rain water - test case 1', async () => { - expect(trap([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1])).toBe(6); - }); - - test('trapping rain water - test case 2', async () => { - expect(trap([0, 1, 1, 0])).toBe(0); - }); - - test('trapping rain water - test case 3', async () => { - expect(trap([0, 1, 0, 1, 0])).toBe(1); - }); -}); diff --git a/test/leetcode/two_pointers/trapping_rain_water_test.py b/test/leetcode/two_pointers/trapping_rain_water_test.py new file mode 100644 index 0000000..3100c02 --- /dev/null +++ b/test/leetcode/two_pointers/trapping_rain_water_test.py @@ -0,0 +1,16 @@ +from leetcode.two_pointers.trapping_rain_water import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.trap([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]) == 6 + + +def test_case_2(): + assert soln.trap([4, 2, 0, 3, 2, 5]) == 9 + + +def test_case_3(): + assert soln.trap([4, 2, 3]) == 1 From dd81d6786417fadfe99bfe3d6bca8976870a115f Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 20 Mar 2025 14:57:26 -0700 Subject: [PATCH 155/158] 2sum --- .../two-sum-input-array-sorted.ts | 40 ----------------- .../two_sum_ii_input_array_sorted.py | 45 +++++++++++++++++++ .../two-sum-input-array-sorted.test.ts | 15 ------- .../two_sum_ii_input_array_sorted_test.py | 12 +++++ 4 files changed, 57 insertions(+), 55 deletions(-) delete mode 100644 src/leetcode/two_pointers/two-sum-input-array-sorted.ts create mode 100644 src/leetcode/two_pointers/two_sum_ii_input_array_sorted.py delete mode 100644 test/leetcode/two_pointers/two-sum-input-array-sorted.test.ts create mode 100644 test/leetcode/two_pointers/two_sum_ii_input_array_sorted_test.py diff --git a/src/leetcode/two_pointers/two-sum-input-array-sorted.ts b/src/leetcode/two_pointers/two-sum-input-array-sorted.ts deleted file mode 100644 index f3b6284..0000000 --- a/src/leetcode/two_pointers/two-sum-input-array-sorted.ts +++ /dev/null @@ -1,40 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given a 1-indexed array of integers numbers that is already sorted in non-decreasing order, find two numbers such -// that they add up to a specific target number. Let these two numbers be numbers[index1] and numbers[index2] where -// 1 <= index1 < index2 <= numbers.length. -// -// Return the indices of the two numbers, index1 and index2, added by one as an integer array [index1, index2] of -// length 2. -// -// The tests are generated such that there is exactly one solution. You may not use the same element twice. -// -// Your solution must use only constant extra space. -// -// See {@link https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/} -export { twoSum }; - -// SOLUTION: -// -// Use the two pointer approach to narrow in on the target sum from both the left and the right. -function twoSum(xs: number[], target: number) { - let left = 0; - let right = xs.length - 1; - - while (left < right) { - const sum = xs[left] + xs[right]; - if (sum === target) { - return [left + 1, right + 1]; - } - - if (sum < target) { - left++; - } - - if (sum > target) { - right--; - } - } - - return []; -} diff --git a/src/leetcode/two_pointers/two_sum_ii_input_array_sorted.py b/src/leetcode/two_pointers/two_sum_ii_input_array_sorted.py new file mode 100644 index 0000000..c2b8584 --- /dev/null +++ b/src/leetcode/two_pointers/two_sum_ii_input_array_sorted.py @@ -0,0 +1,45 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given a 1-indexed array of integers numbers that is already sorted in non-decreasing order, find two numbers such +# that they add up to a specific target number. Let these two numbers be numbers[index1] and numbers[index2] where +# 1 <= index1 < index2 <= numbers.length. +# +# Return the indices of the two numbers, index1 and index2, added by one as an integer array [index1, index2] of +# length 2. +# +# The tests are generated such that there is exactly one solution. You may not use the same element twice. +# +# Your solution must use only constant extra space. +# +# See https://leetcode.com/problems/two-sum-ii-input-array-is-sorted +class Solution: + def twoSum(self, numbers: list[int], target: int) -> list[int]: + """ + SOLUTION + -------- + + Use the two pointer approach to narrow in on the target sum from both the left and the right. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of elements in the array. + + Space complexity is O(1). + """ + left = 0 + right = len(numbers) - 1 + + while left < right: + sum = numbers[left] + numbers[right] + if sum == target: + return [left + 1, right + 1] + + if sum < target: + left += 1 + + if sum > target: + right -= 1 + + return [] diff --git a/test/leetcode/two_pointers/two-sum-input-array-sorted.test.ts b/test/leetcode/two_pointers/two-sum-input-array-sorted.test.ts deleted file mode 100644 index c23caee..0000000 --- a/test/leetcode/two_pointers/two-sum-input-array-sorted.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { twoSum } from '../../src/two-pointer/two-sum-input-array-sorted'; - -describe('two sum - input array is sorted', () => { - test('two sum sorted - test case 1', async () => { - expect(twoSum([2, 7, 11, 15], 9)).toStrictEqual([1, 2]); - }); - - test('two sum sorted - test case 2', async () => { - expect(twoSum([2, 3, 4], 6)).toStrictEqual([1, 3]); - }); - - test('two sum sorted - test case 3', async () => { - expect(twoSum([-1, 0], -1)).toStrictEqual([1, 2]); - }); -}); diff --git a/test/leetcode/two_pointers/two_sum_ii_input_array_sorted_test.py b/test/leetcode/two_pointers/two_sum_ii_input_array_sorted_test.py new file mode 100644 index 0000000..ccd6259 --- /dev/null +++ b/test/leetcode/two_pointers/two_sum_ii_input_array_sorted_test.py @@ -0,0 +1,12 @@ +from leetcode.two_pointers.two_sum_ii_input_array_sorted import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.twoSum([2, 7, 11, 15], 9) == [1, 2] + + +def test_case_2(): + assert soln.twoSum([2, 3, 4], 6) == [1, 3] From 502f84fb0025bf91710fa2c9d8ac517d09985c06 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 20 Mar 2025 15:13:23 -0700 Subject: [PATCH 156/158] valid 2 --- .../two_pointers/valid-palindrome-ii.ts | 63 ------------------ src/leetcode/two_pointers/valid-palindrome.ts | 36 ---------- src/leetcode/two_pointers/valid_palindrome.py | 42 ++++++++++++ .../two_pointers/valid_palindrome_ii.py | 61 +++++++++++++++++ .../__snapshots__/three-sum.test.ts.snap | 66 ------------------- .../two_pointers/valid-palindrome-ii.test.ts | 7 -- .../two_pointers/valid-palindrome.test.ts | 14 ---- .../two_pointers/valid_palindrome_ii_test.py | 8 +++ .../two_pointers/valid_palindrome_test.py | 8 +++ 9 files changed, 119 insertions(+), 186 deletions(-) delete mode 100644 src/leetcode/two_pointers/valid-palindrome-ii.ts delete mode 100644 src/leetcode/two_pointers/valid-palindrome.ts create mode 100644 src/leetcode/two_pointers/valid_palindrome.py create mode 100644 src/leetcode/two_pointers/valid_palindrome_ii.py delete mode 100644 test/leetcode/two_pointers/__snapshots__/three-sum.test.ts.snap delete mode 100644 test/leetcode/two_pointers/valid-palindrome-ii.test.ts delete mode 100644 test/leetcode/two_pointers/valid-palindrome.test.ts create mode 100644 test/leetcode/two_pointers/valid_palindrome_ii_test.py create mode 100644 test/leetcode/two_pointers/valid_palindrome_test.py diff --git a/src/leetcode/two_pointers/valid-palindrome-ii.ts b/src/leetcode/two_pointers/valid-palindrome-ii.ts deleted file mode 100644 index 640ef72..0000000 --- a/src/leetcode/two_pointers/valid-palindrome-ii.ts +++ /dev/null @@ -1,63 +0,0 @@ -// DIFFICULTY: EASY -// -// Given a string s, return true if the s can be palindrome after deleting at most one character from it. -// -// See {@link https://leetcode.com/problems/valid-palindrome-ii/} -export { validPalindrome }; - -// SOLUTION: -// -// Use the two pointer technique to check if the character from the left matches the character on the right. If they -// do, just proceed as normal checking characters. -// -// If they do not match, we have one chance to check if deleting either the left character, or the right character, -// results in a palindrome (we can only delete one character). To do this check, we only need to check a subset of -// the characters. -// -// COMPLEXITY: -// -// The outer loop runs in O(n) time; if the word is a palindrome, the inner loop will never be called, resulting in -// O(n) runtime. -// -// The inner loop from isPalindrome() will also run in O(n) time, and they will be called at most twice. -// -// This results in time complexity of O(n + 2 * n) = O(n). -function validPalindrome(s: string): boolean { - // Check if a string is a palindrome starting using the supplied left/right pointers. - function isPalindrome(left: number, right: number) { - while (left < right) { - if (s[left] !== s[right]) { - return false; - } - - left++; - right--; - } - - return true; - } - - // Use left < right here because it's not necessary to check if the middle character matches itself. - let left = 0; - let right = s.length - 1; - while (left < right) { - // If characters match, proceed as normal. - if (s[left] === s[right]) { - left++; - right--; - continue; - } - - // Characters didn't match; check if deleting/skipping the left character results in a palindrome. - const isSkipLeftOk = isPalindrome(left + 1, right); - - // Characters didn't match; check if deleting/skipping the right character results in a palindrome. - const isSkipRightOk = isPalindrome(left, right - 1); - - // If deleting either was okay, we still have a "valid" palindrome. - return isSkipLeftOk || isSkipRightOk; - } - - // We didn't skip any characters, so it is a palindrome. - return true; -} diff --git a/src/leetcode/two_pointers/valid-palindrome.ts b/src/leetcode/two_pointers/valid-palindrome.ts deleted file mode 100644 index b5a087f..0000000 --- a/src/leetcode/two_pointers/valid-palindrome.ts +++ /dev/null @@ -1,36 +0,0 @@ -// DIFFICULTY: EASY -// -// A phrase is a palindrome if, after converting all uppercase letters into lowercase letters and removing all -// non-alphanumeric characters, it reads the same forward and backward. Alphanumeric characters include letters and -// numbers. -// -// Given a string s, return true if it is a palindrome, or false otherwise. -// -// See {@link https://leetcode.com/problems/valid-palindrome/} -export { isPalindrome }; - -// SOLUTION: -// -// Use the two pointer technique to compare elements from the ends of the array to see if you have a match. -// -// COMPLEXITY: -// -// Runs in O(n) time. -function isPalindrome(s: string): boolean { - // Replace all non-alphanumeric characters with an empty string. - const text = s.replace(/[^a-zA-Z0-9]/g, ''); - - // Use left < right here because it's not necessary to check if the middle character matches itself. - let left = 0; - let right = text.length - 1; - while (left < right) { - if (text[left].toUpperCase() !== text[right].toUpperCase()) { - return false; - } - - left++; - right--; - } - - return true; -} diff --git a/src/leetcode/two_pointers/valid_palindrome.py b/src/leetcode/two_pointers/valid_palindrome.py new file mode 100644 index 0000000..f82f55e --- /dev/null +++ b/src/leetcode/two_pointers/valid_palindrome.py @@ -0,0 +1,42 @@ +# DIFFICULTY: EASY +# ---------------- +# +# A phrase is a palindrome if, after converting all uppercase letters into lowercase letters and removing all +# non-alphanumeric characters, it reads the same forward and backward. Alphanumeric characters include letters and +# numbers. +# +# Given a string s, return true if it is a palindrome, or false otherwise. +# +# See {@link https://leetcode.com/problems/valid-palindrome/} +import re + + +class Solution: + def isPalindrome(self, s: str) -> bool: + """ + SOLUTION + -------- + + Use the two pointer technique to compare elements from the ends of the array to see if you have a match. + + COMPLEXITY + ---------- + + Time complexity is O(n). + + Space complexity is O(1). + """ + # Replace all non-alphanumeric characters with an empty string. + text = re.sub(r"[^a-zA-Z0-9]", "", s).lower() + + # Use left < right here because it's not necessary to check if the middle character matches itself. + left = 0 + right = len(text) - 1 + while left < right: + if text[left] != text[right]: + return False + + left += 1 + right -= 1 + + return True diff --git a/src/leetcode/two_pointers/valid_palindrome_ii.py b/src/leetcode/two_pointers/valid_palindrome_ii.py new file mode 100644 index 0000000..32dfabd --- /dev/null +++ b/src/leetcode/two_pointers/valid_palindrome_ii.py @@ -0,0 +1,61 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given a string s, return true if the s can be palindrome after deleting at most one character from it. +# +# See https://leetcode.com/problems/valid-palindrome-ii +class Solution: + def validPalindrome(self, s: str) -> bool: + """ + SOLUTION + -------- + + Use the two pointer technique to check if the character from the left matches the character on the right. If + they do, just proceed as normal checking characters. + + If they do not match, we have one chance to check if deleting either the left character, or the right character, + results in a palindrome (we can only delete one character). To do this check, we only need to check a subset of + the characters. + + COMPLEXITY + ---------- + + Time complexity is O(n). The outer loop runs in O(n) time; if the word is a palindrome, the inner loop will + never be called, resulting in O(n) runtime. The inner loop from isPalindrome() will also run in O(n) time, and + they will be called at most twice. This results in time complexity of O(n + 2 * n) = O(n). + + Space complexity is O(1). + """ + + # Check if a string is a palindrome starting using the supplied left/right pointers. + def isPalindrome(left: int, right: int) -> bool: + while left < right: + if s[left] != s[right]: + return False + + left += 1 + right -= 1 + + return True + + # Use left < right here because it's not necessary to check if the middle character matches itself. + left = 0 + right = len(s) - 1 + while left < right: + # If characters match, proceed as normal. + if s[left] == s[right]: + left += 1 + right -= 1 + continue + + # Characters didn't match; check if deleting/skipping the left character results in a palindrome. + isSkipLeftOk = isPalindrome(left + 1, right) + + # Characters didn't match; check if deleting/skipping the right character results in a palindrome. + isSkipRightOk = isPalindrome(left, right - 1) + + # If deleting either was okay, we still have a "valid" palindrome. + return isSkipLeftOk or isSkipRightOk + + # We didn't skip any characters, so it is a palindrome. + return True diff --git a/test/leetcode/two_pointers/__snapshots__/three-sum.test.ts.snap b/test/leetcode/two_pointers/__snapshots__/three-sum.test.ts.snap deleted file mode 100644 index 1db462d..0000000 --- a/test/leetcode/two_pointers/__snapshots__/three-sum.test.ts.snap +++ /dev/null @@ -1,66 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`three sum three sum - test case 1 1`] = ` -[ - [ - -1, - -1, - 2, - ], - [ - -1, - 0, - 1, - ], -] -`; - -exports[`three sum three sum - test case 2 1`] = ` -[ - [ - -4, - 0, - 4, - ], - [ - -4, - 1, - 3, - ], - [ - -3, - -1, - 4, - ], - [ - -3, - 0, - 3, - ], - [ - -3, - 1, - 2, - ], - [ - -2, - -1, - 3, - ], - [ - -2, - 0, - 2, - ], - [ - -1, - -1, - 2, - ], - [ - -1, - 0, - 1, - ], -] -`; diff --git a/test/leetcode/two_pointers/valid-palindrome-ii.test.ts b/test/leetcode/two_pointers/valid-palindrome-ii.test.ts deleted file mode 100644 index 1f34c88..0000000 --- a/test/leetcode/two_pointers/valid-palindrome-ii.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { validPalindrome } from '../../src/two-pointer/valid-palindrome-ii'; - -describe('valid palindrome ii', () => { - test('valid palindrome ii - test case 1', async () => { - expect(validPalindrome('aba')).toBe(true); - }); -}); diff --git a/test/leetcode/two_pointers/valid-palindrome.test.ts b/test/leetcode/two_pointers/valid-palindrome.test.ts deleted file mode 100644 index 00644b7..0000000 --- a/test/leetcode/two_pointers/valid-palindrome.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { isPalindrome } from '../../src/two-pointer/valid-palindrome'; - -describe('valid palindrome', () => { - test('valid palindrome - test case 1', async () => { - expect(isPalindrome('A man, a plan, a canal: Panama')).toBe(true); - }); - - test('valid palindrome - test case 2', async () => { - expect(isPalindrome('a')).toBe(true); - expect(isPalindrome('aa')).toBe(true); - expect(isPalindrome('aba')).toBe(true); - expect(isPalindrome('ab')).toBe(false); - }); -}); diff --git a/test/leetcode/two_pointers/valid_palindrome_ii_test.py b/test/leetcode/two_pointers/valid_palindrome_ii_test.py new file mode 100644 index 0000000..a4c417a --- /dev/null +++ b/test/leetcode/two_pointers/valid_palindrome_ii_test.py @@ -0,0 +1,8 @@ +from leetcode.two_pointers.valid_palindrome_ii import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.validPalindrome("aba") \ No newline at end of file diff --git a/test/leetcode/two_pointers/valid_palindrome_test.py b/test/leetcode/two_pointers/valid_palindrome_test.py new file mode 100644 index 0000000..69867ba --- /dev/null +++ b/test/leetcode/two_pointers/valid_palindrome_test.py @@ -0,0 +1,8 @@ +from leetcode.two_pointers.valid_palindrome import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.isPalindrome("A man, a plan, a canal: Panama") From 7f52d4e7f1ce94e0e76a097a5bc8686ef6db2073 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 20 Mar 2025 22:13:57 +0000 Subject: [PATCH 157/158] Automatic commit via GitHub Actions --- test/leetcode/two_pointers/valid_palindrome_ii_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/leetcode/two_pointers/valid_palindrome_ii_test.py b/test/leetcode/two_pointers/valid_palindrome_ii_test.py index a4c417a..c617aa8 100644 --- a/test/leetcode/two_pointers/valid_palindrome_ii_test.py +++ b/test/leetcode/two_pointers/valid_palindrome_ii_test.py @@ -5,4 +5,4 @@ def test_case_1(): - assert soln.validPalindrome("aba") \ No newline at end of file + assert soln.validPalindrome("aba") From f8c63f83e1d3bd0425f9bc719818f21c0d755243 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 20 Mar 2025 15:16:14 -0700 Subject: [PATCH 158/158] triangle --- .../two_pointers/valid-triangle-number.ts | 61 ----------------- .../two_pointers/valid_triangle_number.py | 68 +++++++++++++++++++ .../valid-triangle-number.test.ts | 7 -- .../two_pointers/valid_palindrome_ii_test.py | 2 +- .../valid_triangle_number_test.py | 8 +++ 5 files changed, 77 insertions(+), 69 deletions(-) delete mode 100644 src/leetcode/two_pointers/valid-triangle-number.ts create mode 100644 src/leetcode/two_pointers/valid_triangle_number.py delete mode 100644 test/leetcode/two_pointers/valid-triangle-number.test.ts create mode 100644 test/leetcode/two_pointers/valid_triangle_number_test.py diff --git a/src/leetcode/two_pointers/valid-triangle-number.ts b/src/leetcode/two_pointers/valid-triangle-number.ts deleted file mode 100644 index a5353c0..0000000 --- a/src/leetcode/two_pointers/valid-triangle-number.ts +++ /dev/null @@ -1,61 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an integer array nums, return the number of triplets chosen from the array that can make triangles if we take -// them as side lengths of a triangle. -// -// See {@link https://leetcode.com/problems/valid-triangle-number/} -export { triangleNumber }; - -// SOLUTION: -// -// To form a triangle, 3 numbers have to satisfy the inequalities: -// -// a + b > c -// a + c > b -// b + c > a -// -// The most straightforward approach is to sort the numbers and use the two pointer technique to find triples. -function triangleNumber(xs: number[]): number { - if (xs.length <= 2) { - return 0; - } - - // Once all elements are sorted, we know that if i < j < k, then xs[i] <= xs[j] <= xs[k]. Let a = xs[i], b = xs[j], - // and c = xs[k]. We know that a <= b <= c. - // - // If we find that a + b > c, then we have found a triplet. This is because: - // - // a + c > b, since c > b. - // b + c > a, since c > a. - // - // Once we find a triplet, we know that all elements between the left and right pointer are valid. - xs.sort((a, b) => a - b); - - let result = 0; - for (let i = xs.length - 1; i >= 0; i--) { - // Loop backwards to find our c, which we'll check if it is greater than a + b. - const c = xs[i]; - - // Start at opposite ends of the array, [a, ...., b, c], and let a and b close in on each other to find a valid - // triplet. Starting this way lets us adjust a and b in the right directions to account for c. - let left = 0; - let right = i - 1; - while (left < right) { - const a = xs[left]; - const b = xs[right]; - - // We have found a triplet; all elements between left and right can form a triplet. - if (a + b > c) { - result += right - left; - - // Since c is large compared to a + b, advance the right pointer and make c smaller to find more triplets. - right--; - } else { - // Since c is small compared to a + b, advance the left pointer and make c bigger to find more triplets. - left++; - } - } - } - - return result; -} diff --git a/src/leetcode/two_pointers/valid_triangle_number.py b/src/leetcode/two_pointers/valid_triangle_number.py new file mode 100644 index 0000000..c5bfa23 --- /dev/null +++ b/src/leetcode/two_pointers/valid_triangle_number.py @@ -0,0 +1,68 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an integer array nums, return the number of triplets chosen from the array that can make triangles if we take +# them as side lengths of a triangle. +# +# See https://leetcode.com/problems/valid-triangle-number +class Solution: + def triangleNumber(self, nums: list[int]) -> int: + """ + SOLUTION + -------- + + To form a triangle, 3 numbers have to satisfy the inequalities: + + a + b > c + a + c > b + b + c > a + + The most straightforward approach is to sort the numbers and use the two pointer technique to find triples. + + COMPLEXITY + ---------- + + Time complexity is O(n^2) where n is the number of elements in the array. + + Space complexity is O(1). + """ + if len(nums) <= 2: + return 0 + + # Once all elements are sorted, we know that if i < j < k, then nums[i] <= nums[j] <= nums[k]. Let a = nums[i], + # b = nums[j], and c = nums[k]. We know that a <= b <= c. + # + # If we find that a + b > c, then we have found a triplet. This is because: + # + # a + c > b, since c > b. + # b + c > a, since c > a. + # + # Once we find a triplet, we know that all elements between the left and right pointer are valid. + nums.sort() + + result = 0 + for i in range(len(nums) - 1, -1, -1): + # Loop backwards to find our c, which we'll check if it is greater than a + b. + c = nums[i] + + # Start at opposite ends of the array, [a, ...., b, c], and let a and b close in on each other to find a + # valid triplet. Starting this way lets us adjust a and b in the right directions to account for c. + left = 0 + right = i - 1 + while left < right: + a = nums[left] + b = nums[right] + + # We have found a triplet; all elements between left and right can form a triplet. + if a + b > c: + result += right - left + + # Since c is large compared to a + b, advance the right pointer and make c smaller to find more + # triplets. + right -= 1 + else: + # Since c is small compared to a + b, advance the left pointer and make c bigger to find more + # triplets. + left += 1 + + return result diff --git a/test/leetcode/two_pointers/valid-triangle-number.test.ts b/test/leetcode/two_pointers/valid-triangle-number.test.ts deleted file mode 100644 index 22d0a8a..0000000 --- a/test/leetcode/two_pointers/valid-triangle-number.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { triangleNumber } from '../../src/two-pointer/valid-triangle-number'; - -describe('valid triangle number', () => { - test('valid triangle number - test case 1', async () => { - expect(triangleNumber([2, 2, 3, 4])).toBe(3); - }); -}); diff --git a/test/leetcode/two_pointers/valid_palindrome_ii_test.py b/test/leetcode/two_pointers/valid_palindrome_ii_test.py index a4c417a..c617aa8 100644 --- a/test/leetcode/two_pointers/valid_palindrome_ii_test.py +++ b/test/leetcode/two_pointers/valid_palindrome_ii_test.py @@ -5,4 +5,4 @@ def test_case_1(): - assert soln.validPalindrome("aba") \ No newline at end of file + assert soln.validPalindrome("aba") diff --git a/test/leetcode/two_pointers/valid_triangle_number_test.py b/test/leetcode/two_pointers/valid_triangle_number_test.py new file mode 100644 index 0000000..4257498 --- /dev/null +++ b/test/leetcode/two_pointers/valid_triangle_number_test.py @@ -0,0 +1,8 @@ +from leetcode.two_pointers.valid_triangle_number import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.triangleNumber([2, 2, 3, 4]) == 3