Repository: jaychsu/algorithm
Branch: master
Commit: 91892fd64281
Files: 638
Total size: 761.9 KB
Directory structure:
gitextract_pg3fa3pn/
├── .editorconfig
├── .gitignore
├── .travis.yml
├── README.md
├── bin/
│ ├── test_javascript.js
│ └── test_python.py
├── codewars/
│ ├── delete_occurrences_of_an_element_if_it_occurs_more_than_n_times.js
│ ├── double_cola.js
│ ├── iq_test.js
│ ├── is_a_number_prime.js
│ ├── multiply.js
│ ├── rectangle_into_squares.js
│ ├── reverse_words.js
│ ├── simple_encryption_1_alternating_split.js
│ ├── sudoku_solution_validator.js
│ ├── take_a_ten_minute_walk.js
│ ├── tribonacci_sequence.js
│ ├── unique_in_order.js
│ └── who_likes_it.js
├── leetcode/
│ ├── 101_symmetric_tree.py
│ ├── 105_construct_binary_tree_from_preorder_and_inorder_traversal.py
│ ├── 108_convert_sorted_array_to_binary_search_tree.py
│ ├── 112_path_sum.py
│ ├── 113_path_sum_ii.py
│ ├── 116_populating_next_right_pointers_in_each_node.py
│ ├── 117_populating_next_right_pointers_in_each_node_ii.py
│ ├── 118_pascal_s_triangle.py
│ ├── 11_container_with_most_water.py
│ ├── 124_binary_tree_maximum_path_sum.py
│ ├── 134_gas_station.py
│ ├── 136_single_number.py
│ ├── 149_max_points_on_a_line.py
│ ├── 14_longest_common_prefix.py
│ ├── 150_evaluate_reverse_polish_notation.py
│ ├── 157_read_n_characters_given_read4.py
│ ├── 160_intersection_of_two_linked_lists.py
│ ├── 165_compare_version_numbers.py
│ ├── 166_fraction_to_recurring_decimal.py
│ ├── 169_majority_element.py
│ ├── 171_excel_sheet_column_number.py
│ ├── 172_factorial_trailing_zeroes.py
│ ├── 174_dungeon_game.py
│ ├── 179_largest_number.py
│ ├── 17_letter_combinations_of_a_phone_number.py
│ ├── 190_reverse_bits.py
│ ├── 191_number_of_1_bits.py
│ ├── 19_remove_nth_node_from_end_of_list.py
│ ├── 202_happy_number.py
│ ├── 204_count_primes.py
│ ├── 208_implement_trie_prefix_tree.py
│ ├── 20_valid_parentheses.py
│ ├── 215_kth_largest_element_in_an_array.py
│ ├── 217_contains_duplicate.py
│ ├── 218_the_skyline_problem.py
│ ├── 224_basic_calculator.py
│ ├── 227_basic_calculator_ii.py
│ ├── 229_majority_element_ii.py
│ ├── 22_generate_parentheses.py
│ ├── 230_kth_smallest_element_in_a_bst.py
│ ├── 234_palindrome_linked_list.py
│ ├── 235_lowest_common_ancestor_of_a_binary_search_tree.py
│ ├── 236_lowest_common_ancestor_of_a_binary_tree.py
│ ├── 237_delete_node_in_a_linked_list.py
│ ├── 242_valid_anagram.py
│ ├── 269_alien_dictionary.py
│ ├── 277_find_the_celebrity.py
│ ├── 280_wiggle_sort.py
│ ├── 282_expression_add_operators.py
│ ├── 285_inorder_successor_in_bst.py
│ ├── 288_unique_word_abbreviation.py
│ ├── 289_game_of_life.py
│ ├── 295_find_median_from_data_stream.py
│ ├── 299_bulls_and_cows.py
│ ├── 303_range_sum_query_immutable.py
│ ├── 304_range_sum_query_2d_immutable.py
│ ├── 307_range_sum_query_mutable.py
│ ├── 308_range_sum_query_2d_mutable.py
│ ├── 30_substring_with_concatenation_of_all_words.py
│ ├── 315_count_of_smaller_numbers_after_self.py
│ ├── 323_number_of_connected_components_in_an_undirected_graph.py
│ ├── 324_wiggle_sort_ii.py
│ ├── 326_power_of_three.py
│ ├── 329_longest_increasing_path_in_a_matrix.py
│ ├── 334_increasing_triplet_subsequence.py
│ ├── 33_search_in_rotated_sorted_array.py
│ ├── 340_longest_substring_with_at_most_k_distinct_characters.py
│ ├── 344_reverse_string.py
│ ├── 347_top_k_frequent_elements.py
│ ├── 348_design_tic_tac_toe.py
│ ├── 353_design_snake_game.py
│ ├── 36_valid_sudoku.py
│ ├── 377_combination_sum_iv.py
│ ├── 378_kth_smallest_element_in_a_sorted_matrix.py
│ ├── 37_sudoku_solver.py
│ ├── 380_insert_delete_getrandom_o1.py
│ ├── 381_insert_delete_getrandom_o1_duplicates_allowed.py
│ ├── 382_linked_list_random_node.py
│ ├── 384_shuffle_an_array.py
│ ├── 386_lexicographical_numbers.py
│ ├── 387_first_unique_character_in_a_string.py
│ ├── 388_longest_absolute_file_path.py
│ ├── 389_find_the_difference.py
│ ├── 38_count_and_say.py
│ ├── 390_elimination_game.py
│ ├── 391_perfect_rectangle.py
│ ├── 395_longest_substring_with_at_least_k_repeating_characters.py
│ ├── 398_random_pick_index.py
│ ├── 399_evaluate_division.py
│ ├── 406_queue_reconstruction_by_height.py
│ ├── 416_partition_equal_subset_sum.py
│ ├── 417_pacific_atlantic_water_flow.py
│ ├── 41_first_missing_positive.py
│ ├── 437_path_sum_iii.py
│ ├── 454_4sum_ii.py
│ ├── 461_hamming_distance.py
│ ├── 480_sliding_window_median.py
│ ├── 486_predict_the_winner.py
│ ├── 48_rotate_image.py
│ ├── 490_the_maze.py
│ ├── 494_target_sum.py
│ ├── 499_the_maze_iii.py
│ ├── 505_the_maze_ii.py
│ ├── 518_coin_change_2.py
│ ├── 51_n_queens.py
│ ├── 524_longest_word_in_dictionary_through_deleting.py
│ ├── 52_n_queens_ii.py
│ ├── 530_minimum_absolute_difference_in_bst.py
│ ├── 542_01_matrix.py
│ ├── 54_spiral_matrix.py
│ ├── 592_fraction_addition_and_subtraction.py
│ ├── 593_valid_square.py
│ ├── 59_spiral_matrix_ii.py
│ ├── 5_longest_palindromic_substring.py
│ ├── 616_add_bold_tag_in_string.py
│ ├── 643_maximum_average_subarray_i.py
│ ├── 66_plus_one.py
│ ├── 676_implement_magic_dictionary.py
│ ├── 679_24_game.py
│ ├── 681_next_closest_time.py
│ ├── 682_baseball_game.py
│ ├── 683_k_empty_slots.py
│ ├── 684_redundant_connection.py
│ ├── 685_redundant_connection_ii.py
│ ├── 688_knight_probability_in_chessboard.py
│ ├── 689_maximum_sum_of_3_non_overlapping_subarrays.py
│ ├── 697_degree_of_an_array.py
│ ├── 719_find_k_th_smallest_pair_distance.py
│ ├── 721_accounts_merge.py
│ ├── 734_sentence_similarity.py
│ ├── 737_sentence_similarity_ii.py
│ ├── 744_find_smallest_letter_greater_than_target.py
│ ├── 746_min_cost_climbing_stairs.py
│ ├── 747_largest_number_at_least_twice_of_others.py
│ ├── 748_shortest_completing_word.py
│ ├── 749_contain_virus.py
│ ├── 750_number_of_corner_rectangles.py
│ ├── 758_bold_words_in_string.py
│ ├── 769_max_chunks_to_make_sorted.py
│ ├── 776_split_bst.py
│ ├── 777_swap_adjacent_in_lr_string.py
│ ├── 778_swim_in_rising_water.py
│ ├── 779_k_th_symbol_in_grammar.py
│ ├── 783_minimum_distance_between_bst_nodes.py
│ ├── 784_letter_case_permutation.py
│ ├── 785_is_graph_bipartite.py
│ ├── 786_k_th_smallest_prime_fraction.py
│ ├── 787_cheapest_flights_within_k_stops.py
│ ├── 788_rotated_digits.py
│ ├── 789_escape_the_ghosts.py
│ ├── 790_domino_and_tromino_tiling.py
│ ├── 791_custom_sort_string.py
│ ├── 7_reverse_integer.py
│ ├── 81_search_in_rotated_sorted_array_ii.py
│ └── 8_string_to_integer_atoi.py
├── lintcode/
│ ├── 102_linked_list_cycle.py
│ ├── 103_linked_list_cycle_ii.py
│ ├── 104_merge_k_sorted_lists.py
│ ├── 105_copy_list_with_random_pointer.py
│ ├── 107_word_break.py
│ ├── 108_palindrome_partitioning_ii.py
│ ├── 109_triangle.py
│ ├── 10_string_permutation_ii.py
│ ├── 110_minimum_path_sum.py
│ ├── 111_climbing_stairs.py
│ ├── 114_unique_paths.py
│ ├── 115_unique_paths_ii.py
│ ├── 116_jump_game.py
│ ├── 117_jump_game_ii.py
│ ├── 118_distinct_subsequences.py
│ ├── 119_edit_distance.py
│ ├── 11_search_range_in_binary_search_tree.py
│ ├── 120_word_ladder.py
│ ├── 121_word_ladder_ii.py
│ ├── 122_largest_rectangle_in_histogram.py
│ ├── 123_word_search.py
│ ├── 124_longest_consecutive_sequence.py
│ ├── 125_backpack_ii.py
│ ├── 126_max_tree.py
│ ├── 127_topological_sorting.py
│ ├── 128_hash_function.py
│ ├── 129_rehashing.py
│ ├── 12_min_stack.py
│ ├── 130_heapify.py
│ ├── 131_building_outline.py
│ ├── 132_word_search_ii.py
│ ├── 134_lru_cache.py
│ ├── 135_combination_sum.py
│ ├── 1365_minimum_cycle_section.py
│ ├── 1366_directed_graph_loop.py
│ ├── 1367_police_distance.py
│ ├── 1368_same_number.py
│ ├── 136_palindrome_partitioning.py
│ ├── 137_clone_graph.py
│ ├── 138_subarray_sum.py
│ ├── 139_subarray_sum_closest.py
│ ├── 13_strstr.py
│ ├── 141_sqrtx.py
│ ├── 142_o1_check_power_of_2.py
│ ├── 143_sort_colors_ii.py
│ ├── 148_sort_colors.py
│ ├── 149_best_time_to_buy_and_sell_stock.py
│ ├── 14_first_position_of_target.py
│ ├── 150_best_time_to_buy_and_sell_stock_ii.py
│ ├── 151_best_time_to_buy_and_sell_stock_iii.py
│ ├── 153_combination_sum_ii.py
│ ├── 154_regular_expression_matching.py
│ ├── 155_minimum_depth_of_binary_tree.py
│ ├── 156_merge_intervals.py
│ ├── 158_two_strings_are_anagrams.py
│ ├── 159_find_minimum_in_rotated_sorted_array.py
│ ├── 15_permutations.py
│ ├── 160_find_minimum_in_rotated_sorted_array_ii.py
│ ├── 165_merge_two_sorted_lists.py
│ ├── 167_add_two_numbers.py
│ ├── 168_burst_balloons.py
│ ├── 16_permutations_ii.py
│ ├── 171_anagrams.py
│ ├── 175_invert_binary_tree.py
│ ├── 178_graph_valid_tree.py
│ ├── 17_subsets.py
│ ├── 183_wood_cut.py
│ ├── 18_subsets_ii.py
│ ├── 190_next_permutation_ii.py
│ ├── 191_maximum_product_subarray.py
│ ├── 192_wildcard_matching.py
│ ├── 196_find_the_missing_number.py
│ ├── 197_permutation_index.py
│ ├── 198_permutation_index_ii.py
│ ├── 1_a_b_problem.py
│ ├── 204_singleton.py
│ ├── 211_string_permutation.py
│ ├── 215_rate_limiter.py
│ ├── 221_add_two_numbers_ii.py
│ ├── 231_typeahead.py
│ ├── 232_tiny_url.py
│ ├── 234_webpage_crawler.py
│ ├── 236_swap_bits.py
│ ├── 242_convert_binary_tree_to_linked_lists_by_depth.py
│ ├── 243_amicable_pair
│ ├── 245_subtree.py
│ ├── 246_binary_tree_path_sum_ii.py
│ ├── 24_lfu_cache.py
│ ├── 254_drop_eggs.py
│ ├── 272_climbing_stairs_ii.py
│ ├── 28_search_a_2d_matrix.py
│ ├── 29_interleaving_string.py
│ ├── 30_insert_interval.py
│ ├── 31_partition_array.py
│ ├── 32_minimum_window_substring.py
│ ├── 35_reverse_linked_list.py
│ ├── 360_sliding_window_median.py
│ ├── 362_sliding_window_maximum.py
│ ├── 363_trapping_rain_water.py
│ ├── 364_trapping_rain_water_ii.py
│ ├── 368_expression_evaluation.py
│ ├── 36_reverse_linked_list_ii.py
│ ├── 376_binary_tree_path_sum.py
│ ├── 378_convert_binary_search_tree_to_doubly_linked_list.py
│ ├── 382_triangle_count.py
│ ├── 384_longest_substring_without_repeating_characters.py
│ ├── 386_longest_substring_with_at_most_k_distinct_characters.py
│ ├── 38_search_a_2d_matrix_ii.py
│ ├── 390_find_peak_element_ii.py
│ ├── 391_number_of_airplanes_in_the_sky.py
│ ├── 392_house_robber.py
│ ├── 393_best_time_to_buy_and_sell_stock_iv.py
│ ├── 394_coins_in_a_line.py
│ ├── 395_coins_in_a_line_ii.py
│ ├── 396_coins_in_a_line_iii.py
│ ├── 397_longest_increasing_continuous_subsequence.py
│ ├── 3_digit_counts.py
│ ├── 401_kth_smallest_number_in_sorted_matrix.py
│ ├── 402_continuous_subarray_sum.py
│ ├── 406_minimum_size_subarray_sum.py
│ ├── 40_implement_queue_by_two_stacks.py
│ ├── 414_divide_two_integers.py
│ ├── 415_valid_palindrome.py
│ ├── 417_valid_number.py
│ ├── 418_integer_to_roman.py
│ ├── 419_roman_to_integer.py
│ ├── 41_maximum_subarray.py
│ ├── 42_maximum_subarray_ii.py
│ ├── 430_scramble_string.py
│ ├── 431_connected_component_in_undirected_graph.py
│ ├── 432_find_the_weak_connected_component_in_the_directed_graph.py
│ ├── 433_number_of_islands.py
│ ├── 434_number_of_islands_ii.py
│ ├── 437_copy_books.py
│ ├── 43_maximum_subarray_iii.py
│ ├── 440_backpack_iii.py
│ ├── 442_implement_trie.py
│ ├── 443_two_sum_greater_than_target.py
│ ├── 447_search_in_a_big_sorted_array.py
│ ├── 450_reverse_nodes_in_k_group.py
│ ├── 453_flatten_binary_tree_to_linked_list.py
│ ├── 457_classical_binary_search.py
│ ├── 458_last_position_of_target.py
│ ├── 459_closest_number_in_sorted_array.py
│ ├── 45_maximum_subarray_difference.py
│ ├── 460_k_closest_numbers_in_sorted_array.py
│ ├── 461_kth_smallest_numbers_in_unsorted_array.py
│ ├── 462_total_occurrence_of_target.py
│ ├── 465_kth_smallest_sum_in_two_sorted_arrays.py
│ ├── 469_identical_binary_tree.py
│ ├── 471_top_k_frequent_words.py
│ ├── 472_binary_tree_path_sum_iii.py
│ ├── 473_add_and_search_word.py
│ ├── 474_lowest_common_ancestor_ii.py
│ ├── 475_binary_tree_maximum_path_sum_ii.py
│ ├── 479_second_max_of_array.py
│ ├── 480_binary_tree_paths.py
│ ├── 486_merge_k_sorted_arrays.py
│ ├── 494_implement_stack_by_two_queues.py
│ ├── 496_toy_factory.py
│ ├── 497_shape_factory.py
│ ├── 498_parking_lot.py
│ ├── 499_word_count.py
│ ├── 4_ugly_number_ii.py
│ ├── 500_inverted_index.py
│ ├── 501_mini_twitter.py
│ ├── 502_mini_cassandra.py
│ ├── 503_anagram_map_reduce.py
│ ├── 504_inverted_index_map_reduce.py
│ ├── 505_web_logger.py
│ ├── 509_mini_yelp.py
│ ├── 510_maximal_rectangle.py
│ ├── 512_decode_ways.py
│ ├── 513_perfect_squares.py
│ ├── 515_paint_house.py
│ ├── 516_paint_house_ii.py
│ ├── 517_ugly_number.py
│ ├── 518_super_ugly_number.py
│ ├── 519_consistent_hashing.py
│ ├── 51_previous_permutation.py
│ ├── 520_consistent_hashing_ii.py
│ ├── 521_remove_duplicate_numbers_in_array.py
│ ├── 522_tiny_url_ii.py
│ ├── 523_url_parser.py
│ ├── 525_mini_uber.py
│ ├── 526_load_balancer.py
│ ├── 527_trie_serialization.py
│ ├── 528_flatten_nested_list_iterator.py
│ ├── 529_geohash.py
│ ├── 52_next_permutation.py
│ ├── 530_geohash_ii.py
│ ├── 531_six_degrees.py
│ ├── 532_reverse_pairs.py
│ ├── 533_two_sum_closest_to_target.py
│ ├── 534_house_robber_ii.py
│ ├── 538_memcache.py
│ ├── 539_move_zeroes.py
│ ├── 53_reverse_words_in_a_string.py
│ ├── 540_zigzag_iterator.py
│ ├── 541_zigzag_iterator_ii.py
│ ├── 543_kth_largest_in_n_arrays.py
│ ├── 544_top_k_largest_numbers.py
│ ├── 545_top_k_largest_numbers_ii.py
│ ├── 547_intersection_of_two_arrays.py
│ ├── 548_intersection_of_two_arrays_ii.py
│ ├── 54_string_to_integer_ii.py
│ ├── 551_nested_list_weight_sum.py
│ ├── 552_create_maximum_number.py
│ ├── 553_bomb_enemy.py
│ ├── 555_counting_bloom_filter.py
│ ├── 556_standard_bloom_filter.py
│ ├── 559_trie_service.py
│ ├── 560_friendship_service.py
│ ├── 563_backpack_v.py
│ ├── 564_backpack_vi.py
│ ├── 565_heart_beat.py
│ ├── 566_gfs_client.py
│ ├── 56_two_sum.py
│ ├── 573_build_post_office_ii.py
│ ├── 574_build_post_office.py
│ ├── 575_expression_expand.py
│ ├── 578_lowest_common_ancestor_iii.py
│ ├── 57_3sum.py
│ ├── 582_word_break_ii.py
│ ├── 584_drop_eggs_ii.py
│ ├── 585_maximum_number_in_mountain_sequence.py
│ ├── 586_sqrtx_ii.py
│ ├── 587_two_sum_unique_pairs.py
│ ├── 589_connecting_graph.py
│ ├── 58_4sum.py
│ ├── 590_connecting_graph_ii.py
│ ├── 591_connecting_graph_iii.py
│ ├── 594_strstr_ii.py
│ ├── 595_binary_tree_longest_consecutive_sequence.py
│ ├── 596_minimum_subtree.py
│ ├── 597_subtree_with_maximum_average.py
│ ├── 598_zombie_in_matrix.py
│ ├── 599_insert_into_a_cyclic_sorted_list.py
│ ├── 59_3sum_closest.py
│ ├── 5_kth_largest_element.py
│ ├── 600_smallest_rectangle_enclosing_black_pixels.py
│ ├── 601_flatten_2d_vector.py
│ ├── 602_russian_doll_envelopes.py
│ ├── 603_largest_divisible_subset.py
│ ├── 604_window_sum.py
│ ├── 605_sequence_reconstruction.py
│ ├── 606_kth_largest_element_ii.py
│ ├── 607_two_sum_data_structure_design.py
│ ├── 608_two_sum_input_array_is_sorted.py
│ ├── 609_two_sum_less_than_or_equal_to_target.py
│ ├── 610_two_sum_difference_equals_to_target.py
│ ├── 611_knight_shortest_path.py
│ ├── 612_k_closest_points.py
│ ├── 613_high_five.py
│ ├── 614_binary_tree_longest_consecutive_sequence_ii.py
│ ├── 615_course_schedule.py
│ ├── 616_course_schedule_ii.py
│ ├── 617_maximum_average_subarray.py
│ ├── 618_search_graph_nodes.py
│ ├── 619_binary_tree_longest_consecutive_sequence_iii.py
│ ├── 61_search_for_a_range.py
│ ├── 620_maximum_subarray_iv.py
│ ├── 621_maximum_subarray_v.py
│ ├── 622_frog_jump.py
│ ├── 623_k_edit_distance.py
│ ├── 624_remove_substrings.py
│ ├── 625_partition_array_ii.py
│ ├── 630_knight_shortest_path_ii.py
│ ├── 633_find_the_duplicate_number.py
│ ├── 634_word_squares.py
│ ├── 635_boggle_game.py
│ ├── 636_132_pattern.py
│ ├── 646_first_position_unique_character.py
│ ├── 647_substring_anagrams.py
│ ├── 64_merge_sorted_array.py
│ ├── 654_sparse_matrix_multiplication.py
│ ├── 655_big_integer_addition.py
│ ├── 656_big_integer_multiplication.py
│ ├── 65_median_of_two_sorted_arrays.py
│ ├── 662_guess_number_game.py
│ ├── 664_counting_bits.py
│ ├── 667_longest_palindromic_subsequence.py
│ ├── 668_ones_and_zeroes.py
│ ├── 669_coin_change.py
│ ├── 66_binary_tree_preorder_traversal.py
│ ├── 676_decode_ways_ii.py
│ ├── 67_binary_tree_inorder_traversal.py
│ ├── 689_two_sum_bst_edtion.py
│ ├── 68_binary_tree_postorder_traversal.py
│ ├── 69_binary_tree_level_order_traversal.py
│ ├── 6_merge_two_sorted_arrays.py
│ ├── 70_binary_tree_level_order_traversal_ii.py
│ ├── 717_tree_longest_path_with_same_value.py
│ ├── 718_repeat_string.py
│ ├── 71_binary_tree_zigzag_level_order_traversal.py
│ ├── 724_minimum_partition.py
│ ├── 725_boolean_parenthesization.py
│ ├── 729_last_digit_by_factorial_divide.py
│ ├── 745_palindromic_ranges.py
│ ├── 74_first_bad_version.py
│ ├── 752_rogue_knight_sven.py
│ ├── 75_find_peak_element.py
│ ├── 76_longest_increasing_subsequence.py
│ ├── 772_group_anagrams.py
│ ├── 775_palindrome_pairs.py
│ ├── 776_strobogrammatic_number_ii.py
│ ├── 77_longest_common_subsequence.py
│ ├── 784_the_longest_common_prefix_ii.py
│ ├── 790_parser.py
│ ├── 791_merge_number.py
│ ├── 792_kth_prime_number.py
│ ├── 793_intersection_of_arrays.py
│ ├── 7_binary_tree_serialization.py
│ ├── 813_find_anagram_mappings.py
│ ├── 81_data_stream_median.py
│ ├── 823_input_stream.py
│ ├── 824_single_number_iv.py
│ ├── 826_computer_maintenance.py
│ ├── 830_string_sort.py
│ ├── 831_3sum_ii.py
│ ├── 832_count_negative_number.py
│ ├── 833_process_sequence.py
│ ├── 85_insert_node_in_a_binary_search_tree.py
│ ├── 86_binary_search_tree_iterator.py
│ ├── 87_remove_node_in_binary_search_tree.py
│ ├── 88_lowest_common_ancestor.py
│ ├── 89_k_sum.py
│ ├── 8_rotate_string.py
│ ├── 900_closest_binary_search_tree_value.py
│ ├── 901_closest_binary_search_tree_value_ii.py
│ ├── 919_meeting_rooms_ii.py
│ ├── 920_meeting_rooms.py
│ ├── 92_backpack.py
│ ├── 93_balanced_binary_tree.py
│ ├── 95_validate_binary_search_tree.py
│ ├── 96_partition_list.py
│ ├── 97_maximum_depth_of_binary_tree.py
│ ├── 98_sort_list.py
│ └── 9_fizz_buzz.py
├── other/
│ ├── anti_queue_reconstruction_by_height.py
│ ├── binary_tree_maximum_path_product.py
│ ├── candy_crush.py
│ ├── card_shuffler.py
│ ├── deep_fetch.js
│ ├── deep_fetch.test.js
│ ├── find_treasure_in_maze.py
│ ├── find_ways_in_board_game.py
│ ├── freq_iterator.py
│ ├── gcd_and_lcm.py
│ ├── get_most_popular_word.py
│ ├── guess_secret.py
│ ├── inorder_non_threaded_binary_tree_traversal.py
│ ├── is_valid_relation.py
│ ├── merge_mail.py
│ ├── reservation.py
│ ├── robot_cleaner.py
│ ├── snake_and_ladder_problem.py
│ ├── stock_stream.py
│ ├── string_abbreviation.py
│ ├── the_point_inside_polygon.py
│ ├── uneven_random_get.py
│ ├── unique_paths_with_followups.py
│ ├── unique_word_abbreviation_ii.py
│ └── upside_down_numbers.py
├── pramp/
│ ├── array_index_and_element_equality.py
│ ├── array_of_array_products.py
│ ├── award_budget_cuts.py
│ ├── bracket_match.py
│ ├── busiest_time_in_the_mall.py
│ ├── decrypt_message.py
│ ├── deletion_distance.py
│ ├── drone_flight_planner.py
│ ├── find_the_duplicates.py
│ ├── flatten_a_dictionary.py
│ ├── h_tree_construction.py
│ ├── k_messed_array_sort.py
│ ├── largest_smaller_bst_node.py
│ ├── number_of_paths.py
│ ├── pairs_with_specific_difference.py
│ ├── pancake_sort.py
│ ├── root_of_number.py
│ ├── sales_path.py
│ ├── sentence_reverse.py
│ ├── smallest_substring_of_all_characters.py
│ ├── time_planner.py
│ └── word_count_engine.py
└── topic/
├── README.md
├── _test/
│ └── python/
│ ├── __init__.py
│ └── test_base.py
├── balanced_search_tree/
│ └── python/
│ ├── __init__.py
│ ├── _helper.py
│ └── red_black_tree.py
├── bit_manipulation/
│ ├── README.md
│ └── python/
│ ├── __init__.py
│ ├── calculator_in_bit.py
│ └── calculator_in_bit__test.py
├── complexity_analysis.md
├── dynamic_programming/
│ └── README.md
├── graph/
│ ├── README.md
│ └── python/
│ ├── __init__.py
│ ├── union_find.py
│ └── union_find__test.py
├── hash/
│ └── python/
│ ├── __init__.py
│ ├── geohash.py
│ ├── geohash__test.py
│ ├── hashtable.py
│ └── hashtable__test.py
├── heap/
│ └── python/
│ ├── __init__.py
│ ├── binary_hash_heap.py
│ ├── binary_heap.py
│ ├── heap__test.py
│ ├── lazy_removable_heapq.py
│ └── removable_heapq.py
├── language/
│ ├── javascript/
│ │ ├── README.md
│ │ └── traversal.md
│ ├── php/
│ │ └── README.md
│ ├── python/
│ │ ├── README.md
│ │ ├── matrix.md
│ │ ├── runtime_of_builtins.md
│ │ └── traversal.md
│ └── sql/
│ └── README.md
├── linked_list/
│ ├── README.md
│ └── python/
│ ├── __init__.py
│ ├── _helper.py
│ ├── cyclic_doubly_list.py
│ ├── cyclic_list.py
│ ├── dummy_tail_doubly_list.py
│ ├── dummy_tail_list.py
│ ├── head_tail_doubly_list.py
│ ├── head_tail_list.py
│ ├── linked_list__test.py
│ ├── two_dummy_doubly_list.py
│ └── two_dummy_list.py
├── problem_set/
│ ├── subarray_sum.md
│ └── substring_with_distinct_characters.md
├── range_query/
│ └── python/
│ ├── __init__.py
│ ├── _helper.py
│ ├── binary_indexed_tree.py
│ ├── interval_tree.py
│ ├── prefix_sum.py
│ ├── range_tree.py
│ └── segment_tree.py
├── searching/
│ ├── binary_search.md
│ └── python/
│ ├── __init__.py
│ └── _helper.py
├── sorting/
│ └── python/
│ ├── __init__.py
│ ├── _helper.py
│ ├── merge_sort.py
│ ├── quick_sort.py
│ └── sorting__test.py
├── system_design/
│ └── latency.md
├── traversal/
│ └── python/
│ ├── __init__.py
│ ├── _helper.py
│ ├── binary_tree_traversal__test.py
│ ├── iterative_traversal.py
│ ├── morris_traversal.py
│ └── recursive_traversal.py
└── tree/
├── binary_tree.md
└── python/
├── __init__.py
├── _helper.py
├── binary_tree.py
├── binary_tree__test.py
├── trie.py
└── trie__test.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
root = true
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_size = 2
indent_style = space
[*.py]
indent_size = 4
[*.{md,markdown}]
indent_size = 4
[Makefile]
indent_style = tab
================================================
FILE: .gitignore
================================================
__pycache__
node_modules
*-lock.json
================================================
FILE: .travis.yml
================================================
matrix:
include:
- language: python
python: "3.6"
script:
- $TRAVIS_BUILD_DIR/bin/test_python.py
- language: node_js
node_js:
- "lts/carbon"
before_script:
- npm install -g jest
script: jest --config=$TRAVIS_BUILD_DIR/bin/test_javascript.js
================================================
FILE: README.md
================================================
Algorithm
======
[](https://travis-ci.org/jaychsu/algorithm)
The challenges for algorithm contests, and summary the implementation.
## Structure
| Top-level Folder | Note |
| :--- | :--- |
| bin | commands |
| topic | implementation and summaries of algorithms and data structures |
| other | for problems met in life |
| codeforces | for [Codeforces](http://codeforces.com) |
| codejam | for [PastContests](https://code.google.com/codejam/past-contests) in [code jam](https://code.google.com/codejam) from Google |
| codelab | for [Code Lab](https://codelab.interviewbit.com) from Facebook |
| codewars | for [Codewars](https://www.codewars.com) |
| geeksforgeeks | for [GeeksforGeeks](https://www.geeksforgeeks.org) |
| hackerrank | for [HackerRank](https://www.hackerrank.com/contests) |
| leetcode | for [LeetCode](https://leetcode.com) |
| lintcode | for [LintCode](http://www.lintcode.com) |
| pramp | for [Pramp](https://www.pramp.com) |
| topcoder | for [ActiveContests](https://community.topcoder.com/longcontest/?module=ViewActiveContests), [Practices](https://community.topcoder.com/longcontest/?module=ViewPractice), and [ProblemArchives](https://community.topcoder.com/tc?module=ProblemArchive) in [Topcoder](https://www.topcoder.com) |
| Scaler | for [Scaler Topics](https://www.scaler.com/topics/) |
| InterviewBit | for [Technical Interview](https://www.interviewbit.com/technical-interview-questions/), [Data Structures and Algorithms](https://www.interviewbit.com/courses/programming/), [Contests](https://www.interviewbit.com/contests/), and [Mock Interviews](https://www.interviewbit.com/mock-interview/) |
## Testing
| Language | Command |
| :--- | :--- |
| Python | [./bin/test_python.py](./bin/test_python.py) |
| Javascript | |
================================================
FILE: bin/test_javascript.js
================================================
// TODO: wrap this file to a executable command
// [Configuring Jest](https://facebook.github.io/jest/docs/en/configuration.html)
module.exports = {
"rootDir": "..",
"testEnvironment": "node",
"testRegex": "/(other|topic)/.*(\\.|/)(test|spec)\\.jsx?$",
"verbose": true,
}
================================================
FILE: bin/test_python.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import unittest
import doctest
sys.path.append(os.path.join(
os.path.dirname(__file__),
'../topic'
))
from _test.python.test_base import TaskTimer
def get_unittests(dirname):
ROOT_DIR = os.path.join(
os.path.dirname(__file__),
'../{dirname}'.format(dirname=dirname)
)
tests = []
for path, _, files in os.walk(ROOT_DIR):
if ('pycache' in path or
'python' not in path or
'__init__.py' not in files):
continue
tests.append(unittest.defaultTestLoader.discover(
path,
pattern='*__test.py',
top_level_dir=ROOT_DIR
))
return tests
def get_doctests(dirname):
ROOT_DIR = os.path.join(
os.path.dirname(__file__),
'../{dirname}'.format(dirname=dirname)
)
tests = []
sys.path.append(ROOT_DIR)
for path, _, files in os.walk(ROOT_DIR):
if 'pycache' in path:
continue
for file in files:
if (file.startswith('_') or
not file.endswith('.py')):
continue
timer = TaskTimer()
tests.append(doctest.DocTestSuite(
file[:-3],
setUp=lambda globs: timer.reset_time(),
tearDown=lambda globs: timer.print_duration()
))
sys.path.pop()
return tests
if __name__ == '__main__':
suite = unittest.TestSuite()
for tests in (
get_unittests('topic'),
get_doctests('leetcode'),
get_doctests('pramp'),
get_doctests('other'),
):
suite.addTests(tests)
unittest.TextTestRunner(verbosity=2).run(suite)
================================================
FILE: codewars/delete_occurrences_of_an_element_if_it_occurs_more_than_n_times.js
================================================
function deleteNth(nums, n) {
const freq = {}
for (let i = 0; i < nums.length; i++) {
const num = nums[i]
if (!freq.hasOwnProperty(num)) {
freq[num] = 0
}
freq[num] += 1
}
for (let i = nums.length - 1; i >= 0; i--) {
const num = nums[i]
if (freq[num] <= n) {
continue
}
nums.splice(i, 1)
freq[num] -= 1
}
return nums
}
================================================
FILE: codewars/double_cola.js
================================================
function whoIsNext(names, r) {
let total = names.length
while (r > total) {
r -= total
total *= 2
}
return names[parseInt(names.length * (r - 1) / total)]
}
================================================
FILE: codewars/iq_test.js
================================================
function iqTest(nums) {
nums = nums.split(' ').map(c => +c & 1)
for (let i = 2; i < nums.length; i++) {
if (nums[0] ^ nums[i] && nums[1] ^ nums[i]) {
return i + 1
} else if (nums[i] ^ nums[0] && nums[1] ^ nums[0]) {
return 1
} else if (nums[0] ^ nums[1] && nums[i] ^ nums[1]) {
return 2
}
}
return -1
}
================================================
FILE: codewars/is_a_number_prime.js
================================================
function isPrime(num) {
if (!num || num <= 1) return false
const factors = [2, 3, 5, 7, 9, 11, 13, 17, 19].filter(base => {
if (num <= base) return false
if (num % base === 0) return true
return false
})
return factors.length === 0
}
================================================
FILE: codewars/multiply.js
================================================
function multiply(a, b) {
return a * b
}
================================================
FILE: codewars/rectangle_into_squares.js
================================================
function sqInRect(lng, wdth) {
if (!lng || !wdth || lng === wdth) return null
const ans = []
while (lng != 1 || wdth != 1) {
if (lng > wdth) {
ans.push(wdth)
lng -= wdth
} else if (lng < wdth) {
ans.push(lng)
wdth -= lng
} else {
ans.push(lng)
break
}
}
if (lng == 1 && wdth == 1) {
ans.push(1)
}
return ans
}
================================================
FILE: codewars/reverse_words.js
================================================
function reverseWords(s) {
return s.split(' ').map(word => {
return word.split('').reverse().join('')
}).join(' ')
}
================================================
FILE: codewars/simple_encryption_1_alternating_split.js
================================================
function encrypt(text, n) {
if (!text || !n || n < 0) return text
const TEXT_SIZE = text.length
while (n--) {
const _text = []
for (let i = 1; i < TEXT_SIZE; i += 2) {
_text.push(text[i])
}
for (let i = 0; i < TEXT_SIZE; i += 2) {
_text.push(text[i])
}
text = _text.join('')
}
return text
}
function decrypt(text, n) {
if (!text || !n || n < 0) return text
const TEXT_SIZE = text.length
const HALF_TEXT_SIZE = parseInt(text.length / 2)
while (n--) {
const _text = []
for (let i = 0; i < HALF_TEXT_SIZE + 1; i++) {
if (HALF_TEXT_SIZE + i < TEXT_SIZE) {
_text.push(text[HALF_TEXT_SIZE + i])
}
if (i < HALF_TEXT_SIZE) {
_text.push(text[i])
}
}
text = _text.join('')
}
return text
}
================================================
FILE: codewars/sudoku_solution_validator.js
================================================
function validSolution(board) {
if ( !Array.isArray(board)
|| !Array.isArray(board[0])
|| board.length !== board[0].length) {
return false
}
const n = board.length
const g = Math.sqrt(n) // group counts
const cnts = new Array(n).fill(0)
let cnt = 0
let i, j, x, y
for (x = 0; x < n; x++) {
for (y = 0; y < n; y++) {
if (board[x][y] <= 0 || board[x][y] > n) return false
if (cnts[board[x][y] - 1] !== cnt) return false
cnts[board[x][y] - 1] += 1
}
cnt += 1
for (y = 0; y < n; y++) {
if (board[y][x] <= 0 || board[y][x] > n) return false
if (cnts[board[y][x] - 1] !== cnt) return false
cnts[board[y][x] - 1] += 1
}
cnt += 1
}
let x_from, x_to, y_from, y_to
for (i = 0; i < g; i++) {
for (j = 0; j < g; j++) {
x_from = i * g
x_to = i * g + g
y_from = j * g
y_to = j * g + g
for (x = x_from; x < x_to; x++) {
for (y = y_from; y < y_to; y++) {
if (cnts[board[x][y] - 1] !== cnt) return false
cnts[board[x][y] - 1] += 1
}
}
cnt += 1
}
}
return true
}
================================================
FILE: codewars/take_a_ten_minute_walk.js
================================================
function isValidWalk(walk) {
if (walk.length != 10) {
return false
}
let dx = 0
let dy = 0
walk.forEach(c => {
switch (c) {
case 'n':
dy++
break
case 's':
dy--
break
case 'e':
dx++
break
case 'w':
dx--
break
default:
return false
}
})
return dx === 0 && dy === 0
}
================================================
FILE: codewars/tribonacci_sequence.js
================================================
function tribonacci(signature, n) {
if (!n) return []
const ans = signature.slice()
for (let i = 3; i < n; i++) {
ans.push(ans[i - 3] + ans[i - 2] + ans[i - 1])
}
return ans.slice(0, n)
}
================================================
FILE: codewars/unique_in_order.js
================================================
function uniqueInOrder(iterable) {
if (!iterable) {
return []
}
const ans = [iterable[0]]
for (let i = 1; i < iterable.length; i++) {
if (iterable[i] != ans[ans.length - 1]) {
ans.push(iterable[i])
}
}
return ans
}
================================================
FILE: codewars/who_likes_it.js
================================================
function likes(names) {
if (!Array.isArray(names) || names.length === 0) {
return 'no one likes this'
}
switch (names.length) {
case 1:
return `${names[0]} likes this`
case 2:
return `${names[0]} and ${names[1]} like this`
case 3:
return `${names[0]}, ${names[1]} and ${names[2]} like this`
default:
return `${names[0]}, ${names[1]} and ${names.length - 2} others like this`
}
}
================================================
FILE: leetcode/101_symmetric_tree.py
================================================
"""
Definition for a binary tree node.
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
"""
class Solution:
def isSymmetric(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
if not root:
return True
return self.divide_conquer(root.left, root.right)
def divide_conquer(self, left, right):
if not left and not right:
return True
if not left or not right:
return False
if left.val != right.val:
return False
if not self.divide_conquer(left.left, right.right):
return False
if not self.divide_conquer(left.right, right.left):
return False
return True
================================================
FILE: leetcode/105_construct_binary_tree_from_preorder_and_inorder_traversal.py
================================================
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def buildTree(self, P, I):
"""
:type P: List[int]
:type I: List[int]
:rtype: TreeNode
"""
if not P or not I:
return
i = I.index(P.pop(0))
node = TreeNode(I[i])
node.left = self.buildTree(P, I[:i])
node.right = self.buildTree(P, I[i + 1:])
return node
================================================
FILE: leetcode/108_convert_sorted_array_to_binary_search_tree.py
================================================
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def sortedArrayToBST(self, nums):
"""
:type nums: List[int]
:rtype: TreeNode
"""
if not nums:
return
i = len(nums) // 2
node = TreeNode(nums[i])
node.left = self.sortedArrayToBST(nums[:i])
node.right = self.sortedArrayToBST(nums[i + 1:])
return node
================================================
FILE: leetcode/112_path_sum.py
================================================
"""
Definition for a binary tree node.
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
"""
class Solution:
def hasPathSum(self, root, target):
"""
:type root: TreeNode
:type target: int
:rtype: bool
"""
if not root:
return False
if not root.left and not root.right:
return root.val == target
if root.left and self.hasPathSum(root.left, target - root.val):
return True
if root.right and self.hasPathSum(root.right, target - root.val):
return True
return False
================================================
FILE: leetcode/113_path_sum_ii.py
================================================
"""
Definition for a binary tree node.
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
"""
class Solution:
def pathSum(self, root, target):
"""
:type root: TreeNode
:type target: int
:rtype: List[List[int]]
"""
ans = []
if not root:
return ans
self.dfs(root, target, ans, [])
return ans
def dfs(self, node, remaining, ans, path):
if not node:
return
path.append(node.val)
if not node.left and not node.right and remaining == node.val:
ans.append(path[:])
else:
self.dfs(node.left, remaining - node.val, ans, path)
self.dfs(node.right, remaining - node.val, ans, path)
path.pop()
================================================
FILE: leetcode/116_populating_next_right_pointers_in_each_node.py
================================================
"""
Definition for binary tree with next pointer.
class TreeLinkNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
self.next = None
"""
class Solution:
"""
Recursion
"""
def connect(self, root):
"""
:type root: TreeLinkNode
:rtype: void
"""
if not root:
return
if root.left:
root.left.next = root.right
if root.right and root.next:
root.right.next = root.next.left
# leave to None if root.right: root.right.next = None
self.connect(root.left)
self.connect(root.right)
class Solution:
"""
Iteration
"""
def connect(self, root):
"""
:type root: TreeLinkNode
:rtype: void
"""
if not root:
return
head = root
nxt = None
# this loop only for find left
while head:
nxt = head
head = nxt.left
# this loop find for every next, do level move
while nxt:
if nxt.left:
nxt.left.next = nxt.right
if nxt.right and nxt.next:
nxt.right.next = nxt.next.left
nxt = nxt.next
================================================
FILE: leetcode/117_populating_next_right_pointers_in_each_node_ii.py
================================================
"""
Definition for binary tree with next pointer.
class TreeLinkNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
self.next = None
"""
class Solution:
"""
Recursion
1. recursion from right to left
2. first scan the nxt by level
"""
def connect(self, root):
"""
:type root: TreeLinkNode
:rtype: void
"""
if not root:
return
# scan the nxt by level
head = root.next
nxt = None
while head and not nxt:
if head.left:
nxt = head.left
elif head.right:
nxt = head.right
else:
head = head.next
if root.right:
root.right.next = nxt
nxt = root.right
if root.left:
root.left.next = nxt
self.connect(root.right)
self.connect(root.left)
class Solution:
"""
Iteration
"""
def connect(self, root):
"""
:type root: TreeLinkNode
:rtype: void
"""
if not root:
return
dummy = TreeLinkNode(0)
dummy.next = root
nxt = tail = None
while dummy.next:
tail = dummy
nxt = dummy.next
dummy.next = None
while nxt:
if nxt.left:
tail.next = nxt.left
tail = tail.next
if nxt.right:
tail.next = nxt.right
tail = tail.next
nxt = nxt.next
class Solution:
"""
Level Traversal
"""
def connect(self, root):
"""
:type root: TreeLinkNode
:rtype: void
"""
if not root:
return
queue, _queue = [root], []
while queue:
n = len(queue)
for i in range(n):
if i < n - 1:
queue[i].next = queue[i + 1]
if queue[i].left:
_queue.append(queue[i].left)
if queue[i].right:
_queue.append(queue[i].right)
queue, _queue = _queue, []
================================================
FILE: leetcode/118_pascal_s_triangle.py
================================================
class Solution:
def generate(self, num_rows):
"""
:type num_rows: int
:rtype: List[List[int]]
"""
ans = []
if not num_rows:
return ans
ans.append([1])
for i in range(1, num_rows):
path = [ans[i - 1][0]]
for j in range(1, len(ans[i - 1])):
path.append(ans[i - 1][j] + ans[i - 1][j - 1])
path.append(ans[i - 1][-1])
ans.append(path)
return ans
class Solution:
def generate(self, num_rows):
"""
:type num_rows: int
:rtype: List[List[int]]
"""
ans = []
if not num_rows:
return ans
ans.append([1])
for i in range(1, num_rows):
ans.append([
(ans[i - 1] + [0])[j] + ([0] + ans[i - 1])[j]
for j in range(len(ans[i - 1]) + 1)
])
return ans
================================================
FILE: leetcode/11_container_with_most_water.py
================================================
class Solution:
def maxArea(self, H):
"""
:type H: List[int]
:rtype: int
"""
ans = 0
if not H:
return ans
left, right = 0, len(H) - 1
while left < right:
area = min(H[left], H[right]) * (right - left)
if area > ans:
ans = area
if H[left] < H[right]:
left += 1
else:
right -= 1
return ans
================================================
FILE: leetcode/124_binary_tree_maximum_path_sum.py
================================================
"""
Definition for a binary tree node.
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
"""
class Solution:
def maxPathSum(self, root):
"""
:type root: TreeNode
:rtype: int
"""
if not root:
return 0
ans, _ = self.divide_conquer(root)
return ans
def divide_conquer(self, node):
if not node:
return float('-inf'), 0
max_left, left = self.divide_conquer(node.left)
max_right, right = self.divide_conquer(node.right)
# 0 means discard the negative path
res = max(max_left, max_right, node.val + left + right)
path = max(node.val + left, node.val + right, 0)
return res, path
class Solution:
def maxPathSum(self, root):
"""
:type root: TreeNode
:rtype: int
"""
if not root:
return 0
self.ans = float('-inf')
self.divide_conquer(root)
return self.ans
def divide_conquer(self, node):
if not node:
return 0
left = max(0, self.divide_conquer(node.left))
right = max(0, self.divide_conquer(node.right))
if node.val + left + right > self.ans:
self.ans = node.val + left + right
return node.val + max(left, right)
================================================
FILE: leetcode/134_gas_station.py
================================================
class Solution:
"""
Main Concept:
if the tank is enough, go to next, otherwise back to previous to get gas
"""
def canCompleteCircuit(self, gas, cost):
"""
:type gas: List[int]
:type cost: List[int]
:rtype: int
"""
NOT_FOUND = -1
if not gas or not cost or len(gas) != len(cost):
return NOT_FOUND
end, start = -1, len(gas) - 1 # since its a circle, end start from `-1` means `n - 1`
tank = gas[start] - cost[start]
while start > end:
if tank >= 0:
end += 1
tank += gas[end] - cost[end]
else:
start -= 1
tank += gas[start] - cost[start]
return start if tank >= 0 else NOT_FOUND
class Solution:
"""
TLE: Simulate the process
"""
def canCompleteCircuit(self, gas, cost):
"""
:type gas: List[int]
:type cost: List[int]
:rtype: int
"""
NOT_FOUND = -1
if not gas or not cost or len(gas) != len(cost):
return NOT_FOUND
n = len(gas)
RANGE = list(range(n))
for start in range(n):
tank = 0
is_failed = False
for mid in RANGE[start:n] + RANGE[:start]:
tank += gas[mid]
if tank < cost[mid]:
is_failed = True
break
tank -= cost[mid]
if not is_failed:
return start
return NOT_FOUND
================================================
FILE: leetcode/136_single_number.py
================================================
class Solution:
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
ans = 0
for num in nums:
ans ^= num
return ans
================================================
FILE: leetcode/149_max_points_on_a_line.py
================================================
# Definition for a point.
# class Point:
# def __init__(self, a=0, b=0):
# self.x = a
# self.y = b
class Solution:
def maxPoints(self, P):
"""
:type P: List[Point]
:rtype: int
"""
ans = 0
if not P:
return ans
n = len(P)
if n <= 2:
return n
for i in range(n):
S = {}
points = dups = 0
for j in range(i + 1, n):
dx = P[i].x - P[j].x
dy = P[i].y - P[j].y
if dx == 0 and dy == 0:
dups += 1
continue
gcd = self.get_gcd(dx, dy)
if gcd:
dx //= gcd
dy //= gcd
key = (dx, dy)
S[key] = S.get(key, 0) + 1
if S[key] > points:
points = S[key]
if points + dups + 1 > ans:
ans = points + dups + 1
return ans
def get_gcd(self, a, b):
if b == 0:
return a
return self.get_gcd(b, a % b)
================================================
FILE: leetcode/14_longest_common_prefix.py
================================================
class Solution:
def longestCommonPrefix(self, strs):
"""
:type strs: List[str]
:rtype: str
"""
if not strs or not strs[0]:
return ''
t = strs[0]
for i in range(len(t)):
for s in strs:
if i >= len(s) or s[i] != t[i]:
return t[:i]
return t
================================================
FILE: leetcode/150_evaluate_reverse_polish_notation.py
================================================
class Solution:
def evalRPN(self, E):
"""
:type E: List[str]
:rtype: int
"""
if not E:
return 0
stack = []
operation = {
'+': lambda a, b: a + b,
'-': lambda a, b: a - b,
'*': lambda a, b: a * b,
'/': lambda a, b: a / b,
}
for char in E:
if char not in operation:
stack.append(int(char))
continue
b = stack.pop()
a = stack.pop()
stack.append(int(operation[char](a, b)))
return stack[0]
================================================
FILE: leetcode/157_read_n_characters_given_read4.py
================================================
"""
The read4 API is already defined for you.
@param buf, a list of characters
@return an integer
def read4(buf):
pass
"""
class Solution:
def read(self, buf, n):
"""
:type buf: List[str], Destination buffer
:type n: int, Maximum number of characters to read
:rtype: int, The number of characters read
"""
if not buf or n <= 0:
return 0
i = 0
k = 4
tmp = [0] * k
while i < n and k == 4:
k = read4(tmp)
j = 0
while i < n and j < k:
buf[j] = tmp[j]
i += 1
j += 1
return i
if __name__ == '__main__':
data = 'abcdferrdsjfklsdjfdsr'
n = len(data)
i = 0
k = 4
def read4(buf):
global i
j = 0
while i < n and j < k:
buf[j] = data[i]
i += 1
j += 1
return j
s = Solution()
res = s.read([0] * 4, 4)
print(res)
================================================
FILE: leetcode/160_intersection_of_two_linked_lists.py
================================================
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def getIntersectionNode(self, A, B):
"""
:type A, B: ListNode
:rtype: ListNode
"""
if not A or not B:
return
X, Y = A, B
while X and Y:
X = X.next
Y = Y.next
while X:
X = X.next
A = A.next
while Y:
Y = Y.next
B = B.next
while A is not B:
A = A.next
B = B.next
return A
================================================
FILE: leetcode/165_compare_version_numbers.py
================================================
class Solution:
def compareVersion(self, version1, version2):
"""
:type version1: str
:type version2: str
:rtype: int
"""
if not version1 and not version2:
return 0
if not version1:
return -1
if not version2:
return 1
v = version1.split('.')
w = version2.split('.')
m, n = len(v), len(w)
for i in range(max(m, n)):
a = self.get_int(v[i]) if i < m else 0
b = self.get_int(w[i]) if i < n else 0
if a < b:
return -1
elif a > b:
return 1
return 0
def get_int(self, s):
if not s or not s.isdigit():
return 0
res = 0
zero = ord('0')
for c in s:
res = res * 10 + ord(c) - zero
return res
================================================
FILE: leetcode/166_fraction_to_recurring_decimal.py
================================================
class Solution:
def fractionToDecimal(self, a, b):
"""
:type a: int
:type b: int
:rtype: str
"""
if not b:
return ''
if not a:
return '0'
ans = []
if a ^ b < 0:
ans.append('-')
if a < 0:
a = -a
if b < 0:
b = -b
ans.append(str(a // b))
a %= b
if a == 0:
return ''.join(ans)
ans.append('.')
D = {a: len(ans)} # the index of first occurrence of `a`
while a:
a *= 10
ans.append(str(a // b))
a %= b
if a in D:
ans.insert(D[a], '(')
ans.append(')')
break
D[a] = len(ans)
return ''.join(ans)
================================================
FILE: leetcode/169_majority_element.py
================================================
class Solution:
def majorityElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if not nums:
return 0
ans = None
cnt = 0
for num in nums:
if cnt == 0:
ans, cnt = num, 1
elif ans == num:
cnt += 1
else:
cnt -= 1
return ans
class Solution:
def majorityElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if not nums:
return 0
nums.sort()
return nums[len(nums) // 2]
class Solution:
def majorityElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
NOT_FOUND = 0
if not nums:
return NOT_FOUND
freq = {}
for a in nums:
freq[a] = freq.get(a, 0) + 1
for a, cnt in freq.items():
if cnt > len(nums) // 2:
return a
return NOT_FOUND
================================================
FILE: leetcode/171_excel_sheet_column_number.py
================================================
class Solution:
def titleToNumber(self, s):
"""
:type s: str
:rtype: int
"""
if not s:
return 0
s = s.upper()
ans = 0
BASE = ord('A') - 1
for char in s:
ans = ans * 26 + ord(char) - BASE
return ans
================================================
FILE: leetcode/172_factorial_trailing_zeroes.py
================================================
class Solution:
def trailingZeroes(self, n):
"""
:type n: int
:rtype: int
"""
ans = 0
if not n:
return ans
i = 5
while n // i > 0:
ans += n // i
i *= 5
return ans
================================================
FILE: leetcode/174_dungeon_game.py
================================================
class Solution:
def calculateMinimumHP(self, G):
"""
:type G: List[List[int]]
:rtype: int
"""
INFINITY = float('inf')
m, n = len(G), len(G[0])
dp = [[INFINITY] * (n + 1) for _ in range(m + 1)]
dp[m][n - 1] = 1
dp[m - 1][n] = 1
for i in range(m - 1, -1, -1):
for j in range(n - 1, -1, -1):
dp[i][j] = min(dp[i + 1][j], dp[i][j + 1]) - G[i][j]
if dp[i][j] <= 0:
dp[i][j] = 1
return dp[0][0]
================================================
FILE: leetcode/179_largest_number.py
================================================
"""
REF: https://leetcode.com/articles/largest-number/
"""
class LgSort(str):
def __lt__(a, b):
return a + b > b + a
class Solution:
def largestNumber(self, A):
"""
:type A: List[int]
:rtype: str
"""
A = ''.join(sorted(map(str, A), key=LgSort))
return '0' if A[0] == '0' else A
================================================
FILE: leetcode/17_letter_combinations_of_a_phone_number.py
================================================
class Solution:
def letterCombinations(self, s):
"""
:type s: str
:rtype: List[str]
"""
if not s:
return []
L = {
'2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl',
'6': 'mno', '7': 'pqrs', '8': 'tuv', '9': 'wxyz',
}
queue, _queue = [], []
for d in s:
if d not in L:
return []
if not queue:
queue.extend(list(L[d]))
continue
for c in L[d]:
for _c in queue:
_queue.append(_c + c)
queue, _queue = _queue, []
queue.sort()
return queue
================================================
FILE: leetcode/190_reverse_bits.py
================================================
# Reverse bit every 4 bits
class Solution:
# @param n, an integer
# @return an integer
def reverseBits(self, n):
if not n:
return 0
# the reversed bins for i = 0 -> 15
rev = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15]
ans = 0
msk = 15 # 15 (10) == 1111 (2)
for i in range(8):
ans <<= 4
curr = n & msk
ans |= rev[curr]
n >>= 4
return ans
# Reverse bit every 1 bits
class Solution:
# @param n, an integer
# @return an integer
def reverseBits(self, n):
ans = 0
if not n:
return ans
for i in range(32):
ans <<= 1
ans |= n & 1
n >>= 1
return ans
================================================
FILE: leetcode/191_number_of_1_bits.py
================================================
class Solution:
def hammingWeight(self, n):
"""
:type n: int
:rtype: int
"""
ans = 0
if not n:
return ans
while n != 0:
n = n & (n - 1)
ans += 1
return ans
================================================
FILE: leetcode/19_remove_nth_node_from_end_of_list.py
================================================
"""
Definition for singly-linked list.
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
"""
class Solution:
def removeNthFromEnd(self, head, n):
"""
:type head: ListNode
:type n: int
:rtype: ListNode
"""
if not head or not n:
return head
dummy = slow = ListNode(0)
dummy.next = fast = head
while fast and n:
n -= 1
fast = fast.next
while slow and fast:
slow = slow.next
fast = fast.next
if slow and slow.next:
slow.next = slow.next.next
return dummy.next
================================================
FILE: leetcode/202_happy_number.py
================================================
class Solution:
def isHappy(self, n):
"""
:type n: int
:rtype: bool
"""
slow = self.square_sum(n)
fast = self.square_sum(slow)
while slow != fast:
slow = self.square_sum(slow)
fast = self.square_sum(
self.square_sum(fast)
)
return True if slow == 1 else False
def square_sum(self, a):
res = b = 0
while a != 0:
b = a % 10
res += b * b
a //= 10
return res
================================================
FILE: leetcode/204_count_primes.py
================================================
"""
REF: https://discuss.leetcode.com/topic/14036/fast-python-solution
Count the number of prime numbers LESS THAN a non-negative number, n.
"""
class Solution:
def countPrimes(self, n):
"""
:type n: int
:rtype: int
"""
# note that 2 is prime
# but there is no prime less than 2
# so return 0 if n == 2
if not n or n < 3:
return 0
is_prime = [True] * n
is_prime[0] = is_prime[1] = False
for i in range(2, int(n ** 0.5) + 1):
if not is_prime[i]:
continue
for j in range(i * i, n, i):
is_prime[j] = False
return sum(is_prime)
================================================
FILE: leetcode/208_implement_trie_prefix_tree.py
================================================
class TrieNode:
def __init__(self, val=None):
self.end_at = val
self.children = {}
class Trie:
def __init__(self):
self.trie = TrieNode()
def insert(self, word):
"""
Inserts a word into the trie.
:type word: str
:rtype: void
"""
if not word:
return
node = self.trie
for char in word:
if char not in node.children:
node.children[char] = TrieNode()
node = node.children[char]
node.end_at = word
def search(self, word):
"""
Returns if the word is in the trie.
:type word: str
:rtype: bool
"""
if not word:
return False
node = self.trie
for char in word:
if char not in node.children:
return False
node = node.children[char]
return node.end_at == word
def startsWith(self, prefix):
"""
Returns if there is any word in the trie that starts with the given prefix.
:type prefix: str
:rtype: bool
"""
if not prefix:
return False
node = self.trie
for char in prefix:
if char not in node.children:
return False
node = node.children[char]
return True
# Your Trie object will be instantiated and called as such:
# obj = Trie()
# obj.insert(word)
# param_2 = obj.search(word)
# param_3 = obj.startsWith(prefix)
================================================
FILE: leetcode/20_valid_parentheses.py
================================================
class Solution:
def isValid(self, s):
"""
:type s: str
:rtype: bool
"""
stack = []
pairs = {')': '(', ']': '[', '}': '{'}
for c in s:
if c in '([{':
stack.append(c)
elif c not in ')]}':
return False
elif not stack or pairs[c] != stack.pop():
return False
return not stack
================================================
FILE: leetcode/215_kth_largest_element_in_an_array.py
================================================
from heapq import heappop, heappush
class Solution:
def findKthLargest(self, A, k):
"""
:type A: List[int]
:type k: int
:rtype: int
"""
heap = []
for a in A:
heappush(heap, a)
if len(heap) > k:
heappop(heap)
return heappop(heap)
================================================
FILE: leetcode/217_contains_duplicate.py
================================================
class Solution:
def containsDuplicate(self, A):
"""
:type A: List[int]
:rtype: bool
"""
if not A:
return False
S = set()
for a in A:
if a in S:
return True
S.add(a)
return False
================================================
FILE: leetcode/218_the_skyline_problem.py
================================================
"""
REF: https://briangordon.github.io/2014/08/the-skyline-problem.html
"""
import heapq
class HashHeapq:
def __init__(self):
self.heap = []
self.deleted = {}
def push(self, val):
heapq.heappush(self.heap, val)
def pop(self):
if self.is_empty():
return -1
return heapq.heappop(self.heap)
def remove(self, val):
if self.is_empty():
return
if val not in self.deleted:
self.deleted[val] = 0
self.deleted[val] += 1
def top(self):
if self.is_empty():
return -1
return self.heap[0]
def is_empty(self):
while self.heap and self.deleted.get(self.heap[0]):
val = heapq.heappop(self.heap)
self.deleted[val] -= 1
return not self.heap
class Solution:
def getSkyline(self, buildings):
"""
:type buildings: List[List[int]]
:rtype: List[List[int]]
"""
ans = []
if not buildings:
return ans
time = []
for x, _x, height in buildings:
time.append((x, height, True))
time.append((_x, height, False))
time.sort()
heap = HashHeapq()
for x, height, is_start in time:
if is_start:
heap.push(-height)
else:
heap.remove(-height)
max_h = -heap.top() if not heap.is_empty() else 0
if ans and ans[-1][0] == x:
ans.pop()
if ans and ans[-1][1] == max_h:
continue
ans.append([x, max_h])
return ans
================================================
FILE: leetcode/224_basic_calculator.py
================================================
class Solution:
def calculate(self, s):
"""
:type s: str
:rtype: int
"""
if not s:
return 0
self.OP = {
'+': lambda a, b: a + b,
'-': lambda a, b: a - b,
}
s = self.to_rpn(s)
if not s:
return 0
return self.eval_rpn(s)
def to_rpn(self, s):
stack, res = [], []
for i in range(len(s)):
char = s[i]
if i > 0 and s[i - 1].isdigit() and char.isdigit():
res[-1] += char
elif char.isdigit():
res.append(char)
elif char in self.OP:
while stack and stack[-1] in self.OP:
res.append(stack.pop())
stack.append(char)
elif char == '(':
stack.append(char)
elif char == ')':
while stack and stack[-1] != '(':
res.append(stack.pop())
stack.pop()
while stack:
res.append(stack.pop())
return res
def eval_rpn(self, s):
stack = []
for char in s:
if char.isdigit():
stack.append(int(char))
elif char in self.OP:
b = stack.pop()
a = stack.pop()
stack.append(self.OP[char](a, b))
return stack[0]
class Solution:
def calculate(self, s):
"""
:type s: str
:rtype: int
"""
if not s:
return 0
s = self.to_rpn(s)
if not s:
return 0
return self.eval_rpn(s)
def to_rpn(self, s):
stack, res = [], []
for i in range(len(s)):
char = s[i]
if i > 0 and s[i - 1].isdigit() and char.isdigit():
res[-1] += char
elif char.isdigit():
res.append(char)
elif char in '+-':
while stack and stack[-1] in '+-':
res.append(stack.pop())
stack.append(char)
elif char == '(':
stack.append(char)
elif char == ')':
while stack and stack[-1] != '(':
res.append(stack.pop())
stack.pop()
while stack:
res.append(stack.pop())
return res
def eval_rpn(self, s):
stack = []
for char in s:
if char.isdigit():
stack.append(int(char))
elif char in '+-':
b = stack.pop()
a = stack.pop()
if char == '+':
stack.append(a + b)
elif char == '-':
stack.append(a - b)
return stack[0]
================================================
FILE: leetcode/227_basic_calculator_ii.py
================================================
class Solution:
def calculate(self, s):
"""
:type s: str
:rtype: int
"""
if not s:
return 0
s = self.to_rpn(s, {
'+': 1,
'-': 1,
'*': 2,
'/': 2,
})
if not s:
return 0
return self.eval_rpn(s, {
'+': lambda a, b: a + b,
'-': lambda a, b: a - b,
'*': lambda a, b: a * b,
'/': lambda a, b: a // b,
})
def to_rpn(self, s, P):
stack, res = [], []
for i in range(len(s)):
char = s[i]
if i > 0 and s[i - 1].isdigit() and char.isdigit():
res[-1] += char
elif char.isdigit():
res.append(char)
elif char in P:
while stack and stack[-1] in P and P[char] <= P[stack[-1]]:
res.append(stack.pop())
stack.append(char)
elif char == '(':
stack.append(char)
elif char == ')':
while stack and stack[-1] != '(':
res.append(stack.pop())
stack.pop()
while stack:
res.append(stack.pop())
return res
def eval_rpn(self, s, OP):
stack = []
for char in s:
if char.isdigit():
stack.append(int(char))
elif char in OP:
b = stack.pop()
a = stack.pop()
stack.append(OP[char](a, b))
return stack[0]
class Solution:
def calculate(self, s):
"""
:type s: str
:rtype: int
"""
if not s:
return 0
s = self.to_rpn(s)
if not s:
return 0
return self.eval_rpn(s)
def to_rpn(self, s):
stack, res = [], []
for i in range(len(s)):
char = s[i]
if i > 0 and s[i - 1].isdigit() and char.isdigit():
res[-1] += char
elif char.isdigit():
res.append(char)
elif char in '+-*/':
while stack and stack[-1] in '+-*/':
if char in '*/' and stack[-1] in '+-':
break
res.append(stack.pop())
stack.append(char)
elif char == '(':
stack.append(char)
elif char == ')':
while stack and stack[-1] != '(':
res.append(stack.pop())
stack.pop()
while stack:
res.append(stack.pop())
return res
def eval_rpn(self, s):
stack = []
for char in s:
if char.isdigit():
stack.append(int(char))
elif char in '+-*/':
b = stack.pop()
a = stack.pop()
if char == '+':
stack.append(a + b)
elif char == '-':
stack.append(a - b)
elif char == '*':
stack.append(a * b)
elif char == '/':
stack.append(a // b)
return stack[0]
================================================
FILE: leetcode/229_majority_element_ii.py
================================================
class Solution:
def majorityElement(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
ans = []
if not nums:
return []
a1 = a2 = None
c1 = c2 = 0
for num in nums:
if a1 == num:
c1 += 1
elif a2 == num:
c2 += 1
elif c1 == 0:
a1, c1 = num, 1
elif c2 == 0:
a2, c2 = num, 1
else:
c1 -= 1
c2 -= 1
c1 = c2 = 0
for num in nums:
if num == a1:
c1 += 1
elif num == a2:
c2 += 1
for a, c in ((a1, c1), (a2, c2)):
if c > len(nums) // 3:
ans.append(a)
return ans
================================================
FILE: leetcode/22_generate_parentheses.py
================================================
class Solution:
def generateParenthesis(self, n):
"""
:type n: int
:rtype: list[str]
"""
ans = []
if not n:
return ans
self.dfs(n, 1, 0, ans, ['('])
return ans
def dfs(self, n, lcnt, rcnt, ans, path):
if len(path) == 2 * n:
ans.append(''.join(path))
return
if rcnt < lcnt:
path.append(')')
self.dfs(n, lcnt, rcnt + 1, ans, path)
path.pop()
if lcnt < n:
path.append('(')
self.dfs(n, lcnt + 1, rcnt, ans, path)
path.pop()
================================================
FILE: leetcode/230_kth_smallest_element_in_a_bst.py
================================================
"""
Definition for a binary tree node.
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
"""
class Solution:
def kthSmallest(self, root, k):
"""
:type root: TreeNode
:type k: int
:rtype: int
"""
if not root:
return
stack = []
node = root
while node or stack:
while node:
stack.append(node)
node = node.left
node = stack.pop()
k -= 1
if k == 0:
return node.val
node = node.right
================================================
FILE: leetcode/234_palindrome_linked_list.py
================================================
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def isPalindrome(self, head):
"""
:type head: ListNode
:rtype: bool
"""
rev = None
slow = fast = head
while fast and fast.next:
fast = fast.next.next
rev, rev.next, slow = slow, rev, slow.next
if fast:
slow = slow.next
while slow and slow.val == rev.val:
slow = slow.next
rev = rev.next
return not rev
class Solution:
def isPalindrome(self, head):
"""
:type head: ListNode
:rtype: bool
"""
rev = nxt = None
slow = fast = head
while fast and fast.next:
fast = fast.next.next
nxt = slow.next
slow.next = rev
rev = slow
slow = nxt
if fast:
slow = slow.next
while slow and slow.val == rev.val:
slow = slow.next
rev = rev.next
return not rev
================================================
FILE: leetcode/235_lowest_common_ancestor_of_a_binary_search_tree.py
================================================
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def lowestCommonAncestor(self, root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
while root:
if root.val > p.val and root.val > q.val:
root = root.left
elif root.val < p.val and root.val < q.val:
root = root.right
else:
return root
================================================
FILE: leetcode/236_lowest_common_ancestor_of_a_binary_tree.py
================================================
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def lowestCommonAncestor(self, root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
if not root or root is p or root is q:
return root
left = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p, q)
if left and right:
return root
if left:
return left
if right:
return right
================================================
FILE: leetcode/237_delete_node_in_a_linked_list.py
================================================
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def deleteNode(self, node):
"""
:type node: ListNode
:rtype: void Do not return anything, modify node in-place instead.
"""
if not node or not node.next:
return
node.val = node.next.val
node.next = node.next.next
================================================
FILE: leetcode/242_valid_anagram.py
================================================
class Solution:
def isAnagram(self, s, t):
"""
:type s: str
:type t: str
:rtype: bool
"""
if s == t:
return True
if not s or not t or len(s) != len(t):
return False
freq = {}
for c in s:
freq[c] = freq.get(c, 0) + 1
for c in t:
if c not in freq:
return False
freq[c] -= 1
return all(v == 0 for v in freq.values())
================================================
FILE: leetcode/269_alien_dictionary.py
================================================
class Solution:
def alienOrder(self, words):
"""
:type words: list[str]
:rtype: str
"""
if not words:
return ''
ans = []
gotcha = set()
max_size = max(len(word) for word in words)
for j in range(max_size):
for word in words:
if j >= len(word):
continue
if word[j] in gotcha:
continue
ans.append(word[j])
gotcha.add(word[j])
return ''.join(ans)
class Solution:
"""
REF: https://discuss.leetcode.com/topic/28308/java-ac-solution-using-bfs/2
"""
def alienOrder(self, words):
if not words:
return ''
ans = []
edges = {}
indeg = {}
for w in words:
for c in w:
indeg[c] = 0
for i in range(len(words) - 1):
cur = words[i]
nxt = words[i + 1]
for j in range(min(len(cur), len(nxt))):
if cur[j] == nxt[j]:
continue
if cur[j] not in edges:
edges[cur[j]] = set()
if nxt[j] not in edges[cur[j]]:
edges[cur[j]].add(nxt[j])
indeg[nxt[j]] = indeg.get(nxt[j], 0) + 1
break
queue = [c for c, deg in indeg.items() if deg == 0]
for c in queue:
ans.append(c)
if c not in edges:
continue
for _c in edges[c]:
indeg[_c] -= 1
if indeg[_c] == 0:
queue.append(_c)
return ''.join(ans) if len(ans) == len(indeg) else ''
================================================
FILE: leetcode/277_find_the_celebrity.py
================================================
"""
REF: https://discuss.leetcode.com/topic/25720/java-python-o-n-calls-o-1-space-easy-to-understand-solution
"""
class Solution:
def findCelebrity(self, n):
if not n:
return -1
x = 0
for i in range(n):
if knows(x, i):
x = i
for i in range(x):
if knows(x, i):
return -1
for i in range(n):
if not knows(i, x):
return -1
return x
================================================
FILE: leetcode/280_wiggle_sort.py
================================================
class Solution:
def wiggleSort(self, nums):
"""
:type nums: list[int]
:rtype: void, Do not return anything, modify A in-place instead.
"""
if not nums:
return
for i in range(1, len(nums)):
if i & 1 == 1 and nums[i] >= nums[i - 1]:
continue
if i & 1 == 0 and nums[i] <= nums[i - 1]:
continue
nums[i], nums[i - 1] = nums[i - 1], nums[i]
================================================
FILE: leetcode/282_expression_add_operators.py
================================================
"""
Main Concept:
in product case, needs to remove product in last recursion, and adds the product in current.
"""
class Solution:
def addOperators(self, s, target):
"""
:type s: str
:type target: int
:rtype: List[str]
"""
ans = []
if not s:
return ans
self.dfs(s, 0, target, 0, 0, ans, [])
return ans
def dfs(self, s, start, target, val, multi, ans, path):
if start == len(s) and target == val:
ans.append(''.join(path))
return
if start >= len(s):
return
for i in range(start, len(s)):
if i > start and s[start] == '0':
# only allow i == start and its `0`
break
sa = s[start:i + 1]
a = int(sa)
if start == 0:
self.dfs(s, i + 1, target, a, a, ans, [sa])
continue
self.dfs(s, i + 1, target, val + a, a, ans, path + ['+', sa])
self.dfs(s, i + 1, target, val - a, -a, ans, path + ['-', sa])
self.dfs(s, i + 1, target, val - multi + multi * a, multi * a, ans, path + ['*', sa])
================================================
FILE: leetcode/285_inorder_successor_in_bst.py
================================================
"""
Definition for a binary tree node.
>>> class TreeNode:
... def __init__(self, x):
... self.val = x
... self.left = None
... self.right = None
... self.parent = None
>>> trees = []
>>> tree_infos = [
... ((
... (1, None, None, 2),
... (2, 1, None, None),
... ), '1,2'),
... ((
... (20, None, 8, 22),
... (8, 20, 4, 12),
... (22, 20, None, None),
... (4, 8, None, None),
... (12, 8, 10, 14),
... (10, 12, None, None),
... (14, 12, None, None),
... ), '4,8,10,12,14,20,22'),
... ((
... (10, None, 5, 100),
... (5, 10, None, None),
... (100, 10, 80, 120),
... (80, 100, None, None),
... (120, 100, None, None),
... ), '5,10,80,100,120'),
... ((
... (1, None, None, 2),
... (2, 1, None, 3),
... (3, 2, None, 4),
... (4, 3, None, 5),
... (5, 4, None, 6),
... (6, 5, None, 7),
... (7, 6, None, None),
... ), '1,2,3,4,5,6,7'),
... ]
>>> for info, _ in tree_infos:
... nodes = {node[0]: TreeNode(node[0]) for node in info}
...
... for val, parent, left, right in info:
... if parent:
... nodes[val].parent = nodes[parent]
... if left:
... nodes[val].left = nodes[left]
... if right:
... nodes[val].right = nodes[right]
...
... trees.append(nodes[info[0][0]])
>>> gotcha = []
>>> for _in, _out in (
... ((trees[0], trees[0]), trees[0].right),
... ((trees[1], trees[1].left.right.right), trees[1]),
... ):
... for s in (Solution(), Solution2(), Solution3(), Solution4()):
... res = s.inorderSuccessor(*_in)
... if res is not _out: print(_in[0].val, res.val)
... gotcha.append(res is _out)
>>> bool(gotcha) and all(gotcha)
True
"""
class Solution:
"""
time: O(log n) or O(h), `n` is the number of nodes and `h` is the tree height
1. if `target` has right child, then successor lies at the most-left child in right child of `target`
2. otherwise, just traverse down from root, and record `ans` when every time go left
"""
def inorderSuccessor(self, root, target):
"""
:type root: TreeNode
:type target: TreeNode
:rtype: TreeNode
"""
if not root or not target:
return
ans = None
if target.right:
ans = target.right
while ans and ans.left:
ans = ans.left
return ans
while root and target.val != root.val:
if target.val < root.val:
ans = root
root = root.left
else:
root = root.right
return ans
class Solution2:
"""
time: O(n), `n` is the number of nodes
just do inorder traverse
"""
def inorderSuccessor(self, root, target):
"""
:type root: TreeNode
:type target: TreeNode
:rtype: TreeNode
"""
if not root or not target:
return
stack = []
node = root
got_target = False
while node or stack:
while node:
stack.append(node)
node = node.left
node = stack.pop()
if got_target:
return node
if node.val == target.val:
got_target = True
node = node.right
class Solution3:
"""
* every node has `parent` pointer
time: O(log n) or O(h), `n` is the number of nodes and `h` is the tree height
1. if `target` has right child, then successor lies at the most-left child in right child of `target`
2. otherwise, just traverse top to root
"""
def inorderSuccessor(self, root, target):
"""
:type root: TreeNode
:type target: TreeNode
:rtype: TreeNode
"""
if not root or not target:
return
ans = None
if target.right:
ans = target.right
while ans and ans.left:
ans = ans.left
return ans
ans = target.parent
while ans and target is ans.right:
target = ans
ans = ans.parent
return ans
class Solution4:
"""
* every node has `parent` pointer
time: O(log n) or O(h), `n` is the number of nodes and `h` is the tree height
1. if `target` has right child, then successor lies at the most-left child in right child of `target`
2. otherwise, just traverse top to root
"""
def inorderSuccessor(self, root, target):
"""
:type root: TreeNode
:type target: TreeNode
:rtype: TreeNode
"""
if not root or not target:
return
if target.right:
ans = target.right
while ans and ans.left:
ans = ans.left
return ans
ans = target.parent
while ans and ans.val < target.val:
ans = ans.parent
return ans
================================================
FILE: leetcode/288_unique_word_abbreviation.py
================================================
"""
Your ValidWordAbbr object will be instantiated and called as such:
vwa = ValidWordAbbr(dictionary)
vwa.isUnique("word")
vwa.isUnique("anotherWord")
Testing:
>>> s = ValidWordAbbr(['deer', 'door', 'cake', 'card'])
>>> all((
... s.isUnique('dear') is False,
... s.isUnique('cart') is True,
... s.isUnique('cane') is False,
... s.isUnique('make') is True,
... ))
True
"""
class ValidWordAbbr:
def __init__(self, dictionary):
"""
initialize your data structure here.
:type dictionary: List[str]
"""
self.abbrs = {}
for word in dictionary:
abbr = self.abbreviation(word)
self.abbrs[abbr] = word
def isUnique(self, word):
"""
check if a word is unique.
:type word: str
:rtype: bool
"""
abbr = self.abbreviation(word)
return abbr not in self.abbrs
def abbreviation(self, word):
if len(word) < 3:
return word
cnt = len(word) - 2
return '{}{}{}'.format(
word[0],
str(cnt),
word[-1]
)
================================================
FILE: leetcode/289_game_of_life.py
================================================
"""
REF: https://leetcode.com/problems/game-of-life/discuss/73223
lives < 2 => 1 -> 0
lives == 2 or 3 => 1 -> 1
lives > 3 => 1 -> 0
lives == 3 => 0 -> 1
"""
class Solution:
"""
Use bits to save the status in next round
"""
def gameOfLife(self, board):
"""
:type board: List[List[int]]
:rtype: void Do not return anything, modify board in-place instead.
"""
if not board or not board[0]:
return
m, n = len(board), len(board[0])
for x in range(m):
for y in range(n):
lives = self.get_live_neibs(board, x, y)
if board[x][y] == 1 and lives in (2, 3):
board[x][y] = 3
elif board[x][y] == 0 and lives == 3:
board[x][y] = 2
for x in range(m):
for y in range(n):
board[x][y] >>= 1
def get_live_neibs(self, board, x, y):
cnt = 0
m, n = len(board), len(board[0])
for dx in (-1, 0, 1):
for dy in (-1, 0, 1):
if dx == 0 and dy == 0:
continue
_x = x + dx
_y = y + dy
if not (0 <= _x < m and 0 <= _y < n):
continue
cnt += board[_x][_y] & 1
return cnt
class Solution:
"""
Not in-place solution
"""
def gameOfLife(self, board):
"""
:type board: List[List[int]]
:rtype: void Do not return anything, modify board in-place instead.
"""
if not board or not board[0]:
return
m, n = len(board), len(board[0])
ans = [[0] * n for _ in range(m)]
for x in range(m):
for y in range(n):
lives = self.get_live_neibs(board, x, y)
ans[x][y] = board[x][y]
if board[x][y] == 1 and lives < 2:
ans[x][y] = 0
elif board[x][y] == 1 and lives in (2, 3):
ans[x][y] = 1
elif board[x][y] == 1 and lives > 3:
ans[x][y] = 0
elif board[x][y] == 0 and lives == 3:
ans[x][y] = 1
# return ans
# hacking for in-place
for x in range(m):
board[x][:] = ans[x][:]
def get_live_neibs(self, board, x, y):
cnt = 0
m, n = len(board), len(board[0])
for dx in (-1, 0, 1):
for dy in (-1, 0, 1):
if dx == 0 and dy == 0:
continue
_x = x + dx
_y = y + dy
if not (
0 <= _x < m and
0 <= _y < n and
board[_x][_y] == 1
):
continue
cnt += 1
return cnt
================================================
FILE: leetcode/295_find_median_from_data_stream.py
================================================
"""
Your MedianFinder object will be instantiated and called as such:
obj = MedianFinder()
obj.addNum(num)
param_2 = obj.findMedian()
"""
import heapq
class MedianFinder:
def __init__(self):
self.minheap = []
self.maxheap = []
def addNum(self, num):
"""
:type num: int
:rtype: void
"""
if self.minheap and num < self.minheap[0]:
heapq.heappush(self.maxheap, -num)
else:
heapq.heappush(self.minheap, num)
def findMedian(self):
"""
:rtype: float
"""
if not self.minheap:
return 0.0
# to handle odd case, it make sure `minheap` has one more child than `maxheap`
while len(self.minheap) > len(self.maxheap) + 1:
heapq.heappush(self.maxheap, - heapq.heappop(self.minheap))
# to handle even case, it make sure `minheap` and `maxheap` are same size
while len(self.maxheap) > len(self.minheap):
heapq.heappush(self.minheap, - heapq.heappop(self.maxheap))
if len(self.minheap) > len(self.maxheap):
return self.minheap[0] * 1.0
# since the child in maxheap is negative
return (self.minheap[0] - self.maxheap[0]) / 2.0
================================================
FILE: leetcode/299_bulls_and_cows.py
================================================
class Solution:
def getHint(self, secret, guess):
"""
:type secret: str
:type guess: str
:rtype: str
"""
if not secret or not guess or len(secret) != len(guess):
return ''
TMPL = '{}A{}B'
bulls = 0
cows = 0
cnts = [0] * 10
for i in range(len(secret)):
s = ord(secret[i]) - ord('0')
g = ord(guess[i]) - ord('0')
if s == g:
bulls += 1
continue
cnts[s] += 1
cnts[g] -= 1
if cnts[s] <= 0:
cows += 1
if cnts[g] >= 0:
cows += 1
return TMPL.format(bulls, cows)
class Solution:
def getHint(self, secret, guess):
"""
:type secret: str
:type guess: str
:rtype: str
"""
if not secret or not guess or len(secret) != len(guess):
return ''
TMPL = '{}A{}B'
bulls = 0
cows = 0
cnt_s = [0] * 10
cnt_g = [0] * 10
for i in range(len(secret)):
if secret[i] == guess[i]:
bulls += 1
else:
cnt_s[int(secret[i])] += 1
cnt_g[int(guess[i])] += 1
for i in range(10):
cows += min(cnt_s[i], cnt_g[i])
return TMPL.format(bulls, cows)
================================================
FILE: leetcode/303_range_sum_query_immutable.py
================================================
"""
Your NumArray object will be instantiated and called as such:
obj = NumArray(nums)
param_1 = obj.sumRange(i,j)
"""
class NumArray:
def __init__(self, nums):
"""
:type nums: List[int]
"""
if not nums:
return
n = len(nums)
self.prefix_sum = [0] * (n + 1)
for i in range(1, n + 1):
self.prefix_sum[i] = self.prefix_sum[i - 1] + nums[i - 1]
def sumRange(self, i, j):
"""
:type i: int
:type j: int
:rtype: int
"""
if (
not self.prefix_sum or
i < 0 or
j + 1 >= len(self.prefix_sum)
):
return 0
return self.prefix_sum[j + 1] - self.prefix_sum[i]
================================================
FILE: leetcode/304_range_sum_query_2d_immutable.py
================================================
"""
Your NumMatrix object will be instantiated and called as such:
obj = NumMatrix(matrix)
param_1 = obj.sumRegion(x1, y1, x2, y2)
"""
class NumMatrix:
def __init__(self, matrix):
"""
:type matrix: List[List[int]]
"""
if not matrix or not matrix[0]:
return
m, n = len(matrix), len(matrix[0])
self.prefix_sum = [[0] * (n + 1) for _ in range(m + 1)]
for x in range(1, m + 1):
for y in range(1, n + 1):
self.prefix_sum[x][y] = sum((
self.prefix_sum[x - 1][y],
self.prefix_sum[x][y - 1],
- self.prefix_sum[x - 1][y - 1],
matrix[x - 1][y - 1],
))
def sumRegion(self, x1, y1, x2, y2):
"""
:type x1: int
:type y1: int
:type x2: int
:type y2: int
:rtype: int
"""
if not all((
self.prefix_sum,
self.prefix_sum[0],
0 <= x1 < len(self.prefix_sum),
0 <= x2 + 1 < len(self.prefix_sum),
0 <= y1 < len(self.prefix_sum[0]),
0 <= y2 + 1 < len(self.prefix_sum[0]),
)):
return 0
return sum((
self.prefix_sum[x2 + 1][y2 + 1],
- self.prefix_sum[x2 + 1][y1],
- self.prefix_sum[x1][y2 + 1],
self.prefix_sum[x1][y1],
))
================================================
FILE: leetcode/307_range_sum_query_mutable.py
================================================
"""
REF: https://cs.stackexchange.com/questions/10538/bit-what-is-the-intuition-behind-a-binary-indexed-tree-and-how-was-it-thought-a
REF: http://www.cnblogs.com/grandyang/p/4985506.html
Your NumArray object will be instantiated and called as such:
obj = NumArray(nums)
obj.update(i,val)
param_2 = obj.sumRange(i,j)
"""
class NumArray:
def __init__(self, nums):
"""
:type nums: List[int]
"""
if not nums:
return
n = len(nums)
self.bits = [0] * (n + 1) # bits
self.incr = [0] * (n + 1) # increments
for i in range(n):
self.update(i, nums[i])
def update(self, i, val):
"""
:type i: int
:type val: int
:rtype: void
It must increase `i` here, since this api is public,
so look from outside, the `i` is just the index of `nums`
"""
j = i + 1
delta = val - self.incr[j]
self.incr[j] = val
while j < len(self.incr):
self.bits[j] += delta
j += (j & -j)
def sumRange(self, i, j):
"""
:type i: int
:type j: int
:rtype: int
"""
return self.sum(j + 1) - self.sum(i)
def sum(self, i):
res = 0
j = i
while j > 0:
res += self.bits[j]
j -= (j & -j)
return res
================================================
FILE: leetcode/308_range_sum_query_2d_mutable.py
================================================
"""
Your NumMatrix object will be instantiated and called as such:
obj = NumMatrix(matrix)
obj.update(x,y,val)
param_1 = obj.sumRegion(x1,y1,x2,y2)
"""
class NumMatrix:
def __init__(self, matrix):
"""
:type matrix: List[List[int]]
"""
if not matrix or not matrix[0]:
return
m, n = len(matrix), len(matrix[0])
self.bits = [[0] * (n + 1) for _ in range(m + 1)] # bits
self.incr = [[0] * (n + 1) for _ in range(m + 1)] # increments
for x in range(m):
for y in range(n):
self.update(x, y, matrix[x][y])
def update(self, x, y, val):
"""
:type x: int
:type y: int
:type val: int
:rtype: void
"""
i = x + 1
j = y + 1
delta = val - self.incr[i][j]
self.incr[i][j] = val
m, n = len(self.incr), len(self.incr[0])
while i < m:
j = y + 1
while j < n:
self.bits[i][j] += delta
j += (j & -j)
i += (i & -i)
def sumRegion(self, x1, y1, x2, y2):
"""
:type x1: int
:type y1: int
:type x2: int
:type y2: int
:rtype: int
"""
return sum((
self.sum(x2 + 1, y2 + 1),
- self.sum(x1, y2 + 1),
- self.sum(x2 + 1, y1),
self.sum(x1, y1),
))
def sum(self, x, y):
res = 0
i = x
j = y
while i > 0:
j = y
while j > 0:
res += self.bits[i][j]
j -= (j & -j)
i -= (i & -i)
return res
================================================
FILE: leetcode/30_substring_with_concatenation_of_all_words.py
================================================
class Solution:
def findSubstring(self, s, S):
"""
:type s: str
:type S: List[str]
:rtype: List[int]
"""
ans = []
if not s or not S or len(s) < len(S) * len(S[0]):
return ans
n, m, k = len(s), len(S), len(S[0])
F = {}
for c in S:
F[c] = F.get(c, 0) + 1
for start in range(k):
_F = {}
cnt = 0
left = start
for right in range(start, n - k + 1, k):
sr = s[right:right + k]
if sr not in F:
_F = {}
cnt = 0
left = right + k
continue
_F[sr] = _F.get(sr, 0) + 1
if _F[sr] <= F[sr]:
cnt += 1
while _F[sr] > F[sr]:
sl = s[left:left + k]
if _F[sl] == F[sl]:
cnt -= 1
_F[sl] -= 1
left += k
if cnt == m:
ans.append(left)
sl = s[left:left + k]
cnt -= 1
_F[sl] -= 1
left += k
return ans
================================================
FILE: leetcode/315_count_of_smaller_numbers_after_self.py
================================================
class Solution:
"""
REF: https://leetcode.com/problems/count-of-smaller-numbers-after-self/discuss/76657/
"""
def countSmaller(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
if not nums:
return []
n = len(nums)
ans = [0] * n
cands = sorted(set(nums))
v2i = {cands[i]: i for i in range(len(cands))}
self.bits = [0] * (len(v2i) + 1)
for i in range(n - 1, -1, -1):
j = v2i[nums[i]]
ans[i] = self.sum(j)
self.update(j)
return ans
def update(self, i):
i += 1
while i < len(self.bits):
self.bits[i] += 1
i += (i & -i)
def sum(self, i):
res = 0
while i > 0:
res += self.bits[i]
i -= (i & -i)
return res
class Solution:
"""
Brute Force: TLE
"""
def countSmaller(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
ans = []
if not nums:
return ans
n = len(nums)
for i in range(n):
ans.append(0)
for j in range(i, n):
if nums[j] < nums[i]:
ans[-1] += 1
return ans
================================================
FILE: leetcode/323_number_of_connected_components_in_an_undirected_graph.py
================================================
"""
Testing:
>>> gotcha = []
>>> for s in (Solution(), Solution2()):
... for _in, _out in (
... ((5, [[0, 1], [1, 2], [3, 4]]), 2),
... ((5, [[0, 1], [1, 2], [2, 3], [3, 4]]), 1),
... ):
... res = s.countComponents(*_in)
... if res != _out: print(_in, res)
... gotcha.append(res == _out)
>>> bool(gotcha) and all(gotcha)
True
"""
class Solution:
"""
Union Find
"""
def countComponents(self, n, edges):
"""
:type n: int
:type edges: list[int]
:rtype: int
"""
if not n or not edges:
return 0
nodes = [i for i in range(n)]
ans = n
for a, b in edges:
if self.union(nodes, a, b):
ans -= 1
return ans
def union(self, nodes, a, b):
_a = self.find(nodes, a)
_b = self.find(nodes, b)
if _a == _b:
return False
nodes[_b] = _a
return True
def find(self, nodes, a):
if a not in nodes:
nodes[a] = a
return a
if nodes[a] == a:
return a
nodes[a] = self.find(nodes[a])
return nodes[a]
class Solution2:
"""
DFS
"""
def countComponents(self, n, edges):
"""
:type n: int
:type edges: list[int]
:rtype: int
"""
ans = 0
if not n or not edges:
return ans
adj = {}
for i in range(n):
adj[i] = set()
for a, b in edges:
adj[a].add(b)
adj[b].add(a)
visited = set()
for i in range(n):
if i in visited:
continue
ans += 1
self.dfs(i, adj, visited)
return ans
def dfs(self, a, adj, visited):
if a not in adj:
return
visited.add(a)
for b in adj[a]:
if b in visited:
continue
self.dfs(b, adj, visited)
================================================
FILE: leetcode/324_wiggle_sort_ii.py
================================================
"""
Follow Up: https://discuss.leetcode.com/topic/32929/o-n-o-1-after-median-virtual-indexing
"""
class Solution:
def wiggleSort(self, A):
"""
:type A: List[int]
:rtype: void Do not return anything, modify A in-place instead.
"""
if not A:
return
n = len(A)
S = sorted(A)
for i in range(1, n, 2):
A[i] = S.pop()
for i in range(0, n, 2):
A[i] = S.pop()
================================================
FILE: leetcode/326_power_of_three.py
================================================
class Solution:
def isPowerOfThree(self, n):
"""
:type n: int
:rtype: bool
"""
if not n:
return False
for i in (81, 27, 9, 3):
while n % i == 0:
n //= i
return n == 1
================================================
FILE: leetcode/329_longest_increasing_path_in_a_matrix.py
================================================
class Solution:
def longestIncreasingPath(self, G):
"""
:type G: List[List[int]]
:rtype: int
"""
if not G or not G[0]:
return 0
self.V = (
(-1, 0),
( 1, 0),
( 0, -1),
( 0, 1),
)
ans = 1
m, n = len(G), len(G[0])
memo = [[0] * n for _ in range(m)]
for x in range(m):
for y in range(n):
size = self.dfs(G, x, y, memo)
if size > ans:
ans = size
return ans
def dfs(self, G, x, y, memo):
if memo[x][y] > 0:
return memo[x][y]
res = 1
for dx, dy in self.V:
_x = x + dx
_y = y + dy
if not (0 <= _x < len(G) and 0 <= _y < len(G[0])):
continue
if G[x][y] >= G[_x][_y]:
continue
size = 1 + self.dfs(G, _x, _y, memo)
if size > res:
res = size
memo[x][y] = res
return res
================================================
FILE: leetcode/334_increasing_triplet_subsequence.py
================================================
class Solution:
def increasingTriplet(self, A):
"""
:type A: List[int]
:rtype: bool
"""
if not A:
return False
a = b = float('inf')
for x in A:
if x <= a:
a = x
elif x <= b:
b = x
else:
return True
return False
================================================
FILE: leetcode/33_search_in_rotated_sorted_array.py
================================================
class Solution:
def search(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
NOT_FOUND = -1
if not nums:
return NOT_FOUND
left, right = 0, len(nums) - 1
while left + 1 < right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
if nums[mid] < nums[0]:
if nums[mid] < target <= nums[right]:
left = mid
else:
right = mid
else:
if nums[left] <= target < nums[mid]:
right = mid
else:
left = mid
for mid in (left, right):
if nums[mid] == target:
return mid
return NOT_FOUND
================================================
FILE: leetcode/340_longest_substring_with_at_most_k_distinct_characters.py
================================================
"""
- count chars: minimum length: `while cnt == k` to reduce window size if possible
- count chars: maximum length: `while cnt > k` to record the maximum window size
"""
import collections
class Solution:
def lengthOfLongestSubstringKDistinct(self, s, k):
"""
:type s: str
:type k: int
:rtype: int
"""
ans = 0
if not s or not k or k < 0:
return ans
freqs = collections.defaultdict(int)
i = cnt = 0
for j in range(len(s)):
freqs[s[j]] += 1
if freqs[s[j]] == 1:
cnt += 1
while cnt > k:
freqs[s[i]] -= 1
if freqs[s[i]] == 0:
cnt -= 1
i += 1
ans = max(ans, j - i + 1)
return ans
================================================
FILE: leetcode/344_reverse_string.py
================================================
class Solution:
def reverseString(self, s):
"""
:type s: str
:rtype: str
"""
return ''.join(reversed(s))
================================================
FILE: leetcode/347_top_k_frequent_elements.py
================================================
class Solution:
def topKFrequent(self, A, k):
"""
:type A: List[int]
:type k: int
:rtype: List[int]
"""
ans = []
if not A:
return ans
F = {}
for a in A:
F[a] = F.get(a, 0) + 1
for a, _ in sorted(F.items(), key=lambda x: -x[1]):
if k == 0:
break
ans.append(a)
k -= 1
return ans
================================================
FILE: leetcode/348_design_tic_tac_toe.py
================================================
class TicTacToe(object):
PLER_A = 1
PLER_B = 2
def __init__(self, n):
"""
Initialize your data structure here.
:type n: int
"""
self.R = [0] * n
self.C = [0] * n
self.DR = 0 # only one
self.DL = 0 # only one
def move(self, x, y, player):
"""
Player {player} makes a move at ({x}, {y}).
:type x: int The row of the board.
:type y: int The column of the board.
:type player: int The player, can be either 1 or 2.
:rtype: int The current winning condition, can be either:
0: No one wins.
1: Player 1 wins.
2: Player 2 wins.
"""
n = len(self.R)
delta = 1 if player == self.PLER_A else -1
self.R[x] += delta
self.C[y] += delta
self.DR += delta if x == y else 0 # x - y == 0
self.DL += delta if x == n - 1 - y else 0 # x + y == n - 1
return (abs(self.R[x]) == n or
abs(self.C[y]) == n or
abs(self.DR) == n or
abs(self.DL) == n)
# Your TicTacToe object will be instantiated and called as such:
# obj = TicTacToe(n)
# param_1 = obj.move(x,y,player)
================================================
FILE: leetcode/353_design_snake_game.py
================================================
"""
0. self.height is for `m`, self.width is for `n`
1. note that, REMOVE FIRST,
MUST remove the snake tail **BEFORE** check it hit its body
2. this game is over if
- hit wall
- hit it self
- no food
3. dont pop food in every move, just track its index
4. the score is just the len(body) - 1, except the head
5. what its return if no food or food is all taken?
6. does allow the snake just back if its body only 2?
Initially the snake appears at position (0,0) and the food at (1,2).
Your SnakeGame object will be instantiated and called as such:
obj = SnakeGame(width, height, food)
param_1 = obj.move(direction)
>>> snake = SnakeGame(3, 2, [[1, 2], [0, 1]])
>>> [snake.move(d) for d in 'RDRULU']
[0, 0, 1, 1, 2, -1]
>>> all(snake.move(d) == -1 for d in 'LUDR')
True
>>> snake = SnakeGame(3, 3, [[2, 0], [0, 0]])
>>> [snake.move(d) for d in 'DDUU']
[0, 1, 1, 2]
>>> all(snake.move(d) == -1 for d in 'RUDL')
True
"""
import collections
class SnakeGame:
def __init__(self, width, height, food):
"""
:type width: int, screen width
:type height: int, screen height
:type food: List[List[int]], A list of food positions
E.g food = [[1,1], [1,0]] means the first food is positioned at [1,1],
the second is at [1,0].
"""
if not width or not height or not food:
# raise error
return
self.width = width
self.height = height
self.food = food
self.fi = 0
self.is_over = False
self.SCORE_IN_OVER = -1
pos = [(0, 0)]
self.snake = collections.deque(pos)
self.body = set(pos)
self.dn = {
'U': (-1, 0),
'D': ( 1, 0),
'L': ( 0, -1),
'R': ( 0, 1),
}
def move(self, direction):
"""Moves the snake.
:type direction: str, 'U' = Up, 'L' = Left, 'R' = Right, 'D' = Down
:rtype: int, The game's score after the move. Return -1 if game over.
Game over when snake crosses the screen boundary or bites its body.
"""
if direction not in self.dn:
# treat this move as invalid action
return len(self.snake) - 1
if self.is_over:
# this game is over
return self.SCORE_IN_OVER
"""
new head will hit wall?
"""
x, y = self.snake[0]
dx, dy = self.dn[direction]
hx = x + dx
hy = y + dy
if not (0 <= hx < self.height and 0 <= hy < self.width):
self.is_over = True
return self.SCORE_IN_OVER
"""
eat food or not
"""
fx, fy = self.food[self.fi]
if fx == hx and fy == hy:
# eat that food
self.fi += 1
else:
# move to empty cell and need to remove tail
tail = self.snake.pop()
self.body.discard(tail)
"""
new head will hit its self?
this detection MUST AFTER removing tail
"""
if (hx, hy) in self.body:
self.is_over = True
return self.SCORE_IN_OVER
"""
new head is valid, track it
"""
self.snake.appendleft((hx, hy))
self.body.add((hx, hy))
"""
There is no food anymore
"""
if self.fi >= len(self.food):
self.is_over = True
return len(self.snake) - 1
================================================
FILE: leetcode/36_valid_sudoku.py
================================================
class Solution:
def isValidSudoku(self, board):
"""
:type board: List[List[str]]
:rtype: bool
"""
if not board or not board[0] or len(board) != len(board[0]):
return False
n = len(board)
EMPTY = '.'
CANDS = '123456789'
for x in range(n):
used = set()
for y in range(n):
if board[x][y] == EMPTY:
continue
if board[x][y] not in CANDS:
return False
if board[x][y] in used:
return False
used.add(board[x][y])
for y in range(n):
used = set()
for x in range(n):
if board[x][y] == EMPTY:
continue
if board[x][y] in used:
return False
used.add(board[x][y])
for i in range(3):
for j in range(3):
used = set()
for x in range(i * 3, i * 3 + 3):
for y in range(j * 3, j * 3 + 3):
if board[x][y] == EMPTY:
continue
if board[x][y] in used:
return False
used.add(board[x][y])
return True
================================================
FILE: leetcode/377_combination_sum_iv.py
================================================
"""
- item can only pick once (even diff item with same val): num loop -> amount loop, amount from end to start, + break
- item can pick multiple but diff order is same path: num loop -> amount loop, amount from start to end
- item can pick multiple but diff order is diff path: amount loop -> num loop, amount from start to end
"""
class Solution:
"""
Dynamic Programming
"""
def combinationSum4(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
if not nums:
return 0
dp = [0] * (target + 1)
dp[0] = 1
# if iterate num first, then the answer will become the number of unique set
# see the last Solution in this file
for amount in range(1, target + 1):
for num in nums:
if amount >= num:
dp[amount] += dp[amount - num]
return dp[target]
class Solution:
"""
Memory Search /
Dynamic Programming /
DFS
"""
def combinationSum4(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
if not nums:
return 0
dp = [-1] * (target + 1)
dp[0] = 1
self.memo_search(nums, target, dp)
return dp[target]
def memo_search(self, nums, remain, dp):
if dp[remain] > -1:
return dp[remain]
res = 0
for a in nums:
if remain < a:
continue
res += self.memo_search(nums, remain - a, dp)
dp[remain] = res
return res
class Solution:
"""
DFS: TLE
"""
def combinationSum4(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
if not nums:
return 0
ans = []
nums.sort(reverse=True)
self.dfs(nums, target, ans, [])
return len(ans)
def dfs(self, nums, remain, ans, path):
if remain == 0:
ans.append(path[::-1])
return
for a in nums:
if remain < a:
continue
path.append(a)
self.dfs(nums, remain - a, ans, path)
path.pop()
# ======
class Solution:
"""
Dynamic Programming: wrong answer for this question
This approach is to find the unique combination
"""
def combinationSum4(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
if not nums:
return 0
dp = [0] * (target + 1)
dp[0] = 1
for num in nums:
for amount in range(num, target + 1):
dp[amount] += dp[amount - num]
return dp[target]
================================================
FILE: leetcode/378_kth_smallest_element_in_a_sorted_matrix.py
================================================
from heapq import heappop, heappush
class Solution:
def kthSmallest(self, G, k):
"""
:type G: List[List[int]]
:type k: int
:rtype: int
"""
if not G or not G[0]:
return 0
heap = []
for x in range(len(G)):
heappush(heap, (G[x][0], x, 0))
while heap:
a, x, y = heappop(heap)
k -= 1
if k == 0:
return a
if y + 1 < len(G[x]):
heappush(heap, (G[x][y + 1], x, y + 1))
return 0
================================================
FILE: leetcode/37_sudoku_solver.py
================================================
class Solution:
def solveSudoku(self, board):
"""
:type board: List[List[str]]
:rtype: void Do not return anything, modify board in-place instead.
"""
if not board or not board[0] or len(board) != len(board[0]):
return
self.dfs(board, 0, 0)
def dfs(self, board, x, y):
n = len(board)
if x == n:
return True
_x, _y = x, y + 1
if y == n - 1:
_x = x + 1
_y = 0
if board[x][y] != '.':
if not self.is_valid(board, x, y):
return False
return self.dfs(board, _x, _y)
for i in range(1, n + 1):
board[x][y] = str(i)
if (
self.is_valid(board, x, y) and
self.dfs(board, _x, _y)
):
return True
board[x][y] = '.'
return False
def is_valid(self, board, x, y):
if board[x][y] not in '123456789':
return False
n = len(board)
for i in range(n):
if y != i and board[x][y] == board[x][i]:
return False
if x != i and board[x][y] == board[i][y]:
return False
r = x // 3 * 3
c = y // 3 * 3
for i in range(r, r + 3):
for j in range(c, c + 3):
if x == i and y == j:
continue
if board[x][y] == board[i][j]:
return False
return True
================================================
FILE: leetcode/380_insert_delete_getrandom_o1.py
================================================
"""
Your RandomizedSet object will be instantiated and called as such:
obj = RandomizedSet()
param = obj.insert(val)
param = obj.remove(val)
param = obj.getRandom()
"""
import random
class RandomizedSet:
def __init__(self):
self.nums = []
self.val2idx = {}
def insert(self, val):
"""
Inserts a value to the set. Returns true if the set did not already contain the specified element.
:type val: int
:rtype: bool
"""
self.val2idx[val] = len(self.nums)
self.nums.append(val)
def remove(self, val):
"""
Removes a value from the set. Returns true if the set contained the specified element.
:type val: int
:rtype: bool
"""
if val not in self.val2idx:
return False
i = self.val2idx[val]
key = self.nums[-1]
self.val2idx[key] = i
self.nums[i] = self.nums[-1]
self.nums.pop()
del self.val2idx[val]
return True
def getRandom(self):
"""
Get a random element from the set.
:rtype: int
"""
i = random.randrange(len(self.nums))
return self.nums[i]
================================================
FILE: leetcode/381_insert_delete_getrandom_o1_duplicates_allowed.py
================================================
import random
class RandomizedCollection:
def __init__(self):
self.A = []
self.I = {}
def insert(self, val):
"""
Inserts a value to the collection. Returns true if the collection did not already contain the specified element.
:type val: int
:rtype: bool
"""
A, I = self.A, self.I
if val not in I:
I[val] = set()
A.append(val)
I[val].add(len(A) - 1)
return len(I[val]) == 1
def remove(self, val):
"""
Removes a value from the collection. Returns true if the collection contained the specified element.
:type val: int
:rtype: bool
"""
A, I = self.A, self.I
if val not in I or not I[val]:
return False
i = I[val].pop()
_val = A[-1]
I[_val].add(i)
I[_val].discard(len(A) - 1)
A[i] = _val
A.pop()
return True
def getRandom(self):
"""
Get a random element from the collection.
:rtype: int
"""
return random.choice(self.A)
# Your RandomizedCollection object will be instantiated and called as such:
# obj = RandomizedCollection()
# param_1 = obj.insert(val)
# param_2 = obj.remove(val)
# param_3 = obj.getRandom()
================================================
FILE: leetcode/382_linked_list_random_node.py
================================================
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
from random import randint
class Solution:
def __init__(self, head):
"""
@param head The linked list's head.
Note that the head is guaranteed to be not null, so it contains at least one node.
:type head: ListNode
"""
self.head = head
def getRandom(self):
"""
Returns a random node's value.
:rtype: int
"""
res = node = self.head
i = 0
while node:
if randint(0, i) == i:
res = node
node = node.next
i += 1
return res.val
# Your Solution object will be instantiated and called as such:
# obj = Solution(head)
# param_1 = obj.getRandom()
================================================
FILE: leetcode/384_shuffle_an_array.py
================================================
from random import randrange
class Solution:
def __init__(self, nums):
"""
:type nums: List[int]
"""
self.origin = nums[:]
self.nums = nums
def reset(self):
"""
Resets the array to its original configuration and return it.
:rtype: List[int]
"""
self.nums = self.origin[:]
return self.nums
def shuffle(self):
"""
Returns a random shuffling of the array.
:rtype: List[int]
"""
a = self.nums
n = len(a)
for i in range(n):
_i = randrange(i, n)
a[i], a[_i] = a[_i], a[i]
return a
# Your Solution object will be instantiated and called as such:
# obj = Solution(nums)
# param_1 = obj.reset()
# param_2 = obj.shuffle()
================================================
FILE: leetcode/386_lexicographical_numbers.py
================================================
class Solution:
def lexicalOrder(self, n):
"""
:type n: int
:rtype: List[int]
"""
ans = []
if not n:
return ans
stack = [1]
while stack:
x = stack.pop()
ans.append(x)
# considering the case no carry up if x + 1
# that is, x in [1, 8]
if x < n and x % 10 < 9:
stack.append(x + 1)
if x * 10 <= n:
stack.append(x * 10)
return ans
================================================
FILE: leetcode/387_first_unique_character_in_a_string.py
================================================
class Solution:
def firstUniqChar(self, s):
"""
:type s: str
:rtype: int
"""
if not s:
return -1
freq = {}
for c in s:
freq[c] = freq.get(c, 0) + 1
for i in range(len(s)):
if freq[s[i]] == 1:
return i
return -1
================================================
FILE: leetcode/388_longest_absolute_file_path.py
================================================
"""
Main Concept:
if meet file, record the maximum size of the abs path `root/dir/file`
if meet dir, save current size for `root/dir`
for files:
two cases are at same depth
1. for hidden files
2. for extension in file
for dirs:
do NOT save the max size in each depth
since the children in same depth
may be under different parent
- root
- p1
- f-loooooong-1
- p2
- f-short-2
there is error in that case if save the max size
"""
class Solution:
def lengthLongestPath(self, path):
"""
:type path: str
:rtype: int
"""
ans = 0
if not path:
return ans
dep2size = {0: 0}
for line in path.split('\n'):
name = line.lstrip('\t')
size = len(name)
depth = len(line) - len(name)
if '.' in name:
ans = max(ans, dep2size[depth] + size)
else:
dep2size[depth + 1] = dep2size[depth] + size + 1
return ans
================================================
FILE: leetcode/389_find_the_difference.py
================================================
class Solution:
def findTheDifference(self, s, t):
"""
:type s: str
:type t: str
:rtype: str
"""
a = ord('a')
ans = ord(t[-1]) - a
for i in range(len(s)):
ans ^= ord(s[i]) - a
ans ^= ord(t[i]) - a
return chr(ans + a)
class Solution:
def findTheDifference(self, s, t):
"""
:type s: str
:type t: str
:rtype: str
"""
if not t:
return ''
freq = {}
for c in s:
if c not in freq:
freq[c] = 0
freq[c] += 1
for c in t:
if c not in freq:
return c
freq[c] -= 1
for c, cnt in freq.items():
if cnt:
return c
return ''
================================================
FILE: leetcode/38_count_and_say.py
================================================
class Solution:
def countAndSay(self, N):
"""
:type N: int
:rtype: str
"""
queue = '1'
if not N:
return queue
_queue = []
for _ in range(N - 1):
cnt = 0
char = queue[0]
for c in queue:
if c == char:
cnt += 1
continue
_queue.extend((str(cnt), char))
cnt = 1
char = c
_queue.extend((str(cnt), char))
queue, _queue = ''.join(_queue), []
return queue
================================================
FILE: leetcode/390_elimination_game.py
================================================
class Solution:
def lastRemaining(self, n):
"""
:type n: int
:rtype: int
"""
ans = gap = 1
turn = 0
while n > 1:
turn += 1
n //= 2
gap *= 2
delta = gap // 2 + gap * (n - 1)
if turn & 1:
ans += delta
else:
ans -= delta
return ans
================================================
FILE: leetcode/391_perfect_rectangle.py
================================================
import collections
class Solution:
def isRectangleCover(self, recs):
"""
:type recs: List[List[int]]
:rtype: bool
"""
if not recs:
return False
left = bottom = float('inf')
right = top = float('-inf')
points = collections.defaultdict(int)
for l, b, r, t in recs:
left = min(left, l)
bottom = min(bottom, b)
right = max(right, r)
top = max(top, t)
for x, y, val in (
(l, b, 1),
(r, b, 2),
(r, t, 4),
(l, t, 8),
):
if points[x, y] & val:
return False
points[x, y] |= val
if any(
# only check the mid-points
val not in (3, 6, 9, 12, 15)
for (x, y), val in points.items()
if left < x < right or bottom < y < top
):
return False
return True
================================================
FILE: leetcode/395_longest_substring_with_at_least_k_repeating_characters.py
================================================
class Solution:
def longestSubstring(self, s, k):
"""
:type s: str
:type k: int
:rtype: int
"""
ans = 0
if not s:
return ans
n = len(s)
_F = dict.fromkeys(s, 0)
for m in range(1, len(_F) + 1):
F = _F.copy()
left = right = 0
kind = valid = 0
while right < n:
if kind <= m:
F[s[right]] += 1
if F[s[right]] == 1:
kind += 1
if F[s[right]] == k:
valid += 1
right += 1
else:
if F[s[left]] == 1:
kind -= 1
if F[s[left]] == k:
valid -= 1
F[s[left]] -= 1
left += 1
if kind == valid == m and right - left > ans:
ans = right - left
return ans
================================================
FILE: leetcode/398_random_pick_index.py
================================================
from random import randint
class Solution:
def __init__(self, A):
"""
:type A: List[int]
"""
self.A = A
def pick(self, target):
"""
:type target: int
:rtype: int
"""
res = -1
cnt = 0
for i in range(len(self.A)):
if self.A[i] != target:
continue
cnt += 1
if randint(1, cnt) == cnt:
res = i
return res
# Your Solution object will be instantiated and called as such:
# obj = Solution(nums)
# param_1 = obj.pick(target)
================================================
FILE: leetcode/399_evaluate_division.py
================================================
import collections
class Solution:
def calcEquation(self, equations, values, queries):
"""
:type equations: List[List[str]]
:type values: List[float]
:type queries: List[List[str]]
:rtype: List[float]
"""
ans = []
if not (
equations and
values and
queries and
len(equations) == len(values)
):
return ans
nexts = collections.defaultdict(set)
evals = collections.defaultdict(float)
for i in range(len(equations)):
a, b = equations[i]
nexts[a].add(b)
nexts[b].add(a)
evals[a, b] = 1.0 * values[i]
evals[b, a] = 1.0 / values[i]
for a, b in queries:
res = self.dfs(a, b, 1, nexts, evals, set())
ans.append(float(res))
return ans
def dfs(self, a, b, val, nexts, evals, visited):
res = -1
if a not in nexts:
return res
if a == b:
# this condition must be after `a not in nexts`
# to prevent the node not in graph
return val
visited.add(a)
for c in nexts[a]:
if c in visited or (a, c) not in evals:
continue
res = self.dfs(c, b, val * evals[a, c], nexts, evals, visited)
if res != -1:
break
visited.discard(a)
return res
================================================
FILE: leetcode/406_queue_reconstruction_by_height.py
================================================
"""
Main Concept:
1. group the mans with same height and record origin index
2. record all heights
3. sort heights and iterate from end to start
4. get the corresponding mans and insert to `k` index
input: [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]
heights: [4, 5, 6, 7]
process:
7 [(0, 0), (1, 2)] # input[0], input[2]
[[7, 0]]
[[7, 0], [7, 1]]
6 [(1, 4)]
[[7, 0], [6, 1], [7, 1]]
5 [(0, 3), (2, 5)]
[[5, 0], [7, 0], [6, 1], [7, 1]]
[[5, 0], [7, 0], [5, 2], [6, 1], [7, 1]]
4 [(4, 1)]
[[5, 0], [7, 0], [5, 2], [6, 1], [4, 4], [7, 1]]
"""
class Solution:
def reconstructQueue(self, people):
"""
:type people: List[List[int]]
:rtype: List[List[int]]
"""
ans = []
if not people:
return ans
people.sort(key=lambda p: (p[0], -p[1]))
for i in range(len(people) - 1, -1, -1):
ans.insert(people[i][1], people[i])
return ans
class Solution:
def reconstructQueue(self, people):
"""
:type people: List[List[int]]
:rtype: List[List[int]]
"""
ans = []
if not people:
return ans
h2mans = {}
heights = []
for i in range(len(people)):
h, k = people[i]
if h in h2mans:
h2mans[h].append((k, i))
else:
h2mans[h] = [(k, i)]
heights.append(h)
heights.sort()
for height in heights[::-1]:
for k, i in sorted(h2mans[height]):
ans.insert(k, people[i])
return ans
================================================
FILE: leetcode/416_partition_equal_subset_sum.py
================================================
"""
REF: https://leetcode.com/problems/partition-equal-subset-sum/discuss/90592
`dp[s]` means the specific sum `s` can be gotten from the sum of subset in `nums`
"""
class Solution:
def canPartition(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
if not nums:
return True
target = sum(nums)
if target & 1 == 1:
return False
target //= 2
dp = [False] * (target + 1)
dp[0] = True
for a in nums:
for s in range(target, a - 1, -1):
if dp[s]:
continue
dp[s] = dp[s - a]
return dp[target]
================================================
FILE: leetcode/417_pacific_atlantic_water_flow.py
================================================
class Solution:
"""
BFS
1. for both p-side and a-side, init with queue and visit set
2. add the edge and do bfs
3. only add the cell which higher or equal the previous cell
4. get the intersection in both set
"""
def pacificAtlantic(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: List[List[int]]
"""
if not matrix or not matrix[0]:
return []
m, n = len(matrix), len(matrix[0])
pqueue = []
aqueue = []
for x in range(m):
pqueue.append((x, 0))
aqueue.append((x, n - 1))
for y in range(n):
pqueue.append((0, y))
aqueue.append((m - 1, y))
pvisited = set(pqueue)
avisited = set(aqueue)
self.bfs(matrix, pqueue, pvisited)
self.bfs(matrix, aqueue, avisited)
return list(pvisited & avisited)
def bfs(self, matrix, queue, visited):
m, n = len(matrix), len(matrix[0])
for x, y in queue:
for dx, dy in (
(0, -1), (0, 1),
(-1, 0), (1, 0),
):
_x = x + dx
_y = y + dy
if not (0 <= _x < m and 0 <= _y < n):
continue
if (_x, _y) in visited:
continue
if matrix[_x][_y] < matrix[x][y]:
continue
queue.append((_x, _y))
visited.add((_x, _y))
class Solution:
"""
DFS
"""
def pacificAtlantic(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: List[List[int]]
"""
if not matrix or not matrix[0]:
return []
m, n = len(matrix), len(matrix[0])
pvisited = set()
avisited = set()
for x in range(m):
self.dfs(matrix, x, 0, pvisited)
self.dfs(matrix, x, n - 1, avisited)
for y in range(n):
self.dfs(matrix, 0, y, pvisited)
self.dfs(matrix, m - 1, y, avisited)
return list(pvisited & avisited)
def dfs(self, matrix, x, y, visited):
visited.add((x, y))
for dx, dy in (
(0, -1), (0, 1),
(-1, 0), (1, 0),
):
_x = x + dx
_y = y + dy
if not (
0 <= _x < len(matrix) and
0 <= _y < len(matrix[0])
):
continue
if (_x, _y) in visited:
continue
if matrix[_x][_y] < matrix[x][y]:
continue
self.dfs(matrix, _x, _y, visited)
================================================
FILE: leetcode/41_first_missing_positive.py
================================================
"""
REF: http://www.cnblogs.com/yuzhangcmu/p/4200096.html
"""
class Solution:
def firstMissingPositive(self, A):
"""
:type A: List[int]
:rtype: int
"""
left, right = 0, len(A) - 1
while left <= right:
"""
for `A[left]`, the index it should be at is `A[left] - 1`
1. if it is already at `i` => pass
2. if it out of range or duplicated => let `A[right]` in
3. if it is legal => swap to let `A[left]` go to `i`
"""
i = A[left] - 1
if i == left:
left += 1
elif i < 0 or i > right or A[i] == A[left]:
A[left], A[right] = A[right], A[left]
# `A[left] = A[right]` is also ok, since no need to visit `A[right]` again
right -= 1
else:
A[left], A[i] = A[i], A[left]
return left + 1
================================================
FILE: leetcode/437_path_sum_iii.py
================================================
"""
Definition for a binary tree node.
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
"""
class Solution:
"""
find path count
"""
def pathSum(self, root, target):
"""
:type root: TreeNode
:type target: int
:rtype: int
"""
if not root:
return 0
return sum((
self.count_valid_path(root, target),
self.pathSum(root.left, target),
self.pathSum(root.right, target),
))
def count_valid_path(self, node, remaining):
if not node:
return 0
return sum((
int(node.val == remaining),
self.count_valid_path(node.left, remaining - node.val),
self.count_valid_path(node.right, remaining - node.val),
))
class Solution:
"""
print path
"""
def pathSum(self, root, target):
"""
:type root: TreeNode
:type target: int
:rtype: list[list[int]]
"""
ans = []
if not root:
return ans
self.dfs(root, target, ans, [])
return ans
def dfs(self, node, target, ans, path):
if not node:
return
path.append(node.val)
remaining = target
for i in range(len(path) - 1, -1, -1):
remaining -= path[i]
if remaining == 0:
ans.append(path[i:])
self.dfs(node.left, target, ans, path)
self.dfs(node.right, target, ans, path)
path.pop()
================================================
FILE: leetcode/454_4sum_ii.py
================================================
class Solution:
def fourSumCount(self, A, B, C, D):
"""
:type A: List[int]
:type B: List[int]
:type C: List[int]
:type D: List[int]
:rtype: int
"""
ans = 0
if not A or not B or not C or not D:
return ans
S = {}
for c in C:
for d in D:
key = - (c + d)
S[key] = S.get(key, 0) + 1
for a in A:
for b in B:
if a + b in S:
ans += S[a + b]
return ans
================================================
FILE: leetcode/461_hamming_distance.py
================================================
class Solution:
def hammingDistance(self, x, y):
"""
:type x: int
:type y: int
:rtype: int
"""
n = x ^ y
ans = 0
if not n:
return ans
while n != 0:
n = n & (n - 1)
ans += 1
return ans
================================================
FILE: leetcode/480_sliding_window_median.py
================================================
import heapq
class HashHeapq:
def __init__(self):
self.__heap = []
def __repr__(self):
return repr(self.__heap)
def __len__(self):
return len(self.__heap)
def __bool__(self):
return bool(self.__heap)
def push(self, val):
heapq.heappush(self.__heap, val)
def pop(self):
if not self.__heap:
return
return heapq.heappop(self.__heap)
def remove(self, val):
if not self.__heap:
return
i = 0
n = len(self.__heap)
while i < n and self.__heap[i] != val:
i += 1
if i == n:
return
if i == n - 1:
self.__heap.pop()
else:
self.__heap[i] = self.__heap[-1]
self.__heap.pop()
heapq._siftup(self.__heap, i)
heapq._siftdown(self.__heap, 0, i)
def top(self):
if not self.__heap:
return
return self.__heap[0]
class Solution:
def medianSlidingWindow(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[float]
"""
ans = []
if not nums or k <= 0 or len(nums) < k:
return ans
self.minheap = HashHeapq()
self.maxheap = HashHeapq()
for i in range(len(nums)):
# remove nums[i - k]
if i >= k:
if self.minheap and nums[i - k] >= self.minheap.top():
self.minheap.remove(nums[i - k])
else:
self.maxheap.remove(- nums[i - k])
# add nums[i]
if self.minheap and nums[i] >= self.minheap.top():
self.minheap.push(nums[i])
else:
self.maxheap.push(- nums[i])
# get median
if i >= k - 1:
ans.append(self.get_median())
return ans
def get_median(self):
if not self.minheap and not self.maxheap:
return 0.0
while len(self.minheap) > len(self.maxheap) + 1:
self.maxheap.push(- self.minheap.pop())
while len(self.maxheap) > len(self.minheap):
self.minheap.push(- self.maxheap.pop())
if len(self.minheap) > len(self.maxheap):
return self.minheap.top() * 1.0
return (self.minheap.top() - self.maxheap.top()) / 2.0
================================================
FILE: leetcode/486_predict_the_winner.py
================================================
"""
DP:
time: O(n^2)
space: O(n)
"""
class Solution:
def PredictTheWinner(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
if not nums:
return False
n = len(nums)
dp = [0] * n
for i in range(n - 1, -1, -1):
for j in range(i + 1, n):
dp[j] = max(
nums[i] - dp[j],
nums[j] - dp[j - 1]
)
return dp[n - 1] >= 0
"""
DP:
time: O(n^2)
space: O(n^2)
"""
class Solution:
def PredictTheWinner(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
if not nums:
return False
n = len(nums)
dp = [[0] * n for _ in range(n)]
for i in range(n - 1, -1, -1):
for j in range(i + 1, n):
dp[i][j] = max(
nums[i] - dp[i + 1][j],
nums[j] - dp[i][j - 1]
)
return dp[0][n - 1] >= 0
================================================
FILE: leetcode/48_rotate_image.py
================================================
class Solution:
def rotate(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: void Do not return anything, modify matrix in-place instead.
"""
if not matrix or not matrix[0] or len(matrix) != len(matrix[0]):
return
n = len(matrix)
# swap by diagonal axis
for i in range(n - 1):
for j in range(n - 1 - i):
x = n - 1 - j
y = n - 1 - i
matrix[i][j], matrix[x][y] = matrix[x][y], matrix[i][j]
# swap by x-mid axis
for i in range(n // 2):
for j in range(n):
x = n - 1 - i
y = j
matrix[i][j], matrix[x][y] = matrix[x][y], matrix[i][j]
class Solution:
def rotate(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: void Do not return anything, modify matrix in-place instead.
"""
if not matrix or not matrix[0] or len(matrix) != len(matrix[0]):
return
n = len(matrix)
ans = [[0] * n for _ in range(n)]
for x in range(n):
for y in range(n):
ans[y][n - 1 - x] = matrix[x][y]
matrix[:] = ans[:]
================================================
FILE: leetcode/490_the_maze.py
================================================
class Solution:
def hasPath(self, maze, start, destination):
"""
:type maze: List[List[int]]
:type start: List[int]
:type destination: List[int]
:rtype: bool
>>> s = Solution()
>>> maze = [[0, 0, 1, 0, 0],
... [0, 0, 0, 0, 0],
... [0, 0, 0, 1, 0],
... [1, 1, 0, 1, 1],
... [0, 0, 0, 0, 0]]
>>> s.hasPath(maze, [0, 4], [4, 4])
True
>>> s.hasPath(maze, [0, 4], [3, 2])
False
"""
if not maze or not maze[0] or not start or not destination:
return False
x, y = start
tx, ty = destination
visited = set()
return self.dfs(maze, x, y, tx, ty, visited)
def dfs(self, maze, x, y, tx, ty, visited):
if x == tx and y == ty:
return True
if (x, y) in visited:
return False
visited.add((x, y))
m, n = len(maze), len(maze[0])
for dx, dy in ((-1, 0), (1, 0), (0, -1), (0, 1)):
_x = x + dx
_y = y + dy
while 0 <= _x < m and 0 <= _y < n and maze[_x][_y] == 0:
_x += dx
_y += dy
# since for now its means the position of wall
_x -= dx
_y -= dy
if self.dfs(maze, _x, _y, tx, ty, visited):
return True
return False
================================================
FILE: leetcode/494_target_sum.py
================================================
"""
REF: https://leetcode.com/problems/target-sum/discuss/97334
P: children used in positive,
N: children used in negative,
A: all children
sum(P) - sum(N) = target
=> sum(P) - sum(N) + sum(P) + sum(N) = target + sum(P) + sum(N)
=> 2 * sum(P) = target + sum(A)
=> sum(P) = (target + sum(A)) // 2
=> find the subset sum
"""
class Solution:
def findTargetSumWays(self, A, target):
"""
:type A: List[int]
:type target: int
:rtype: int
"""
if not A:
return 0
_sum = sum(A)
if _sum < target or (_sum + target) % 2 == 1:
return 0
return self.subset_sum(A, (_sum + target) // 2)
def subset_sum(self, A, target):
"""
`dp[i]` means the number of ways
to make sum `i` using non-repeated children in `A`
`i` from `target` to `a - 1` => to make sure `i >= a`
"""
dp = [0] * (target + 1)
dp[0] = 1
for a in A:
for i in range(target, a - 1, -1):
dp[i] += dp[i - a]
return dp[target]
================================================
FILE: leetcode/499_the_maze_iii.py
================================================
class Solution:
"""
BFS
"""
def findShortestWay(self, maze, ball, hole):
"""
:type maze: List[List[int]]
:type ball: List[int]
:type hole: List[int]
:rtype: str
>>> s = Solution()
>>> maze = [[0, 0, 0, 0, 0],
... [1, 1, 0, 0, 1],
... [0, 0, 0, 0, 0],
... [0, 1, 0, 0, 1],
... [0, 1, 0, 0, 0]]
>>> s.findShortestWay(maze, [4, 3], [0, 1])
'lul'
>>> s.findShortestWay(maze, [4, 3], [3, 0])
'impossible'
"""
NOT_FOUND = 'impossible'
if not maze or not maze[0]:
return NOT_FOUND
m, n = len(maze), len(maze[0])
sx, sy = ball
tx, ty = hole
queue = [(sx, sy)]
paths = {(sx, sy): []}
distance = {(sx, sy): 0}
for x, y in queue:
for dx, dy, dn in (
(-1, 0, 'u'), (1, 0, 'd'),
(0, -1, 'l'), (0, 1, 'r'),
):
_x = x + dx
_y = y + dy
_step = 0
while (
0 <= _x < m and 0 <= _y < n and
maze[_x][_y] == 0 and
not (_x == tx and _y == ty)
):
_x += dx
_y += dy
_step += 1
if not (_x == tx and _y == ty):
_x -= dx
_y -= dy
_step -= 1
if ((_x, _y) in distance and
distance[x, y] + _step > distance[_x, _y]):
continue
if ((_x, _y) in paths and
paths[x, y] + [dn] > paths[_x, _y]):
continue
distance[_x, _y] = distance[x, y] + _step
paths[_x, _y] = paths[x, y] + [dn]
queue.append((_x, _y))
return ''.join(paths[tx, ty]) if (tx, ty) in paths else NOT_FOUND
import heapq
class Solution2:
"""
Dijkstra
"""
def findShortestWay(self, maze, ball, hole):
"""
:type maze: List[List[int]]
:type ball: List[int]
:type hole: List[int]
:rtype: str
>>> s = Solution2()
>>> maze = [[0, 0, 0, 0, 0],
... [1, 1, 0, 0, 1],
... [0, 0, 0, 0, 0],
... [0, 1, 0, 0, 1],
... [0, 1, 0, 0, 0]]
>>> s.findShortestWay(maze, [4, 3], [0, 1])
'lul'
>>> s.findShortestWay(maze, [4, 3], [3, 0])
'impossible'
"""
NOT_FOUND = 'impossible'
if not maze or not maze[0]:
return NOT_FOUND
m, n = len(maze), len(maze[0])
sx, sy = ball
tx, ty = hole
heap = [(sx, sy)]
paths = {(sx, sy): []}
distance = {(sx, sy): 0}
while heap:
x, y = heapq.heappop(heap)
for dx, dy, dn in (
(-1, 0, 'u'), (1, 0, 'd'),
(0, -1, 'l'), (0, 1, 'r'),
):
_x = x + dx
_y = y + dy
while (
0 <= _x < m and 0 <= _y < n and
maze[_x][_y] == 0 and
not (_x == tx and _y == ty)
):
_x += dx
_y += dy
if not (_x == tx and _y == ty):
_x -= dx
_y -= dy
_step = distance[x, y] + abs(_x - x) + abs(_y - y)
if (_x, _y) in distance and _step > distance[_x, _y]:
continue
if (_x, _y) in paths and paths[x, y] + [dn] > paths[_x, _y]:
continue
distance[_x, _y] = _step
paths[_x, _y] = paths[x, y] + [dn]
heapq.heappush(heap, (_x, _y))
return ''.join(paths[tx, ty]) if (tx, ty) in paths else NOT_FOUND
================================================
FILE: leetcode/505_the_maze_ii.py
================================================
class Solution:
"""
BFS
"""
def shortestDistance(self, maze, start, destination):
"""
:type maze: List[List[int]]
:type start: List[int]
:type destination: List[int]
:rtype: int
>>> s = Solution()
>>> maze = [[0, 0, 1, 0, 0],
... [0, 0, 0, 0, 0],
... [0, 0, 0, 1, 0],
... [1, 1, 0, 1, 1],
... [0, 0, 0, 0, 0]]
>>> s.shortestDistance(maze, [0, 4], [4, 4])
12
>>> s.shortestDistance(maze, [0, 4], [3, 2])
-1
"""
if not maze or not maze[0]:
return -1
m, n = len(maze), len(maze[0])
sx, sy = start
tx, ty = destination
queue = [(sx, sy)]
distance = {(sx, sy): 0}
for x, y in queue:
for dx, dy in ((-1, 0), (1, 0), (0, -1), (0, 1)):
_x = x + dx
_y = y + dy
_step = 0
while 0 <= _x < m and 0 <= _y < n and maze[_x][_y] == 0:
_x += dx
_y += dy
_step += 1
_x -= dx
_y -= dy
if ((_x, _y) in distance and
distance[x, y] + _step >= distance[_x, _y]):
continue
distance[_x, _y] = distance[x, y] + _step
if _x == tx and _y == ty:
return distance[_x, _y]
queue.append((_x, _y))
return -1
import heapq
class Solution2:
"""
Dijkstra
"""
def shortestDistance(self, maze, start, destination):
"""
:type maze: List[List[int]]
:type start: List[int]
:type destination: List[int]
:rtype: int
>>> s = Solution2()
>>> maze = [[0, 0, 1, 0, 0],
... [0, 0, 0, 0, 0],
... [0, 0, 0, 1, 0],
... [1, 1, 0, 1, 1],
... [0, 0, 0, 0, 0]]
>>> s.shortestDistance(maze, [0, 4], [4, 4])
12
>>> s.shortestDistance(maze, [0, 4], [3, 2])
-1
"""
if not maze or not maze[0]:
return -1
m, n = len(maze), len(maze[0])
sx, sy = start
tx, ty = destination
heap = [(sx, sy)]
distance = {(sx, sy): 0}
while heap:
x, y = heapq.heappop(heap)
for dx, dy in ((-1, 0), (1, 0), (0, -1), (0, 1)):
_x = x + dx
_y = y + dy
while 0 <= _x < m and 0 <= _y < n and maze[_x][_y] == 0:
_x += dx
_y += dy
_x -= dx
_y -= dy
_step = distance[x, y] + abs(_x - x) + abs(_y - y)
if (_x, _y) in distance and _step >= distance[_x, _y]:
continue
if _x == tx and _y == ty:
return _step
distance[_x, _y] = _step
heapq.heappush(heap, (_x, _y))
return -1
================================================
FILE: leetcode/518_coin_change_2.py
================================================
class Solution:
def change(self, amount, coins):
"""
:type amount: int
:type coins: List[int]
:rtype: int
"""
"""
`dp[a]` means the ways to make up amount `a`
"""
dp = [0] * (amount + 1)
dp[0] = 1 # the ways to make up `0` is just only 1
"""
iterate coin first, and then amount
otherwise it will add the dup ways in
re-count if `[1, 2]` and `[2, 1]`, but they are same
"""
for c in coins:
for a in range(c, amount + 1):
# if a < c: continue
dp[a] += dp[a - c]
return dp[amount]
================================================
FILE: leetcode/51_n_queens.py
================================================
class Solution:
def solveNQueens(self, n):
"""
:type n: int
:rtype: List[List[str]]
"""
ans = []
if not n:
return ans
G = [['.'] * n for _ in range(n)]
self.dfs(G, 0, ans)
return ans
def dfs(self, G, y, ans):
if y == len(G):
ans.append(self.clone_board(G))
return
for x in range(len(G)):
if self.is_valid(G, x, y):
G[x][y] = 'Q'
self.dfs(G, y + 1, ans)
G[x][y] = '.'
def is_valid(self, G, x, y):
"""
traverse left half => i in [0, n], j in [0, y - 1] to
1. avoid `j == y` and speed up
and return False if
2. `x == i` => at same row
3. `x - i = y - j` => `x + j = y + i` => left diagonal line
4. `x - i = -(y - j)` => `x + y = i + j` => right diagonal line
"""
for i in range(len(G)):
for j in range(y):
if G[i][j] != 'Q':
continue
if (x + j == y + i or
x + y == i + j or
x == i):
return False
return True
def clone_board(self, G):
res = []
for R in G:
res.append(''.join(R))
return res
================================================
FILE: leetcode/524_longest_word_in_dictionary_through_deleting.py
================================================
class Solution:
"""
1. to check the word in list is subsequence of given s
2. ignoring if the length less than current ans
3. ignoring if the length equal current ans but has larger lexicographical order
"""
def findLongestWord(self, s, words):
"""
:type s: str
:type words: List[str]
:rtype: str
"""
ans = ''
for w in words:
if any((
not self.is_subseq(s, w),
len(w) < len(ans),
len(w) == len(ans) and w >= ans, # means w has larger lexicographical order
)):
continue
ans = w
return ans
def is_subseq(self, s, t):
"""
return True if `t` is subsequence of `s`
"""
m, n = len(s), len(t)
i = j = 0
while i < m and j < n:
if s[i] == t[j]:
j += 1
i += 1
return j == n
class Solution:
"""
Brute Force: TLE
"""
def findLongestWord(self, s, words):
"""
:type s: str
:type words: List[str]
:rtype: str
"""
cands = []
self.find_cands(s, 0, cands, [])
ans = ''
target = set(words)
for w in cands:
if any((
w not in target,
len(w) < len(ans),
len(w) == len(ans) and w >= ans,
)):
continue
ans = w
return ans
def find_cands(self, s, i, cands, path):
if i == len(s):
cands.append(''.join(path))
return
# keep s[i]
path.append(s[i])
self.find_cands(s, i + 1, cands, path)
path.pop()
# ignore s[i]
self.find_cands(s, i + 1, cands, path)
================================================
FILE: leetcode/52_n_queens_ii.py
================================================
class Solution:
def totalNQueens(self, n):
"""
:type n: int
:rtype: int
"""
Xs = set()
DLs = set() # left diagonal lines
DRs = set() # right diagonal lines
return self.divide_conquer(n, 0, 0, Xs, DLs, DRs)
def divide_conquer(self, n, y, cnt, Xs, DLs, DRs):
for x in range(n):
if x in Xs:
continue
dl = x - y
if dl in DLs:
continue
dr = x + y
if dr in DRs:
continue
if y == n - 1:
cnt += 1
continue
Xs.add(x)
DLs.add(dl)
DRs.add(dr)
cnt = self.divide_conquer(n, y + 1, cnt, Xs, DLs, DRs)
Xs.discard(x)
DLs.discard(dl)
DRs.discard(dr)
return cnt
================================================
FILE: leetcode/530_minimum_absolute_difference_in_bst.py
================================================
"""
Definition for a binary tree node.
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
"""
class Solution:
"""
time: O(n)
space: O(1)
"""
ans = float('inf')
pre = None
def getMinimumDifference(self, root):
"""
:type root: TreeNode
:rtype: int
"""
if not root:
return self.ans
self.getMinimumDifference(root.left)
if self.pre and root.val - self.pre.val < self.ans:
self.ans = root.val - self.pre.val
self.pre = root
self.getMinimumDifference(root.right)
return self.ans
================================================
FILE: leetcode/542_01_matrix.py
================================================
class Solution:
def updateMatrix(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: List[List[int]]
"""
if not matrix or not matrix[0]:
return []
m, n = len(matrix), len(matrix[0])
ans = [[float('inf')] * n for _ in range(m)]
queue = []
for x in range(m):
for y in range(n):
if matrix[x][y] == 0:
ans[x][y] = 0
queue.append((x, y))
for x, y in queue:
for dx, dy in (
(-1, 0), (1, 0),
(0, -1), (0, 1),
):
_x = x + dx
_y = y + dy
if not (0 <= _x < m and 0 <= _y < n):
continue
if ans[_x][_y] < ans[x][y] + 1:
continue
ans[_x][_y] = ans[x][y] + 1
queue.append((_x, _y))
return ans
================================================
FILE: leetcode/54_spiral_matrix.py
================================================
class Solution:
def spiralOrder(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: List[int]
"""
ans = []
if not matrix or not matrix[0]:
return ans
# need keep its order to go right, bottom, left, top
delta = (
(0, 1), (1, 0),
(0, -1), (-1, 0),
)
m, n = len(matrix), len(matrix[0])
x = y = turn = 0
for _ in range(m * n):
ans.append(matrix[x][y])
matrix[x][y] = None
_x = x + delta[turn][0]
_y = y + delta[turn][1]
if not (0 <= _x < m and 0 <= _y < n) or matrix[_x][_y] is None:
turn = (turn + 1) % len(delta)
_x = x + delta[turn][0]
_y = y + delta[turn][1]
x = _x
y = _y
return ans
================================================
FILE: leetcode/592_fraction_addition_and_subtraction.py
================================================
import re
class Solution:
def fractionAddition(self, E):
"""
:type E: str
:rtype: str
"""
S = [] # signs
if E[0] != '-':
S.append('+')
for c in E:
if c == '+' or c == '-':
S.append(c)
a, b = 0, 1
i = 0
for frac in re.split('\+|-', E):
if not frac:
continue
_a, _b = frac.split('/')
_a = int(_a)
_b = int(_b)
# if needs to prevent overflow, `// gcd`
if S[i] == '+':
a = a * _b + _a * b
else:
a = a * _b - _a * b
b = b * _b
gcd = self.get_gcd(a, b)
a //= gcd
b //= gcd
i += 1
return '{}/{}'.format(a, b)
def get_gcd(self, a, b):
while b:
a, b = b, a % b
return a
================================================
FILE: leetcode/593_valid_square.py
================================================
class Solution:
def validSquare(self, p1, p2, p3, p4):
"""
:type p1: List[int]
:type p2: List[int]
:type p3: List[int]
:type p4: List[int]
:rtype: bool
"""
ps = []
for p in (p1, p2, p3, p4):
if not p:
return False
ps.append(p)
ps.sort()
# 0: lb, 1: lt, 2:rb, 3: rt
l01 = self.get_distance(ps[0], ps[1])
l02 = self.get_distance(ps[0], ps[2])
l13 = self.get_distance(ps[1], ps[3])
l23 = self.get_distance(ps[2], ps[3])
l03 = self.get_distance(ps[0], ps[3])
l12 = self.get_distance(ps[1], ps[2])
return all((
l01 == l02 == l13 == l23 > 0,
l03 == l12,
))
def get_distance(self, a, b):
"""
find the size of 'ab'
"""
dx = a[0] - b[0]
dy = a[1] - b[1]
return (dx * dx + dy * dy) ** 0.5
================================================
FILE: leetcode/59_spiral_matrix_ii.py
================================================
class Solution:
def generateMatrix(self, n):
"""
:type n: int
:rtype: List[List[int]]
"""
if not n or n < 1:
return []
if n == 1:
return [[1]]
ans = [[0] * n for _ in range(n)]
delta = (
(0, 1), (1, 0),
(0, -1), (-1, 0),
)
x = y = turn = 0
for i in range(1, n * n + 1):
ans[x][y] = i
_x = x + delta[turn][0]
_y = y + delta[turn][1]
if not (0 <= _x < n and 0 <= _y < n) or ans[_x][_y] != 0:
turn = (turn + 1) % len(delta)
_x = x + delta[turn][0]
_y = y + delta[turn][1]
x = _x
y = _y
return ans
================================================
FILE: leetcode/5_longest_palindromic_substring.py
================================================
class Solution:
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
if not s:
return ''
start = size = 0
for i in range(len(s)):
_start, _size = self.check_palindrome(s, i, i)
if _size > size:
size = _size
start = _start
_start, _size = self.check_palindrome(s, i, i + 1)
if _size > size:
size = _size
start = _start
return s[start:start + size]
def check_palindrome(self, s, left, right):
n = len(s)
while left >= 0 and right < n and s[left] == s[right]:
left -= 1
right += 1
return left + 1, right - left - 1
================================================
FILE: leetcode/616_add_bold_tag_in_string.py
================================================
"""
>>> gotcha = []
>>> for s in (Solution(),):
... for _in, _out in (
... (('', ['']), ''),
... (('abcxyz123', ['abc', '123']), 'abcxyz123'),
... (('aaabbcc', ['aaa','aab','bc']), 'aaabbcc'),
... (('aabcd', ['ab', 'bc']), 'aabcd'),
... ):
... res = s.addBoldTag(*_in)
... if res != _out: print(_in, res)
... gotcha.append(res == _out)
>>> bool(gotcha) and all(gotcha)
True
"""
class Solution:
def addBoldTag(self, s, words):
"""
:type s: str
:type words: List[str]
:rtype: str
"""
if not s or not words:
return ''
TMPL = '{}'
n = len(s)
ans = []
is_bold = [False] * n
left = right = 0
for left in range(n):
for w in words:
size = len(w)
if s[left:left + size] == w and left + size > right:
right = left + size
is_bold[left] = right > left
left = right = 0
while left < n:
if not is_bold[left]:
ans.append(s[left])
left += 1
continue
right = left
while right < n and is_bold[right]:
right += 1
ans.append(TMPL.format(s[left:right]))
left = right # imply left' = left + 1
return ''.join(ans)
================================================
FILE: leetcode/643_maximum_average_subarray_i.py
================================================
class Solution:
def findMaxAverage(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: float
"""
"""
Assuming the answer we want is `Ans = Max(A[i] + A[i+1] + ... + A[i+k-1])`,
and we define the `P[i] = A[0] + A[1] + ... + A[i-1]`,
so that `Ans = Max(P[i+k] - P[i])`, and `i+k-1 < n -> i < n-k+1`
* max index in P is `i+k`, so in A is `(i+k)-1`, `i+k-1 < n`
"""
"""
P[0] = 0
P[1] = P[0] + A[0]
P[2] = P[1] + A[1]
P[i] = P[i-1] + A[i-1]
= A[0] + A[1] + ... + A[i-1]
"""
P = [0]
for x in nums: P.append(P[-1] + x)
max_sum = max(P[i+k] - P[i] for i in range(len(nums) - k + 1))
return max_sum / float(k)
class Solution:
def findMaxAverage(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: float
"""
"""
Assuming k = 3
i: 0 1 2 3
|--> Start to find max sum
|--> Start to remove past child
"""
max_sum, tmp_sum = float('-inf'), 0
for i in range(len(nums)):
tmp_sum += nums[i]
if i >= k:
tmp_sum -= nums[i-k]
if i + 1 >= k:
max_sum = max(max_sum, tmp_sum)
return max_sum / float(k)
================================================
FILE: leetcode/66_plus_one.py
================================================
class Solution:
def plusOne(self, digits):
"""
:type digits: List[int]
:rtype: List[int]
"""
ans = []
if not digits:
return ans
carry = 1
for i in range(len(digits) - 1, -1, -1):
carry += digits[i]
ans.append(carry % 10)
carry //= 10
if carry:
ans.append(carry)
ans.reverse()
return ans
class Solution:
def plusOne(self, digits):
"""
:type digits: List[int]
:rtype: List[int]
"""
if not digits:
return []
carry = 0
digits[-1] += 1
for i in range(len(digits) - 1, -1, -1):
carry += digits[i]
digits[i] = carry % 10
carry //= 10
if carry:
digits.append(carry)
for i in range(len(digits) - 1, 0, -1):
digits[i], digits[i - 1] = digits[i - 1], digits[i]
return digits
================================================
FILE: leetcode/676_implement_magic_dictionary.py
================================================
"""
Your MagicDictionary object will be instantiated and called as such:
obj = MagicDictionary()
obj.buildDict(dict)
param_2 = obj.search(word)
"""
class MagicDictionary:
def __init__(self):
"""
Initialize your data structure here.
"""
self.words = collections.defaultdict(set)
def buildDict(self, words):
"""
Build a dictionary through a list of words
:type words: List[str]
:rtype: void
"""
for word in words:
for i in range(len(word)):
key = '{0},{1}'.format(word[:i], word[i + 1:])
if key not in self.words:
self.words[key] = set()
# add char to distinct word if its same
self.words[key].add(word[i])
def search(self, word):
"""
Returns if there is any word in the trie that equals to the given word after modifying exactly one character
:type word: str
:rtype: bool
"""
for i in range(len(word)):
key = '{0},{1}'.format(word[:i], word[i + 1:])
if key not in self.words:
continue
words = self.words[key]
# 1. word[i] not in words => means not same word
# 2. len(words) > 1 => if got same but still can mapping other
if word[i] not in words or len(words) > 1:
return True
return False
================================================
FILE: leetcode/679_24_game.py
================================================
class Solution:
EPS = 1e-6
OP = (
lambda a, b: a + b,
lambda a, b: a * b,
lambda a, b: a - b,
lambda a, b: a / b,
)
def judgePoint24(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
if not nums:
return False
n = len(nums)
if n == 1:
return abs(nums[0] - 24) < self.EPS
for i in range(n):
for j in range(n):
if i == j:
continue
nxts = [nums[k] for k in range(n) if i != k != j] # i != j != k is different
for k in range(len(self.OP)):
if i < j and k < 2:
# since a + b == b + a, so just do half in j >= i
# same for `*`
continue
if nums[j] == 0 and k == 3:
# divide by 0
continue
nxts.append(self.OP[k](nums[i], nums[j]))
if self.judgePoint24(nxts):
return True
nxts.pop()
return False
================================================
FILE: leetcode/681_next_closest_time.py
================================================
"""
it's very similar with `next permutation`
just a `next combination`
1. `digits` save unique num and in order
2. `ids` record current used num
3. keep doing plus one and check valid
>>> gotcha = []
>>> s = Solution()
>>> for _in, _out in (
... ('19:34', '19:39'), ('23:59', '22:22'),
... ('22:22', '22:22'), ('24:24', ''),
... ('23:23', '23:32'), ('12:34', '12:41'),
... ('00:00', '00:00'), ('01:32', '01:33'),
... ):
... res = s.nextClosestTime(_in)
... if res != _out: print('in: {}, out: {}, exp: {}'.format(_in, res, _out))
... gotcha.append(res == _out)
>>> bool(gotcha) and all(gotcha)
True
"""
class Solution:
def nextClosestTime(self, time):
"""
:type time: str
:rtype: str
"""
if not (
time and len(time) == 5 and time[2] == ':' and
0 <= int(time[:2]) < 24 and 0 <= int(time[3:]) < 60
):
return ''
times = [int(t) for t in time if t != ':']
digits = []
for a in sorted(times):
if digits and a == digits[-1]:
continue
digits.append(a)
ids = [digits.index(t) for t in times]
ids[-1] += 1
while not self.is_valid(ids, digits):
ids[-1] += 1
h = digits[ids[0]] * 10 + digits[ids[1]]
m = digits[ids[2]] * 10 + digits[ids[3]]
return '{}:{}'.format(
'0' + str(h) if h < 10 else str(h),
'0' + str(m) if m < 10 else str(m)
)
def is_valid(self, ids, digits):
n = len(digits)
carry = 0
i = len(ids) - 1
while i >= 0:
carry += ids[i]
ids[i] = carry % n
carry = carry // n
i -= 1
if carry:
ids[:] = [0] * len(ids)
return True
h = digits[ids[0]] * 10 + digits[ids[1]]
m = digits[ids[2]] * 10 + digits[ids[3]]
if 0 <= h < 24 and 0 <= m < 60:
return True
return False
================================================
FILE: leetcode/682_baseball_game.py
================================================
class Solution:
def calPoints(self, ops):
"""
:type ops: List[str]
:rtype: int
"""
if not ops:
return 0
stack = []
for op in ops:
if op == 'C':
stack.pop()
elif op == 'D':
stack.append(2 * stack[-1])
elif op == '+':
stack.append(stack[-1] + stack[-2])
else:
stack.append(int(op))
return sum(stack)
================================================
FILE: leetcode/683_k_empty_slots.py
================================================
"""
>>> gotcha = []
>>> for s in (Solution(), Solution2()):
... for _in, _out in (
... (([1,3,2], 1), 2),
... (([1,2,3], 1), -1),
... ):
... res = s.kEmptySlots(*_in)
... if res != _out: print(_in, res)
... gotcha.append(res == _out)
>>> bool(gotcha) and all(gotcha)
True
"""
import bisect
class Solution:
"""
Maintain pos by bloom order
"""
def kEmptySlots(self, flowers, k):
"""
:type flowers: List[int]
:type k: int
:rtype: int
"""
bloom = []
for day in range(len(flowers)):
x = flowers[day]
i = bisect.bisect_left(bloom, x)
for _x in bloom[max(0, i - 1):i + 1]:
if abs(_x - x) - 1 == k:
return day + 1 # changed to 1-based
bloom.insert(i, x)
return -1
class Solution2:
"""
Two Pointer:
https://blog.csdn.net/magicbean2/article/details/79235465
"""
def kEmptySlots(self, flowers, k):
"""
:type flowers: List[int]
:type k: int
:rtype: int
"""
n = len(flowers)
x2day = [0] * n
for day in range(n):
"""
day: 0-based => 1-based
x: 1-based => 0-based
"""
x = flowers[day]
x2day[x - 1] = day + 1
ans = INF = float('inf')
left, right = 0, k + 1
i = 0
while right < n:
if any((
x2day[i] < x2day[left],
x2day[i] <= x2day[right],
)):
if i == right:
ans = min(ans, max(x2day[left], x2day[right]))
left, right = i, k + i + 1
i += 1
return ans if ans < INF else -1
================================================
FILE: leetcode/684_redundant_connection.py
================================================
"""
given input is an undirected graph
1. iterate edges
2. if u and v are connected before we add edge in nodes (graph)
=> that is the edge should be removed
"""
class Solution:
"""
UnionFind
"""
def findRedundantConnection(self, edges):
"""
:type edges: List[List[int]]
:rtype: List[int]
"""
if not edges:
return []
nodes = {}
for u, v in edges:
if not self.union(nodes, u, v):
return [u, v]
return []
def union(self, nodes, u, v):
a = self.find(nodes, u)
b = self.find(nodes, v)
if a == b:
return False
nodes[a] = b
return True
def find(self, nodes, u):
if u not in nodes:
nodes[u] = u
return u
if nodes[u] == u:
return u
nodes[u] = self.find(nodes, nodes[u])
return nodes[u]
import collections
class Solution:
"""
DFS
"""
def findRedundantConnection(self, edges):
"""
:type edges: List[List[int]]
:rtype: List[int]
"""
if not edges:
return []
nodes = collections.defaultdict(set)
for u, v in edges:
# dfs to check u and v are connected already => cycle
if u in nodes and v in nodes and self.dfs(nodes, u, v, set()):
return [u, v]
nodes[u].add(v)
nodes[v].add(u)
return []
def dfs(self, nodes, u, v, visited):
if u == v:
return True
if u in visited:
return False
visited.add(u)
for x in nodes[u]:
if self.dfs(nodes, x, v, visited):
return True
return False
================================================
FILE: leetcode/685_redundant_connection_ii.py
================================================
"""
given input is an directed graph
3 corner cases:
1. There is a loop in the graph, and no vertex has more than 1 parent.
2. A vertex has more than 1 parent, but there isn’t a loop in the graph.
3. A vertex has more than 1 parent, and is part of a loop.
REF: https://leetcode.com/problems/redundant-connection-ii/discuss/108070/Python-O(N)-concise-solution-with-detailed-explanation-passed-updated-testcases
"""
import collections
class Solution:
def findRedundantDirectedConnection(self, edges):
"""
:type edges: List[List[int]]
:rtype: List[int]
"""
ans = edge = None # `edge` is the last edge in a loop
adj = collections.defaultdict(set)
uf = collections.defaultdict(int)
has_parent = set()
for u, v in edges:
adj[u].add(v)
if v in has_parent:
ans = (u, v)
if not self.union(uf, u, v):
edge = (u, v)
has_parent.add(v)
if not ans:
return edge
res = self.dfs(ans[1], adj, set())
return res if res else ans
def union(self, uf, u, v):
a = self.find(uf, u)
b = self.find(uf, v)
if a == b:
return False
uf[b] = a
return True
def find(self, uf, u):
if uf[u] == 0:
uf[u] = u
return u
if uf[u] == u:
return u
uf[u] = self.find(uf, uf[u])
return uf[u]
def dfs(self, u, adj, visited):
# to detect cycle
visited.add(u)
for v in adj[u]:
if v in visited:
return (u, v)
res = self.dfs(v, adj, visited)
if res:
return res
================================================
FILE: leetcode/688_knight_probability_in_chessboard.py
================================================
import collections
class Solution:
"""
DP:
1. init the first pos as 1
2. keep simulate the process and divide the probability
3. sum the values
"""
def knightProbability(self, n, k, r, c):
"""
:type n: int
:type k: int
:type r: int
:type c: int
:rtype: float
"""
dp = collections.defaultdict(int)
dp[r, c] = 1.0
for _ in range(k):
nxt = collections.defaultdict(int)
for x in range(n):
for y in range(n):
for dx, dy in (
(-1, -2),
( 1, -2),
(-2, -1),
( 2, -1),
(-2, 1),
( 2, 1),
(-1, 2),
( 1, 2),
):
_x = x + dx
_y = y + dy
if not (0 <= _x < n and 0 <= _y < n):
continue
nxt[_x, _y] += dp[x, y] / 8.0
dp = nxt
return sum(dp.values())
class Solution:
"""
BFS: TLE
"""
def knightProbability(self, n, k, r, c):
"""
:type n: int
:type k: int
:type r: int
:type c: int
:rtype: float
"""
if n == 1 and k == 0:
return 1.0
queue, _queue = [(r, c)], []
total = 8 ** k
valid = 0
while queue and k:
k -= 1
for x, y in queue:
for dx, dy in (
(-1, -2),
( 1, -2),
(-2, -1),
( 2, -1),
(-2, 1),
( 2, 1),
(-1, 2),
( 1, 2),
):
_x = x + dx
_y = y + dy
if not (0 <= _x < n and 0 <= _y < n):
continue
if k == 0:
valid += 1
if k > 0:
_queue.append((_x, _y))
queue, _queue = _queue, []
return valid / total
================================================
FILE: leetcode/689_maximum_sum_of_3_non_overlapping_subarrays.py
================================================
"""
REF: https://leetcode.com/problems/maximum-sum-of-3-non-overlapping-subarrays/discuss/108231
"""
class Solution:
def maxSumOfThreeSubarrays(self, A, k):
"""
:type A: List[int]
:type k: int
:rtype: List[int]
"""
ans = [-1] * 3
if not A or len(A) < 3 * k:
return ans
n = len(A)
S = [0] * (n + 1) # prefix sum
L = [0] * n # the starting index of the maximum interval sum with length k in [0, i]
R = [n - k] * n # the starting index of the maximum interval sum with length k in [i, n - 1]
for i in range(1, n + 1):
S[i] = S[i - 1] + A[i - 1]
max_sum = S[k] - S[0] # maximum interval sum
_sum = 0
for i in range(k, n):
L[i] = L[i - 1]
_sum = S[i + 1] - S[i + 1 - k]
if _sum > max_sum:
L[i] = i + 1 - k
max_sum = _sum
max_sum = S[n] - S[n - k]
_sum = 0
for i in range(n - k - 1, -1, -1):
R[i] = R[i + 1]
_sum = S[i + k] - S[i]
if _sum >= max_sum:
R[i] = i
max_sum = _sum
max_sum = _sum = 0
for i in range(k, n - 2 * k + 1):
left = L[i - 1]
right = R[i + k]
_sum = S[i + k] - S[i] + S[left + k] - S[left] + S[right + k] - S[right]
if _sum > max_sum:
max_sum = _sum
ans[:] = left, i, right
return ans
================================================
FILE: leetcode/697_degree_of_an_array.py
================================================
class Solution:
def findShortestSubArray(self, A):
"""
:type A: List[int]
:rtype: int
"""
if not A:
return 0
n = len(A)
L, R, C = {}, {}, {}
for i in range(n):
if A[i] not in L:
L[A[i]] = i
R[A[i]] = i
C[A[i]] = C.get(A[i], 0) + 1
ans = len(A)
degree = max(C.values())
for a, c in C.items():
if c == degree and R[a] - L[a] + 1 < ans:
ans = R[a] - L[a] + 1
return ans
================================================
FILE: leetcode/719_find_k_th_smallest_pair_distance.py
================================================
class Solution:
def smallestDistancePair(self, A, k):
"""
:type A: List[int]
:type k: int
:rtype: int
"""
if not A or not k:
return -1
A.sort()
left, right = 0, A[-1] - A[0]
while left + 1 < right:
mid = (left + right) // 2
if self.check_valid(A, mid, k):
right = mid
else:
left = mid
return left if self.check_valid(A, left, k) else right
def check_valid(self, A, mid, k):
"""
valid if there are at least `k` pairs when distance is `mid`
"""
cnt = left = 0
for right in range(len(A)):
while A[right] - A[left] > mid:
left += 1
cnt += right - left
if cnt >= k:
return True
return False
================================================
FILE: leetcode/721_accounts_merge.py
================================================
class Solution:
def accountsMerge(self, A):
"""
:type A: List[List[str]]
:rtype: List[List[str]]
"""
if not A:
return []
M = {} # mails
M2N = {} # mail to name
for L in A:
for i in range(1, len(L)):
M2N[L[i]] = L[0]
self.connect(M, L[i], L[1])
for a in M:
self.find(M, a)
res = {}
for m1, m0 in M.items():
if m0 not in res:
res[m0] = []
res[m0].append(m1)
return [[M2N[m]] + sorted(M) for m, M in res.items()]
def connect(self, N, a, b):
_a = self.find(N, a)
_b = self.find(N, b)
if _a is not _b:
N[_a] = _b
def find(self, N, a):
if a not in N:
N[a] = a
return a
if N[a] is a:
return a
N[a] = self.find(N, N[a])
return N[a]
================================================
FILE: leetcode/734_sentence_similarity.py
================================================
"""
>>> pairs = [['great', 'fine'], ['acting', 'drama'], ['skills', 'talent']]
>>> gotcha = []
>>> s = Solution()
>>> for _in, _out in (
... ((['great', 'acting'], ['fine', 'drama'], pairs), True),
... ((['great', 'acting'], ['fine', 'talent'], pairs), False),
... ((['great'], ['great'], []), True),
... ((['great'], ['fine', 'drama'], pairs), False),
... ):
... res = s.areSentencesSimilar(*_in)
... if res != _out: print(_in, res)
... gotcha.append(res == _out)
>>> bool(gotcha) and all(gotcha)
True
"""
import collections
class Solution:
def areSentencesSimilar(self, words1, words2, pairs):
"""
:type words1: List[str]
:type words2: List[str]
:type pairs: List[List[str]]
:rtype: bool
"""
if len(words1) != len(words2):
return False
simils = collections.defaultdict(set)
for a, b in pairs:
simils[a].add(b)
simils[b].add(a)
for i in range(len(words1)):
a = words1[i]
b = words2[i]
if a != b and b not in simils[a]:
return False
return True
================================================
FILE: leetcode/737_sentence_similarity_ii.py
================================================
"""
>>> pairs = [['great', 'fine'], ['acting', 'drama'], ['skills', 'talent']]
>>> gotcha = []
>>> for s in (Solution(), Solution2()):
... for _in, _out in (
... ((['great', 'acting'], ['fine', 'drama'], pairs), True),
... ((['great', 'acting'], ['fine', 'talent'], pairs), False),
... ((['great'], ['great'], []), True),
... ((['great'], ['fine', 'drama'], pairs), False),
... ):
... res = s.areSentencesSimilarTwo(*_in)
... if res != _out: print(_in, res)
... gotcha.append(res == _out)
>>> bool(gotcha) and all(gotcha)
True
"""
class Solution:
"""
UnionFind
"""
def areSentencesSimilarTwo(self, words1, words2, pairs):
"""
:type words1: List[str]
:type words2: List[str]
:type pairs: List[List[str]]
:rtype: bool
"""
if len(words1) != len(words2):
return False
nodes = {}
for a, b in pairs:
self.union(nodes, a, b)
for i in range(len(words1)):
a = words1[i]
b = words2[i]
_a = self.find(nodes, a)
_b = self.find(nodes, b)
if a != b and _a != _b:
return False
return True
def union(self, nodes, a, b):
_a = self.find(nodes, a)
_b = self.find(nodes, b)
if _a is not _b:
nodes[_a] = _b
return _b
def find(self, nodes, a):
if a not in nodes:
nodes[a] = a
return a
if nodes[a] is a:
return a
nodes[a] = self.find(nodes, nodes[a])
return nodes[a]
import collections
class Solution2:
"""
DFS
"""
def areSentencesSimilarTwo(self, words1, words2, pairs):
"""
:type words1: List[str]
:type words2: List[str]
:type pairs: List[List[str]]
:rtype: bool
"""
if len(words1) != len(words2):
return False
simils = collections.defaultdict(set)
for a, b in pairs:
simils[a].add(b)
simils[b].add(a)
for i in range(len(words1)):
a = words1[i]
b = words2[i]
if a != b and not self.dfs(a, b, simils, set()):
return False
return True
def dfs(self, start, end, simils, path):
# check start and end are connected
if start == end:
return True
if start not in simils or start in path:
return False
path.add(start)
for nxt in simils[start]:
if nxt in path:
continue
res = self.dfs(nxt, end, simils, path)
if res:
return True
path.discard(start)
return False
================================================
FILE: leetcode/744_find_smallest_letter_greater_than_target.py
================================================
class Solution:
def nextGreatestLetter(self, L, target):
"""
:type L: List[str]
:type target: str
:rtype: str
"""
n = len(L)
left, right = 0, n - 1
while left + 1 < right:
mid = (left + right) // 2
if L[mid] <= target:
left = mid
else:
right = mid
if target < L[left]:
return L[left]
if target < L[right]:
return L[right]
return L[0]
================================================
FILE: leetcode/746_min_cost_climbing_stairs.py
================================================
class Solution:
def minCostClimbingStairs(self, cost):
"""
:type cost: List[int]
:rtype: int
"""
if not cost:
return 0
n = len(cost)
"""
`dp[i]` means the min cost to possible to reach previous `i` steps
"""
dp = [0] * (n + 1)
for i in range(2, n + 1):
"""
If you decide to come from some step,
and then pay the fee to the from step
"""
dp[i] = min(
dp[i - 1] + cost[i - 1],
dp[i - 2] + cost[i - 2]
)
return dp[n]
================================================
FILE: leetcode/747_largest_number_at_least_twice_of_others.py
================================================
class Solution:
def dominantIndex(self, A):
"""
:type A: List[int]
:rtype: int
"""
_max = __max = float('-inf')
_max_i = -1
for i in range(len(A)):
if A[i] > _max:
__max = _max
_max = A[i]
_max_i = i
continue
if A[i] > __max:
__max = A[i]
if _max >= __max * 2:
return _max_i
return -1
================================================
FILE: leetcode/748_shortest_completing_word.py
================================================
class Solution:
def shortestCompletingWord(self, P, words):
"""
:type P: str
:type words: List[str]
:rtype: str
"""
ans = ''
if not P or not words:
return ans
p_times = self.get_times(P)
_min_size = float('inf')
for word in words:
times = self.get_times(word)
if len(word) < _min_size and self.is_included(p_times, times):
ans = word
_min_size = len(word)
return ans
def is_included(self, a_times, b_times):
"""True if A is a subset of B"""
for char, times in a_times.items():
if char not in b_times:
return False
if b_times[char] < times:
return False
return True
def get_times(self, word):
times = {}
ord_a = ord('a')
ord_z = ord('z')
for char in word.lower():
if ord_a <= ord(char) <= ord_z:
times[char] = times.get(char, 0) + 1
return times
================================================
FILE: leetcode/749_contain_virus.py
================================================
"""
Test Case:
[[1,1,1], [1,0,1], [1,1,1]]
[[1,1,1,0,0,0,0,0,0], [1,0,1,0,1,1,1,1,1], [1,1,1,0,0,0,0,0,0]]
# In the process of spreading will intersect
[[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,1,0,0],[1,0,0,0,0,0,0,0,0,0],[0,0,1,0,0,0,1,0,0,0],[0,0,0,0,0,0,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,0,0,1,0],[0,0,0,0,1,0,1,0,0,0],[0,0,0,0,0,0,0,0,0,0]]
# needs to dedup `ex_virus` and `spreading`
[[0,1,0,1,1,1,1,1,1,0],[0,0,0,1,0,0,0,0,0,0],[0,0,1,1,1,0,0,0,1,0],[0,0,0,1,1,0,0,1,1,0],[0,1,0,0,1,0,1,1,0,1],[0,0,0,1,0,1,0,1,1,1],[0,1,0,0,1,0,0,1,1,0],[0,1,0,1,0,0,0,1,1,0],[0,1,1,0,0,1,1,0,0,1],[1,0,1,1,0,1,0,1,0,1]]
"""
class Solution:
NORMAL = 0
VIRUS = 1
EX_VIRUS = -1
V = (
(-1, 0),
( 1, 0),
( 0, -1),
( 0, 1),
)
def containVirus(self, G):
"""
:type G: List[List[int]]
:rtype: int
"""
walls = 0
if not G or not G[0]:
return walls
while True:
_walls = self.build_walls(G)
if _walls == 0:
break
walls += _walls
return walls
def build_walls(self, G):
m, n = len(G), len(G[0])
ex_virus = []
spreading = []
walls = []
visited = [[False] * n for _ in range(m)]
for x in range(m):
for y in range(n):
if G[x][y] == self.VIRUS and not visited[x][y]:
ex_virus.append(set())
spreading.append(set())
walls.append(0)
self.dfs(x, y, G, visited, ex_virus, spreading, walls)
_max_save = _max_i = -1
s = len(spreading)
for i in range(s):
t = len(spreading[i])
if t > _max_save:
_max_save = t
_max_i = i
if _max_save == -1:
return 0
for i in range(s):
if i == _max_i:
for x, y in ex_virus[i]:
G[x][y] = self.EX_VIRUS
else:
for x, y in spreading[i]:
G[x][y] = self.VIRUS
return walls[_max_i]
def dfs(self, x, y, G, visited, ex_virus, spreading, walls):
m, n = len(G), len(G[0])
if not (0 <= x < m and 0 <= y < n) or visited[x][y]:
return
if G[x][y] == self.VIRUS:
visited[x][y] = True
ex_virus[-1].add((x, y))
for dx, dy in self.V:
_x = x + dx
_y = y + dy
self.dfs(_x, _y, G, visited, ex_virus, spreading, walls)
elif G[x][y] == self.NORMAL:
spreading[-1].add((x, y))
walls[-1] += 1
================================================
FILE: leetcode/750_number_of_corner_rectangles.py
================================================
class Solution:
def countCornerRectangles(self, G):
"""
:type G: List[List[int]]
:rtype: int
iterate every row and save all the pairs of 1 in current row
if there is some pair with same indices in passed row
and then we can ensure rectangles exist
note that:
1 pair: 0 rec
2 pairs: 0+1= 1 rec
4 pairs: 0+1+2+3= 6 recs
n pairs: C(n, 2) = n * (n - 1) / 2 = 0+1+2+...+(n-1)
example: 4 pairs => C(4, 2) = 6 recs
1 1
1 1
1 1
1 1
"""
ans = 0
if not G:
return ans
n = 0
count = {}
for R in G:
for end in range(1, len(R)):
if R[end] == 0:
continue
for start in range(end):
if R[start] == 0:
continue
if (start, end) not in count:
count[start, end] = 0
continue
count[start, end] += 1
ans += count[start, end]
return ans
================================================
FILE: leetcode/758_bold_words_in_string.py
================================================
"""
>>> gotcha = []
>>> for s in (Solution(),):
... for _in, _out in (
... (([''], ''), ''),
... ((['abc', '123'], 'abcxyz123'), 'abcxyz123'),
... ((['aaa','aab','bc'], 'aaabbcc'), 'aaabbcc'),
... ((['ab', 'bc'], 'aabcd'), 'aabcd'),
... ):
... res = s.boldWords(*_in)
... if res != _out: print(_in, res)
... gotcha.append(res == _out)
>>> bool(gotcha) and all(gotcha)
True
"""
class Solution:
def boldWords(self, words, s):
"""
:type words: List[str]
:type s: str
:rtype: str
"""
if not s or not words:
return ''
TMPL = '{}'
n = len(s)
ans = []
is_bold = [False] * n
left = right = 0
for left in range(n):
for w in words:
size = len(w)
if s[left:left + size] == w and left + size > right:
right = left + size
is_bold[left] = right > left
left = right = 0
while left < n:
if not is_bold[left]:
ans.append(s[left])
left += 1
continue
right = left
while right < n and is_bold[right]:
right += 1
ans.append(TMPL.format(s[left:right]))
left = right # imply left' = left + 1
return ''.join(ans)
================================================
FILE: leetcode/769_max_chunks_to_make_sorted.py
================================================
"""
Main Concept:
given nums: 0, 2, 1, 4, 3, 5, 7, 6
1. the sorted nums is just its index
2. maintain a `M` to record, and `M[i]` means the max in [0:i] in nums
3. if `M[i] == i`, ans += 1
sorted: 0, 1, 2, 3, 4, 5, 6, 7
index: 0, 1, 2, 3, 4, 5, 6, 7
max: 0, 2, 2, 4, 4, 5, 7, 7
ans: 1, 1, 2, 2, 3, 4, 4, 5
chunks: 0| 2, 1| 4, 3| 5| 7, 6
Improvement:
just compare when visit it
space: O(n) -> O(1)
"""
class Solution:
def maxChunksToSorted(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
ans = 0
if not nums:
return ans
_max = 0 # since 0 is the min in nums
for i in range(len(nums)):
_max = max(_max, nums[i])
if _max == i:
ans += 1
return ans
class Solution:
def maxChunksToSorted(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
ans = 0
if not nums:
return ans
n = len(nums)
M = [0] * n # `M[i]` means the max in [0:i] in nums
for i in range(n):
M[i] = nums[i]
if i > 0 and M[i - 1] > M[i]:
M[i] = M[i - 1]
for i in range(n):
if M[i] == i:
ans += 1
return ans
================================================
FILE: leetcode/776_split_bst.py
================================================
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def splitBST(self, root, target):
"""
:type root: TreeNode
:type target: int
:rtype: List[TreeNode]
"""
if not root:
return None, None
if root.val > target:
left, right = self.splitBST(root.left, target)
root.left = right
return left, root
else:
left, right = self.splitBST(root.right, target)
root.right = left
return root, right
================================================
FILE: leetcode/777_swap_adjacent_in_lr_string.py
================================================
class Solution:
def canTransform(self, start, end):
"""
:type start: str
:type end: str
:rtype: bool
"""
if len(start) != len(end):
return False
m, n = len(start), len(end)
i = j = 0
while i < m and j < n:
while i < m and start[i] == 'X':
i += 1
while j < n and end[j] == 'X':
j += 1
if i == m and j == n:
return True
if i == m or j == n:
return False
if start[i] != end[j]:
return False
if start[i] == 'L' and j > i:
return False
if start[i] == 'R' and i > j:
return False
i += 1
j += 1
return True
================================================
FILE: leetcode/778_swim_in_rising_water.py
================================================
from heapq import heappush, heappop
class Solution:
def swimInWater(self, G):
"""
:type G: List[List[int]]
:rtype: int
"""
ans = 0
if not G or not G[0]:
return ans
n = len(G)
V = ((-1, 0), (1, 0), (0, -1), (0, 1))
heap = [(G[0][0], 0, 0)]
visited = {(0, 0): True}
while heap:
depth, x, y = heappop(heap)
if depth > ans:
ans = depth
if x == y == n - 1:
return ans
for dx, dy in V:
_x = x + dx
_y = y + dy
if not (0 <= _x < n and 0 <= _y < n):
continue
if (_x, _y) in visited:
continue
visited[_x, _y] = True
heappush(heap, (G[_x][_y], _x, _y))
return ans
================================================
FILE: leetcode/779_k_th_symbol_in_grammar.py
================================================
class Solution:
def kthGrammar(self, N, K):
"""
:type N: int
:type K: int
:rtype: int
"""
if N == 1:
return 0
if K % 2 == 0:
if self.kthGrammar(N - 1, K // 2) == 0:
return 1
else:
return 0
else:
if self.kthGrammar(N - 1, (K + 1) // 2) == 0:
return 0
else:
return 1
================================================
FILE: leetcode/783_minimum_distance_between_bst_nodes.py
================================================
"""
Definition for a binary tree node.
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
"""
class Solution:
"""
Recursion
time: O(n)
space: O(1)
"""
ans = float('inf')
pre = None
def minDiffInBST(self, root):
"""
:type root: TreeNode
:rtype: int
"""
if not root:
return self.ans
self.minDiffInBST(root.left)
if self.pre and root.val - self.pre.val < self.ans:
self.ans = root.val - self.pre.val
self.pre = root
self.minDiffInBST(root.right)
return self.ans
class Solution:
"""
Iteration
time: O(n)
space: O(n)
"""
def minDiffInBST(self, root):
"""
:type root: TreeNode
:rtype: int
"""
if not root:
return
ans = float('inf')
pre = None
stack = []
node = root
while node or stack:
while node:
stack.append(node)
node = node.left
node = stack.pop()
if pre and node.val - pre.val < ans:
ans = node.val - pre.val
pre = node
node = node.right
return ans
================================================
FILE: leetcode/784_letter_case_permutation.py
================================================
class Solution:
def letterCasePermutation(self, s):
"""
:type s: str
:rtype: List[str]
"""
if not s:
return ['']
ans = []
self.dfs(s, 0, ans, [])
return ans
def dfs(self, s, i, ans, path):
if i == len(s):
ans.append(''.join(path))
return
options = [s[i]] if s[i].isdigit() else [s[i].lower(), s[i].upper()]
for c in options:
path.append(c)
self.dfs(s, i + 1, ans, path)
path.pop()
================================================
FILE: leetcode/785_is_graph_bipartite.py
================================================
class Solution:
def isBipartite(self, graph):
"""
:type graph: List[List[int]]
:rtype: bool
"""
color = {}
for node in range(len(graph)):
if node not in color:
color[node] = 0
for nei in graph[node]:
if nei not in color:
color[nei] = color[node] ^ 1
elif color[nei] == color[node]:
return False
return True
================================================
FILE: leetcode/786_k_th_smallest_prime_fraction.py
================================================
"""
Main Concept:
just like found kth smallest num in matrix
example: [1, 2, 5, 7]
7 5 2 1
1 1/7 1/5 1/2 1/1
2 2/7 2/5 ...
5 ...
7 ...
"""
from heapq import heappush, heappop
class Solution:
def kthSmallestPrimeFraction(self, A, K):
"""
:type A: List[int]
:type K: int
:rtype: List[int]
"""
heap = []
n = len(A)
A.sort()
for i in range(n):
heappush(heap, (A[i]/A[-1], i, n - 1))
for _ in range(K - 1):
_, i, j = heappop(heap)
j -= 1
if j >= 0:
heappush(heap, (A[i]/A[j], i, j))
_, i, j = heappop(heap)
return [A[i], A[j]]
================================================
FILE: leetcode/787_cheapest_flights_within_k_stops.py
================================================
class Solution:
def findCheapestPrice(self, n, flights, src, dst, K):
"""
:type n: int
:type flights: List[List[int]]
:type src: int
:type dst: int
:type K: int
:rtype: int
"""
min_cost = [float('inf')] * n # the minimum cost to get to node
costs = [float('inf')] * n
min_cost[src] = costs[src] = 0
for _ in range(K + 1):
for u, v, cost in flights:
costs[v] = min(costs[v], min_cost[u] + cost)
min_cost = costs
return costs[dst] if costs[dst] < float('inf') else -1
class Solution:
def findCheapestPrice(self, n, flights, src, dst, K):
"""
:type n: int
:type flights: List[List[int]]
:type src: int
:type dst: int
:type K: int
:rtype: int
"""
if src == dst:
return 0
INF = float('inf')
"""
`dp[i][k]` means the cost when the end is `i` with `k` stop
"""
dp = [[INF] * (K + 1) for _ in range(n)]
dp[src][0] = 0
for start, end, cost in flights:
if start == src and cost < dp[end][0]:
dp[end][0] = cost
for k in range(1, K + 1):
for i in range(n):
dp[i][k] = dp[i][k - 1]
for start, end, cost in flights:
dp[end][k] = min(
dp[end][k],
dp[start][k - 1] + cost
)
return dp[dst][K] if dp[dst][K] < INF else -1
================================================
FILE: leetcode/788_rotated_digits.py
================================================
class Solution:
def rotatedDigits(self, N):
"""
:type N: int
:rtype: int
"""
ans = 0
for i in range(1, N + 1):
if self.is_good(i):
ans += 1
return ans
def is_good(self, N):
res = False
while N > 0:
D = N % 10
if D in (3, 4, 7):
return False
if D in (2, 5, 6, 9):
res = True
N = N // 10
return res
================================================
FILE: leetcode/789_escape_the_ghosts.py
================================================
class Solution:
def escapeGhosts(self, ghosts, target):
"""
:type ghosts: List[List[int]]
:type target: List[int]
:rtype: bool
"""
R, C = target
pacman_dist = abs(R) + abs(C) # (R - 0) + (C - 0)
for x, y in ghosts:
ghost_dist = abs(R - x) + abs(C - y)
if ghost_dist <= pacman_dist:
return False
return True
================================================
FILE: leetcode/790_domino_and_tromino_tiling.py
================================================
class Solution:
def numTilings(self, N):
"""
:type N: int
:rtype: int
"""
if N < 3:
return N
MOD = 10 ** 9 + 7
dp = [0] * (N + 1)
dp[:3] = 1, 1, 2
for i in range(3, N + 1):
dp[i] = (dp[i - 1] * 2 + dp[i - 3]) % MOD
return dp[N]
================================================
FILE: leetcode/791_custom_sort_string.py
================================================
class Solution:
def customSortString(self, S, T):
"""
:type S: str
:type T: str
:rtype: str
"""
return ''.join(sorted(T, key=lambda c: S.find(c)))
================================================
FILE: leetcode/7_reverse_integer.py
================================================
class Solution:
def reverse(self, x):
"""
:type x: int
:rtype: int
"""
ans = 0
if not x:
return ans
INT_MAX = 0x7FFFFFFF
_x = x if x > 0 else -x
while _x:
ans = ans * 10 + _x % 10
_x //= 10
if ans >= INT_MAX:
return 0
return ans if x > 0 else -ans
================================================
FILE: leetcode/81_search_in_rotated_sorted_array_ii.py
================================================
class Solution:
def search(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: bool
"""
if not nums:
return False
for num in nums:
if num == target:
return True
return False
================================================
FILE: leetcode/8_string_to_integer_atoi.py
================================================
class Solution:
def myAtoi(self, s):
"""
:type s: str
:rtype: int
"""
ans = 0
if not s:
return ans
INT_MAX = 0x7FFFFFFF
N = len(s)
ZERO = ord('0')
sign = 1
i = 0
while i < N and s[i] == ' ':
i += 1
if i < N and s[i] in ('+', '-'):
sign = -1 if s[i] == '-' else 1
i += 1
while i < N and s[i].isdigit():
val = ord(s[i]) - ZERO
if (ans > INT_MAX // 10 or
(ans == INT_MAX // 10 and val > 7)):
return INT_MAX if sign == 1 else ~INT_MAX
ans = ans * 10 + val
i += 1
return sign * ans
================================================
FILE: lintcode/102_linked_list_cycle.py
================================================
"""
Definition of ListNode
class ListNode(object):
def __init__(self, val, next=None):
self.val = val
self.next = next
"""
class Solution:
"""
@param: head: The first node of linked list.
@return: True if it has a cycle, or false
"""
def hasCycle(self, head):
"""
if its a cycle, and then we can ensure
the 2-pace pointer and the 1-pace pointer
will eventually meet
otherwise its a list => at some point there will be no `node.next`
"""
if not head or not head.next:
return False
slow = head
fast = head.next
while slow != fast:
if not fast.next or not fast.next.next:
return False
slow = slow.next
fast = fast.next.next
return True
================================================
FILE: lintcode/103_linked_list_cycle_ii.py
================================================
"""
Definition of ListNode
class ListNode(object):
def __init__(self, val, next=None):
self.val = val
self.next = next
"""
class Solution:
"""
@param: head: The first node of linked list.
@return: The node where the cycle begins. if there is no cycle, return null
"""
def detectCycle(self, head):
"""
example: 1->2->3
^ v
5<-4
* h: head, s: slow, f: fast, i: intersection node
r1/
s f
1 -> 2 -> 3
^ v
5 <- 4
r2/
s
1 -> 2 -> 3
^ v
5 <- 4 f
r3/
f s
1 -> 2 -> 3
^ v
5 <- 4
r4/
h i
1 -> 2 -> 3
^ v
5 <- 4 s, f
h->i == 1
s->i == 2
"""
if not head or not head.next:
return
"""
if its a cycle, and then we can ensure
the 2-pace pointer and the 1-pace pointer
will eventually meet
otherwise its a list => at some point there will be no `node.next`
"""
slow, fast = head, head.next
while slow is not fast:
if not fast or not fast.next:
return
slow = slow.next
fast = fast.next.next
"""
at this point, slow meet fast
and at the intersection node
the steps from the first node is equal to from meet node plus 1
"""
while head is not slow.next:
head = head.next
slow = slow.next
return head
================================================
FILE: lintcode/104_merge_k_sorted_lists.py
================================================
"""
Definition of ListNode
class ListNode(object):
def __init__(self, val, next=None):
self.val = val
self.next = next
"""
import heapq
class Solution:
def mergeKLists(self, lists):
"""
:type lists: list[ListNode]
:rtype: ListNode
"""
if not lists:
return
dummy = tail = ListNode(-1)
heap = []
for i in range(len(lists)):
if not lists[i]:
continue
heapq.heappush(heap, (lists[i].val, i))
while heap:
val, i = heapq.heappop(heap)
tail.next = ListNode(val)
tail = tail.next
if lists[i].next:
lists[i] = lists[i].next
heapq.heappush(heap, (lists[i].val, i))
return dummy.next
================================================
FILE: lintcode/105_copy_list_with_random_pointer.py
================================================
"""
Definition for singly-linked list with a random pointer.
class RandomListNode:
def __init__(self, x):
self.label = x
self.next = None
self.random = None
"""
"""
using hashmap
time: O(2n) => O(n)
space: O(n)
"""
class Solution:
def copyRandomList(self, head):
"""
:type head: RandomListNode
:rtype: RandomListNode
"""
N = {}
dummy = tail = RandomListNode(-1)
while head:
node = RandomListNode(head.label)
node.random = head.random
tail.next = node
N[head] = node
tail = tail.next
head = head.next
head = dummy.next
while head:
if head.random:
head.random = N[head.random]
head = head.next
return dummy.next
"""
temply save in n.next
time: O(3n) => O(n)
space: O(1)
example: 1->2->3
copy_next/
|--------->|
1 -> 1' -> 2 -> 2' -> 3 -> 3'
replace_random/
|--------->|
|----+---->| |
1 -> 1' -> 2 -> 2' -> 3 -> 3'
split_list/
|--------->|
1 -> 2 -> 3
1' -> 2' -> 3'
|--------->|
"""
class Solution:
def copyRandomList(self, head):
"""
:type head: RandomListNode
:rtype: RandomListNode
"""
if not head:
return
tail = head
node = None
while tail:
node = RandomListNode(tail.label)
node.random = tail.random
node.next = tail.next
tail.next = node
tail = tail.next.next
tail = head
while tail:
if tail.next and tail.random:
tail.next.random = tail.random.next
tail = tail.next.next
node = tail = head.next
while tail and tail.next:
tail.next = tail.next.next
tail = tail.next
return node
================================================
FILE: lintcode/107_word_break.py
================================================
class Solution:
"""
`dp[i]` means `s[:i]` is segmented by words
"""
def wordBreak(self, s, words):
"""
:type s: str
:type words: List[str]
:rtype: bool
"""
if not s and not words:
return True
if not s or not words:
return False
max_size = max(len(w) for w in words)
word_set = set(words)
n = len(s)
dp = [False] * (n + 1)
dp[0] = True
for i in range(1, n + 1):
for j in range(1, min(i, max_size) + 1):
if dp[i - j] and s[i - j:i] in word_set:
dp[i] = True
break
return dp[n]
================================================
FILE: lintcode/108_palindrome_partitioning_ii.py
================================================
class Solution:
# @param S, a string
# @return an integer
def minCut(self, S):
if not S:
return -1
n = len(S)
INFINITY = float('inf')
is_palindrome = self.get_palin_map(S)
"""
`dp[i]` means the minimum palindrome count
broken from the substring ended at `i`
"""
dp = [INFINITY] * (n + 1)
dp[0] = 0
for end in range(1, n + 1):
for start in range(end):
if (not is_palindrome[start][end - 1] or
dp[start] is INFINITY):
continue
if dp[start] + 1 < dp[end]:
dp[end] = dp[start] + 1
return dp[n] - 1
def get_palin_map(self, S):
n = len(S)
is_palindrome = [[False] * n for _ in range(n)]
is_palindrome[0][0] = True
for end in range(1, n):
is_palindrome[end][end] = True
start = end - 1
is_palindrome[start][end] = (S[start] == S[end])
for start in range(n - 1 - 2, -1, -1):
for end in range(start + 2, n):
if not is_palindrome[start + 1][end - 1]:
continue
is_palindrome[start][end] = (S[start] == S[end])
return is_palindrome
================================================
FILE: lintcode/109_triangle.py
================================================
"""
DP: Memory Searching
"""
class Solution:
"""
@param: triangle: a list of lists of integers
@return: An integer, minimum path sum
"""
def minimumTotal(self, triangle):
if not triangle or not triangle[0]:
return 0
return self.memo_search(0, 0, triangle, {})
def memo_search(self, depth, start, triangle, memo):
if depth == len(triangle) - 1:
return triangle[depth][start]
key = (depth, start)
if key in memo:
return memo[key]
memo[key] = min(
self.memo_search(depth + 1, start, triangle, memo),
self.memo_search(depth + 1, start + 1, triangle, memo)
)
memo[key] += triangle[depth][start]
return memo[key]
"""
DP: Top-down Recuring + Rolling Array
"""
class Solution:
"""
@param: triangle: a list of lists of integers
@return: An integer, minimum path sum
"""
def minimumTotal(self, triangle):
if not triangle or not triangle[0]:
return 0
INFINITY = float('inf')
m = len(triangle)
dp = [[INFINITY] * (m + 1) for _ in range(2)]
prev = curr = 0
for i in range(1, m + 1):
prev = curr
curr = 1 - curr
for j in range(1, i + 1):
"""
dp[prev][j] == dp[i - 1][j]
dp[curr][j] == dp[i][j]
"""
dp[curr][j] = triangle[i - 1][j - 1]
if dp[prev][j - 1] < INFINITY or dp[prev][j] < INFINITY:
"""
NO need to calculate first,
since only one path from top
"""
dp[curr][j] += min(
dp[prev][j - 1],
dp[prev][j]
)
return min(dp[curr])
"""
DP: Bottom-up Recuring + Rolling Array
"""
class Solution:
"""
@param: triangle: a list of lists of integers
@return: An integer, minimum path sum
"""
def minimumTotal(self, triangle):
if not triangle or not triangle[0]:
return 0
INFINITY = float('inf')
m = len(triangle)
dp = [[INFINITY] * (m + 1) for _ in range(m + 1)]
prev = curr = 0
for i in range(m - 1, -1, -1):
prev = curr
curr = 1 - curr
for j in range(i + 1):
"""
dp[prev][j] == dp[i + 1][j]
dp[curr][j] == dp[i][j]
"""
dp[curr][j] = triangle[i][j]
if dp[prev][j] < INFINITY or dp[prev][j + 1] < INFINITY:
"""
MUST be calculated first,
since there are two paths from bottom
and `dp[curr][j]` maybe negative
"""
dp[curr][j] = min(
dp[curr][j] + dp[prev][j],
dp[curr][j] + dp[prev][j + 1]
)
return dp[curr][0]
================================================
FILE: lintcode/10_string_permutation_ii.py
================================================
class Solution:
"""
@param: S: A string
@return: all permutations
"""
def stringPermutation2(self, S):
if not S:
return ['']
S = sorted(S)
ans = []
self.dfs(S, ans, [])
return ans
def dfs(self, S, ans, path):
if not S:
ans.append(''.join(path))
return
for i in range(len(S)):
if i > 0 and S[i] == S[i - 1]:
continue
path.append(S[i])
self.dfs(S[:i] + S[i + 1:], ans, path)
path.pop()
================================================
FILE: lintcode/110_minimum_path_sum.py
================================================
class Solution:
"""
@param: grid: a list of lists of integers
@return: An integer, minimizes the sum of all numbers along its path
"""
def minPathSum(self, grid):
if not grid:
return 0
m, n = len(grid), len(grid[0])
dp = [[0] * n for _ in range(m)]
for j in range(n):
if j == 0:
dp[0][j] = grid[0][j]
continue
dp[0][j] = grid[0][j] + dp[0][j - 1]
for i in range(1, m):
dp[i][0] = grid[i][0] + dp[i - 1][0]
for j in range(1, n):
if dp[i - 1][j] < dp[i][j - 1]:
dp[i][j] = grid[i][j] + dp[i - 1][j]
else:
dp[i][j] = grid[i][j] + dp[i][j - 1]
return dp[m - 1][n - 1]
================================================
FILE: lintcode/111_climbing_stairs.py
================================================
"""
DP: rolling array with `n + 1`
"""
class Solution:
"""
@param n: An integer
@return: An integer
"""
def climbStairs(self, n):
if n <= 0:
return 0
dp = [0] * 3
pre2, pre1, curr = 0, 0, 1
dp[0] = dp[1] = 1
for i in range(2, n + 1):
pre2 = pre1
pre1 = curr
curr = i % 3
dp[curr] = dp[pre1] + dp[pre2]
return dp[curr]
"""
DP: origin `n`
"""
class Solution:
"""
@param n: An integer
@return: An integer
"""
def climbStairs(self, n):
if n <= 0:
return 0
dp = [0] * n
dp[0] = 1
dp[1] = 2
for i in range(2, n):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n - 1]
"""
DP: origin `n + 1`
"""
class Solution:
"""
@param n: An integer
@return: An integer
"""
def climbStairs(self, n):
if n <= 0:
return 0
dp = [0] * (n + 1)
dp[0] = dp[1] = 1
for i in range(2, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]
================================================
FILE: lintcode/114_unique_paths.py
================================================
class Solution:
def uniquePaths(self, m, n):
"""
:type m: int
:type n: int
:rtype: int
"""
dp = [[1] * n for _ in range(m)]
for x in range(1, m):
for y in range(1, n):
dp[x][y] = dp[x - 1][y] + dp[x][y - 1]
return dp[m - 1][n - 1]
================================================
FILE: lintcode/115_unique_paths_ii.py
================================================
class Solution:
"""
@param: G: A list of lists of integers
@return: An integer
"""
def uniquePathsWithObstacles(self, G):
if not G or not G[0]:
return 0
OBSTACLE = 1
m, n = len(G), len(G[0])
dp = [[0] * n for _ in range(2)]
prev = curr = 0
for j in range(n):
if G[0][j] == OBSTACLE:
break
dp[curr][j] = 1
for i in range(1, m):
prev = curr
curr = 1 - curr
dp[curr][0] = 0 if G[i][0] == OBSTACLE else dp[prev][0]
for j in range(1, n):
if G[i][j] == OBSTACLE:
dp[curr][j] = 0
continue
dp[curr][j] = dp[prev][j] + dp[curr][j - 1]
return dp[curr][n - 1]
================================================
FILE: lintcode/116_jump_game.py
================================================
"""
Greedy
https://leetcode.com/articles/jump-game/
"""
class Solution:
def canJump(self, A):
"""
:type A: List[int]
:rtype: bool
"""
if not A:
return False
last_at = len(A) - 1
for i in range(last_at, -1, -1):
if i + A[i] >= last_at:
last_at = i
return last_at == 0
"""
DP
"""
class Solution:
def canJump(self, A):
"""
:type A: List[int]
:rtype: bool
"""
if not A:
return False
n = len(A)
dp = [False] * n
"""
`dp[i]` means `i` could be reached
"""
dp[0] = True
for i in range(1, n):
for j in range(i):
"""
backtracking
if `j` could be reached
"""
if dp[j] and j + A[j] >= i:
"""
if jump from `j` can reach `i`
"""
dp[i] = True
break
return dp[n - 1]
================================================
FILE: lintcode/117_jump_game_ii.py
================================================
"""
Greedy
"""
class Solution:
"""
@param: A: A list of integers
@return: An integer
"""
def jump(self, A):
if not A:
return -1
target = len(A) - 1
start = end = jumps = 0
while end < target:
jumps += 1
furthest = end
for i in range(start, end + 1):
if i + A[i] > furthest:
furthest = i + A[i]
start = end + 1
end = furthest
return jumps
"""
DP
"""
class Solution:
"""
@param: A: A list of integers
@return: An integer
"""
def jump(self, A):
if not A:
return -1
INFINITY = float('inf')
n = len(A)
dp = [INFINITY] * n
dp[0] = 0
for i in range(1, n):
for j in range(i):
if (dp[j] < INFINITY and j + A[j] >= i and
dp[j] + 1 < dp[i]):
dp[i] = dp[j] + 1
return dp[n - 1]
================================================
FILE: lintcode/118_distinct_subsequences.py
================================================
class Solution:
"""
@param: : A string
@param: : A string
@return: Count the number of distinct subsequences
"""
def numDistinct(self, S, T):
if S is None or T is None:
return 0
if S is '' and '':
return 1
m, n = len(S), len(T)
"""
`dp[i][j]` means the count of distinct subsequences
(the substr end at `T[j - 1]`) in the substr end at `S[i - 1]`
"""
dp = [[0] * (n + 1) for _ in range(2)]
prev = curr = 0
dp[curr][0] = 1
for i in range(1, m + 1):
prev = curr
curr = 1 - curr
dp[curr][0] = 1
for j in range(1, n + 1):
"""
case 1: `S[i - 1]` and `T[j - 1]` is not a pair
so keep `T[j - 1]` in candidates
"""
dp[curr][j] = dp[prev][j]
"""
case 2: `S[i - 1]` and `T[j - 1]` is a pair
do NOT `+1` -> its for size, this problem is for count
"""
if S[i - 1] == T[j - 1]:
dp[curr][j] += dp[prev][j - 1]
return dp[curr][n]
================================================
FILE: lintcode/119_edit_distance.py
================================================
class Solution:
def minDistance(self, s, t):
"""
:type s: str
:type t: str
:rtype: int
"""
if s is None or t is None:
return 0
m, n = len(s), len(t)
"""
`dp[i][j]` means the minimum operations to convert
the substr end at `A[i - 1]` to
the substr end at `B[j - 1]`
"""
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(1, m + 1):
dp[i][0] = i
for j in range(1, n + 1):
dp[0][j] = j
for i in range(1, m + 1):
for j in range(1, n + 1):
"""
no need to init dp[curr][j]
case 1: no need to do any operations
"""
if s[i - 1] == t[j - 1]:
dp[i][j] = dp[i - 1][j - 1]
continue
"""
case 2: remove last char in A
case 3: add `B[j - 1]` to the end of A
case 4: replace laster char in A
"""
dp[i][j] = 1 + min(
dp[i - 1][j],
dp[i][j - 1],
dp[i - 1][j - 1]
)
return dp[m][n]
================================================
FILE: lintcode/11_search_range_in_binary_search_tree.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
def searchRange(self, root, a, b):
"""
:type root: TreeNode
:type a: int
:type b: int
:rtype: list[int]
"""
ans = []
if not root:
return ans
self.dfs(root, a, b, ans)
return ans
def dfs(self, node, a, b, ans):
if not node:
return
self.dfs(node.left, a, b, ans)
if a <= node.val <= b:
ans.append(node.val)
self.dfs(node.right, a, b, ans)
================================================
FILE: lintcode/120_word_ladder.py
================================================
"""
Test Case:
"a"
"a"
["b"]
=> should check again words in queue
"""
class Solution:
def ladderLength(self, s, e, D):
"""
:type s: str
:type e: str
:type D: List[str]
:rtype: int
"""
if (not s or not e or
len(s) != len(e) or not D):
return 0
if s == e:
return 1
if s not in D:
D.append(s)
if e not in D:
D.append(e)
n = len(s)
next_words = [None] * n
for i in range(n):
next_words[i] = _words = {}
for word in D:
key = word[:i] + word[i + 1:]
if key not in _words:
_words[key] = set()
_words[key].add(word)
queue = [e]
distance = {e: 1}
for word in queue:
for _word in self.get_next_word(word, next_words):
if _word in distance:
continue
distance[_word] = distance[word] + 1
if _word == s:
return distance[_word]
queue.append(_word)
return 0
def get_next_word(self, word, next_words):
for i in range(len(word)):
key = word[:i] + word[i + 1:]
if key not in next_words[i]:
continue
for _word in next_words[i][key]:
if _word == word:
continue
yield _word
================================================
FILE: lintcode/121_word_ladder_ii.py
================================================
"""
Main Concept:
1. building `next_words` in advance to speed up
2. using BFS from `B` to `A` to calculate the distance guide
3. using DFS step by step to find all possible path to get `B`
"""
class Solution:
"""
@param: A: a string
@param: B: a string
@param: D: a set of string
@return: a list of lists of string
"""
def findLadders(self, A, B, D):
ans = []
if (D is None or A is None or B is None or
len(A) != len(B)):
return ans
if A not in D:
D.add(A)
if B not in D:
D.add(B)
n = len(A)
next_words = [None] * n
for i in range(n):
next_words[i] = W = {}
for word in D:
key = word[:i] + word[i + 1:]
if key not in W:
W[key] = set()
W[key].add(word)
queue = [B]
distance = {B: 1}
for word in queue:
if word == A:
break
for _word in self.get_next_word(word, next_words):
if _word in distance:
continue
distance[_word] = distance[word] + 1
queue.append(_word)
self.dfs(A, B, next_words, distance, ans, [A])
return ans
def dfs(self, word, B, next_words, distance, ans, path):
if word == B:
ans.append(path[:])
return
for _word in self.get_next_word(word, next_words):
if (_word not in distance or
distance[_word] != distance[word] - 1):
continue
path.append(_word)
self.dfs(_word, B, next_words, distance, ans, path)
path.pop()
def get_next_word(self, word, next_words):
for i in range(len(word)):
key = word[:i] + word[i + 1:]
if key not in next_words[i]:
continue
for _word in next_words[i][key]:
if _word == word:
continue
yield _word
================================================
FILE: lintcode/122_largest_rectangle_in_histogram.py
================================================
"""
REF: https://aaronice.gitbooks.io/lintcode/content/data_structure/largest_rectangle_in_histogram.html
"""
"""
Brute Force: TLE
"""
class Solution:
def largestRectangleArea(self, H):
"""
:type H: List[int]
:rtype: int
"""
ans = 0
if not H:
return ans
n = len(H)
L = [0] * n # lowest height
for left in range(n):
for right in range(left, n):
L[right] = H[right]
if right > left and L[right - 1] < H[right]:
L[right] = L[right - 1]
area = L[right] * (right - left + 1)
if area > ans:
ans = area
return ans
"""
Brute Force with Pruning
"""
class Solution:
def largestRectangleArea(self, H):
"""
:type H: List[int]
:rtype: int
"""
ans = 0
if not H:
return ans
n = len(H)
for right in range(len(H)):
if right < n - 1 and H[right] <= H[right + 1]:
continue
Hmin = H[right]
for left in range(right, -1, -1):
if H[left] < Hmin:
Hmin = H[left]
area = Hmin * (right - left + 1)
if area > ans:
ans = area
return ans
"""
Mono-stack
"""
class Solution:
def largestRectangleArea(self, H):
"""
:type H: List[int]
:rtype: int
"""
ans = 0
if not H:
return ans
H.append(0)
stack = []
for right in range(len(H)):
while stack and H[stack[-1]] >= H[right]:
height = H[stack.pop()]
left = stack[-1] if stack else -1
area = height * (right - left - 1)
if area > ans:
ans = area
stack.append(right)
return ans
================================================
FILE: lintcode/123_word_search.py
================================================
class Solution:
V = (
(-1, 0),
( 1, 0),
( 0, -1),
( 0, 1),
)
"""
@param: G: A list of lists of character
@param: s: A string
@return: A boolean
"""
def exist(self, G, s):
if G is None or G[0] is None or s is None:
return False
m, n = len(G), len(G[0])
visited = [[False] * n for _ in range(m)]
for x in range(m):
for y in range(n):
if (G[x][y] == s[0] and
self.dfs(G, x, y, s, 1, visited)):
return True
return False
def dfs(self, G, x, y, s, i, visited):
if i >= len(s):
return True
for dx, dy in self.V:
_x = x + dx
_y = y + dy
if not (0 <= _x < len(G) and 0 <= _y < len(G[0])):
continue
if visited[_x][_y] or G[_x][_y] != s[i]:
continue
visited[_x][_y] = True
if self.dfs(G, _x, _y, s, i + 1, visited):
return True
visited[_x][_y] = False
return False
================================================
FILE: lintcode/124_longest_consecutive_sequence.py
================================================
class Solution:
"""
maintain a set to record if there is unused cands
"""
def longestConsecutive(self, nums):
"""
:type nums: list[int]
:rtype: int
"""
ans = 0
if not nums:
return ans
cands = set(nums) # dedup
for a in nums:
if a not in cands:
continue
cands.discard(a)
size = 1
b, c = a - 1, a + 1
while b in cands:
cands.discard(b)
b -= 1
size += 1
while c in cands:
cands.discard(c)
c += 1
size += 1
if size > ans:
ans = size
return ans
class Solution:
"""
1. sorted
2. if its consecutive, add 1 for size
3. save the maximum size
"""
def longestConsecutive(self, nums):
"""
:type nums: list[int]
:rtype: int
"""
ans = 0
if not nums:
return ans
nums.sort()
size = 1
for i in range(1, len(nums)):
if nums[i] == nums[i - 1]:
continue
if nums[i] == nums[i - 1] + 1:
size += 1
else:
size = 1
if size > ans:
ans = size
return ans if ans > 0 else size
================================================
FILE: lintcode/125_backpack_ii.py
================================================
"""
Rolling Array
"""
class Solution:
"""
@param: m: An integer m denotes the size of a backpack
@param: A: Given n items with size A[i]
@param: V: Given n items with value V[i]
@return: The maximum value
"""
def backPackII(self, m, A, V):
if not m or not A or not V:
return 0
# `dp[i][w]` means the maximum value
# with weight `w` in the former `i` items
dp = [[0] * (m + 1) for _ in range(2)]
prev = curr = 0
for i in range(1, len(A) + 1):
prev = curr
curr = 1 - curr
for w in range(1, m + 1):
dp[curr][w] = dp[prev][w]
if w >= A[i - 1]:
dp[curr][w] = max(
dp[curr][w],
dp[prev][w - A[i - 1]] + V[i - 1]
)
return dp[curr][m]
"""
Origin
"""
class Solution:
"""
@param: m: An integer m denotes the size of a backpack
@param: A: Given n items with size A[i]
@param: V: Given n items with value V[i]
@return: The maximum value
"""
def backPackII(self, m, A, V):
if not m or not A or not V:
return 0
n = len(A)
dp = [[0] * (m + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
for w in range(1, m + 1):
dp[i][w] = dp[i - 1][w]
if w >= A[i - 1]:
dp[i][w] = max(
dp[i][w],
dp[i - 1][w - A[i - 1]] + V[i - 1]
)
return dp[n][m]
================================================
FILE: lintcode/126_max_tree.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
"""
@param: A: Given an integer array with no duplicates.
@return: The root of max tree.
"""
"""
Assuming A = [2,5,6,0,3,1]
step1/ val: 2, stack: [2]
step2/ val: 5, stack: [5{2,}]
step3/ val: 6, stack: [6{5{2,},}]
step4/ val: 0, stack: [6{5{2,},0}, 0]
step5/ val: 3, stack: [6{5{2,},3{0,}}, 3{0,}]
step6/ val: 1, stack: [6{5{2,},3{0,1}}, 3{0,1}, 1]
"""
def maxTree(self, A):
stack = []
for val in A:
node = TreeNode(val)
while stack and val > stack[-1].val:
node.left = stack.pop()
# current val less than the last node in stack
if stack:
stack[-1].right = node
stack.append(node)
return stack[0]
================================================
FILE: lintcode/127_topological_sorting.py
================================================
"""
Definition for a Directed graph node
class DirectedGraphNode:
def __init__(self, x):
self.label = x
self.neighbors = []
"""
class Solution:
"""
@param: graph: A list of Directed graph node
@return: Any topological order for the given graph.
"""
def topSort(self, graph):
ans = []
if not graph:
return ans
indegs = {}
for node in graph:
if node not in indegs:
indegs[node] = 0
for _node in node.neighbors:
if _node not in indegs:
indegs[_node] = 0
indegs[_node] += 1
queue = [node for node, indeg in indegs.items() if indeg == 0]
for node in queue:
ans.append(node)
for _node in node.neighbors:
indegs[_node] -= 1
if indegs[_node] == 0:
queue.append(_node)
return ans
================================================
FILE: lintcode/128_hash_function.py
================================================
class Solution:
"""
@param: key: A string you should hash
@param: HASH_SIZE: An integer
@return: An integer
"""
def hashCode(self, key, HASH_SIZE):
if not key:
return 0
MAGIC_NUMBER = 33
_code = 0
for char in key:
_code = (_code * MAGIC_NUMBER + ord(char)) % HASH_SIZE
return _code
================================================
FILE: lintcode/129_rehashing.py
================================================
"""
Definition of ListNode
class ListNode(object):
def __init__(self, val, next=None):
self.val = val
self.next = next
"""
class Solution:
"""
@param hash_table: A list of The first node of linked list
@return: A list of The first node of linked list which have twice size
"""
def rehashing(self, hash_table):
if not hash_table:
return
CAPACITY = len(hash_table) * 2
heads = [None] * CAPACITY
tails = [None] * CAPACITY
curr = _node = i = None
for node in hash_table:
curr = node
while curr:
i = curr.val % CAPACITY
_node = ListNode(curr.val)
if heads[i]:
tails[i].next = _node
else:
heads[i] = _node
tails[i] = _node
curr = curr.next
return heads
================================================
FILE: lintcode/12_min_stack.py
================================================
class MinStack:
def __init__(self):
self.stack = []
self.mins = []
def push(self, x):
"""
:type x: int
:rtype: void
"""
self.stack.append(x)
if not self.mins or x <= self.mins[-1]:
self.mins.append(x)
def pop(self):
"""
:rtype: int
"""
if not self.stack:
return -1
x = self.stack.pop()
if self.mins and x == self.mins[-1]:
self.mins.pop()
return x
def top(self):
"""
:rtype: int
"""
if not self.stack:
return -1
return self.stack[-1]
def min(self):
"""
:rtype: int
"""
if not self.mins:
return -1
return self.mins[-1]
# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(x)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.min()
================================================
FILE: lintcode/130_heapify.py
================================================
class Solution:
"""
@param: A: Given an integer array
@return: nothing
"""
def heapify(self, A):
# start from mid-depth to sift down
for i in range(len(A) // 2, -1, -1):
self.siftdown(A, i)
def siftdown(self, A, i):
"""
sift down
1. pick the smaller child to swap
2. if parent is already small than both children, no need to continue
3. continue to sift down in next depth
"""
n = len(A)
while i * 2 + 1 < n:
# left child
_i = i * 2 + 1
if _i + 1 < n and A[_i + 1] < A[_i]:
# right child
_i += 1
if A[_i] >= A[i]:
# if its already steady
break
A[i], A[_i] = A[_i], A[i]
i = _i
================================================
FILE: lintcode/131_building_outline.py
================================================
"""
this problem familiar with `leetcode/218_the_skyline_problem.py`
with different output
"""
import heapq
class HashHeapq:
def __init__(self):
self.heap = []
self.deleted = {}
def push(self, val):
heapq.heappush(self.heap, val)
def pop(self):
if self.is_empty():
return -1
return heapq.heappop(self.heap)
def remove(self, val):
if self.is_empty():
return
if val not in self.deleted:
self.deleted[val] = 0
self.deleted[val] += 1
def top(self):
if self.is_empty():
return -1
return self.heap[0]
def is_empty(self):
while self.heap and self.deleted.get(self.heap[0]):
val = heapq.heappop(self.heap)
self.deleted[val] -= 1
return not self.heap
class Solution:
def buildingOutline(self, buildings):
"""
:type buildings: List[List[int]]
:rtype: List[List[int]]
"""
ans = []
if not buildings:
return ans
time = []
for x, _x, height in buildings:
time.append((x, height, True))
time.append((_x, height, False))
time.sort()
heap = HashHeapq()
tmp = []
for x, height, is_start in time:
if is_start:
heap.push(-height)
else:
heap.remove(-height)
max_h = -heap.top() if not heap.is_empty() else 0
if tmp and tmp[-1][0] == x:
tmp.pop()
if tmp and tmp[-1][1] == max_h:
continue
tmp.append([x, max_h])
_x = pre_h = 0
for x, height in tmp:
if pre_h > 0:
ans.append([_x, x, pre_h])
_x = x
pre_h = height
return ans
================================================
FILE: lintcode/132_word_search_ii.py
================================================
class Solution:
def __init__(self):
self.root = self.new_node()
self.row_vector = [1, -1, 0, 0]
self.col_vector = [0, 0, 1, -1]
def new_node(self):
return {
'end_of': '',
'children': {}
}
def put(self, parent, string):
if not string:
return
for char in string:
if char in parent['children']:
parent = parent['children'][char]
else:
parent['children'][char] = self.new_node()
parent = parent['children'][char]
parent['end_of'] = string
"""
@param: board: A list of lists of character
@param: words: A list of string
@return: A list of string
"""
def wordSearchII(self, board, words):
if not words or len(words) < 1 \
or not board or len(board) < 1 \
or len(board[0]) < 1:
return []
self.m, self.n = len(board), len(board[0])
self.board = board
for word in words:
self.put(self.root, word)
result = {}
for row in range(self.m):
for col in range(self.n):
if board[row][col] in self.root['children']:
self.find(row, col, self.root, result)
return result.keys()
def find(self, x, y, parent, result):
char = self.board[x][y]
if char not in parent['children']:
return
parent = parent['children'][char]
if parent['end_of']:
result[parent['end_of']] = 1
parent['end_of'] = ''
# To avoid returning along the original path, just simply set the last visited cell to `'#'`
self.board[x][y] = '#'
for d in range(4):
_x = x + self.row_vector[d]
_y = y + self.col_vector[d]
if 0 <= _x < self.m \
and 0 <= _y < self.n \
and self.board[_x][_y] in parent['children']:
self.find(_x, _y, parent, result)
self.board[x][y] = char
================================================
FILE: lintcode/134_lru_cache.py
================================================
"""
Main Concept:
Dm <-> a <-> b <-> c <-> dm |<- cache_list (dll)
1. if cache is updated (set/get)
=> move to the end of cache_list
2. if cache is full
=> evict the most left node in cache first,
that is `a` in above diagram
=> add the new cache to the end of the cache_list as new tail
"""
class LRUCache:
def __init__(self, capacity):
"""
:type capacity: int
"""
self.cap = capacity
self.nodes = {}
self.D = CacheNode(-1)
self.d = CacheNode(-1)
self.D.nxt = self.d
self.d.pre = self.D
def get(self, key):
"""
:type key: int
:rtype: int
"""
if key not in self.nodes:
return -1
self._update(key)
return self.nodes[key].val
def set(self, key, val):
"""
:type key: int
:type val: int
:rtype: void
"""
if self.cap <= 0:
return
if key in self.nodes:
self._update(key, val)
return
while len(self.nodes) >= self.cap:
self._evict()
self._add(key, val)
def _evict(self):
node = self._pop_head()
del self.nodes[node.key]
def _update(self, key, val=None):
node = self.nodes[key]
if val:
node.val = val
node.unlink()
self._add_tail(node)
def _add(self, key, val):
self.nodes[key] = CacheNode(key, val)
self._add_tail(self.nodes[key])
def _pop_head(self):
node = self.D.nxt
node.unlink()
return node
def _add_tail(self, node):
node.link(self.d.pre, self.d)
class CacheNode:
def __init__(self, key, val=None, pre=None, nxt=None):
self.key = key
self.val = val
self.pre = pre
self.nxt = nxt
def link(self, pre, nxt):
self.pre = pre
self.nxt = nxt
pre.nxt = self
nxt.pre = self
def unlink(self):
self.pre.nxt = self.nxt
self.nxt.pre = self.pre
self.pre = self.nxt = None
================================================
FILE: lintcode/135_combination_sum.py
================================================
class Solution:
"""
@param: A: A list of integers
@param: target: An integer
@return: A list of lists of integers
"""
def combinationSum(self, A, target):
ans = []
if not A:
return ans
A.sort()
self.dfs(A, 0, target, ans, [])
return ans
def dfs(self, A, start, remaining, ans, path):
if remaining == 0:
ans.append(path[:])
return
for i in range(start, len(A)):
if remaining < A[i]:
# note that, its `return` here
# since `remaining < A[i]` and `A[i] <= A[i + 1] <= ...`
# so once it continued, all iteration after i is no need
return
path.append(A[i])
self.dfs(A, i, remaining - A[i], ans, path)
path.pop()
================================================
FILE: lintcode/1365_minimum_cycle_section.py
================================================
class Solution:
"""
@param array: an integer array
@return: the length of the minimum cycle section
"""
def minimumCycleSection(self, array):
if not array:
return 0
n = len(array)
for size in range(1, n + 1):
gotcha = True
for i in range(size):
if any(array[i] != array[j] for j in range(i + size, n, size)):
gotcha = False
if gotcha:
return size
return n
================================================
FILE: lintcode/1366_directed_graph_loop.py
================================================
import collections
class Solution:
"""
@param start: The start points set
@param end: The end points set
@return: Return if the graph is cyclic
"""
def isCyclicGraph(self, start, end):
if not start or not end or len(start) != len(end):
return False
n = len(start)
nxt = collections.defaultdict(set)
visited = set()
rec_stack = set()
for i in range(n):
nxt[start[i]].add(end[i])
for i in range(n):
if start[i] in visited:
continue
if self.dfs(start[i], nxt, visited, rec_stack):
return True
return False
def dfs(self, u, nxt, visited, rec_stack):
if u not in nxt:
return False
visited.add(u)
rec_stack.add(u)
for v in nxt[u]:
if v in rec_stack:
return True
if v not in visited and self.dfs(v, nxt, visited, rec_stack):
return True
rec_stack.discard(u)
return False
================================================
FILE: lintcode/1367_police_distance.py
================================================
class Solution:
"""
@param matrix : the martix
@return: the distance of grid to the police
"""
def policeDistance(self, matrix):
m, n = len(matrix), len(matrix)
POLICE = 1
WALL = -1
EMPTY = 0
ans = [[float('inf')] * n for _ in range(m)]
queue = []
for x in range(m):
for y in range(n):
if matrix[x][y] == WALL:
ans[x][y] = -1
elif matrix[x][y] == POLICE:
ans[x][y] = 0
queue.append((x, y))
for x, y in queue:
for dx, dy in (
(-1, 0), (1, 0),
(0, -1), (0, 1),
):
_x = x + dx
_y = y + dy
if not (0 <= _x < m and 0 <= _y < n):
continue
if matrix[_x][_y] == WALL:
continue
if ans[_x][_y] <= ans[x][y] + 1:
continue
ans[_x][_y] = ans[x][y] + 1
queue.append((_x, _y))
return ans
================================================
FILE: lintcode/1368_same_number.py
================================================
class Solution:
"""
@param nums: the arrays
@param k: the distance of the same number
@return: the ans of this question
"""
def sameNumber(self, nums, k):
RES = ('NO', 'YES')
if not nums or not k:
return RES[0]
idx = {}
gotcha = False
for i in range(len(nums)):
if nums[i] in idx and i - idx[nums[i]] < k:
gotcha = True
idx[nums[i]] = i
return RES[int(gotcha)]
================================================
FILE: lintcode/136_palindrome_partitioning.py
================================================
class Solution:
ans = []
n = 0
is_palindrome = None
"""
@param: s: A string
@return: A list of lists of string
"""
def partition(self, s):
if not s:
return self.ans
self.n = len(s)
self.check_palindrome(s)
self.dfs(s, 0, [])
return self.ans
def check_palindrome(self, s):
"""
assuming string = 'aabb'
s: start_index, e: end_index
`is_palindrome[s][e] == T` means
the substring(string[s:e+1]) is a palindrome
the benefit to have the `is_palindrome` in advance is
we won't need to traverse the whole string again
when every time we need
e 0 1 2 3
s a a b b
0 a [[T, T, F, F],
1 a [F, T, F, F],
2 b [F, F, T, T],
3 b [F, F, F, T]]
and the traversal order to init this matrix is below:
x: means `start > end`, its impossible
[[r1, r2, r4, r4],
[ x, r1, r2, r3],
[ x, x, r1, r2],
[ x, x, x, r1]]
"""
self.is_palindrome = [[False] * self.n for _ in range(self.n)]
start = end = 0
# check the diagonal line `r1` and `r2`
# the traversal order is top-left -> bottom-right, see graph above
# since the status of `r3`, `r4`, ... depends on that
for end in range(self.n):
self.is_palindrome[end][end] = True
if end > 0:
start = end - 1
self.is_palindrome[start][end] = (s[start] == s[end])
# check the remaining triangle and traverse by line: `r3`, `r4`, ...
# the traversal order is bottom -> top, see graph above
# n - 3 = (n - 1) - 2
# start + 2
for start in range(self.n - 3, -1, -1):
for end in range(start + 2, self.n):
self.is_palindrome[start][end] = (
self.is_palindrome[start + 1][end - 1]
and s[start] == s[end]
)
# traverse all of the possible substring from start
# if is a palindrome, continue to traverse
# otherwise will be ignored
# and catch all result at the end
def dfs(self, s, start, palindromes):
if start >= self.n:
self.ans.append(palindromes)
next_start = 0
for end in range(start, self.n):
if self.is_palindrome[start][end]:
# `palindromes + [s[start:next_start]]`
# will create and return new list
next_start = end + 1
self.dfs(s, next_start, palindromes + [s[start:next_start]])
================================================
FILE: lintcode/137_clone_graph.py
================================================
"""
Definition for a undirected graph node
class UndirectedGraphNode:
def __init__(self, x):
self.label = x
self.neighbors = []
"""
"""
Iteration
"""
class Solution:
def cloneGraph(self, node):
"""
:type node: UndirectedGraphNode
:rtype: UndirectedGraphNode
"""
if not node:
return
queue = [node]
root = UndirectedGraphNode(node.label)
N = {root.label: root}
for node in queue:
for neighbor in node.neighbors:
_node = None
if neighbor.label in N:
_node = N[neighbor.label]
else:
_node = UndirectedGraphNode(neighbor.label)
N[neighbor.label] = _node
queue.append(neighbor)
N[node.label].neighbors.append(_node)
return root
"""
Recursion
"""
class Solution:
def cloneGraph(self, node):
"""
:type node: UndirectedGraphNode
:rtype: UndirectedGraphNode
"""
if not node:
return
return self.dfs(node, {})
def dfs(self, node, N):
if node.label in N:
return N[node.label]
N[node.label] = UndirectedGraphNode(node.label)
for neighbor in node.neighbors:
N[node.label].neighbors.append(self.dfs(neighbor, N))
return N[node.label]
================================================
FILE: lintcode/138_subarray_sum.py
================================================
class Solution:
"""
@param: nums: A list of integers
@return: A list of integers includes the index of the first number and the index of the last number
"""
def subarraySum(self, nums):
"""
len(nums) == 5
if `A[1] + A[2] + A[3] == 0`
the cumulative sum in the `i == 3` iteration
is same as the cumulative sum in `A[0]`
=> save every sum in hashmap
=> if got the same sum in following iteration
=> start = hashmap[sum] + 1, end = i
"""
if not nums:
return []
sum_to_index = {}
sum_to_index[0] = -1
prefix_sum = 0
for i in range(len(nums)):
prefix_sum += nums[i]
if prefix_sum in sum_to_index:
return [
sum_to_index[prefix_sum] + 1,
i
]
sum_to_index[prefix_sum] = i
return []
================================================
FILE: lintcode/139_subarray_sum_closest.py
================================================
from collections import namedtuple
Pair = namedtuple('Pair', ['sum', 'index'])
class Solution:
"""
@param: nums: A list of integers
@return: A list of integers includes the index of the first number and the index of the last number
"""
def subarraySumClosest(self, nums):
"""
Prefix Sum
prefix_sum[i] means the culmulative sum before `i + 1` in `nums`
=> if we want to know the sum of [1, 2]
=> sum(nums[1:3]) == sum(nums[0:3]) - sum(nums[0:0])
"""
ans = [0] * 2
if not nums:
return ans
n = len(nums)
if n == 1:
return ans
prefix_sum = [0] * n
prefix_sum[0] = Pair(nums[0], 0)
for i in range(1, n):
prefix_sum[i] = Pair(prefix_sum[i - 1].sum + nums[i], i)
# since the closest sum occurred when the sum is closest
# so we can simply calculate the difference
# between every two adjacent indexes
prefix_sum.sort(key=lambda p: p.sum)
closest_sum = float('inf')
tmp_sum = 0
for i in range(1, n):
# calculate all the closest sum occurred in two adjacent indexes
tmp_sum = prefix_sum[i].sum - prefix_sum[i - 1].sum
# keep finding the minimum closest sum
if tmp_sum >= closest_sum:
continue
closest_sum = tmp_sum
ans = sorted([prefix_sum[i].index, prefix_sum[i - 1].index])
# sum(nums[1:3]) == sum(nums[0:3]) - sum(nums[0:0])
# so start need `+1`
ans[0] += 1
return ans
================================================
FILE: lintcode/13_strstr.py
================================================
class Solution:
def strStr(self, haystack, needle):
"""
:type haystack: str
:type needle: str
:rtype: int
"""
NOT_FOUND = -1
if haystack is None or needle is None:
return NOT_FOUND
if haystack == needle:
return 0
m, n = len(haystack), len(needle)
for i in range(m - n + 1):
if haystack[i:i + n] == needle:
return i
return NOT_FOUND
================================================
FILE: lintcode/141_sqrtx.py
================================================
class Solution:
def sqrt(self, x):
"""
:type x: int
:rtype: int
"""
if not x or x <= 1:
return x
left, right = 0, x
while left + 1 < right:
mid = (left + right) // 2
square = mid * mid
if square == x:
return mid
if square < x:
left = mid
else:
right = mid
return left
================================================
FILE: lintcode/142_o1_check_power_of_2.py
================================================
class Solution:
"""
@param: n: An integer
@return: True or false
"""
def checkPowerOf2(self, n):
if not n or n <= 0:
return False
return n & (n - 1) == 0
================================================
FILE: lintcode/143_sort_colors_ii.py
================================================
"""
Rainbow Sort
time: O(nlogk)
"""
class Solution:
"""
@param: colors: A list of integer
@param: k: An integer
@return: nothing
"""
def sortColors2(self, colors, k):
if not colors or not k:
return
self.rainbow_sort(colors, 0, len(colors) - 1, 1, k)
def rainbow_sort(self, colors, start, end, color_from, color_to):
"""
like quick sort
"""
if color_from >= color_to:
return
if start >= end:
return
left, right = start, end
color_mid = (color_from + color_to) // 2
while left <= right:
while left <= right and colors[left] <= color_mid:
left += 1
while left <= right and colors[right] > color_mid:
right -= 1
if left <= right:
colors[left], colors[right] = colors[right], colors[left]
left += 1
right -= 1
self.rainbow_sort(colors, start, right, color_from, color_mid)
self.rainbow_sort(colors, left, end, color_mid + 1, color_to)
"""
TLE
Extend from sort color
time: O(nk)
"""
class Solution:
"""
@param: colors: A list of integer
@param: k: An integer
@return: nothing
"""
def sortColors2(self, colors, k):
"""
1. ensure the `min_color` in the `left_scope`,
and the `max_color` in the `right_scope`
in every iteration
2. keep sorting the `mid_scope` to
pick min/max color to the left/right scope
"""
count = 0
n = len(colors)
left, right = 0, n - 1
i = _min = _max = 0
while count < k:
_min = _max = colors[left]
for i in range(left + 1, right + 1):
if colors[i] < _min:
_min = colors[i]
if colors[i] > _max:
_max = colors[i]
i = left
while i <= right:
if colors[i] == _min:
colors[left], colors[i] = colors[i], colors[left]
left += 1
i += 1
elif _min < colors[i] < _max:
# leave it to
# the next iteration of `while count < k` to sort
i += 1
else:
colors[i], colors[right] = colors[right], colors[i]
right -= 1
count += 2
================================================
FILE: lintcode/148_sort_colors.py
================================================
class Solution:
"""
@param: A: A list of integer which is 0, 1 or 2
@return: nothing
"""
def sortColors(self, A):
if not A:
return
left, right = 0, len(A) - 1
i = 0
while i <= right:
if A[i] == 0:
A[left], A[i] = A[i], A[left]
left += 1
i += 1
elif A[i] == 1:
"""
temply ignore it
it will be swapped if there is `0` later
"""
i += 1
else:
"""
cannot `i += 1` since the swapped value still need to check
"""
A[right], A[i] = A[i], A[right]
right -= 1
================================================
FILE: lintcode/149_best_time_to_buy_and_sell_stock.py
================================================
class Solution:
"""
@param: P: Given an integer array
@return: Maximum profit
"""
def maxProfit(self, P):
ans = 0
if not P:
return ans
Pmin = P[0]
for i in range(1, len(P)):
if P[i] - Pmin > ans:
ans = P[i] - Pmin
if P[i] < Pmin:
Pmin = P[i]
return ans
================================================
FILE: lintcode/14_first_position_of_target.py
================================================
class Solution:
# @param A: The integer array
# @param target: Target number to find
# @return the first position of target in A, position start from 0
def binarySearch(self, A, target):
if not A:
return -1
left, mid, right = 0, 0, len(A) - 1
while left + 1 < right:
mid = left + (right - left) // 2
if A[mid] < target:
left = mid
else:
right = mid
if A[left] == target:
return left
elif A[right] == target:
return right
return -1
================================================
FILE: lintcode/150_best_time_to_buy_and_sell_stock_ii.py
================================================
class Solution:
"""
@param: P: Given an integer array
@return: Maximum profit
"""
def maxProfit(self, P):
ans = 0
if not P:
return ans
for i in range(1, len(P)):
if P[i] > P[i - 1]:
ans += P[i] - P[i - 1]
return ans
================================================
FILE: lintcode/151_best_time_to_buy_and_sell_stock_iii.py
================================================
class Solution:
"""
@param: P: Given an integer array
@return: Maximum profit
"""
def maxProfit(self, P):
if not P:
return 0
K = 2
STAGE = 2 * K + 1
"""
`dp[i][j]` means the `i`th day at the `j`th stage
stage 0: before the first buying
stage 1: holding the first stock
stage 2: after the first selling, before the second buying
stage 3: holding the second stock
stage 4: after the second selling
note that, `dp[i][0]` means always stay in stage 0,
so its never going to be profitable
"""
dp = [[0] * STAGE for _ in range(2)]
i = j = prev = curr = profit = 0
for i in range(1, len(P)):
prev = curr
curr = 1 - curr
profit = P[i] - P[i - 1]
for j in range(1, STAGE, 2):
"""
in stage 1 and 3, holding a stock
profit comes from:
1. still holding a stock yesterday,
and gains profit (may be negative) today
2. holding no any stock yesterday,
just makes a buying today, so no profit
choose the maximum
"""
dp[curr][j] = max(dp[prev][j] + profit, dp[prev][j - 1])
for j in range(2, STAGE, 2):
"""
in stage 2 and 4, holding no any stock
profit comes from:
1. still holding no any stock yesterday,
just makes a buying today, so no profit
2. holding a stock yesterday,
and gains profit (may be negative) today
choose the maximum
"""
dp[curr][j] = max(dp[prev][j], dp[prev][j - 1] + profit)
return max(dp[curr])
================================================
FILE: lintcode/153_combination_sum_ii.py
================================================
class Solution:
"""
@param: A: Given the candidate numbers
@param: target: Given the target number
@return: All the combinations that sum to target
"""
def combinationSum2(self, A, target):
ans = []
if not A:
return ans
A.sort()
self.dfs(A, 0, target, ans, [])
return ans
def dfs(self, A, start, remaining, ans, path):
if remaining == 0:
ans.append(path[:])
return
for i in range(start, len(A)):
if remaining < A[i]:
return
# to prevent [1', 2, 5] and [1", 2, 5]
# appear in result at same time
if i > start and A[i] == A[i - 1]:
continue
path.append(A[i])
self.dfs(A, i + 1, remaining - A[i], ans, path)
path.pop()
================================================
FILE: lintcode/154_regular_expression_matching.py
================================================
"""
Main Concept:
end by '*' and has no matched
case 1-1: `P[j-2:j]` is `c*` and have no matched in `S`
=> `j-2` in `dp[i][j-2]` means ignored `c` and `*`
end by `*` and is the last matched in `*`
case 1-2: `P[j-2:j]` is `.*` and `.` always matched `S[i-1]`
case 1-3: `P[j-2:j]` is `a*` and `a` == `P[j-2]` == `S[i-1]`
=> `i-1` in `dp[i-1][j]` means to check the `?` below
'...a?a'
\|
'...xa*'
case 2: `P[j-1]` is `.` and `.` always matched `S[i-1]`
case 3: `P[j-1]` is `a` and `a` == `P[j-1]` == `S[i-1]`
=> `-1` in `dp[i-1][j-1]` means previous char
"""
class Solution:
def isMatch(self, s, p):
"""
:type s: str
:type p: str
:rtype: bool
"""
if s == p == '':
return True
m, n = len(s), len(p)
MULTI = '*'
ANY = '.'
"""
`dp[i][j]` means the substr end at `s[i - 1]` was matched by
the substr end at `p[j - 1]`
"""
dp = [[False] * (n + 1) for _ in range(m + 1)]
dp[0][0] = True
# dp[i][0] = False # i = 1 -> m + 1
# dp[0][j] -> ?, need to check
for i in range(m + 1):
for j in range(1, n + 1):
if i > 0 and p[j - 1] == s[i - 1] and dp[i - 1][j - 1]:
dp[i][j] = True
elif i > 0 and p[j - 1] == ANY and dp[i - 1][j - 1]:
dp[i][j] = True
elif j > 1 and p[j - 1] == MULTI:
if dp[i][j - 2]:
dp[i][j] = True
elif i > 0 and p[j - 2] == s[i - 1] and dp[i - 1][j]:
dp[i][j] = True
elif i > 0 and p[j - 2] == ANY and dp[i - 1][j]:
dp[i][j] = True
return dp[m][n]
================================================
FILE: lintcode/155_minimum_depth_of_binary_tree.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
"""
@param: root: The root of binary tree
@return: An integer
"""
def minDepth(self, root):
ans = 0
if not root:
return ans
queue = [root]
while queue:
_queue = []
ans += 1
for node in queue:
if not node.left and not node.right:
return ans
if node.left:
_queue.append(node.left)
if node.right:
_queue.append(node.right)
queue = _queue
return ans
class Solution:
"""
@param: root: The root of binary tree
@return: An integer
"""
def minDepth(self, root):
if not root:
return 0
if not root.left and not root.right:
return 1
left = self.minDepth(root.left)
right = self.minDepth(root.right)
if left == 0:
return right + 1
if right == 0:
return left + 1
return min(left, right) + 1
================================================
FILE: lintcode/156_merge_intervals.py
================================================
"""
Definition of Interval.
class Interval(object):
def __init__(self, start, end):
self.start = start
self.end = end
"""
class Solution:
def merge(self, intvs):
"""
:type intvs: List[Interval]
:rtype: List[Interval]
"""
ans = []
if not intvs:
return ans
intvs.sort(key=lambda intv: (intv.start, intv.end))
for intv in intvs:
if not ans or intv.start > ans[-1].end:
ans.append(intv)
elif intv.end > ans[-1].end:
ans[-1].end = intv.end
return ans
================================================
FILE: lintcode/158_two_strings_are_anagrams.py
================================================
class Solution:
"""
@param s: The first string
@param b: The second string
@return true or false
"""
def anagram(self, s, t):
if s == '' and t == '':
return True
if not s or not t:
return False
s = sorted(s)
t = sorted(t)
return s == t
================================================
FILE: lintcode/159_find_minimum_in_rotated_sorted_array.py
================================================
class Solution:
"""
@param: nums: a rotated sorted array
@return: the minimum number in the array
"""
def findMin(self, nums):
if not nums:
return -1
l, m, r = 0, 0, len(nums) - 1
"""
since the children between `nums[0:maximum]`
are all great than `nums[minimum:-1]`
so we can pick the last child, and do the binary searching, if:
1. child in `nums[0:maximum]`,
then the left boundary will continue to move to the maximum
2. child in `nums[minimum:-1]`,
then the right boundary will to the minimum
"""
last = nums[-1]
while l + 1 < r:
m = l + (r - l) // 2
if nums[m] > last:
l = m
else:
r = m
return min(nums[l], nums[r])
================================================
FILE: lintcode/15_permutations.py
================================================
class Solution:
def permute(self, nums):
"""
:type nums: list[int]
:rtype: list[list[int]]
"""
if not nums:
return [[]]
ans = []
nums.sort()
self.dfs(nums, ans, [])
return ans
def dfs(self, nums, ans, path):
if not nums:
ans.append(path[:])
return
for i in range(len(nums)):
path.append(nums[i])
self.dfs(nums[:i] + nums[i + 1:], ans, path)
path.pop()
================================================
FILE: lintcode/160_find_minimum_in_rotated_sorted_array_ii.py
================================================
"""
Iteration
"""
class Solution:
"""
@param: A: a rotated sorted array
@return: the minimum number in the array
"""
def findMin(self, A):
if not A:
return -1
_min = A[0]
for i in range(1, len(A)):
if A[i] < _min:
_min = A[i]
break
return _min
"""
Binary Searching
"""
class Solution:
"""
@param: A: a rotated sorted array
@return: the minimum number in the array
"""
def findMin(self, A):
"""
all chilren before the pivot are great than or equal the child at end
all chilren after the pivot are less than or equal the child at end
"""
if not A:
return -1
left, right = 0, len(A) - 1
while left + 1 < right:
mid = (left + right) // 2
if A[mid] == A[right]:
# means it's ok to remove the end child
right -= 1
elif A[mid] < A[right]:
# mid at the right side of pivot
right = mid
else:
# mid at the left side of pivot
left = mid
return A[left] if A[left] < A[right] else A[right]
================================================
FILE: lintcode/165_merge_two_sorted_lists.py
================================================
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def mergeTwoLists(self, a, b):
"""
:type a: ListNode
:type b: ListNode
:rtype: ListNode
"""
dummy = tail = ListNode(-1)
while a and b:
if a.val < b.val:
tail.next = ListNode(a.val)
a = a.next
else:
tail.next = ListNode(b.val)
b = b.next
tail = tail.next
while a:
tail.next = ListNode(a.val)
a = a.next
tail = tail.next
while b:
tail.next = ListNode(b.val)
b = b.next
tail = tail.next
return dummy.next
================================================
FILE: lintcode/167_add_two_numbers.py
================================================
"""
Definition for singly-linked list.
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
"""
class Solution:
def addLists(self, A, B):
"""
:type A: ListNode
:type B: ListNode
:rtype: ListNode
"""
dummy = tail = ListNode(-1)
carry = 0
while A and B:
carry += A.val + B.val
tail.next = ListNode(carry % 10)
carry //= 10
tail = tail.next
A = A.next
B = B.next
while A:
carry += A.val
tail.next = ListNode(carry % 10)
carry //= 10
tail = tail.next
A = A.next
while B:
carry += B.val
tail.next = ListNode(carry % 10)
carry //= 10
tail = tail.next
B = B.next
if carry:
tail.next = ListNode(carry)
return dummy.next
================================================
FILE: lintcode/168_burst_balloons.py
================================================
class Solution:
"""
@param: V: A list of integer
@return: An integer, maximum coins
"""
def maxCoins(self, V):
if not V:
return 0
"""
the value of last balloons is `1 * v * 1`
"""
V = [1] + V + [1]
n = len(V)
"""
`dp[i][j]` means the maximum value when
all the balloons in [i+1, j-1] was bursted
"""
dp = [[0] * n for _ in range(n)]
# pi = [[0] * n for _ in range(n)]
for i in range(n - 1 - 2, -1, -1):
for j in range(i + 2, n):
"""
leave last balloon `k` to burst
`i + 1 <= k <= j - 1`
"""
for k in range(i + 1, j):
dp[i][j] = max(
dp[i][j],
dp[i][k] + dp[k][j] + V[i] * V[k] * V[j]
)
# if dp[i][j] == dp[i][k] + dp[k][j] + V[i] * V[k] * V[j]:
# pi[i][j] = k
# self.print_paths(0, n - 1, V, pi)
return dp[0][n - 1]
# def print_paths(self, i, j, V, pi):
# if i + 1 == j:
# return
# self.print_paths(i, pi[i][j], V, pi)
# self.print_paths(pi[i][j], j, V, pi)
# print("burst {vk}, get coins {vi} * {vk} * {vj} = {vsum}".format(
# vi=V[i],
# vk=V[pi[i][j]],
# vj=V[j],
# vsum=V[i] * V[pi[i][j]] * V[j]
# ))
================================================
FILE: lintcode/16_permutations_ii.py
================================================
class Solution:
"""
dfs with ignoring self and same num
"""
def permuteUnique(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
if not nums:
return [[]]
ans = []
nums.sort()
self.dfs(nums, ans, [])
return ans
def dfs(self, nums, ans, path):
if not nums:
ans.append(path[:])
return
for i in range(len(nums)):
"""
ignore same num
"""
if i > 0 and nums[i] == nums[i - 1]:
continue
"""
ignore self
"""
path.append(nums[i])
self.dfs(nums[:i] + nums[i + 1:], ans, path)
path.pop()
class Solution:
"""
dfs with visited indices
"""
def permuteUnique(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
if not nums:
return [[]]
ans = []
visited = [False] * len(nums)
nums.sort()
self.dfs(nums, visited, ans, [])
return ans
def dfs(self, nums, visited, ans, path):
if len(path) == len(nums):
ans.append(path[:])
return
for i in range(len(nums)):
if visited[i]:
continue
"""
example: [0, 3, 3', 3"]
if current iteration is `3"`
we need to ensure `3`, `3'` is picked
otherwise repeated result will be included
"""
if i > 0 and not visited[i - 1] and nums[i] == nums[i - 1]:
continue
visited[i] = True
path.append(nums[i])
self.dfs(nums, visited, ans, path)
visited[i] = False
path.pop()
================================================
FILE: lintcode/171_anagrams.py
================================================
class Solution:
"""
@param: S: A list of strings
@return: A list of strings
"""
def anagrams(self, S):
ans = []
if not S:
return ans
D = {}
for s in S:
_s = ''.join(sorted(s))
if _s not in D:
D[_s] = []
D[_s].append(s)
for k, S in D.items():
if len(S) > 1:
ans.extend(S)
return ans
================================================
FILE: lintcode/175_invert_binary_tree.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
"""
@param: root: a TreeNode, the root of the binary tree
@return: nothing
"""
def invertBinaryTree(self, root):
self.divide_conquer(root)
def divide_conquer(self, node):
if not node:
return
self.divide_conquer(node.left)
self.divide_conquer(node.right)
node.left, node.right = node.right, node.left
================================================
FILE: lintcode/178_graph_valid_tree.py
================================================
class Solution:
def validTree(self, n, edges):
"""
:type n: int
:type edges: list[int]
:rtype: bool
"""
if len(edges) != n - 1:
return False
nodes = [i for i in range(n)]
for a, b in edges:
_a = self.find(nodes, a)
_b = self.find(nodes, b)
if _a is _b:
return False
nodes[_a] = _b
return True
def find(self, nodes, a):
if nodes[a] is a:
return a
nodes[a] = self.find(nodes, nodes[a])
return nodes[a]
================================================
FILE: lintcode/17_subsets.py
================================================
"""
DFS
"""
class Solution:
"""
@param: A: A set of numbers
@return: A list of lists
"""
def subsets(self, A):
if not A:
return [[]]
ans = []
self.dfs(sorted(A), 0, ans, [])
return ans
def dfs(self, A, start, ans, subset):
ans.append(subset[:])
if start >= len(A):
return
for i in range(start, len(A)):
self.dfs(A, i + 1, ans, subset + [A[i]])
"""
Bit Manipulation
"""
class Solution:
"""
@param: A: A set of numbers
@return: A list of lists
"""
def subsets(self, A):
if not A:
return [[]]
ans = []
n = len(A)
A.sort()
for i in range(1 << n):
subset = []
for j in range(n):
"""
check `j`th digit in `bin(i)`
example:
i == 011
j == 0 => 1 << 0 == 001 => 011 & 001 == 1
j == 1 => 1 << 1 == 010 => 011 & 010 == 1
j == 2 => 1 << 2 == 100 => 011 & 100 == 0
"""
if i & (1 << j):
subset.append(A[j])
ans.append(subset)
return ans
================================================
FILE: lintcode/183_wood_cut.py
================================================
class Solution:
"""
@param: L: Given n pieces of wood with length L[i]
@param: k: An integer
@return: The maximum length of the small pieces
"""
def woodCut(self, L, k):
"""
Assuming the `m` is the maximum length
len | ... m-2 m-1 m m+1 m+2 ...
check | T T T T F F F
* check: is it ok to cut into at least `k` pieces
"""
if not L or not k:
return 0
left = 1
total_len = right = L[0]
for i in range(1, len(L)):
if L[i] > right:
right = L[i]
total_len += L[i]
if total_len < k:
return 0
while left + 1 < right:
mid = (left + right) // 2
if self.check_if_possible(L, mid, k):
left = mid
else:
right = mid
return right if self.check_if_possible(L, right, k) else left
def check_if_possible(self, L, size, max_pieces):
pieces = 0
for i in range(len(L)):
pieces += L[i] // size
if pieces >= max_pieces:
return True
return False
================================================
FILE: lintcode/18_subsets_ii.py
================================================
"""
DFS
"""
class Solution:
"""
@param: A: A set of numbers.
@return: A list of lists. All valid subsets.
"""
def subsetsWithDup(self, A):
if not A:
return [[]]
ans = []
self.dfs(sorted(A), 0, ans, [])
return ans
def dfs(self, A, start, ans, subset):
ans.append(subset[:])
if start >= len(A):
return
for i in range(start, len(A)):
if i - 1 >= start and A[i] == A[i - 1]:
continue
self.dfs(A, i + 1, ans, subset + [A[i]])
"""
backtracking if using same list
"""
# subset.append(A[i])
# self.dfs(A, i + 1, ans, subset)
# subset.pop()
================================================
FILE: lintcode/190_next_permutation_ii.py
================================================
class Solution:
def nextPermutation(self, nums):
"""
:type nums: list[int]
:rtype: list[int]
"""
if not nums or len(nums) < 2:
return nums
n = len(nums)
i = n - 2
while i >= 0 and nums[i] >= nums[i + 1]:
i -= 1
if i >= 0:
j = n - 1
while i < j and nums[i] >= nums[j]:
j -= 1
nums[i], nums[j] = nums[j], nums[i]
i = i + 1
j = n - 1
while i < j:
nums[i], nums[j] = nums[j], nums[i]
i += 1
j -= 1
================================================
FILE: lintcode/191_maximum_product_subarray.py
================================================
"""
since the fact:
the minimum negative number * -1 -> the maximum
the maximum positive number -> the maximum
so we need record the minimum and the maximum number for each child in nums
"""
class Solution:
"""
@param: A: An array of integers
@return: An integer
"""
def maxProduct(self, A):
if not A:
return 0
ans = Pmin = Pmax = A[0]
for i in range(1, len(A)):
"""
adding `A[i]` to reset `min` and `max`
if its so lowest or highest
"""
C = (A[i], Pmin * A[i], Pmax * A[i])
Pmin, Pmax = min(C), max(C)
if Pmax > ans:
ans = Pmax
return ans
================================================
FILE: lintcode/192_wildcard_matching.py
================================================
"""
Main Concept:
case 1-1: `dp[i-1][j]` means `*` may start to matched new char
=> `i-1` in `dp[i-1][j]` means to check the `?` below
'...a?a'
\|
'...xa*'
case 1-2: `dp[i][j-1]` means `*` continue to matched same char
=> `j-1` in `dp[i][j-1]` means to check the `?` below
'...aa?'
/
'...xa*'
case 2: `P[j-1]` is `.` and `.` always matched `S[i-1]`
case 3: `P[j-1]` is `a` and `a` == `P[j-1]` == `S[i-1]`
=> `-1` in `dp[i-1][j-1]` means previous char
"""
class Solution:
def isMatch(self, s, p):
"""
:type s: str, target string
:type p: str, regex
:rtype: bool
"""
if s is None or p is None:
return False
if s == '' and p == '':
return True
ANY = '?'
ANY_MULTI = '*'
m, n = len(s), len(p)
"""
`dp[i][j]` means the substr end at `S[i - 1]` was matched by
the substr end at `P[j - 1]`
"""
dp = [[False] * (n + 1) for _ in range(m + 1)]
dp[0][0] = True
# dp[i][0] = False
# dp[0][j] -> need to check
for i in range(m + 1):
for j in range(1, n + 1):
if p[j - 1] == ANY_MULTI:
dp[i][j] = dp[i - 1][j] or dp[i][j - 1]
elif p[j - 1] == ANY and dp[i - 1][j - 1]:
dp[i][j] = True
elif p[j - 1] == s[i - 1] and dp[i - 1][j - 1]:
dp[i][j] = True
return dp[m][n]
================================================
FILE: lintcode/196_find_the_missing_number.py
================================================
class Solution:
def findMissing(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if not nums:
return 0
ans = n = len(nums)
for i in range(n):
ans ^= i ^ nums[i]
return ans
class Solution:
def findMissing(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if not nums:
return 0
nums.sort()
for i in range(len(nums)):
if i != nums[i]:
return i
return i + 1
================================================
FILE: lintcode/197_permutation_index.py
================================================
"""
https://segmentfault.com/a/1190000004683277
in the origin/unsorted `A`,
if there are `cnt` child after and less than `A[i]`,
meaning there are `cnt * (n - i - 1)!` permutations
before `A[i]`.
"""
class Solution:
"""
@param: A: An array of integers
@return: A long integer
"""
def permutationIndex(self, A):
ans = 1
if not A:
return ans
n = len(A)
factorial = 1
for i in range(n - 1, -1, -1):
cnt = 0
for j in range(i + 1, n):
if A[i] > A[j]:
cnt += 1
"""
use the `factorial` from previous iteration `i + 1`
that is, `(n - i - 1)! = (n - (i + 1))!`
"""
ans += cnt * factorial
factorial *= n - i
return ans
================================================
FILE: lintcode/198_permutation_index_ii.py
================================================
"""
https://segmentfault.com/a/1190000004683277
if there are `dups`, dividing `factorial` with `dup_fact`
e.g., 3 `A[i]`s and 4 `A[j]`s in `A`
`dup_fact = 3! * 4!`
"""
class Solution:
"""
@param: A: An array of integers
@return: A long integer
"""
def permutationIndexII(self, A):
ans = 1
if not A:
return ans
n = len(A)
factorial = dup_fact = 1
dups = {}
for i in range(n - 1, -1, -1):
dups[A[i]] = dups.get(A[i], 0) + 1
dup_fact *= dups[A[i]]
cnt = 0
for j in range(i + 1, n):
if A[i] > A[j]:
cnt += 1
ans += cnt * factorial // dup_fact
factorial *= n - i
return ans
================================================
FILE: lintcode/1_a_b_problem.py
================================================
class Solution:
"""
@param: a: An integer
@param: b: An integer
@return: The sum of a and b
"""
def aplusb(self, a, b):
if not a:
return b
if not b:
return a
INT_RANGE = 0xFFFFFFFF
while b != 0:
a, b = a ^ b, (a & b) << 1
a &= INT_RANGE
return a if a >> 31 <= 0 else a ^ ~INT_RANGE
================================================
FILE: lintcode/204_singleton.py
================================================
class Solution:
instance = None
# @return: The same instance of this class every time
@classmethod
def getInstance(cls):
if not cls.instance:
cls.instance = Solution()
return cls.instance
================================================
FILE: lintcode/211_string_permutation.py
================================================
class Solution:
"""
@param: A: a string
@param: B: a string
@return: a boolean
"""
def Permutation(self, A, B):
if A is '' and B is '':
return True
if not A or not B:
return False
cnts = {}
for char in A:
cnts[char] = cnts.get(char, 0) + 1
for char in B:
if char not in cnts or cnts[char] == 0:
return False
cnts[char] -= 1
for cnt in cnts.values():
if cnt != 0:
return False
return True
================================================
FILE: lintcode/215_rate_limiter.py
================================================
import collections
class Solution:
"""
maintain a list to record the timestamps for every event
and if a query coming => we need ensure
there is no more `freq` logs between `begin_time` and `timestamp`(current)
1. split rate
2. cal begin time
3. do binary searching to find the closest index of `begin time`
4. count the child between res of (3) to the end of list
5. if cnt >= freq => is limited
"""
def __init__(self):
self.times = {
's': 1,
'm': 60,
'h': 3600,
'd': 86400,
}
self.logs = collections.defaultdict(list)
def isRatelimited(self, timestamp, event, rate, increment):
"""
:type timestamp: int
:type event: str
:type rate: str
:type increment: bool
:rtype: bool
"""
if '/' not in rate:
return False
freq, t = rate.split('/')
freq = int(freq)
begin_time = timestamp - self.times.get(t, 1) + 1
is_limited = self.check_limited(event, freq, begin_time)
if increment and not is_limited:
self.logs[event].append(timestamp)
return is_limited
def check_limited(self, event, freq, begin_time):
logs = self.logs[event]
if not logs or logs[-1] < begin_time:
# if freq is 0 => is limited
return freq == 0
left, right = 0, len(logs) - 1
while left + 1 < right:
mid = (left + right) // 2
if logs[mid] < begin_time:
left = mid
else:
right = mid
mid = left if logs[left] >= begin_time else right
return len(logs) - mid >= freq
================================================
FILE: lintcode/221_add_two_numbers_ii.py
================================================
"""
Definition for singly-linked list.
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
"""
class Solution:
def addLists2(self, a, b):
"""
:type a: ListNode
:type b: ListNode
:rtype: ListNode
"""
if not a and not b:
return
if not a:
return b
if not b:
return a
a = self.rev_list(a)
b = self.rev_list(b)
dummy = tail = ListNode(0)
carry = 0
while a and b:
carry += a.val + b.val
tail.next = ListNode(carry % 10)
carry //= 10
a = a.next
b = b.next
tail = tail.next
while a:
carry += a.val
tail.next = ListNode(carry % 10)
carry //= 10
a = a.next
tail = tail.next
while b:
carry += b.val
tail.next = ListNode(carry % 10)
carry //= 10
b = b.next
tail = tail.next
if carry:
tail.next = ListNode(carry)
return self.rev_list(dummy.next)
def rev_list(self, head):
pre = nxt = None
while head:
nxt = head.next
head.next = pre
pre = head
head = nxt
return pre
================================================
FILE: lintcode/231_typeahead.py
================================================
class Typeahead:
"""
@param: dict: A dictionary of words dict
"""
def __init__(self, dict):
self.map = {}
n = 0
for key in dict:
n = len(key)
for l in range(n):
for r in range(l + 1, n + 1):
substr = key[l:r]
if substr not in self.map:
self.map[substr] = [key]
elif self.map[substr][-1] != key:
self.map[substr].append(key)
"""
@param: str: a string
@return: a list of words
"""
def search(self, str):
if str in self.map:
return self.map[str]
return []
# Solution2
'''
This solution does not meet the demand.
Since the description said 'return all words that contains the string as a substring.'
But its a good solution for the auto-completion
'''
class Trie:
def __init__(self):
self.root = self.new_node()
def new_node(self):
return {
'result': [],
'children': {}
}
def put(self, key):
if not key:
return
for word in key.split():
self._put(word, key)
def _put(self, word, key):
parent = self.root
for char in word.lower():
if char not in parent['children']:
parent['children'][char] = self.new_node()
parent = parent['children'][char]
parent['result'].append(key)
def search(self, key):
if not key:
return []
parent = self.root
for char in key.lower():
if char not in parent['children']:
return []
parent = parent['children'][char]
return parent['result']
# To support search with 2+ word
# def search(self, key):
# if not key:
# return []
# result = []
# for word in key.split():
# result += self._search(word)
# return result
# def _search(self, word):
# parent = self.root
# for char in word.lower():
# if char not in parent['children']:
# return []
# parent = parent['children'][char]
# return parent['result']
class Typeahead:
"""
@param: dict: A dictionary of words dict
"""
def __init__(self, dict):
self.trie = Trie()
for word in dict:
self.trie.put(word)
"""
@param: str: a string
@return: a list of words
"""
def search(self, str):
return self.trie.search(str)
================================================
FILE: lintcode/232_tiny_url.py
================================================
import random
class TinyUrl:
def __init__(self):
self.chars = [str(i) for i in range(10)]
self.chars.extend(chr(i) for i in range(ord('a'), ord('z') + 1))
self.chars.extend(chr(i) for i in range(ord('A'), ord('Z') + 1))
self.host = 'http://tiny.url/'
self.size = 6
self.lg2st = {}
self.st2lg = {}
def longToShort(self, url):
"""
:type url: str
:rtype: str
"""
if not url:
return 'error'
if url in self.lg2st:
return self.get_tiny_url(self.lg2st[url])
key = self.get_hash_key(self.size)
while key in self.st2lg:
key = self.get_hash_key(self.size)
self.lg2st[url] = key
self.st2lg[key] = url
return self.get_tiny_url(key)
def shortToLong(self, url):
"""
:type url: str
:rtype: str
"""
if not url:
return 'error'
key = url.replace(self.host, '')
if key in self.st2lg:
return self.st2lg[key]
return 'error'
def get_tiny_url(self, hash_key):
return '{}{}'.format(self.host, hash_key)
def get_hash_key(self, size):
return ''.join(
random.choice(self.chars)
for _ in range(size)
)
================================================
FILE: lintcode/234_webpage_crawler.py
================================================
from threading import Thread
from Queue import Queue
from urlparse import urlparse
queue = Queue()
results = {}
class CrawlerThread(Thread):
def run(self):
global queue, results
while True:
url = queue.get()
if url not in results \
and urlparse(url).hostname.endswith("wikipedia.org"):
results[url] = True
urls = HtmlHelper.parseUrls(url)
for url in urls:
queue.put(url)
queue.task_done()
#class HtmlHelper:
# @classmethod
# def parseUrls(cls, url)
# # Get all urls from a webpage of given url.
class Solution:
# @param {string} url a url of root page
# @return {string[]} all urls
def crawler(self, url):
# Write your code here
global queue, results
thread_pools = []
for i in xrange(10):
thread_pools.append(CrawlerThread())
thread_pools[i].setDaemon(True)
thread_pools[i].start()
queue.put(url)
queue.join()
rt = []
for key, value in results.items():
rt.append(key)
return rt
================================================
FILE: lintcode/236_swap_bits.py
================================================
class Solution:
"""
@param: x: An integer
@return: An integer
"""
def swapOddEvenBits(self, x):
"""
0x55555555 == 01010101010101010101010101010101 (2)
0xAAAAAAAA == 10101010101010101010101010101010 (2)
"""
return ( ((x & 0xAAAAAAAA) >> 1) | ((x & 0x55555555) << 1) )
================================================
FILE: lintcode/242_convert_binary_tree_to_linked_lists_by_depth.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
this.val = val
this.left, this.right = None, None
Definition for singly-linked list.
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
"""
class Solution:
# @param {TreeNode} root the root of binary tree
# @return {ListNode[]} a lists of linked list
def binaryTreeToLists(self, root):
ans = []
if not root:
return ans
queue = [root]
while queue:
_queue = []
dummy = tail = ListNode(-1)
for node in queue:
tail.next = ListNode(node.val)
tail = tail.next
if node.left:
_queue.append(node.left)
if node.right:
_queue.append(node.right)
queue = _queue
ans.append(dummy.next)
return ans
================================================
FILE: lintcode/243_amicable_pair
================================================
class Solution:
"""
@param: n: An integer
@return: all amicable pairs
"""
def amicablePair(self, n):
ans = []
if not n or n < 2:
return ans
for a in range(2, n + 1):
b = self.get_factor_sum(a)
if a < b <= n and self.get_factor_sum(b) == a:
ans.append([a, b])
return ans
def get_factor_sum(self, a):
"""
if `b` is a factor of `a`
=> `b` and `a // b` are both factor of `a`
=> only needs to check til `sqrt(a)`
"""
_sum = 1
_a = int(math.sqrt(a))
for b in range(2, _a):
if a % b == 0:
_sum += b + a // b
if _a * _a == a:
_sum += _a
return _sum
================================================
FILE: lintcode/245_subtree.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
"""
@param: A: The roots of binary tree A.
@param: B: The roots of binary tree B.
@return: True if B is a subtree of A, or false.
"""
def isSubtree(self, A, B):
if not B:
# Empty tree is also treated as subtree in Lintcode
return True
if not A:
return False
return (
self.isEqual(A, B) or
self.isSubtree(A.left, B) or
self.isSubtree(A.right, B)
)
def isEqual(self, A, B):
if not A and not B:
return True
if not A or not B:
return False
return (
A.val == B.val and
self.isEqual(A.left, B.left) and
self.isEqual(A.right, B.right)
)
================================================
FILE: lintcode/246_binary_tree_path_sum_ii.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
"""
@param: root: the root of binary tree
@param: target: An integer
@return: all valid paths
"""
def binaryTreePathSum2(self, root, target):
ans = []
self.dfs(root, target, ans, [])
return ans
def dfs(self, node, target, ans, path):
if not node:
return
path.append(node.val)
remaining = target
for i in range(len(path) - 1, -1, -1):
remaining -= path[i]
if remaining == 0:
ans.append(path[i:])
self.dfs(node.left, target, ans, path)
self.dfs(node.right, target, ans, path)
path.pop()
================================================
FILE: lintcode/24_lfu_cache.py
================================================
"""
Main Concept:
Dm <-> 2 <-> 3 <-> 8 <-> dm |<- freq_list (dll)
| | |
a c d |<- cache_list (dll)
& &
b e
1. if cache is updated (set/get)
=> freq += 1
=> move to the end of the cache_list in new freq_list
2. if cache is full
=> evict the most top-left in cache first,
that is `a` in above diagram
=> add the new cache to the end of the cache_list in freq `0`
"""
class LFUCache:
def __init__(self, capacity):
"""
:type capacity: int
"""
self.cap = capacity
self.nodes = {}
self.D = FreqNode(-1)
self.d = FreqNode(-1)
self.D.nxt = self.d
self.d.pre = self.D
def get(self, key):
"""
:type key: int
:rtype: int
"""
if key not in self.nodes:
return -1
self._update(key)
return self.nodes[key].val
def set(self, key, val):
"""
:type key: int
:type val: int
:rtype: void
"""
if self.cap <= 0:
return
if key in self.nodes:
self._update(key, val)
return
while len(self.nodes) >= self.cap:
self._evict()
self._add(key, val)
def _evict(self):
freq_head = self.D.nxt
cache_node = freq_head.pop_head()
del self.nodes[cache_node.key]
if freq_head.is_empty():
freq_head.unlink()
def _update(self, key, val=None):
cache_node = self.nodes[key]
if val:
cache_node.val = val
from_freq = cache_node.freq_node
to_freq = None
if from_freq.nxt and from_freq.nxt.freq == from_freq.freq + 1:
to_freq = from_freq.nxt
else:
to_freq = FreqNode(from_freq.freq + 1)
from_freq.after(to_freq)
cache_node.unlink()
to_freq.append_tail(cache_node)
if from_freq.is_empty():
from_freq.unlink()
def _add(self, key, val):
cache_node = CacheNode(key, val)
self.nodes[key] = cache_node
freq_head = self.D.nxt
if freq_head and freq_head.freq == 0:
freq_head.append_tail(cache_node)
return
freq_head = FreqNode(0)
freq_head.append_tail(cache_node)
self.D.after(freq_head)
class CacheNode:
def __init__(self, key, val=None, freq_node=None, pre=None, nxt=None):
self.key = key
self.val = val
self.freq_node = freq_node
self.pre = pre
self.nxt = nxt
# to change self in cache nodes
def unlink(self):
self.pre.nxt = self.nxt
self.nxt.pre = self.pre
self.freq_node = self.pre = self.nxt = None
class FreqNode:
def __init__(self, freq, pre=None, nxt=None):
self.freq = freq
self.pre = pre
self.nxt = nxt
self.D = CacheNode(-1)
self.d = CacheNode(-1)
self.D.nxt = self.d
self.d.pre = self.D
# to change self in freq nodes
def unlink(self):
self.pre.nxt = self.nxt
self.nxt.pre = self.pre
self.pre = self.nxt = self.D = self.d = None
# to change self in freq nodes
def after(self, freq_node):
freq_node.pre = self
freq_node.nxt = self.nxt
self.nxt.pre = freq_node
self.nxt = freq_node
# to manage cache nodes
def is_empty(self):
return self.D.nxt is self.d
# to manage cache nodes
def pop_head(self):
if self.is_empty():
return
head = self.D.nxt
head.unlink()
return head
# to manage cache nodes
def append_tail(self, cache_node):
cache_node.freq_node = self
cache_node.pre = self.d.pre
cache_node.nxt = self.d
self.d.pre.nxt = cache_node
self.d.pre = cache_node
================================================
FILE: lintcode/254_drop_eggs.py
================================================
"""
REF: http://blog.csdn.net/shaya118/article/details/40823225
Main Concept:
assuming the minimum times to drop is `x`
according to REF, the relation of `x` is below
|
| x
|....|
. | |....|
x + 1
0 + 1 + 2 + ... + x
=> x(x + 1) / 2 >= n
"""
"""
cumulative the sum of the triangle
"""
class Solution:
"""
@param: n: An integer
@return: The sum of a and b
"""
def dropEggs(self, n):
if n <= 0:
return 0
_sum = 0
for i in range(n):
_sum += i
if _sum >= n:
return i
return n
"""
optimized: cumulative the sum of the triangle
x(x + 1) / 2 >= n
=> sqrt(x(x + 1)) >= sqrt(2n)
=> sqrt(x(x + 1)) >= sqrt(x^2) == x >= sqrt(2n)
so `x` can start from `sqrt(2n)`
"""
from math import sqrt
class Solution:
"""
@param: n: An integer
@return: The sum of a and b
"""
def dropEggs(self, n):
if n <= 0:
return 0
x = int(sqrt(2 * n))
while x * (x + 1) // 2 < n:
x += 1
return x
================================================
FILE: lintcode/272_climbing_stairs_ii.py
================================================
"""
dp: space optimized by rolling array
"""
class Solution:
"""
@param: n: An integer
@return: An integer
"""
def climbStairs2(self, n):
if not n or n <= 1:
return 1
if n == 2:
return 2
a, b, c = 1, 1, 2
for i in range(3, n + 1):
a, b, c = b, c, a + b + c
return c
"""
dp
"""
class Solution:
"""
@param: n: An integer
@return: An integer
"""
def climbStairs2(self, n):
if not n or n <= 1:
return 1
if n == 2:
return 2
dp = [0] * (n + 1)
dp[0] = dp[1] = 1
dp[2] = 2
for i in range(3, n + 1):
dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]
return dp[n]
================================================
FILE: lintcode/28_search_a_2d_matrix.py
================================================
class Solution:
def searchMatrix(self, matrix, target):
"""
:type matrix: list[list[int]]
:type target: int
:rtype: bool
"""
if not matrix or not matrix[0]:
return False
m, n = len(matrix), len(matrix[0])
left, right = 0, m * n - 1
while left + 1 < right:
mid = (left + right) // 2
x = mid // n
y = mid % n
if matrix[x][y] < target:
left = mid
elif matrix[x][y] > target:
right = mid
else:
return True
return any(
matrix[mid // n][mid % n] == target
for mid in (left, right)
)
================================================
FILE: lintcode/29_interleaving_string.py
================================================
class Solution:
"""
@param: A: A string
@param: B: A string
@param: C: A string
@return: Determine whether C is formed by interleaving of A and B
"""
def isInterleave(self, A, B, C):
if A is None or B is None or C is None:
return False
if A is '' and B is '' and C is '':
return True
a, b, c = len(A), len(B), len(C)
if c != a + b:
return False
"""
`dp[i][j]` means the substr end at `C[i + j - 1]`
is mixed by the substr end at `A[i - 1]`
and the substr end at `B[j - 1]`
"""
dp = [[False] * (b + 1) for _ in range(2)]
prev = curr = 0
dp[curr][0] = True
for j in range(1, b + 1):
"""
dp[0][j] = (dp[0][j - 1] and B[j - 1] == C[j - 1])
"""
if not dp[curr][j - 1]:
break
if B[j - 1] == C[j - 1]:
dp[curr][j] = True
for i in range(1, a + 1):
prev = curr
curr = 1 - curr
"""
dp[i][0] = (dp[i - 1][0] and A[i - 1] == C[i - 1])
"""
if dp[prev][0] and A[i - 1] == C[i - 1]:
dp[curr][0] = True
for j in range(1, b + 1):
dp[curr][j] = False
if dp[prev][j] and A[i - 1] == C[i + j - 1]:
dp[curr][j] = True
continue
if dp[curr][j - 1] and B[j - 1] == C[i + j - 1]:
dp[curr][j] = True
return dp[curr][b]
================================================
FILE: lintcode/30_insert_interval.py
================================================
"""
Main Concept:
1. iterate from end, find the position to insert new one
2. append new one to `intvs`
3. iterate from end, and swap `intvs[i]` and `intvs[i - 1]`
til meet the position found in (1)
4. iterate from start, do merge intv
Definition of Interval.
class Interval(object):
def __init__(self, start, end):
self.start = start
self.end = end
"""
class Solution:
def insert(self, intvs, intv):
"""
:type intvs: list[Interval]
:type intv: Interval
:rtype: list[Interval]
"""
if not intvs and not intv:
return []
if not intvs:
return [intv]
if not intv:
return intvs
ans = []
index = len(intvs)
for i in range(len(intvs) - 1, -1, -1):
if intvs[i].start <= intv.start:
break
index -= 1
intvs.append(intv)
for i in range(len(intvs) - 1, index, -1):
intvs[i], intvs[i - 1] = intvs[i - 1], intvs[i]
for i in range(len(intvs)): # since there is one more child in intvs
if ans and intvs[i].start <= ans[-1].end:
ans[-1].end = max(ans[-1].end, intvs[i].end)
else:
ans.append(intvs[i])
return ans
================================================
FILE: lintcode/31_partition_array.py
================================================
class Solution:
"""
@param: A: The integer array you should partition
@param: k: An integer
@return: The index after partition
"""
def partitionArray(self, A, k):
if not A:
return 0
left, right = 0, len(A) - 1
while left <= right:
while left <= right and A[left] < k:
left += 1
while left <= right and A[right] >= k:
right -= 1
if left <= right:
A[left], A[right] = A[right], A[left]
left += 1
right -= 1
return left
================================================
FILE: lintcode/32_minimum_window_substring.py
================================================
class Solution:
def minWindow(self, s, t):
"""
:type s: str
:type t: str
:rtype: str
"""
if not s or not t:
return ''
F = {}
for c in t:
F[c] = F.get(c, 0) + 1
n, cnt = len(s), len(F)
start = size = INFINITY = float('inf')
left = right = 0
while right < n:
if s[right] in F:
F[s[right]] -= 1
if F[s[right]] == 0:
cnt -= 1
right += 1
while cnt == 0:
if s[left] in F:
F[s[left]] += 1
if F[s[left]] == 1:
cnt += 1
if right - left < size:
size = right - left
start = left
left += 1
return s[start:start + size] if size < INFINITY else ''
================================================
FILE: lintcode/35_reverse_linked_list.py
================================================
"""
Definition of ListNode
class ListNode(object):
def __init__(self, val, next=None):
self.val = val
self.next = next
"""
class Solution:
"""
@param: head: n
@return: The new head of reversed linked list.
"""
def reverse(self, head):
"""
example: 1->2->3
r0:
head: 1
pre: None
nxt: None
r1:
head: 2
pre: 1
nxt: 2->3
r2:
head: 3
pre: 2->1
nxt: 3
r3:
head: None
pre: 3->2->1
nxt: None
"""
pre = nxt = None
while head:
nxt = head.next # save the remaining children
head.next = pre # break the link
pre = head # save the new head
head = nxt # pointer the old head
return pre
================================================
FILE: lintcode/360_sliding_window_median.py
================================================
import heapq
class HashHeapqWithLazy:
def __init__(self):
self.__heap = []
self.__deleted = {}
self.__size = 0
def __len__(self):
return self.__size
def __bool__(self):
return self.__size > 0
def push(self, val):
heapq.heappush(self.__heap, val)
self.__size += 1
def pop(self):
if self._is_empty():
return
self.__size -= 1
return heapq.heappop(self.__heap)
def remove(self, val):
if self._is_empty():
return
self.__size -= 1
self.__deleted[val] = self.__deleted.get(val, 0) + 1
def top(self):
if self._is_empty():
return
return self.__heap[0]
def _is_empty(self):
while self.__heap and self.__deleted.get(self.__heap[0]):
val = heapq.heappop(self.__heap)
self.__deleted[val] -= 1
return self.__size == 0
class Solution:
def medianSlidingWindow(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[float]
"""
ans = []
if not nums or k <= 0 or len(nums) < k:
return ans
self.minheap = HashHeapqWithLazy()
self.maxheap = HashHeapqWithLazy()
for i in range(len(nums)):
# remove nums[i - k]
if i >= k:
if self.minheap and nums[i - k] >= self.minheap.top():
self.minheap.remove(nums[i - k])
else:
self.maxheap.remove(-1 * nums[i - k])
# add nums[i]
if self.minheap and nums[i] >= self.minheap.top():
self.minheap.push(nums[i])
else:
self.maxheap.push(-1 * nums[i])
# get median
if i >= k - 1:
ans.append(self.get_median())
return ans
def get_median(self):
if not self.maxheap and not self.minheap:
return 0
while len(self.maxheap) > len(self.minheap) + 1:
self.minheap.push(-1 * self.maxheap.pop())
while len(self.minheap) > len(self.maxheap):
self.maxheap.push(-1 * self.minheap.pop())
return -1 * self.maxheap.top()
================================================
FILE: lintcode/362_sliding_window_maximum.py
================================================
from heapq import heappush, heappop
class HashHeapq:
def __init__(self):
self.heap = []
self.deleted = {}
def push(self, val):
heappush(self.heap, val)
def pop(self):
if self.is_empty():
return
heappop(self.heap)
def remove(self, val):
if self.is_empty():
return
self.deleted[val] = self.deleted.get(val, 0) + 1
def top(self):
if self.is_empty():
return
return self.heap[0]
def is_empty(self):
while self.heap and self.deleted.get(self.heap[0]):
self.deleted[self.heap[0]] -= 1
heappop(self.heap)
return not self.heap
class Solution:
def maxSlidingWindow(self, A, k):
"""
:type A: List[int]
:type k: int
:rtype: List[int]
"""
ans = []
if not A:
return ans
heap = HashHeapq()
for i in range(len(A)):
heap.push(-A[i])
if i >= k - 1:
ans.append(-heap.top())
heap.remove(-A[i - k + 1])
return ans
"""
Failed Solution
Since its a O(n*k) solution
"""
class Solution:
"""
@param: A: A list of integers
@param: k: An integer
@return: The maximum number inside the window at each moving
"""
def maxSlidingWindow(self, A, k):
ans = []
if not A or len(A) < 1:
return ans
for r in range(len(A)):
if r >= k - 1:
ans.append(max(A[r - k + 1 : r + 1]))
return ans
================================================
FILE: lintcode/363_trapping_rain_water.py
================================================
class Solution:
"""
@param: heights: a list of integers
@return: a integer
"""
def trapRainWater(self, heights):
if not heights:
return 0
# mx_l: max height for index `l`
# mx_r: max height for index `r`
mx_l = mx_r = ans = 0
l, r = 0, len(heights) - 1
while l < r:
# To find the max height in heights
if heights[l] < heights[r]:
mx_l = max(mx_l, heights[l])
ans += mx_l - heights[l]
l += 1
else:
mx_r = max(mx_r, heights[r])
ans += mx_r - heights[r]
r -= 1
return ans
================================================
FILE: lintcode/364_trapping_rain_water_ii.py
================================================
from heapq import heappush, heappop
class Solution:
"""
@param: heights: a matrix of integers
@return: an integer
"""
def trapRainWater(self, heights):
if not heights:
return 0
m, n = len(heights), len(heights[0])
dx, dy = [1, -1, 0, 0], [0, 0, 1, -1]
ans = bound = x = y = i = 0
bounds = []
visited = [[0 for _ in range(n)] for _ in range(m)]
# Put the cells on the matrix boundaries into `bounds`
for i in range(m):
heappush(bounds, (heights[i][0], i, 0))
visited[i][0] = 1
heappush(bounds, (heights[i][n - 1], i, n - 1))
visited[i][n - 1] = 1
for i in range(1, n - 1):
heappush(bounds, (heights[0][i], 0, i))
visited[0][i] = 1
heappush(bounds, (heights[m - 1][i], m - 1, i))
visited[m - 1][i] = 1
while bounds:
# Find the min bound of any current boundary
bound, x, y = heappop(bounds)
# To keep the water in, keep finding the boundary
for i in range(4):
_x = x + dx[i]
_y = y + dy[i]
if 0 <= _x < m and 0 <= _y < n and not visited[_x][_y]:
visited[_x][_y] = 1
# Choosing the boundary of current cell
# if its lower than the bound outside
# than this cell will store water
# otherwise this cell will become the new boundary
_bound = max(bound, heights[_x][_y])
heappush(bounds, (_bound, _x, _y))
if _bound > heights[_x][_y]:
ans += _bound - heights[_x][_y]
return ans
================================================
FILE: lintcode/368_expression_evaluation.py
================================================
"""
REF: [Reverse Polish Notation](https://zh.wikipedia.org/wiki/%E9%80%86%E6%B3%A2%E5%85%B0%E8%A1%A8%E7%A4%BA%E6%B3%95)
REF: [Direct Algebraic Logic to Reverse Polish Notation](http://blog.csdn.net/sgbfblog/article/details/8001651)
"""
class Solution:
P = {
'+': 1,
'-': 1,
'*': 2,
'/': 2,
}
"""
@param: E: a list of strings
@return: an integer
"""
def evaluateExpression(self, E):
if not E:
return 0
E = self.dal2rpn(E)
"""
for cases like `["(","(","(","(","(",")",")",")",")",")"]`
"""
if not E:
return 0
return self.eval_rpn(E)
def dal2rpn(self, E):
"""
`stack` to save operators and brackets temply
`res` is the RPN of `E`, to save digits and operators
"""
stack, res = [], []
for char in E:
if char.isdigit():
"""
if its digit
then directly output
"""
res.append(char)
continue
if char in self.P:
"""
if `stack` is not empty
and `stack[-1]` is an operator
and its priority is higher or same ('*' == '/' > '+' == '-')
then pop and output
"""
while (stack and stack[-1] in self.P and
self.P[stack[-1]] >= self.P[char]):
res.append(stack.pop())
stack.append(char)
elif char == '(':
stack.append(char)
elif char == ')':
"""
if its ')'
then continue to pop and output
until meet '('
"""
while stack and stack[-1] != '(':
res.append(stack.pop())
stack.pop() # evict '('
"""
output the remaining in `stack`
"""
while stack:
res.append(stack.pop())
return res
def eval_rpn(self, E):
stack = []
for char in E:
if char.isdigit():
"""
if its digit
then temply save in `stack`
"""
stack.append(int(char))
continue
"""
the first poped one is base,
otherwise there is error occurred when '/' and '-'
"""
b = stack.pop()
a = stack.pop()
if char == '+':
stack.append(a + b)
elif char == '-':
stack.append(a - b)
elif char == '*':
stack.append(a * b)
elif char == '/':
stack.append(a // b)
return stack[0]
================================================
FILE: lintcode/36_reverse_linked_list_ii.py
================================================
"""
Definition of ListNode
class ListNode(object):
def __init__(self, val, next=None):
self.val = val
self.next = next
"""
class Solution:
"""
@param: head: ListNode head is the head of the linked list
@param: m: An integer
@param: n: An integer
@return: The head of the reversed ListNode
"""
def reverseBetween(self, head, m, n):
if not head:
return
"""
to get to `n`th node from `m`th node needs `n - m` operations
"""
n -= m
"""
`A` will stay at `m-1`th node
"""
A = dummy = ListNode(-1, head)
while m > 1:
m -= 1
A = A.next
"""
`B` will stay at `n+1`th node
`cur` stay at (`m`th -> `n`th) node
"""
B = cur = A.next
pre = nxt = None
while n >= 0:
n -= 1
nxt = B.next
B.next = pre
pre = B
B = nxt
A.next = pre
cur.next = B
return dummy.next
================================================
FILE: lintcode/376_binary_tree_path_sum.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
"""
@param: root: the root of binary tree
@param: target: An integer
@return: all valid paths
"""
def binaryTreePathSum(self, root, target):
ans = []
self.dfs(root, target, ans, [])
return ans
def dfs(self, node, remaining, ans, path):
if not node:
return
path.append(node.val)
remaining -= node.val
if remaining == 0 and not node.left and not node.right:
ans.append(path[:])
self.dfs(node.left, remaining, ans, path)
self.dfs(node.right, remaining, ans, path)
path.pop()
================================================
FILE: lintcode/378_convert_binary_search_tree_to_doubly_linked_list.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
this.val = val
this.left, this.right = None, None
Definition of Doubly-ListNode
class DoublyListNode(object):
def __init__(self, val, next=None):
self.val = val
self.next = self.prev = next
"""
class Solution:
"""
@param: root: The root of tree
@return: the head of doubly list node
"""
def bstToDoublyList(self, root):
if not root:
return
dummy = tail = DoublyListNode(-1)
stack = []
node = root
while node or stack:
while node:
stack.append(node)
node = node.left
node = stack.pop()
_node = DoublyListNode(node.val)
_node.prev = tail
tail.next = _node
tail = tail.next
node = node.right
return dummy.next
================================================
FILE: lintcode/382_triangle_count.py
================================================
class Solution:
"""
@param: S: A list of integers
@return: An integer
"""
def triangleCount(self, S):
ans = 0
if not S or len(S) < 3:
return ans
n = len(S)
S.sort()
"""
just like two sum
`c` is the target
"""
for c in range(n - 1, 1, -1):
a = 0
b = c - 1
while a < b:
if S[a] + S[b] > S[c]:
ans += b - a
b -= 1
else:
a += 1
return ans
================================================
FILE: lintcode/384_longest_substring_without_repeating_characters.py
================================================
import collections
class Solution:
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
ans = 0
if not s:
return ans
freqs = collections.defaultdict(int)
i = rep = 0
for j in range(len(s)):
if freqs[s[j]] == 1:
rep += 1
freqs[s[j]] += 1
while rep > 0:
freqs[s[i]] -= 1
if freqs[s[i]] == 1:
rep -= 1
i += 1
ans = max(ans, j - i + 1)
return ans
================================================
FILE: lintcode/386_longest_substring_with_at_most_k_distinct_characters.py
================================================
import collections
class Solution:
def lengthOfLongestSubstringKDistinct(self, s, k):
"""
:type s: str
:type k: int
:rtype: int
"""
ans = 0
if not s or not k:
return ans
freq = collections.defaultdict(int)
n = len(s)
left = right = cnt = 0
while right < n:
freq[s[right]] += 1
if freq[s[right]] == 1:
cnt += 1
right += 1
while cnt > k:
freq[s[left]] -= 1
if freq[s[left]] == 0:
cnt -= 1
left += 1
if right - left > ans:
ans = right - left
return ans
================================================
FILE: lintcode/38_search_a_2d_matrix_ii.py
================================================
"""
Heap
"""
import heapq
class Solution:
def searchMatrix(self, matrix, target):
"""
:type matrix: list[list[int]]
:type target: int
:rtype: int
"""
ans = 0
if not matrix or not matrix[0]:
return ans
heap = []
m, n = len(matrix), len(matrix[0])
for i in range(m):
heapq.heappush(heap, (matrix[i][0], i, 0))
while heap and heap[0][0] <= target:
num, x, y = heapq.heappop(heap)
if num == target:
ans += 1
y += 1
if y < n:
heapq.heappush(heap, (matrix[x][y], x, y))
return ans
"""
Iteration
start from bottom-left of matrix
if `G[x][y] > target`:
need to check `x - 1`
all cells before `y - 1` are confirmed in last iteration
else:
all cells before `x + 1` are confirmed in last iteration
need to check `y + 1`
"""
class Solution:
def searchMatrix(self, matrix, target):
"""
:type matrix: list[list[int]]
:type target: int
:rtype: int
"""
ans = 0
if not matrix or not matrix[0]:
return ans
m, n = len(matrix), len(matrix[0])
x, y = m - 1, 0
while x >= 0 and y < n:
if matrix[x][y] < target:
y += 1
elif matrix[x][y] > target:
x -= 1
else:
ans += 1
x -= 1
y += 1
return ans
================================================
FILE: lintcode/390_find_peak_element_ii.py
================================================
"""
Test Case
[[ 1, 2, 3, 6, 5 ]
,[ 16, 41, 23, 22, 6 ]
,[ 15, 17, 24, 21, 7 ]
,[ 14, 18, 19, 20, 10 ]
,[ 13, 14, 11, 10, 9 ]]
[[ 1, 3, 2 ]
,[ 4, 6, 5 ]
,[ 7, 9, 8 ]
,[ 13, 15, 14 ]
,[ 10, 12, 11 ]]
"""
class Solution:
NULL_POS = [-1, -1]
dx = [1, -1, 0, 0]
dy = [0, 0, 1, -1]
"""
@param: A: An integer matrix
@return: The index of the peak
"""
"""
step0/ given matrix and its size is 6*6
step1/ chosen the mid_col is 3, the max_value on that col at (2, 3),
and the value at (2, 2) great than it
step2/ chosen the mid_row is 2, the max_value in the segment[0:3] on that row at (2, 1),
and the value at (3, 1) great than it
step3/ keep slicing row or col if its range is more wide
...
step-1/ find a square and check the vertices
"""
def findPeakII(self, A):
if not A:
return self.NULL_POS
self.m, self.n = len(A), len(A[0])
left, col, right = 0, 0, self.n - 1
up, row, down = 0, 0, self.m - 1
while left + 1 < right or up + 1 < down:
if down - up > right - left:
row = up + (down - up) // 2
col = self.findRowMax(A, row, left, right)
if self.isPeak(A, row, col):
return [row, col]
if A[row][col] < A[row-1][col]:
down = row
else:
up = row
else:
col = left + (right - left) // 2
row = self.findColMax(A, col, up, down)
if self.isPeak(A, row, col):
return [row, col]
if A[row][col] < A[row][col-1]:
right = col
else:
left = col
for r in [up, down]:
for c in [left, right]:
if self.isPeak(A, r, c):
return [r, c]
return self.NULL_POS
# given col index, return the row index of the max value on that col
def findColMax(self, A, col, up, down):
row = 0
for r in range(up, down + 1):
if A[row][col] < A[r][col]:
row = r
return row
# given row index, return the col index of the max value on that row
def findRowMax(self, A, row, left, right):
col = 0
for c in range(left, right + 1):
if A[row][col] < A[row][c]:
col = c
return col
def isPeak(self, A, row, col):
_r = _c = 0
for i in range(4):
_r = row + self.dx[i]
_c = col + self.dy[i]
if 0 <= _r < self.m \
and 0 <= _c < self.n \
and A[row][col] < A[_r][_c]:
return 0
return 1
================================================
FILE: lintcode/391_number_of_airplanes_in_the_sky.py
================================================
"""
Definition of Interval.
class Interval(object):
def __init__(self, start, end):
self.start = start
self.end = end
"""
class Solution:
"""
@param: airplanes: An interval array
@return: Count of airplanes are in the sky.
"""
def countOfAirplanes(self, airplanes):
ans = 0
if not airplanes:
return ans
cnt = 0
timeline = []
for interval in airplanes:
timeline.append((interval.start, 1))
timeline.append((interval.end, 0))
timeline.sort()
for _, in_sky in timeline:
if in_sky:
cnt += 1
else:
cnt -= 1
ans = max(ans, cnt)
return ans
================================================
FILE: lintcode/392_house_robber.py
================================================
"""
to borrow the idea of `./lintcode/515_paint_house.py`
a house can be at one of two status: steal or not
"""
class Solution:
"""
@param: A: An array of non-negative integers
@return: The maximum amount of money you can rob tonight
"""
def houseRobber(self, A):
if not A:
return 0
n = len(A)
prev = curr = 0
dp = [[0] * 2 for _ in range(2)]
dp[0][1] = A[0]
for i in range(1, n):
prev = curr # (i - 1) % 2
curr = i % 2
dp[curr][0] = max(dp[prev])
dp[curr][1] = dp[prev][0] + A[i]
return max(dp[curr])
class Solution:
"""
@param: A: An array of non-negative integers
@return: The maximum amount of money you can rob tonight
"""
def houseRobber(self, A):
if not A:
return 0
if len(A) == 1:
return A[0]
n = len(A)
# `dp[i]`: the maximum amount of money we can get until house `i`
dp = [0] * 3
dp[0] = A[0]
dp[1] = max(A[0], A[1])
prev2, prev1, curr = 0, 0, 1
for i in range(2, n):
prev2, prev1 = prev1, curr
curr = i % 3
"""
if house `i` was stolen, we can't steal house `i - 1`,
the amount is `dp[i - 2] + A[i]`
if house `i` is not stolen, we can steal house `i - 1`,
but we lose the `A[i]`,
the amount is `dp[i - 1] + 0`
"""
dp[curr] = max(dp[prev1], dp[prev2] + A[i])
return dp[curr]
class Solution:
def houseRobber(self, A):
"""
:type A: List[int]
:rtype: int
"""
if not A:
return 0
n = len(A)
dp = [0] * (n + 1)
dp[1] = A[0]
for i in range(2, n + 1):
dp[i] = max(
dp[i - 2] + A[i - 1],
dp[i - 1]
)
return dp[n]
================================================
FILE: lintcode/393_best_time_to_buy_and_sell_stock_iv.py
================================================
class Solution:
"""
@param: K: An integer
@param: P: An integer array
@return: Maximum profit
"""
def maxProfit(self, K, P):
if not K or not P:
return 0
n = len(P)
"""
if `K >= n`, this problem is just
`./lintcode/150_best_time_to_buy_and_sell_stock_ii.py`
so we dont need to use `dp`, since it lead to overflow
"""
if K >= n:
profit = 0
for i in range(1, n):
if P[i] > P[i - 1]:
profit += P[i] - P[i - 1]
return profit
"""
the main concept is in
`./lintcode/151_best_time_to_buy_and_sell_stock_iii.py`
"""
STAGE = 2 * K + 1
dp = [[0] * STAGE for _ in range(2)]
i = j = prev = curr = profit = 0
for i in range(1, n):
prev = curr
curr = 1 - prev
profit = P[i] - P[i - 1]
for j in range(1, STAGE, 2):
dp[curr][j] = max(dp[prev][j] + profit, dp[prev][j - 1])
for j in range(2, STAGE, 2):
dp[curr][j] = max(dp[prev][j], dp[prev][j - 1] + profit)
return max(dp[curr])
================================================
FILE: lintcode/394_coins_in_a_line.py
================================================
"""
There are n coins in a line.
Two players take turns to take one or two coins from right side
until there are no more coins left.
The player who take the last coin wins.
Could you please decide the first play will win or lose?
`dp[i]` means first play will win if left `i` stones in line
`dp[i]` =
True, if dp[i - 1] == False and dp[i - 2] == False
True, if dp[i - 1] == True and dp[i - 2] == False
True, if dp[i - 1] == False and dp[i - 2] == True
False, if dp[i - 1] == True and dp[i - 2] == True
=> dp[i - 1] == False or dp[i - 2] == False
the meaning is if the p2 is possible to lose
when stones == i - 1 or i - 2
=> p1 will win
"""
class Solution:
def firstWillWin(self, n):
"""
:type n: int
:rtype: bool
"""
if not n:
return False
if n < 3:
return True
dp = [False] * n
dp[0] = dp[1] = True
for i in range(2, n):
if (
dp[i - 1] is False or
dp[i - 2] is False
):
dp[i] = True
return dp[n - 1]
================================================
FILE: lintcode/395_coins_in_a_line_ii.py
================================================
"""
There are n coins with different value in a line.
Two players take turns to take one or two coins from left side
until there are no more coins left.
The player who take the coins with the most value wins.
Could you please decide the first player will win or lose?
`dp[i]` means the p1 earned values if game start from `i`
case 1: if p2 will take the `i + 1`
=> values[i] - dp[i + 1]
case 2: if p2 will take the `i + 2`
=> values[i] + values[i + 1] - dp[i + 2]
"""
class Solution:
def firstWillWin(self, values):
"""
:type values: list[int]
:rtype: bool
"""
if not values:
return False
n = len(values)
if n < 3:
return True
dp = [False] * n
dp[-1] = values[-1]
dp[-2] = values[-1] + values[-2]
for i in range(n - 3, -1, -1):
dp[i] = max((
values[i] - dp[i + 1],
values[i] + values[i + 1] - dp[i + 2],
))
return dp[0] >= 0
================================================
FILE: lintcode/396_coins_in_a_line_iii.py
================================================
"""
There are n coins in a line.
Two players take turns to take a coin from one of the ends of the line
until there are no more coins left.
The player with the larger amount of money wins.
Could you please decide the first player will win or lose?
`dp[i][j]` means the maximum value when
the current player chosen from [i, j] in `values`
recurring from the end of the game to start
so init with `dp[i][i]`
i i+1 j-1 j
c1 c2 c3 c4 c5
there are two cases:
1. if the current player chosen `values[i]`, then
the cost is `dp[i + 1][j]`
2. if the current player chosen `values[j]`, then
the cost is `dp[i][j - 1]`
chosen the maximum
"""
class Solution:
def firstWillWin(self, values):
"""
:type values: list[int]
:rtype: bool
"""
if not values:
return False
n = len(values)
if n < 2:
return True
dp = [[0] * n for _ in range(n)]
for i in range(n):
dp[i][i] = values[i]
for i in range(n - 1 - 1, -1, -1):
for j in range(i + 1, n):
dp[i][j] = max((
values[i] - dp[i + 1][j],
values[j] - dp[i][j - 1],
))
return dp[0][n - 1] >= 0
================================================
FILE: lintcode/397_longest_increasing_continuous_subsequence.py
================================================
"""
DP + print path
remove the single line comment to see the path in result
"""
class Solution:
"""
@param: A: An array of Integer
@return: an integer
"""
def longestIncreasingContinuousSubsequence(self, A):
if not A:
return 0
size = self.get_lics_size(A)
A.reverse()
_size = self.get_lics_size(A)
return max(size, _size)
def get_lics_size(self, A):
ans = 0
n = len(A)
"""
`dp[i]` means the size of LICS ended at `A[i]`
note that there is size, so init with `1`
"""
dp = [1] * n
# pi = [-1] * n
# end_at = -1
for i in range(n):
if i > 0 and A[i] > A[i - 1]:
dp[i] = dp[i - 1] + 1
# pi[i] = i - 1
if dp[i] > ans:
ans = dp[i]
# end_at = i
# paths = [0] * ans
# for i in range(ans - 1, -1, -1):
# paths[i] = A[end_at]
# end_at = pi[end_at]
# print(paths)
return ans
"""
Optimized
"""
class Solution:
"""
@param: A: An array of Integer
@return: an integer
"""
def longestIncreasingContinuousSubsequence(self, A):
if not A:
return 0
size = self.get_lics_size(A)
A.reverse()
_size = self.get_lics_size(A)
return max(size, _size)
def get_lics_size(self, A):
ans = size = 1
for i in range(1, len(A)):
if A[i] > A[i - 1]:
size += 1
else:
size = 1
if size > ans:
ans = size
return ans
================================================
FILE: lintcode/3_digit_counts.py
================================================
class Solution:
"""
@param: k: An integer
@param: n: An integer
@return: An integer denote the count of digit k in 1..n
"""
def digitCounts(self, k, n):
ans = 0
for i in range(n + 1):
ans += self.count(k, i)
return ans
def count(self, k, a):
if k == a == 0:
return 1
cnt = 0
while a:
if a % 10 == k:
cnt += 1
a //= 10
return cnt
================================================
FILE: lintcode/401_kth_smallest_number_in_sorted_matrix.py
================================================
import heapq
class Solution:
"""
@param: matrix: a matrix of integers
@param: k: An integer
@return: the kth smallest number in the matrix
"""
def kthSmallest(self, matrix, k):
ans = j = 0
heap, m, n = [], len(matrix), len(matrix[0])
for i in range(min(k, m)): heapq.heappush(heap, (matrix[i][0], i, 0))
while k > 0:
ans = heapq.heappop(heap)
j = ans[2] + 1
if j < n:
heapq.heappush(heap, (matrix[ans[1]][j], ans[1], j))
k -= 1
return ans[0]
================================================
FILE: lintcode/402_continuous_subarray_sum.py
================================================
class Solution:
"""
@param: A: An integer array
@return: A list of integers includes the index of the first number and the index of the last number
"""
def continuousSubarraySum(self, A):
ans = [-1, -1]
if not A:
return ans
left = right = 0
_max = _sum = float('-inf')
for i in range(len(A)):
if _sum < 0:
_sum = A[i]
left = right = i
else:
_sum += A[i]
right = i
if _sum > _max:
_max = _sum
ans = [left, right]
return ans
================================================
FILE: lintcode/406_minimum_size_subarray_sum.py
================================================
class Solution:
"""
@param: nums: an array of integers
@param: s: An integer
@return: an integer representing the minimum size of subarray
"""
def minimumSize(self, nums, s):
if not nums or len(nums) == 0:
return -1
n = len(nums)
min_len = n + 1
l = r = t = 0
while r < n:
# Keep adding the next int into total until total >= s
while r < n and t < s:
t += nums[r]
r += 1
# Terminate iteration if all the children in nums have been added
if r >= n and t < s:
break
# Keep substracting the prev int from total until total < s
while l < r and t >= s:
t -= nums[l]
l += 1
# Save the min_size
min_len = min(min_len, r - l + 1)
return -1 if min_len > n else min_len
================================================
FILE: lintcode/40_implement_queue_by_two_stacks.py
================================================
class MyQueue:
def __init__(self, ):
self.stack1 = []
self.stack2 = []
"""
@param: element: An integer
@return: nothing
"""
def push(self, element):
self.stack1.append(element)
"""
@return: An integer
"""
def pop(self, ):
self.top()
return self.stack2.pop()
"""
@return: An integer
"""
def top(self, ):
# While `self.stack2` is empty, get element from `self.stack1`
# step0/ stack1: [1, 2, 3], stack2: []
# step1/ stack1: [], stack2: [3, 2, 1]
# step2/ stack1: [4, 5, 6], stack2: [3, 2] // 1
# step3/ stack1: [4, 5, 6], stack2: [] // 2, 3
# step4/ stack1: [7, 8], stack2: [6, 5, 4]
if not self.stack2:
while self.stack1:
self.stack2.append(self.stack1.pop())
return self.stack2[-1]
================================================
FILE: lintcode/414_divide_two_integers.py
================================================
class Solution:
def divide(self, a, b):
"""
:type a: int
:type b: int
:rtype: int
"""
INT_MAX = 0x7FFFFFFF
ans = 0
if not b:
return INT_MAX
if not a:
return ans
_a = -a if a < 0 else a
_b = -b if b < 0 else b
for i in range(31, -1, -1):
"""
let `ci = 1 << i = 2 ** i`
if `_a // _b >= ci`, that is `_a // ci >= _b`
=> `(_a - _b * ci) // _b >= 0`, and `ans += ci`
start to approch `_a' // _b >= ci'`
where _a' = _a - _b * ci, i' = i - 1
"""
if (_a >> i) < _b:
continue
ans += 1 << i
_a -= _b << i
if a ^ b < 0:
ans = -ans
if ans > INT_MAX:
return INT_MAX
if ans < ~INT_MAX:
return ~INT_MAX
return ans
================================================
FILE: lintcode/415_valid_palindrome.py
================================================
class Solution:
def isPalindrome(self, s):
"""
:type s: str
:rtype: bool
"""
if not s:
return True
s = s.lower()
n = len(s)
left, right = 0, n - 1
while left < right:
while left < right and not s[left].isalnum():
left += 1
while left < right and not s[right].isalnum():
right -= 1
if s[left] != s[right]:
return False
left += 1
right -= 1
return True
================================================
FILE: lintcode/417_valid_number.py
================================================
class Solution:
"""
@param: s: the string that represents a number
@return: whether the string is a valid number
"""
def isNumber(self, s):
if not s:
return False
n = len(s)
left, right = 0, n - 1
while left < n and s[left] == ' ':
left += 1
while right >= 0 and s[right] == ' ':
right -= 1
if left < n and s[left] in ('+', '-'):
left += 1
if left > right:
return False
if left != 0 or right != n - 1:
s = s[left:right + 1]
zero = ord('0')
nine = ord('9')
is_contained_dot = False
is_contained_num = False
for char in s:
if char == '.' and is_contained_dot:
return False
if not (char == '.' or zero <= ord(char) <= nine):
return False
if char == '.':
is_contained_dot = True
if zero <= ord(char) <= nine:
is_contained_num = True
return is_contained_num
================================================
FILE: lintcode/418_integer_to_roman.py
================================================
class Solution:
def intToRoman(self, num):
"""
:type num: int
:rtype: str
"""
if not num:
return ''
# I, V, X, L, C, D, M
symbs = (
('M', 1000),
('CM', 900),
('D', 500),
('CD', 400),
('C', 100),
('XC', 90),
('L', 50),
('XL', 40),
('X', 10),
('IX', 9),
('V', 5),
('IV', 4),
('I', 1),
)
ans = []
for symb, amount in symbs:
if not num:
break
while num >= amount: # num - amount >= 0
num -= amount
ans.append(symb)
return ''.join(ans)
================================================
FILE: lintcode/419_roman_to_integer.py
================================================
class Solution:
def romanToInt(self, s):
"""
:type s: str
:rtype: int
"""
ans = 0
if not s:
return ans
symbs = {
'I': 1,
'V': 5,
'X': 10,
'L': 50,
'C': 100,
'D': 500,
'M': 1000,
}
ans += symbs[s[-1]]
for i in range(len(s) - 2, -1, -1):
if symbs[s[i]] >= symbs[s[i + 1]]:
ans += symbs[s[i]]
else:
ans -= symbs[s[i]]
return ans
================================================
FILE: lintcode/41_maximum_subarray.py
================================================
"""
Prefix Sum: space optimization
"""
class Solution:
"""
@param: A: A list of integers
@return: A integer indicate the sum of max subarray
"""
def maxSubArray(self, A):
"""
the answer is the maximum segment sum,
that is, `S[i] - Smin`
"""
if not A:
return 0
ans = float('-inf')
S = Smin = 0
for i in range(len(A)):
S += A[i]
if S - Smin > ans:
ans = S - Smin
"""
since the sum of [i, j] in A is `S[j] - S[i - 1]`
so we need to find the `Smin` at the end of iteration
"""
if S < Smin:
Smin = S
return ans
"""
Prefix Sum
"""
class Solution:
"""
@param: A: A list of integers
@return: A integer indicate the sum of max subarray
"""
def maxSubArray(self, A):
"""
the answer is the maximum segment sum,
that is, `S[i] - Smin`
"""
if not A:
return 0
n = len(A)
ans = float('-inf')
Smin = 0
S = [0] * (n + 1)
for i in range(1, n + 1):
S[i] = S[i - 1] + A[i - 1]
if S[i] - Smin > ans:
ans = S[i] - Smin
"""
since the sum of [i, j] in A is `S[j] - S[i - 1]`
so we need to find the `Smin` at the end of iteration
"""
if S[i] < Smin:
Smin = S[i]
return ans
================================================
FILE: lintcode/42_maximum_subarray_ii.py
================================================
"""
main concept
there is MUST a separator to distinct that two subarrays
`left[i]` means the maxsum before `i + 1`
`right[i]` means the maxsum after `i - 1`
|<- the separator
nums[i] | nums[i + 1]
the `ans` is to find the maximum of `left[i] + right[i + 1]`
"""
class Solution:
def maxTwoSubArrays(self, nums):
"""
:type nums: list[int]
:rtype: int
"""
NOT_FOUND = 0
if not nums:
return NOT_FOUND
n = len(nums)
left = self.get_max_sums(nums, range(n))
right = self.get_max_sums(nums, range(n - 1, -1, -1))
ans = _INF = float('-inf')
for i in range(n - 1):
s = left[i] + right[i + 1]
if s > ans:
ans = s
return ans if ans > _INF else NOT_FOUND
def get_max_sums(self, nums, num_range):
res = [0] * len(nums)
smax = float('-inf')
s = smin = 0
for i in num_range:
s += nums[i]
if s - smin > smax:
smax = s - smin
if s < smin:
smin = s
res[i] = smax
return res
================================================
FILE: lintcode/430_scramble_string.py
================================================
class Solution:
"""
@param: s1: A string
@param: s2: Another string
@return: whether s2 is a scrambled string of s1
"""
def isScramble(self, s1, s2):
if not s1 or not s2 or len(s1) != len(s2):
return False
n = len(s1)
"""
`dp[i][j][k]` means the substring in `s1` (start: `i`, len: `k`)
could be transformed into the substring in `s2` (start: `j`, len: `k`)
"""
dp = [[[False] * (n + 1) for _ in range(n)] for _ in range(n)]
for i in range(n):
for j in range(n):
dp[i][j][1] = (s1[i] == s2[j])
for k in range(2, n + 1):
for i in range(n):
"""
allow: i < n - k + 1 => i <= n - k
disallow: i > n - k
"""
if i + k > n:
continue
for j in range(n):
if j + k > n:
continue
for l in range(1, k):
"""
If its already calculated and possible to transform
"""
if dp[i][j][k]:
continue
"""
* u1, u2: substring in s1
* v1, v2: substring in s2
* `l` == len(u1) == len(v1)
* `k - l` == len(u2) == len(v2)
case1: u1 -> v1, u2 -> v2
`(dp[i][j][l] and dp[i + l][j + l][k - l])`
- `dp[i][j][l]` means its possible to u1 -> v1
- `dp[i + l][j + l][k - l]` => u2 -> v2
u1 u2
s1: |`i`---`l`--->|`i+l`---`k-l`--->|
v1 v2
s2: |`j`---`l`--->|`j+l`---`k-l`--->|
case2: u1 -> v2, u2 -> v1
`(dp[i][j + k - l][l] and dp[i + l][j][k - l])`
- `dp[i][j + k - l][l]` => u1 -> v2
- `dp[i + l][j][k - l]` => u2 -> v1
u1 u2
s1: |`i`---`l`--->|`i+l`---`k-l`--->|
v1 v2
s2: |`j`---`k-l`--->|`j+k-l`--`l`-->|
"""
if dp[i][j][l] and dp[i + l][j + l][k - l]:
dp[i][j][k] = True
continue
if dp[i][j + k - l][l] and dp[i + l][j][k - l]:
dp[i][j][k] = True
return dp[0][0][n]
================================================
FILE: lintcode/431_connected_component_in_undirected_graph.py
================================================
"""
Definition for a undirected graph node
class UndirectedGraphNode:
def __init__(self, x):
self.label = x
self.neighbors = []
"""
class Solution:
"""
Union Find
"""
def connectedSet(self, nodes):
"""
:type nodes: list[UndirectedGraphNode]
:rtype: list[list[UndirectedGraphNode]]
"""
if not nodes:
return []
uf = {}
for node in nodes:
for neib in node.neighbors:
self.union(uf, node, neib)
ans = {}
for node in nodes:
# to correct root again
root = self.find(uf, node)
if root not in ans:
ans[root] = []
ans[root].append(node.label)
return list(ans.values())
def union(self, nodes, a, b):
_a = self.find(nodes, a)
_b = self.find(nodes, b)
if _a is not _b:
nodes[_b] = _a
def find(self, nodes, a):
if a not in nodes:
nodes[a] = a
return a
if nodes[a] is a:
return a
nodes[a] = self.find(nodes, nodes[a])
return nodes[a]
class Solution:
"""
DFS
"""
def connectedSet(self, nodes):
"""
:type nodes: list[UndirectedGraphNode]
:rtype: list[list[UndirectedGraphNode]]
"""
ans = []
if not nodes:
return ans
visited = set()
for node in nodes:
if node in visited:
continue
path = []
self.dfs(node, visited, path)
ans.append(sorted(path))
return ans
def dfs(self, a, visited, path):
visited.add(a)
path.append(a.label)
for b in a.neighbors:
if b in visited:
continue
self.dfs(b, visited, path)
================================================
FILE: lintcode/432_find_the_weak_connected_component_in_the_directed_graph.py
================================================
"""
Definition for a directed graph node
class DirectedGraphNode:
def __init__(self, x):
self.label = x
self.neighbors = []
"""
class Solution:
def __init__(self):
self.nodes = {}
"""
@param {DirectedGraphNode[]} nodes a array of directed graph node
@return {int[][]} a connected set of a directed graph
"""
def connectedSet2(self, nodes):
# Build UnionFind
for node in nodes:
for nei in node.neighbors:
self.connect(nei.label, node.label)
# Categorify result
result = {}
root_label = ''
for node in nodes:
root_label = self.find(node.label)
if root_label not in result:
result[root_label] = []
result[root_label].append(node.label)
return result.values()
def connect(self, a, b):
root_a = self.find(a)
root_b = self.find(b)
if root_a is not root_b:
self.nodes[root_a] = root_b
def find(self, a):
if a not in self.nodes:
self.nodes[a] = a
return a
elif self.nodes[a] is a:
return a
self.nodes[a] = self.find(self.nodes[a])
return self.nodes[a]
================================================
FILE: lintcode/433_number_of_islands.py
================================================
class Solution:
def numIslands(self, grid):
"""
:type grid: list[list[int]]
:rtype: int
"""
ans = 0
if not grid or not grid[0]:
return ans
m, n = len(grid), len(grid[0])
for x in range(m):
for y in range(n):
if grid[x][y] == 1:
ans += 1
self.dfs(grid, x, y)
return ans
def dfs(self, grid, x, y):
m, n = len(grid), len(grid[0])
grid[x][y] = 0
for dx, dy in (
(0, -1), (0, 1),
(-1, 0), (1, 0),
):
_x = x + dx
_y = y + dy
if not (
0 <= _x < m and
0 <= _y < n and
grid[_x][_y] == 1
):
continue
self.dfs(grid, _x, _y)
================================================
FILE: lintcode/434_number_of_islands_ii.py
================================================
"""
Definition for a point.
class Point:
def __init__(self, a=0, b=0):
self.x = a
self.y = b
"""
class Solution:
def numIslands2(self, m, n, operators):
"""
:type m: int
:type n: int
:type operators: Point
:rtype: list[int]
"""
ans = []
if not m or not n or not operators:
return ans
cnt = 0
nodes = {}
for op in operators:
node = (op.x, op.y)
if node not in nodes:
nodes[node] = node
cnt += 1
for dx, dy in (
(0, -1), (0, 1),
(-1, 0), (1, 0),
):
_x = op.x + dx
_y = op.y + dy
if not (
0 <= _x < m and
0 <= _y < n and
(_x, _y) in nodes
):
continue
if self.union(nodes, node, (_x, _y)):
cnt -= 1
ans.append(cnt)
return ans
def union(self, nodes, a, b):
_a = self.find(nodes, a)
_b = self.find(nodes, b)
if _a == _b:
return False
nodes[_b] = _a
return True
def find(self, nodes, a):
if a not in nodes:
nodes[a] = a
return a
if nodes[a] == a:
return a
nodes[a] = self.find(nodes, nodes[a])
return nodes[a]
================================================
FILE: lintcode/437_copy_books.py
================================================
"""
DP: TLE
"""
class Solution:
"""
@param: P: an array of integers
@param: k: An integer
@return: an integer
"""
def copyBooks(self, P, k):
if not P or not k:
return 0
n = len(P)
if n == 1:
return P[0]
if k > n:
k = n
INFINITY = float('inf')
"""
`dp[i][j]` means the minimum time to assign `i` books to `j` people
"""
dp = [[INFINITY] * k for _ in range(n)]
for j in range(k):
dp[0][j] = P[0]
for i in range(1, n):
dp[i][0] = dp[i - 1][0] + P[i]
for i in range(1, n):
for j in range(1, k):
if j > i:
"""
if `j > i`, means books more than copiers
the people after `j`th people dont have to work
"""
dp[i][j] = dp[i][j - 1]
continue
for h in range(j - 1, i + 1):
"""
`copied_pages` is the maximum copied pages,
and also means the maximum time spent
by the `j - 1` people before `j`
if the `j - 1` people can copy as much as they can
then `j`th man will be able to spend less time finishing it
"""
copied_pages = dp[i][0] - dp[h][0]
if dp[h][j - 1] > copied_pages:
copied_pages = dp[h][j - 1]
if copied_pages < dp[i][j]:
dp[i][j] = copied_pages
return dp[n - 1][k - 1]
"""
DP
"""
class Solution:
"""
@param: P: an array of integers
@param: k: An integer
@return: an integer
"""
def copyBooks(self, P, k):
if not P or not k:
return 0
n = len(P)
if n == 1:
return P[0]
if k > n:
k = n
"""
`dp[i][j]` means the minimum time to assign `i` books to `j` people
"""
dp = [[0] * k for _ in range(n)]
for j in range(k):
dp[0][j] = P[0]
for i in range(1, n):
dp[i][0] = dp[i - 1][0] + P[i]
for j in range(1, k):
for i in range(1, j):
"""
if `j > i`, means books more than copiers
the people after `j`th people dont have to work
"""
dp[i][j] = dp[i][j - 1]
h = copied_pages = 0
for i in range(j, n):
"""
`h` means the maximum books `j - 1` men copied in shortest time
"""
while h < i and dp[h][j - 1] < dp[i][0] - dp[h][0]:
h += 1
dp[i][j] = dp[h][j - 1]
if h == 0:
continue
"""
check again the `h - 1` books
`copied_pages` is the maximum copied pages,
and also means the maximum time spent
by the `j - 1` people before `j`
if the `j - 1` people can copy as much as they can
then `j`th man will be able to spend less time finishing it
"""
copied_pages = dp[i][0] - dp[h - 1][0]
if dp[h - 1][j - 1] > copied_pages:
copied_pages = dp[h - 1][j - 1]
if copied_pages < dp[i][j]:
dp[i][j] = copied_pages
return dp[n - 1][k - 1]
"""
Binary Search
1. the minimum spent time, that is `m`,
locates between `max(pages)` and `sum(pages)` (1 page spent 1 min)
2. the `p` below means possible to finish work,
if the spent time less than `m`
and then the `k` people are impossible to finish that
t | ... m-2 m-1 m m+1 m+2 ...
p | ..F F F T T T T..
"""
class Solution:
"""
@param: P: an array of integers
@param: k: An integer
@return: an integer
"""
def copyBooks(self, P, k):
if not P or not k:
return 0
n = len(P)
if n == 1:
return P[0]
if k > n:
k = n
left = right = P[0]
for i in range(1, len(P)):
if P[i] > left:
left = P[i]
right += P[i]
while left + 1 < right:
mid = (left + right) // 2
if self.check_if_possible(P, mid, k):
right = mid
else:
left = mid
"""
MUST check `left` first, since we need the min spent time
"""
return left if self.check_if_possible(P, left, k) else right
def check_if_possible(self, P, spent_time, max_copiers):
"""
check if possible to copy all `pages` in `spent_time`
and participation is not more than `max_copiers`
"""
copied_pages, copiers = 0, 1
for i in range(len(P)):
"""
if a copier will spend more than `spent_time`
add one more copier in
"""
if copied_pages + P[i] > spent_time:
copied_pages = 0
copiers += 1
if copiers > max_copiers:
return False
copied_pages += P[i]
return True
================================================
FILE: lintcode/43_maximum_subarray_iii.py
================================================
"""
http://www.cnblogs.com/sherylwang/p/5635665.html
"""
class Solution:
"""
@param: A: A list of integers
@param: k: An integer denote to find k non-overlapping subarrays
@return: An integer denote the sum of max k non-overlapping subarrays
"""
def maxSubArray(self, A, k):
if not A or not k or len(A) < k:
return 0
_INFINITY = float('-inf')
n = len(A)
"""
`G[i][j]` means the global max sum, to pick `j` arrays in [0, i)
`L[i][j]` means the local max sum, to pick `j` arrays in [0, i),
and the `i - 1`th child MUST be included
"""
G = [[_INFINITY] * (k + 1) for _ in range(n + 1)]
L = [[_INFINITY] * (k + 1) for _ in range(n + 1)]
for i in range(n + 1):
L[i][0] = 0
G[i][0] = 0
for i in range(1, n + 1):
end = i
if k < end:
end = k
for j in range(1, end + 1):
L[i][j] = A[i - 1] + max(L[i - 1][j], G[i - 1][j - 1])
G[i][j] = max(L[i][j], G[i - 1][j])
return G[n][k]
================================================
FILE: lintcode/440_backpack_iii.py
================================================
"""
Optimize Space
"""
class Solution:
"""
@param: A: an integer array
@param: V: an integer array
@param: m: an integer
@return: an integer
"""
def backPackIII(self, A, V, m):
if not A or not V or not m:
return 0
# `dp[w]` means the maximum value
# with weight `w`
dp = [0] * (m + 1)
_val = 0
for i in range(len(A)):
for w in range(A[i], m + 1):
_val = dp[w - A[i]] + V[i]
if _val > dp[w]:
dp[w] = _val
return dp[m]
"""
Origin
"""
class Solution:
"""
@param: A: an integer array
@param: V: an integer array
@param: m: an integer
@return: an integer
"""
def backPackIII(self, A, V, m):
if not A or not V or not m:
return 0
n = len(A)
# `dp[i][w]` means the maximum value
# with weight `w` in the former `i` items
dp = [[0] * (m + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
for w in range(1, m + 1):
dp[i][w] = dp[i - 1][w]
if w >= A[i - 1]:
dp[i][w] = max(
dp[i][w],
dp[i][w - A[i - 1]] + V[i - 1]
)
return dp[n][w]
================================================
FILE: lintcode/442_implement_trie.py
================================================
class TrieNode:
def __init__(self):
self.end_of = None
self.children = {}
class Trie:
def __init__(self):
self.root = TrieNode()
"""
@param: word: a word
@return: nothing
"""
def insert(self, word):
if word is None:
return
node = self.root
for c in word:
if c not in node.children:
node.children[c] = TrieNode()
node = node.children[c]
node.end_of = word
"""
@param: word: A string
@return: if the word is in the trie.
"""
def search(self, word):
if word is None:
return False
node = self.root
for c in word:
if c not in node.children:
return False
node = node.children[c]
return node.end_of == word
"""
@param: prefix: A string
@return: if there is any word in the trie that starts with the given prefix.
"""
def startsWith(self, prefix):
if prefix is None:
return False
node = self.root
for c in prefix:
if c not in node.children:
return False
node = node.children[c]
return True
================================================
FILE: lintcode/443_two_sum_greater_than_target.py
================================================
class Solution:
"""
@param: A: an array of integer
@param: target: An integer
@return: an integer
"""
def twoSum2(self, A, target):
ans = 0
if not A or len(A) < 2:
return ans
A.sort()
left, right = 0, len(A) - 1
while left < right:
# if minimum + maximum still <= target
# ignore the 2nd, 3rd maximum
if A[left] + A[right] <= target:
left += 1
continue
# if minimum + maximum > target
# we can ensure the 2nd, 3rd minimum also fit demand
ans += right - left
right -= 1
return ans
================================================
FILE: lintcode/447_search_in_a_big_sorted_array.py
================================================
"""
Test Case:
# if `reader.get(m) == target` when binary searching
# should set the `mid` as the right bound to keep looking for the minimum index
[1,1,1,1,2,2,3,3,3,4,4,4,5,5,5,5,5,5,5,6,6,6,6,6,6,7,7,7,8,8,8,8,9,9,9,9,10,10,10,10,10,10,10,10,10,11,11,11,11,12,12,12,13,13,13,13,13,14,14,14,14,14,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,18,18,19,19,19,19,20,20,20,20,20,20,20,20,20]
4
# error occurred when `if not reader or not target:`
[0,0,1,1]
0
"""
class Solution:
"""
@param: reader: An instance of ArrayReader.
@param: target: An integer
@return: An integer which is the first index of target.
"""
def searchBigSortedArray(self, reader, target):
end = 0
while reader.get(end) < target:
# `+1` is to avoid `end == 0`
end = end * 2 + 1
l, m, r = 0, 0, end
while l + 1 < r:
m = l + (r - l) // 2
if reader.get(m) < target:
l = m
else:
r = m
for i in [l, r]:
if reader.get(i) == target:
return i
return -1
================================================
FILE: lintcode/450_reverse_nodes_in_k_group.py
================================================
"""
Definition for singly-linked list.
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
"""
class Solution:
"""
@param: head: a ListNode
@param: k: An integer
@return: a ListNode
"""
def reverseKGroup(self, head, k):
if not head:
return
dummy = ListNode(0)
dummy.next = head
head = dummy
while head:
head = self.reverse_next_kth(head, k)
return dummy.next
def find_kth(self, head, k):
for i in range(k):
if not head:
return
head = head.next
return head
def reverse(self, head):
pre = nxt = None
while head:
nxt = head.next
head.next = pre
pre = head
head = nxt
return pre
def reverse_next_kth(self, head, k):
nk = self.find_kth(head, k)
if not nk:
return
nk_nxt = nk.next
n1_pre = head
n1 = head.next
nk.next = None
self.reverse(n1)
n1_pre.next = nk
n1.next = nk_nxt
return n1
================================================
FILE: lintcode/453_flatten_binary_tree_to_linked_list.py
================================================
"""
Main Concept:
For given tree:
1-5-6
=2-4
=3
take postorder traversal, and the visited order will be `3,4,2,1,5,6`
1. rebase the right child with the last right child in left child
when visit `2`
1-5-6
=2=3-4
2. move the left child to the right
when visit `2`
1-5-6
=2-3-4
3. keep doing (1) and (2)
when visit `1`
1-2-3-4-5-6
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
this.val = val
this.left, this.right = None, None
"""
class Solution:
"""
@param: root: a TreeNode, the root of the binary tree
@return:
"""
def flatten(self, root):
if not root:
return
self.flatten(root.left)
self.flatten(root.right)
if not root.left:
return
node = root.left
while node.right:
node = node.right
node.right = root.right
root.right = root.left
root.left = None
================================================
FILE: lintcode/457_classical_binary_search.py
================================================
class Solution:
"""
@param: A: An integer array sorted in ascending order
@param: target: An integer
@return: An integer
"""
def findPosition(self, A, target):
if not A:
return -1
left, right = 0, len(A) - 1
while left + 1 < right:
mid = (left + right) // 2
if A[mid] == target:
return mid
if A[mid] < target:
left = mid
else:
right = mid
if A[left] == target:
return left
if A[right] == target:
return right
return -1
================================================
FILE: lintcode/458_last_position_of_target.py
================================================
class Solution:
"""
@param: nums: An integer array sorted in ascending order
@param: target: An integer
@return: An integer
"""
def lastPosition(self, nums, target):
if not nums or not target:
return -1
l, m, r = 0, 0, len(nums) - 1
while l + 1 < r:
m = l + (r - l) // 2
if nums[m] > target:
r = m
else:
l = m
"""
considering the edge case: [1, 1, 1, 1]
we've compared all child above but the both ends
so we should check the both ends
"""
if nums[r] == target:
return r
elif nums[l] == target:
return l
else:
return -1
================================================
FILE: lintcode/459_closest_number_in_sorted_array.py
================================================
class Solution:
"""
@param: A: an integer array sorted in ascending order
@param: target: An integer
@return: an integer
"""
def closestNumber(self, A, target):
if not A or not target:
return -1
l, m, r = 0, 0, len(A) - 1
while l + 1 < r:
m = l + (r - l) // 2
if A[m] == target:
return m
elif A[m] > target:
r = m
else:
l = m
if A[r] - target > target - A[l]:
return l
else:
return r
================================================
FILE: lintcode/45_maximum_subarray_difference.py
================================================
class Solution:
"""
@param: A: A list of integers
@return: An integer indicate the value of maximum difference between two substrings
"""
def maxDiffSubArrays(self, A):
if not A:
return 0
n = len(A)
B = [-1 * num for num in A]
Lmin = self.get_sum(B, range(n), factor=-1)
Lmax = self.get_sum(A, range(n), factor=1)
Rmin = self.get_sum(B, range(n - 1, -1, -1), factor=-1)
Rmax = self.get_sum(A, range(n - 1, -1, -1), factor=1)
ans = float('-inf')
for i in range(n - 1):
ans = max(
ans,
Lmax[i] - Rmin[i + 1],
Rmax[i + 1] - Lmin[i]
)
return ans
def get_sum(self, A, scope, factor):
"""
factor == 1: max sum
factor == -1: min sum
"""
M = [0] * len(A)
Smax = float('-inf')
S = Smin = 0
for i in scope:
S += A[i]
if S - Smin > Smax:
Smax = S - Smin
if S < Smin:
Smin = S
M[i] = Smax * factor
return M
================================================
FILE: lintcode/460_k_closest_numbers_in_sorted_array.py
================================================
class Solution:
"""
@param: A: an integer array
@param: target: An integer
@param: k: An integer
@return: an integer array
"""
def kClosestNumbers(self, A, target, k):
if not A:
return []
n = len(A)
"""
if `b` in `A`:
[a, b, b, b, c]
l r
else:
[a, c, d, e, f]
l r
"""
left, mid, right = 0, 0, n - 1
while left + 1 < right:
mid = left + (right - left) // 2
if A[mid] <= target:
left = mid
else:
right = mid
"""
# handle out of range
if `left` less than `0`
only append `A[right]`
if `right` great than `n - 1`
only append `A[left]`
# handle closest
append first if that `num` is more closer `target`
"""
ans = [0] * k
for i in range(k):
if left < 0:
ans[i] = A[right]
right += 1
elif right >= n:
ans[i] = A[left]
left -= 1
elif A[right] - target < target - A[left]:
ans[i] = A[right]
right += 1
else:
ans[i] = A[left]
left -= 1
return ans
================================================
FILE: lintcode/461_kth_smallest_numbers_in_unsorted_array.py
================================================
class Solution:
"""
@param: K: An integer
@param: A: An integer array
@return: kth smallest element
"""
def kthSmallest(self, K, A):
"""
the index of `K`th child is `K - 1`
"""
return self.quick_select(K - 1, A, 0, len(A) - 1)
def quick_select(self, k, A, start, end):
if start >= end:
return A[end]
left, right = start, end
pivot = A[(start + end) // 2]
while left <= right:
while left <= right and A[left] < pivot:
left += 1
while left <= right and A[right] > pivot:
right -= 1
if left <= right:
A[left], A[right] = A[right], A[left]
left += 1
right -= 1
if start <= k <= right:
return self.quick_select(k, A, start, right)
elif left <= k <= end:
return self.quick_select(k, A, left, end)
else:
return A[k]
================================================
FILE: lintcode/462_total_occurrence_of_target.py
================================================
class Solution:
def totalOccurrence(self, A, target):
"""
:type A: List[int]
:type target: int
:rtype: int
given A == [a, b, b, b, c]
s e
using binary searching to find `s` and `e`
ans is `e - s + 1`
* s: start, e: end, l: left, r: right
[a, b, b, b, c]
r1 l r,s
r2 e,l r
"""
if not A:
return 0
n = len(A)
left, mid, right = 0, 0, n - 1
while left + 1 < right:
mid = left + (right - left) // 2
if A[mid] < target:
left = mid
else:
right = mid
start = left if A[left] == target else right
if A[start] != target:
return 0
left, mid, right = 0, 0, n - 1
while left + 1 < right:
mid = left + (right - left) // 2
if A[mid] <= target:
left = mid
else:
right = mid
end = right if A[right] == target else left
return end - start + 1
================================================
FILE: lintcode/465_kth_smallest_sum_in_two_sorted_arrays.py
================================================
import heapq
"""
Assuming: len(A) == len(B) == 3
To find the result(min of A[i]+B[j]), we got a 3x3 matrix:
AB | 0 | 1 | 2 |
0 | a | b | c |
1 | d | e | f |
2 | g | h | i |
1. put the first column (0, j) into heap, these child is the min for each row
2. get the minest one, and move the `x` pointer forward, put the child at (x+1, y)
3. keep find the minest one in current heap, then we can find the kth child
"""
class Solution:
"""
@param: A: an integer arrays sorted in ascending order
@param: B: an integer arrays sorted in ascending order
@param: k: An integer
@return: An integer
"""
def kthSmallestSum(self, A, B, k):
m, n = len(A), len(B)
ans = j = 0
heap = []
for i in range(min(m, k)): heapq.heappush(heap, (A[i] + B[0], i, 0))
while k > 0:
ans = heapq.heappop(heap)
j = ans[2] + 1
if j < n:
heapq.heappush(heap, (A[ans[1]] + B[j], ans[1], j))
k -= 1
return ans[0]
================================================
FILE: lintcode/469_identical_binary_tree.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
this.val = val
this.left, this.right = None, None
"""
class Solution:
"""
@param: A: the root of binary tree A.
@param: B: the root of binary tree B.
@return: true if they are identical, or false.
"""
def isIdentical(self, A, B):
if not A and not B:
return True
if not A or not B:
return False
if A.val != B.val:
return False
return (
self.isIdentical(A.left, B.left) and
self.isIdentical(A.right, B.right)
)
================================================
FILE: lintcode/471_top_k_frequent_words.py
================================================
class Solution:
"""
@param: words: an array of string
@param: k: An integer
@return: an array of string
"""
def topKFrequentWords(self, words, k):
if not words or not k:
return []
F = {}
for word in words:
F[word] = F.get(word, 0) + 1
W = [(freq, word) for word, freq in F.items()]
W.sort(key=lambda item: (-item[0], item[1]))
return [W[i][1] for i in range(k)]
================================================
FILE: lintcode/472_binary_tree_path_sum_iii.py
================================================
"""
Definition of ParentTreeNode:
class ParentTreeNode:
def __init__(self, val):
self.val = val
self.parent, self.left, self.right = None, None, None
"""
class Solution:
"""
@param: root: the root of binary tree
@param: target: An integer
@return: all valid paths
"""
def binaryTreePathSum3(self, root, target):
"""
1. using `dfs` to visit every node in that tree
2. once enter a node, start to find the path based on it
to parent, left child, and right child.
"""
ans = []
self.dfs(root, target, ans)
return ans
def dfs(self, node, target, ans):
if not node:
return
self.find_path(node, node, target, ans, [])
self.dfs(node.left, target, ans)
self.dfs(node.right, target, ans)
def find_path(self, node, start, remaining, ans, path):
path.append(node.val)
remaining -= node.val
if remaining == 0:
ans.append(path[:])
if node.parent and node.parent is not start:
self.find_path(node.parent, node, remaining, ans, path)
if node.left and node.left is not start:
self.find_path(node.left, node, remaining, ans, path)
if node.right and node.right is not start:
self.find_path(node.right, node, remaining, ans, path)
path.pop()
================================================
FILE: lintcode/473_add_and_search_word.py
================================================
class Trie:
def __init__(self):
self.root = {}
def insert(self, string):
if not string:
return
parent = self.root
for char in string:
if char in parent:
parent = parent[char]
else:
parent[char] = {}
parent = parent[char]
parent['_end'] = True
def search(self, string):
if not string:
return False
parent = self.root
for char in string:
if char in parent:
parent = parent[char]
else:
return False
return True
def search_in_regex(self, string):
if not string:
return False
return self._search_in_regex(string, self.root, 0)
def _search_in_regex(self, string, parent, i):
if i == len(string):
return parent.get('_end', False)
result = False
if string[i] == '.':
for child in parent:
if child[0] != '_' and self._search_in_regex(string, parent[child], i + 1):
result = True
elif string[i] in parent:
if self._search_in_regex(string, parent[string[i]], i + 1):
result = True
return result
class WordDictionary:
def __init__(self):
self.trie = Trie()
"""
@param: word: Adds a word into the data structure.
@return: nothing
"""
def addWord(self, word):
if not word:
return
self.trie.insert(word)
"""
@param: word: A word could contain the dot character '.' to represent any one letter.
@return: if the word is in the data structure.
"""
def search(self, word):
if not word:
return False
return self.trie.search_in_regex(word)
# Your WordDictionary object will be instantiated and called as such:
# wordDictionary = WordDictionary()
# wordDictionary.addWord("word")
# wordDictionary.search("pattern")
================================================
FILE: lintcode/474_lowest_common_ancestor_ii.py
================================================
"""
The node has an extra attribute parent which point to the father of itself.
The root's parent is null.
Definition of ParentTreeNode:
class ParentTreeNode:
def __init__(self, val):
self.val = val
self.parent, self.left, self.right = None, None, None
"""
class Solution:
def lowestCommonAncestorII(self, root, a, b):
"""
:type root: ParentTreeNode
:type a: ParentTreeNode
:type b: ParentTreeNode
:rtype: ParentTreeNode
"""
if not root:
return root
nodes = set()
while a:
nodes.add(a)
a = a.parent
while b:
if b in nodes:
return b
b = b.parent
return root
================================================
FILE: lintcode/475_binary_tree_maximum_path_sum_ii.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
def maxPathSum2(self, root):
"""
:type root: TreeNode
:rtype: int
"""
if not root:
return 0
left = self.maxPathSum2(root.left)
right = self.maxPathSum2(root.right)
return root.val + max(0, left, right)
================================================
FILE: lintcode/479_second_max_of_array.py
================================================
class Solution:
"""
@param: A: An integer array
@return: The second max number in the array.
"""
def secondMax(self, A):
if not A:
return
max1 = max2 = float('-inf')
for a in A:
if a > max1:
max2 = max1
max1 = a
continue
if a > max2:
max2 = a
return max2
================================================
FILE: lintcode/480_binary_tree_paths.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
def binaryTreePaths(self, root):
"""
:type root: TreeNode
:rtype: list[str]
"""
ans = []
if not root:
return ans
self.dfs(root, ans, [])
return ans
def dfs(self, node, ans, path):
path.append(str(node.val))
if not node.left and not node.right:
ans.append('->'.join(path))
path.pop()
return
if node.left:
self.dfs(node.left, ans, path)
if node.right:
self.dfs(node.right, ans, path)
path.pop()
================================================
FILE: lintcode/486_merge_k_sorted_arrays.py
================================================
from heapq import heappop, heappush
class Solution:
"""
@param: G: k sorted integer arrays
@return: a sorted array
"""
def mergekSortedArrays(self, G):
ans = []
if not G:
return ans
heap = []
for i in range(len(G)):
if not G[i]:
continue
heappush(heap, (G[i][0], i, 0))
while heap:
num, x, y = heappop(heap)
ans.append(num)
if y + 1 < len(G[x]):
heappush(heap, (G[x][y + 1], x, y + 1))
return ans
================================================
FILE: lintcode/494_implement_stack_by_two_queues.py
================================================
from lintcode import ListNode
class Stack:
def __init__(self):
self.dummy = self.tail = ListNode(-1)
"""
@param: x: An integer
@return: nothing
"""
def push(self, x):
node = ListNode(x)
node.pre = self.tail
self.tail.nxt = node
self.tail = node
"""
@return: nothing
"""
def pop(self):
if self.isEmpty():
return
node = self.tail
self.tail = node.pre
self.tail.nxt = node.pre = None
"""
@return: An integer
"""
def top(self):
if self.isEmpty():
return
return self.tail.val
"""
@return: True if the stack is empty
"""
def isEmpty(self):
return self.dummy is self.tail
================================================
FILE: lintcode/496_toy_factory.py
================================================
"""
Your object will be instantiated and called as such:
ty = ToyFactory()
toy = ty.getToy(type)
toy.talk()
"""
class Toy:
def talk(self):
raise NotImplementedError('This method should have implemented.')
class Dog(Toy):
def talk(self):
print 'Wow'
class Cat(Toy):
def talk(self):
print 'Meow'
class ToyFactory:
# @param {string} shapeType a string
# @return {Toy} Get object of the type
def getToy(self, type):
if type == 'Dog':
return Dog()
if type == 'Cat':
return Cat()
================================================
FILE: lintcode/497_shape_factory.py
================================================
"""
Your object will be instantiated and called as such:
sf = ShapeFactory()
shape = sf.getShape(shapeType)
shape.draw()
"""
class Shape:
def draw(self):
raise NotImplementedError('This method should have implemented.')
class Triangle(Shape):
def draw(self):
print ' /\\'
print ' / \\'
print '/____\\'
class Rectangle(Shape):
def draw(self):
print ' ----'
print '| |'
print ' ----'
class Square(Shape):
def draw(self):
print ' ----'
print '| |'
print '| |'
print ' ----'
class ShapeFactory:
# @param {string} shapeType a string
# @return {Shape} Get object of type Shape
def getShape(self, shapeType):
if shapeType == 'Triangle':
return Triangle()
if shapeType == 'Rectangle':
return Rectangle()
if shapeType == 'Square':
return Square()
================================================
FILE: lintcode/498_parking_lot.py
================================================
VEHICLE_ID = {
'MOTOCYCLE': 'MOTOCYCLE',
'CAR': 'CAR',
'BUS': 'BUS',
}
class Vehicle:
def __init__(self):
self.type = ''
self.costs = 0
self.at_level = None
self.at_spots = None
def unpark(self):
if not self.at_level:
return
for x, y in self.at_spots:
self.at_level.spots[x, y] = None
self.at_level = None
self.at_spots = None
class Motorcycle(Vehicle):
def __init__(self):
self.type = VEHICLE_ID['MOTOCYCLE']
self.costs = 1
class Car(Vehicle):
def __init__(self):
self.type = VEHICLE_ID['CAR']
self.costs = 1
class Bus(Vehicle):
def __init__(self):
self.type = VEHICLE_ID['BUS']
self.costs = 5
class Level:
def __init__(self, id, m, n):
self.id = id
self.m = m
self.n = n
self.spots = {}
def get_range(self, vehicle_type):
quarter = self.n // 4
if vehicle_type == VEHICLE_ID['BUS']:
return range(quarter * 3, self.n)
if vehicle_type == VEHICLE_ID['CAR']:
return range(quarter, self.n)
return range(self.n)
def park_vehicle(self, vehicle):
"""
:type vehicle: Vehicle
:rtype: bool
"""
RANGE = self.get_range(vehicle.type)
for x in range(self.m):
gotcha = 0
for y in RANGE:
if self.spots.get((x, y)):
gotcha = 0
continue
gotcha += 1
if gotcha == vehicle.costs:
spots = []
for i in range(y, y - gotcha, -1):
spots.append((x, i))
self.spots[x, i] = vehicle
vehicle.at_level = self
vehicle.at_spots = spots
return True
return False
class ParkingLot:
def __init__(self, k, m, n):
"""
:type k: int, number of levels
:type m: int, each level has m rows of spots
:type n: int, each row has n spots
"""
self.levels = [Level(i, m, n) for i in range(k)]
def park_vehicle(self, vehicle):
"""
:type vehicle: Vehicle
:rtype: bool
"""
if any(
level.park_vehicle(vehicle)
for level in self.levels
):
return True
return False
def unpark_vehicle(self, vehicle):
"""
:type vehicle: Vehicle
"""
vehicle.unpark()
================================================
FILE: lintcode/499_word_count.py
================================================
class WordCount:
# @param {str} line a text, for example "Bye Bye see you next"
def mapper(self, _, line):
for word in line.split():
yield word, 1
# @param key is from mapper
# @param values is a set of value with the same key
def reducer(self, key, values):
yield key, sum(values)
================================================
FILE: lintcode/4_ugly_number_ii.py
================================================
"""
Main Concept:
1. start from 1 => `ans = [1]`
2. use `i2, i3, i5` to record current used in `ans`
3. if the candidates is not ahead `ans[-1]`, move forward
4. append the minimum candidate to `ans`
"""
class Solution:
def nthUglyNumber(self, n):
"""
:type n: int
:rtype: int
"""
if not n:
return 0
ans = [1]
i2 = i3 = i5 = 0
for _ in range(1, n):
while ans[i2] * 2 <= ans[-1]:
i2 += 1
while ans[i3] * 3 <= ans[-1]:
i3 += 1
while ans[i5] * 5 <= ans[-1]:
i5 += 1
ans.append(min((
ans[i2] * 2,
ans[i3] * 3,
ans[i5] * 5,
)))
return ans[-1]
================================================
FILE: lintcode/500_inverted_index.py
================================================
'''
Definition of Document
class Document:
def __init__(self, id, cotent):
self.id = id
self.content = content
'''
class Solution:
# @param {Document[]} docs a list of documents
# @return {dict(string, int[])} an inverted index
def invertedIndex(self, docs):
if not docs or len(docs) < 1:
return {}
result = {}
for doc in docs:
if not doc.content:
continue
for word in doc.content.split():
if word not in result:
result[word] = []
if doc.id not in result[word]:
result[word].append(doc.id)
return result
================================================
FILE: lintcode/501_mini_twitter.py
================================================
"""
Definition of Tweet:
class Tweet:
@classmethod
def create(cls, user_id, tweet_text):
# This will create a new tweet object,
# and auto fill id
"""
class MiniTwitter:
def __init__(self):
self.timestamp = 0
self.tweets = {}
self.followings = {}
"""
@param: user_id: An integer
@param: tweet_text: a string
@return: a tweet
"""
def postTweet(self, user_id, tweet_text):
if user_id not in self.tweets:
self.tweets[user_id] = []
self.timestamp += 1
self.tweets[user_id].append((
self.timestamp,
Tweet.create(user_id, tweet_text),
))
return self.tweets[user_id][-1][1]
"""
@param: user_id: An integer
@return: a list of 10 new feeds recently and sort by timeline
"""
def getNewsFeed(self, user_id):
res = []
if user_id in self.tweets:
res.extend(self.tweets[user_id][-10:])
if user_id in self.followings:
for follow_id in self.followings[user_id]:
if follow_id in self.tweets:
res.extend(self.tweets[follow_id][-10:])
if not res:
return []
res.sort()
return [tweet for _, tweet in res[-10:]][::-1]
"""
@param: user_id: An integer
@return: a list of 10 new posts recently and sort by timeline
"""
def getTimeline(self, user_id):
if user_id not in self.tweets:
return []
return [tweet for _, tweet in self.tweets[user_id][-10:]][::-1]
"""
@param: from_id: An integer
@param: to_id: An integer
@return: nothing
"""
def follow(self, from_id, to_id):
if from_id not in self.followings:
self.followings[from_id] = set()
if to_id in self.followings[from_id]:
return
self.followings[from_id].add(to_id)
"""
@param: from_id: An integer
@param: to_id: An integer
@return: nothing
"""
def unfollow(self, from_id, to_id):
if from_id not in self.followings:
return
if to_id not in self.followings[from_id]:
return
self.followings[from_id].discard(to_id)
================================================
FILE: lintcode/502_mini_cassandra.py
================================================
"""
Definition of Column:
class Column:
def __init__(self, key, value):
self.key = key
self.value = value
"""
class MiniCassandra:
storage = {}
"""
@param: raw_key: a string
@param: column_key: An integer
@param: column_value: a string
@return: nothing
"""
def insert(self, raw_key, column_key, column_value):
if raw_key not in self.storage:
self.storage[raw_key] = {}
self.storage[raw_key][column_key] = Column(column_key, column_value)
"""
@param: raw_key: a string
@param: column_start: An integer
@param: column_end: An integer
@return: a list of Columns
"""
def query(self, raw_key, column_start, column_end):
if raw_key not in self.storage:
return []
result = [
column
for column_key, column in self.storage[raw_key].items()
if column_start <= column_key <= column_end
]
return sorted(result, key=lambda column: column.key)
================================================
FILE: lintcode/503_anagram_map_reduce.py
================================================
class Anagram:
# @param {str} line a text, for example "Bye Bye see you next"
def mapper(self, _, line):
for word in line.split():
yield ''.join(sorted(word.lower())), word
# @param key is from mapper
# @param values is a set of value with the same key
def reducer(self, key, values):
yield key, values
================================================
FILE: lintcode/504_inverted_index_map_reduce.py
================================================
'''
Definition of Document
class Document:
def __init__(self, id, cotent):
self.id = id
self.content = content
'''
class InvertedIndex:
# @param {Document} value is a document
def mapper(self, _, value):
for word in value.content.split():
yield word, value.id
# @param key is from mapper
# @param values is a set of value with the same key
def reducer(self, key, values):
yield key, sorted(list(set(values)))
================================================
FILE: lintcode/505_web_logger.py
================================================
class WebLogger:
def __init__(self):
dummy = []
dummy[:] = [dummy, dummy, -1]
self.dummy = dummy
self.size = 0
"""
@param: timestamp: An integer
@return: nothing
"""
def hit(self, timestamp):
tail = self.dummy[0]
tail[1] = self.dummy[0] = _n = [None, None, timestamp]
_n[0] = tail
_n[1] = self.dummy
self.size += 1
"""
@param: timestamp: An integer
@return: An integer
"""
def get_hit_count_in_last_5_minutes(self, timestamp):
head = self.dummy[1]
while (head is not self.dummy and
head[2] + 300 <= timestamp):
_, nxt, _ = head
self.dummy[1] = nxt
nxt[0] = self.dummy
head[0] = head[1] = None
head = nxt
self.size -= 1
return self.size
================================================
FILE: lintcode/509_mini_yelp.py
================================================
"""
Test Case:
addRestaurant("Lint Cafe", 12.4999999, 11.599999)
addRestaurant("Code Cafe", 10.4999999, 11.512109)
neighbors(10.5, 11.6, 6.7)
removeRestaurant(1)
addRestaurant("Cafe2", 11.4999999, 11.599999)
addRestaurant("Cafe3", 12.4999999, 11.512109)
neighbors(10.5, 13.6, 8896.7)
neighbors(8.5, 11.6, 6996.7)
addRestaurant("Cafe4", 11.4999999, 11.599999)
addRestaurant("Cafe5", 12.4999999, 78.512109)
removeRestaurant(3)
neighbors(8.5, 70.6, 3200.7)
: if `k` > the maximum error, return all hashcodes
"""
"""
Definition of Location:
class Location:
# @param {double} latitude, longitude
# @param {Location}
@classmethod
def create(cls, latitude, longitude):
# This will create a new location object
Definition of Restaurant:
class Restaurant:
# @param {str} name
# @param {Location} location
# @return {Restaurant}
@classmethod
def create(cls, name, location):
# This will create a new restaurant object,
# and auto fill id
Definition of Helper
class Helper:
# @param {Location} location1, location2
@classmethod
def get_distance(cls, location1, location2):
# return calculate the distance between two location
Definition of GeoHash
class GeoHash:
# @param {Location} location
# @return a string
@classmethom
def encode(cls, location):
# return convert location to a geohash string
# @param {str} hashcode
# @return {Location}
@classmethod
def decode(cls, hashcode):
# return convert a geohash string to location
"""
"""
range query from list by `bisect`
"""
import bisect
from YelpHelper import Location, Restaurant, GeoHash, Helper
class MiniYelp:
ERROR_IN_KM = (
2500, 630, 78,
20, 2.4, 0.61,
0.076, 0.01911, 0.00478,
0.0005971, 0.0001492, 0.0000186
)
restaurants = {}
restr_to_geohash = {}
geohashs = []
# @param {str} name
# @param {Location} location
# @return {int} restaurant's id
def add_restaurant(self, name, location):
restaurant = Restaurant.create(name, location)
hashcode = self.get_restr_hashcode(restaurant)
self.restaurants[hashcode] = restaurant
self.restr_to_geohash[restaurant.id] = hashcode
bisect.insort(self.geohashs, hashcode)
return restaurant.id
# @param {int} restaurant_id
# @return nothing
def remove_restaurant(self, restaurant_id):
hashcode = self.restr_to_geohash[restaurant_id]
index = bisect.bisect_left(self.geohashs, hashcode)
self.geohashs.pop(index)
del self.restaurants[hashcode]
del self.restr_to_geohash[restaurant_id]
# @param {Location} location
# @param {double} k, distance smaller than k miles
# @return {str[]} a list of restaurant's name and sort by
# distance from near to far.
def neighbors(self, location, k):
length = self.get_length(k)
prefix = GeoHash.encode(location)[:length]
# chr(ord('z') + 1) == '{'
left = bisect.bisect_left(self.geohashs, prefix)
right = bisect.bisect(self.geohashs, prefix + '{')
neighbors = []
hashcode = restaurant = distance = None
for i in range(left, right):
hashcode = self.geohashs[i]
restaurant = self.restaurants[hashcode]
distance = Helper.get_distance(location, restaurant.location)
if distance <= k:
neighbors.append((distance, restaurant))
neighbors.sort(key=lambda item: item[0])
return [
restr.name
for _, restr in neighbors
]
def get_length(self, k):
n = len(self.ERROR_IN_KM)
for i in range(n):
if k > self.ERROR_IN_KM[i]:
return i
return n
def get_restr_hashcode(self, restaurant):
return '{0}:{1}'.format(
GeoHash.encode(restaurant.location),
restaurant.id
)
"""
trie
"""
from YelpHelper import Location, Restaurant, GeoHash, Helper
class Trie:
def __init__(self):
self.root = self._new_node()
def __repr__(self):
return repr(self.root)
def put(self, key):
if not key:
return
parent = self.root
parent['keys'].add(key)
for char in key:
if char not in parent['children']:
parent['children'][char] = self._new_node()
parent['children'][char]['keys'].add(key)
parent = parent['children'][char]
def pick(self, key):
if not key:
return
parent = self.root
parent['keys'].discard(key)
for char in key:
if char not in parent['children']:
return
parent = parent['children'][char]
parent['keys'].discard(key)
def get_keys_by_prefix(self, prefix):
parent = self.root
if not prefix:
return list(parent['keys'])
for char in prefix:
if char not in parent['children']:
return []
parent = parent['children'][char]
return list(parent['keys'])
def _new_node(self):
return {
'keys': set(),
'children': {}
}
class MiniYelp:
ERROR_IN_KM = (
2500, 630, 78,
20, 2.4, 0.61,
0.076, 0.01911, 0.00478,
0.0005971, 0.0001492, 0.0000186
)
trie = Trie()
restaurants = {}
restr_to_geohash = {}
# @param {str} name
# @param {Location} location
# @return {int} restaurant's id
def add_restaurant(self, name, location):
restaurant = Restaurant.create(name, location)
hashcode = self.get_restr_hashcode(restaurant)
self.restaurants[hashcode] = restaurant
self.restr_to_geohash[restaurant.id] = hashcode
self.trie.put(hashcode)
return restaurant.id
# @param {int} restaurant_id
# @return nothing
def remove_restaurant(self, restaurant_id):
hashcode = self.restr_to_geohash[restaurant_id]
del self.restaurants[hashcode]
del self.restr_to_geohash[restaurant_id]
self.trie.pick(hashcode)
# @param {Location} location
# @param {double} k, distance smaller than k miles
# @return {str[]} a list of restaurant's name and sort by
# distance from near to far.
def neighbors(self, location, k):
length = self.get_length(k)
prefix = GeoHash.encode(location)[:length]
hashcodes = self.trie.get_keys_by_prefix(prefix)
neighbors = []
restaurant = distance = None
for hashcode in hashcodes:
restaurant = self.restaurants[hashcode]
distance = Helper.get_distance(location, restaurant.location)
if distance <= k:
neighbors.append((distance, restaurant))
neighbors.sort(key=lambda item: item[0])
return [
restr.name
for _, restr in neighbors
]
def get_length(self, k):
n = len(self.ERROR_IN_KM)
for i in range(n):
if k > self.ERROR_IN_KM[i]:
return i
return n
def get_restr_hashcode(self, restaurant):
return '{0}:{1}'.format(
GeoHash.encode(restaurant.location),
restaurant.id
)
================================================
FILE: lintcode/510_maximal_rectangle.py
================================================
class Solution:
def maximalRectangle(self, G):
"""
:type G: List[List[str]]
:rtype: int
"""
ans = 0
if not G or not G[0]:
return ans
m, n = len(G), len(G[0])
L, R, H = {}, {}, {}
for i in range(m):
curr = 0 # left boundary
for j in range(n):
if G[i][j] == '1':
H[j] = H.get(j, 0) + 1
L[j] = max(L.get(j, 0), curr)
else:
H[j] = L[j] = 0
curr = j + 1
curr = n # right boundary
for j in range(n - 1, -1, -1):
if G[i][j] == '1':
R[j] = min(R.get(j, n), curr)
else:
R[j] = n
curr = j
ans = max(
ans,
H[j] * (R[j] - L[j])
)
return ans
"""
Assuming matrix: [
[1, 1, 0, 0, 1],
[0, 1, 0, 0, 1],
[0, 0, 1, 1, 1],
[0, 0, 1, 1, 1],
[0, 0, 0, 0, 1]
]
Concept:
For each (i, j), 0 <= i < len(mat), 0 <= j < len(mat[0])
the rectangle area is (r[j] - l[j]) * h[j]
m = 0
v 1 1 0 0 1
h 1 1 0 0 1
l 0 0 0 0 4
r 2 2 5 5 5
m = 1
v 0 1 0 0 1
h 0 2 0 0 2
l 0 1 0 0 4
r 5 2 5 5 5
m = 2
v 0 0 1 1 1
h 0 0 1 1 3
l 0 0 2 2 4
r 5 5 5 5 5
m = 3
v 0 0 1 1 1
h 0 0 2 2 4
l 0 0 2 2 4
r 5 5 5 5 5
m = 4
v 0 0 0 0 1
h 0 0 0 0 5
l 0 0 0 0 4
r 5 5 5 5 5
max = 6 = (5 - 2) * 2
"""
# Mono Stack
# This problem could be treated as histogram, see lintcode#122
class Solution:
def maximalRectangle(self, G):
"""
:type G: List[List[str]]
:rtype: int
"""
ans = 0
if not G or not G[0]:
return ans
m, n = len(G), len(G[0])
H = [0] * n
for i in range(m):
for j in range(n):
if G[i][j] == '1':
H[j] += 1
else:
H[j] = 0
ans = max(ans, self.largestRectangleArea(H))
# To remove the trick `0`
H.pop()
return ans
def largestRectangleArea(self, H):
area = 0
if not H:
return area
# To ensure the last element in monostack will be handled
H.append(0)
I = []
left = height = 0
for right in range(len(H)):
while I and H[I[-1]] >= H[right]:
height = H[I.pop()]
left = I[-1] if I else -1
area = max(
area,
height * (right - left - 1)
)
I.append(right)
return area
================================================
FILE: lintcode/512_decode_ways.py
================================================
class Solution:
"""
@param: s: a string, encoded message
@return: an integer, the number of ways decoding
"""
def numDecodings(self, s):
if not s or s == '0':
return 0
n = len(s)
"""
`dp[i]` means the ways to decode
`dp[0]` => ''
`dp[1]` => should check if the code is 0
"""
dp = [0] * (n + 1)
dp[0] = 1
dp[1] = 0 if s[0] == '0' else 1
for i in range(2, n + 1):
if s[i - 1] != '0':
dp[i] += dp[i - 1]
if 10 <= int(s[i - 2:i]) <= 26:
dp[i] += dp[i - 2]
return dp[n]
================================================
FILE: lintcode/513_perfect_squares.py
================================================
"""
Test Case:
1
"""
class Solution:
"""
@param: n: a positive integer
@return: An integer
"""
def numSquares(self, n):
if n <= 0:
return 0
INFINITY = float('inf')
# `dp[i]` means the least number of perfect square numbers of `i`
dp = [INFINITY] * (n + 1)
dp[0] = 0
dp[1] = 1
for i in range(1, n + 1):
j = 1
while j * j <= i:
dp[i] = min(dp[i], dp[i - j * j] + 1)
j += 1
return dp[n]
================================================
FILE: lintcode/515_paint_house.py
================================================
class Solution:
"""
@param: costs: n x 3 cost matrix
@return: An integer, the minimum cost to paint all houses
"""
def minCost(self, costs):
if not costs:
return 0
INFINITY = float('inf')
m, n = len(costs), len(costs[0])
dp = [[0] * n for _ in range(2)]
"""
i: `i`th house
j: `j`th color
k: the used `k`th color in previous house
"""
i = j = k = prev = curr = 0
for j in range(n):
dp[0][j] = costs[0][j]
for i in range(1, m):
prev = curr # (i - 1) % 2
curr = i % 2
for j in range(n):
dp[curr][j] = INFINITY
for k in range(n):
if k != j and dp[prev][k] + costs[i][j] < dp[curr][j]:
dp[curr][j] = dp[prev][k] + costs[i][j]
"""
curr == (m - 1) % 2
"""
return min(dp[curr])
================================================
FILE: lintcode/516_paint_house_ii.py
================================================
"""
in `./lintcode/515_paint_house.py`
we repeatedly iterate `dp[prev]` to find the minimum except the same color
but in doing so, the time complexity is quite high `O(n * k * k)`,
if the number of colors increases
so in this problem, we need find some way to fix it
for `dp[curr][j] = dp[prev][k] + C[i][j]`, `C[i][j]` is constant
so what we want is to find the minimum except the same color
=> if `dp[prev][j]` is the minimum, since we can't use the same color
in adjacency houses, so we use the second minimum
=> if not, we use the minimum
and the time complexity becomes `O(n * 2k)` => `O(nk)`
"""
class Solution:
"""
@param: C: n x k cost matrix
@return: an integer, the minimum cost to paint all houses
"""
def minCostII(self, C):
if not C or not C[0]:
return 0
INFINITY = float('inf')
n, k = len(C), len(C[0])
dp = [[0] * k for _ in range(n)]
prev = curr = 0
for j in range(k):
dp[curr][j] = C[0][j]
for i in range(1, n):
prev = curr
curr = 1 - curr
min1 = min2 = INFINITY
"""
to find the minimum and second minimum in previous iteration
"""
for j in range(k):
if dp[prev][j] < min1:
min2 = min1
min1 = dp[prev][j]
continue
if dp[prev][j] < min2:
min2 = dp[prev][j]
"""
if the `j`th color has been used, that is,
`dp[prev][j]` was the minimum, in previous iteration
and then we need to take the color
with the second minimum in `dp[prev]`
"""
for j in range(k):
dp[curr][j] = C[i][j]
if dp[prev][j] == min1:
dp[curr][j] += min2
else:
dp[curr][j] += min1
return min(dp[curr])
================================================
FILE: lintcode/517_ugly_number.py
================================================
class Solution:
def isUgly(self, num):
"""
:type num: int
:rtype: bool
"""
if not num:
return False
if num == 1:
return True
for factor in (
125, 27, 8,
5, 3, 2,
):
while num % factor == 0:
num //= factor
if num == 1:
return True
return num == 1
================================================
FILE: lintcode/518_super_ugly_number.py
================================================
class Solution:
def nthSuperUglyNumber(self, n, primes):
"""
:type n: int
:type primes: list[int]
:rtype: int
"""
if not n or n <= 1 or not primes:
return 1
k = len(primes)
# i -> same as `i` in `primes`, v -> track of how far `primes[i]` stay in `uglys`
steps = [0] * k
uglys = [0] * n
uglys[0] = 1
for i in range(1, n):
ugly = float('inf')
for j in range(k):
ugly = min(ugly, uglys[steps[j]] * primes[j])
uglys[i] = ugly
for j in range(k):
if uglys[steps[j]] * primes[j] == ugly:
steps[j] += 1
return uglys[n - 1]
================================================
FILE: lintcode/519_consistent_hashing.py
================================================
class Solution:
"""
@param: n: a positive integer
@return: n x 3 matrix
"""
def consistentHashing(self, n):
res = [[0, 359, 1]]
if not isinstance(n, int) \
or n < 2:
return res
ti = 0 # ti: target_index for the **upcoming** machine in results
# mi: machine_index for the **upcoming** machines in results
# for n is 5: got [1, 2, 3, 4]
for mi in range(1, n):
ti = 0
# emi: existing_machine_index for the **existing** machines in results
# for n is 5 and will add last machine: got [0, 1, 2, 3]
for emi in range(mi):
# Before adding each machine, check the current maximum partition
if res[emi][1] - res[emi][0] > res[ti][1] - res[ti][0]:
ti = emi
x, y = res[ti][0], res[ti][1]
res[ti][1] = (x + y) / 2
res.append([(x + y) / 2 + 1, y, mi + 1])
return sorted(res, key=lambda item: item[0])
================================================
FILE: lintcode/51_previous_permutation.py
================================================
"""
Main Concept:
1. from end to start, finding first increasing element,
which is `nums[i]`
2. from end to start, finding first element just smaller than `nums[i]`,
which is `nums[j]`.
and swap `nums[i]` and `nums[j]`.
3. reverse the elements between `nums[i + 1]` and `nums[n - 1]`
REF: [Next Permutation](https://leetcode.com/articles/next-permutation/)
"""
class Solution:
def previousPermuation(self, nums):
"""
:type nums: list[int]
:rtype: list[int]
"""
if not nums or len(nums) < 2:
return nums
n = len(nums)
i = n - 2
while i >= 0 and nums[i] <= nums[i + 1]:
i -= 1
if i >= 0:
j = n - 1
while i < j and nums[i] <= nums[j]:
j -= 1
nums[i], nums[j] = nums[j], nums[i]
i = i + 1
j = n - 1
while i < j:
nums[i], nums[j] = nums[j], nums[i]
i += 1
j -= 1
return nums
================================================
FILE: lintcode/520_consistent_hashing_ii.py
================================================
import bisect
import random
class Solution:
"""
@param {int} n a positive integer
@param {int} k a positive integer
@return {Solution} a Solution object
"""
@classmethod
def create(cls, n, k):
solution = cls()
solution.n = n
solution.k = k
solution.p2l = {} # point to location
solution.l2p = {} # location to points
return solution
"""
@param: machine_id: An integer
@return: a list of shard ids
"""
def addMachine(self, machine_id):
item = self.l2p[machine_id] = []
point = -1
for i in range(self.k):
point = random.randint(0, self.n - 1)
while point in self.p2l:
point = random.randint(0, self.n - 1)
self.p2l[point] = machine_id
item.append(point)
item.sort()
return item
"""
@param: hashcode: An integer
@return: A machine id
"""
def getMachineIdByHashCode(self, hashcode):
points = sorted(self.p2l.keys())
index = bisect.bisect_left(points, hashcode) % len(points)
# # counterclockwise
# index = bisect.bisect(points, hashcode) - 1
# if index < 0:
# index = len(points) - 1
return self.p2l[points[index]]
================================================
FILE: lintcode/521_remove_duplicate_numbers_in_array.py
================================================
"""
time: O(n)
space: O(n)
"""
class Solution:
"""
@param: nums: an array of integers
@return: the number of unique integers
"""
def deduplication(self, nums):
ans = 0
if not nums:
return ans
exists = set()
for i in range(len(nums)):
if nums[i] not in exists:
exists.add(nums[i])
ans += 1
return ans
"""
time: O(nlogn)
space: O(1)
"""
class Solution:
"""
@param: nums: an array of integers
@return: the number of unique integers
"""
def deduplication(self, nums):
ans = 0
if not nums:
return ans
nums.sort()
# for `nums[0]`
ans = 1
for i in range(1, len(nums)):
if nums[i - 1] != nums[i]:
nums[ans] = nums[i]
ans += 1
return ans
================================================
FILE: lintcode/522_tiny_url_ii.py
================================================
import random
class TinyUrl2:
def __init__(self):
self.chars = [str(i) for i in range(10)]
self.chars.extend(chr(i) for i in range(ord('a'), ord('z') + 1))
self.chars.extend(chr(i) for i in range(ord('A'), ord('Z') + 1))
self.host = 'http://tiny.url/'
self.size = 6
self.lg2st = {}
self.st2lg = {}
self.custom_lg2st = {}
self.custom_st2lg = {}
def createCustom(self, url, key):
"""
:type url: str
:type key: str
:rtype: str
"""
if not url or not key:
return 'error'
if (
url in self.custom_lg2st and
key in self.custom_st2lg
):
return self.get_tiny_url(key)
if (
url not in self.custom_lg2st and
key not in self.custom_st2lg
):
self.custom_lg2st[url] = key
self.custom_st2lg[key] = url
return self.get_tiny_url(key)
return 'error'
def longToShort(self, url):
"""
:type url: str
:rtype: str
"""
if not url:
return 'error'
if url in self.lg2st:
return self.get_tiny_url(self.lg2st[url])
if url in self.custom_lg2st:
return self.get_tiny_url(self.custom_lg2st[url])
key = self.get_hash_key(self.size)
while key in self.st2lg:
key = self.get_hash_key(self.size)
self.lg2st[url] = key
self.st2lg[key] = url
return self.get_tiny_url(key)
def shortToLong(self, url):
"""
:type url: str
:rtype: str
"""
if not url:
return 'error'
key = url.replace(self.host, '')
if key in self.st2lg:
return self.st2lg[key]
if key in self.custom_st2lg:
return self.custom_st2lg[key]
return 'error'
def get_tiny_url(self, hash_key):
return '{}{}'.format(self.host, hash_key)
def get_hash_key(self, size):
return ''.join(
random.choice(self.chars)
for _ in range(size)
)
================================================
FILE: lintcode/523_url_parser.py
================================================
import re
class HtmlParser:
"""
@param: content: content source code
@return: a list of links
"""
def parseUrls(self, content):
# r'': use raw string
# [=\s]+: this block should contain `=` or blank, and at least one char here
# ["\']: this block should contain `"` or `'`
# [^"\'>\s]*: this block should contain any chars until `"`, `'`, `>`, or blank appears
links = re.findall(r'href[=\s]+["\']([^"\'>\s]*)', content, re.I)
return [link for link in links if link and not link.startswith('#')]
================================================
FILE: lintcode/525_mini_uber.py
================================================
"""
Definition of Trip:
class Trip:
self.id; # trip's id, primary key
self.driver_id, self.rider_id; # foreign key
self.lat, self.lng; # pick up location
def __init__(self, rider_id, lat, lng):
Definition of Helper
class Helper:
@classmethod
def get_distance(cls, lat1, lng1, lat2, lng2):
# return calculate the distance between (lat1, lng1) and (lat2, lng2)
"""
from Trip import Trip, Helper
class MiniUber:
driver_to_locs = {}
driver_to_trip = {}
INFINITY = float('inf')
# @param {int} driver_id an integer
# @param {double} lat, lng driver's location
# return {trip} matched trip information
# if there have matched rider or null
def report(self, driver_id, lat, lng):
if not driver_id:
return
if driver_id in self.driver_to_trip:
return self.driver_to_trip[driver_id]
if driver_id in self.driver_to_locs:
self.driver_to_locs[driver_id]['lat'] = lat
self.driver_to_locs[driver_id]['lng'] = lng
else:
self.driver_to_locs[driver_id] = self._new_location(lat, lng)
# @param rider_id an integer
# @param lat, lng rider's location
# return a trip
def request(self, rider_id, lat, lng):
if not rider_id:
return
trip = Trip(rider_id, lat, lng)
_distance = distance = self.INFINITY
driver_id = -1
for _driver_id, _loc in self.driver_to_locs.items():
_distance = Helper.get_distance(_loc['lat'], _loc['lng'], lat, lng)
if _distance < distance:
driver_id = _driver_id
distance = _distance
if driver_id == -1:
return trip
trip.driver_id = driver_id
self.driver_to_trip[driver_id] = trip
del self.driver_to_locs[driver_id]
return trip
def _new_location(self, lat, lng):
return {
'lat': lat,
'lng': lng
}
================================================
FILE: lintcode/526_load_balancer.py
================================================
import random
class LoadBalancer:
def __init__(self):
self.servers = []
self.svr2idx = {}
"""
@param: server_id: add a new server to the cluster
@return: nothing
"""
def add(self, server_id):
self.servers.append(server_id)
self.svr2idx[server_id] = len(self.servers) - 1
"""
@param: server_id: server_id remove a bad server from the cluster
@return: nothing
"""
def remove(self, server_id):
svrs = self.servers
i = self.svr2idx[server_id]
key = svrs[-1]
# swap `svrs[-1]` and `svrs[i]`
self.svr2idx[key] = i
svrs[i] = svrs[-1]
svrs.pop()
del self.svr2idx[server_id]
"""
@return: pick a server in the cluster randomly with equal probability
"""
def pick(self):
i = random.randrange(len(self.servers))
return self.servers[i]
================================================
FILE: lintcode/527_trie_serialization.py
================================================
"""
Definition of TrieNode:
class TrieNode:
def __init__(self):
# :
self.children = collections.OrderedDict()
"""
class Solution:
'''
@param root: An object of TrieNode, denote the root of the trie.
This method will be invoked first, you should design your own algorithm
to serialize a trie which denote by a root node to a string which
can be easily deserialized by your own "deserialize" method later.
'''
'''
For `>c<>d>>>`, the visual graph of the return data just like this:
<
a<
b<
e<>
>
c<>
d<
f<>
>
>
>
'''
def serialize(self, root):
if not root:
return ''
data = ''
for key, node in root.children.items():
data += key + self.serialize(node)
return '<%s>' % data
'''
@param data: A string serialized by your serialize method.
This method will be invoked second, the argument data is what exactly
you serialized at method "serialize", that means the data is not given by
system, it's given by your own serialize method. So the format of data is
designed by yourself, and deserialize it here as you serialize it in
"serialize" method.
'''
def deserialize(self, data):
if not data \
or data[0] != '<' \
or data[-1] != '>' \
or len(data) < 1:
return
root = TrieNode()
current = root
queue = []
for char in data:
if char == '<':
queue.append(current)
elif char == '>':
queue.pop()
else:
current = TrieNode()
queue[-1].children[char] = current
return root
================================================
FILE: lintcode/528_flatten_nested_list_iterator.py
================================================
"""
Your NestedIterator object will be instantiated and called as such:
i, v = NestedIterator(nestedList), []
while i.hasNext(): v.append(i.next())
This is the interface that allows for creating nested lists.
You should not implement it, or speculate about its implementation
class NestedInteger(object):
def isInteger(self):
# @return {boolean} True if this NestedInteger holds a single integer,
# rather than a nested list.
def getInteger(self):
# @return {int} the single integer that this NestedInteger holds,
# if it holds a single integer
# Return None if this NestedInteger holds a nested list
def getList(self):
# @return {NestedInteger[]} the nested list that this NestedInteger holds,
# if it holds a nested list
# Return None if this NestedInteger holds a single integer
"""
class NestedIterator(object):
def __init__(self, nestedList):
self.stack = [[nestedList, 0]]
# @return {int} the next element in the iteration
def next(self):
if not self.hasNext():
return -1
lst, i = self.stack[-1]
self.stack[-1][1] += 1
return lst[i].getInteger()
# @return {boolean} true if the iteration has more element or false
def hasNext(self):
stack = self.stack
while stack:
lst, i = stack[-1]
if i >= len(lst):
stack.pop()
elif lst[i].isInteger():
return True
else:
# lst[i] is list
stack[-1][1] += 1
stack.append([lst[i].getList(), 0])
return False
================================================
FILE: lintcode/529_geohash.py
================================================
"""
main concept is in `../module/geohash.py`
"""
class GeoHash:
base32 = []
"""
@param: latitude: one of a location coordinate pair
@param: longitude: one of a location coordinate pair
@param: precision: an integer between 1 to 12
@return: a base32 string
"""
def encode(self, latitude, longitude, precision=5):
if not self.base32:
self.base32 = self.get_base32_list()
times = (precision * 5) // 2 + 1
lat_codes = self._loc_to_bins( latitude, times, -90, 90)
lng_codes = self._loc_to_bins(longitude, times, -180, 180)
bin_codes = []
for i in range(times):
bin_codes.extend((str(lng_codes[i]), str(lat_codes[i])))
hash_codes = []
hash_code = ''
for i in range(0, len(bin_codes), 5):
hash_code = int(''.join(bin_codes[i : i + 5]), 2)
hash_codes.append(self.base32[hash_code])
return ''.join(hash_codes[:precision])
def _loc_to_bins(self, location, times, left, right):
mid = 0
bins = []
for i in range(times):
mid = left + (right - left) / 2.0
if location > mid:
left = mid
bins.append(1)
else:
right = mid
bins.append(0)
return bins
def get_base32_list(self):
base32_list = [str(i) for i in range(10)]
ignored_char = (ord('a'), ord('i'), ord('l'), ord('o'))
for i in range(ord('a'), ord('z') + 1):
if i in ignored_char:
continue
base32_list.append(chr(i))
return base32_list
================================================
FILE: lintcode/52_next_permutation.py
================================================
"""
Main Concept:
1. from end to start, finding first decreasing element,
which is `nums[i]`
2. from end to start, finding first element just larger than `nums[i]`,
which is `nums[j]`.
and swap `nums[i]` and `nums[j]`.
3. reverse the elements between `nums[i + 1]` and `nums[n - 1]`
REF: [Next Permutation](https://leetcode.com/articles/next-permutation/)
"""
class Solution:
def nextPermutation(self, nums):
"""
:type nums: list[int]
:rtype: list[int]
"""
if not nums or len(nums) < 2:
return nums
n = len(nums)
i = n - 2
while i >= 0 and nums[i] >= nums[i + 1]:
i -= 1
if i >= 0:
j = n - 1
while i < j and nums[i] >= nums[j]:
j -= 1
nums[i], nums[j] = nums[j], nums[i]
i = i + 1
j = n - 1
while i < j:
nums[i], nums[j] = nums[j], nums[i]
i += 1
j -= 1
return nums
================================================
FILE: lintcode/530_geohash_ii.py
================================================
"""
main concept is in `../module/geohash.py`
"""
class GeoHash:
base32 = []
"""
@param: geohash: geohash a base32 string
@return: latitude and longitude a location coordinate pair
"""
def decode(self, geohash):
if not geohash:
return []
if not self.base32:
self.base32 = self.get_base32_list()
bin_codes = []
for char in geohash:
if char not in self.base32:
return []
bin_codes.extend(self._oct_to_bins(self.base32.index(char)))
n = len(bin_codes)
lat_codes = [bin_codes[i] for i in range(1, n, 2)]
lng_codes = [bin_codes[i] for i in range(0, n, 2)]
return [
self._bins_to_loc(lat_codes, -90, 90),
self._bins_to_loc(lng_codes, -180, 180)
]
def _bins_to_loc(self, bins, left, right):
mid = 0
for code in bins:
mid = left + (right - left) / 2.0
if code:
left = mid
else:
right = mid
return left + (right - left) / 2.0
def _oct_to_bins(self, val_in_oct):
bins = []
for i in range(5):
if val_in_oct % 2:
bins.append(1)
else:
bins.append(0)
val_in_oct = val_in_oct >> 1
return reversed(bins)
def get_base32_list(self):
base32_list = [str(i) for i in range(10)]
ignored_char = (ord('a'), ord('i'), ord('l'), ord('o'))
for i in range(ord('a'), ord('z') + 1):
if i in ignored_char:
continue
base32_list.append(chr(i))
return base32_list
================================================
FILE: lintcode/531_six_degrees.py
================================================
"""
Definition for Undirected graph node
class UndirectedGraphNode:
def __init__(self, x):
self.label = x
self.neighbors = []
"""
class Solution:
"""
@param: graph: a list of Undirected graph node
@param: s: Undirected graph node
@param: t: Undirected graph nodes
@return: an integer
"""
def sixDegrees(self, graph, s, t):
if not graph or not s or not t:
return -1
if s is t:
return 0
degree = {s: 0}
queue = [s]
for node in queue:
for _node in node.neighbors:
if _node in degree:
continue
degree[_node] = degree[node] + 1
if _node is t:
return degree[_node]
queue.append(_node)
return -1
================================================
FILE: lintcode/532_reverse_pairs.py
================================================
class Solution:
"""
@param: A: an array
@return: total of reverse pairs
"""
def reversePairs(self, A):
n = len(A)
tmp = [0] * n
return self.merge_sort(A, 0, n - 1, tmp)
def merge_sort(self, A, start, end, tmp):
if start >= end:
return 0
mid = (start + end) // 2
left, right = start, mid + 1
ans = self.merge_sort(A, left, mid, tmp)
ans += self.merge_sort(A, right, end, tmp)
i = start
while left <= mid and right <= end:
if A[left] > A[right]:
tmp[i] = A[right]
right += 1
ans += mid - left + 1
else:
tmp[i] = A[left]
left += 1
i += 1
while left <= mid:
tmp[i] = A[left]
left += 1
i += 1
while right <= end:
tmp[i] = A[right]
right += 1
i += 1
for i in range(start, end + 1):
A[i] = tmp[i]
return ans
================================================
FILE: lintcode/533_two_sum_closest_to_target.py
================================================
class Solution:
"""
@param: nums: an integer array
@param: target: An integer
@return: the difference between the sum and the target
"""
def twoSumClosest(self, nums, target):
if not nums or len(nums) < 2:
return -1
nums.sort()
left, right = 0, len(nums) - 1
_sum = 0
diff = float('inf')
while left < right:
_sum = nums[left] + nums[right]
if _sum < target:
diff = min(diff, target - _sum)
left += 1
else:
diff = min(diff, _sum - target)
right -= 1
return diff
================================================
FILE: lintcode/534_house_robber_ii.py
================================================
"""
Main Concept:
if we insist on not stealing one of the houses
=> the remaining houses become a sequence
=> the problem becomes `./lintcode/392_house_robber.py`
we can pick any pair of adjacent houses,
and to make the calculation easier,
we pick the first and last houses
1. insist on not stealing the first house
that is, the range becomes `[1, n - 1]`
2. insist on not stealing the last house
that is, the range becomes `[0, n - 2]`
and choose the maximum amount as the answer
Test Case:
[3,6,4]
[1,3,2,1,5]
"""
class Solution:
"""
@param: A: An array of non-negative integers.
@return: The maximum amount of money you can rob tonight
"""
def houseRobber2(self, A):
if not A:
return 0
if len(A) == 1:
return A[0]
dp = [[0] * 2 for _ in range(2)]
return max(
self.houseRobber(A, 0, dp),
self.houseRobber(A, 1, dp)
)
def houseRobber(self, A, start, dp):
n = len(A)
prev, curr = 0, start % 2
dp[curr][0] = 0
dp[curr][1] = A[start]
for i in range(1 + start, n - 1 + start):
prev = curr
curr = i % 2
dp[curr][0] = max(dp[prev])
dp[curr][1] = dp[prev][0] + A[i]
return max(dp[curr])
class Solution:
"""
@param: A: An array of non-negative integers.
@return: The maximum amount of money you can rob tonight
"""
def houseRobber2(self, A):
if not A:
return 0
n = len(A)
if n == 1:
return A[0]
if n == 2:
return max(A[0], A[1])
dp = [0] * 3
return max(
# range(0, n - 1)
self.houseRobber(A, 0, dp),
# range(1, n)
self.houseRobber(A, 1, dp)
)
def houseRobber(self, A, start, dp):
n = len(A)
prev2, prev1, curr = 0, start % 3, (start + 1) % 3
dp[prev1] = A[start]
dp[curr] = max(A[start], A[start + 1])
for i in range(2 + start, n - 1 + start):
prev2, prev1 = prev1, curr
curr = i % 3
dp[curr] = max(dp[prev1], dp[prev2] + A[i])
return dp[curr]
class Solution:
def houseRobber2(self, A):
"""
:type A: List[int]
:rtype: int
"""
if not A:
return 0
n = len(A)
if n < 2:
return A[0]
return max(
self.rob_in_line(A, 0, n - 2),
self.rob_in_line(A, 1, n - 1)
)
def rob_in_line(self, A, start, end):
n = end - start + 1
dp = [0] * (n + 1)
dp[0] = 0
dp[1] = A[start]
for i in range(2, n + 1):
dp[i] = max(
dp[i - 2] + A[start + i - 1],
dp[i - 1]
)
return dp[n]
================================================
FILE: lintcode/538_memcache.py
================================================
class Memcache:
INT_MAX = 0x7FFFFFFF
PERMANENT_TTL = -1
storage = {}
"""
@param: curtTime: An integer
@param: key: An integer
@return: An integer
"""
def get(self, curtTime, key):
if key not in self.storage:
return self.INT_MAX
if (curtTime < self.storage[key]['expired_at'] or
self.storage[key]['expired_at'] == self.PERMANENT_TTL):
return self.storage[key]['val']
return self.INT_MAX
"""
@param: curtTime: An integer
@param: key: An integer
@param: value: An integer
@param: ttl: An integer
@return: nothing
"""
def set(self, curtTime, key, value, ttl):
if ttl > 0:
self.storage[key] = self._new_item(key, value, curtTime + ttl)
else:
self.storage[key] = self._new_item(key, value, self.PERMANENT_TTL)
"""
@param: curtTime: An integer
@param: key: An integer
@return: nothing
"""
def delete(self, curtTime, key):
if key in self.storage:
del self.storage[key]
"""
@param: curtTime: An integer
@param: key: An integer
@param: delta: An integer
@return: An integer
"""
def incr(self, curtTime, key, delta):
if key not in self.storage:
return self.INT_MAX
if (curtTime < self.storage[key]['expired_at'] or
self.storage[key]['expired_at'] == self.PERMANENT_TTL):
self.storage[key]['val'] += delta
return self.storage[key]['val']
return self.INT_MAX
"""
@param: curtTime: An integer
@param: key: An integer
@param: delta: An integer
@return: An integer
"""
def decr(self, curtTime, key, delta):
return self.incr(curtTime, key, -1 * delta)
def _new_item(self, key, value, expired_at):
return {
'key': key,
'val': value,
'expired_at': expired_at
}
================================================
FILE: lintcode/539_move_zeroes.py
================================================
class Solution:
def moveZeroes(self, nums):
"""
:type nums: List[int]
:rtype: void Do not return anything, modify nums in-place instead.
"""
if not nums:
return
n = len(nums)
left = 0
for right in range(n):
if nums[right] != 0:
nums[left], nums[right] = nums[right], nums[left]
left += 1
================================================
FILE: lintcode/53_reverse_words_in_a_string.py
================================================
class Solution:
"""
@param: s: A string
@return: A string
"""
def reverseWords(self, s):
s = s.strip()
if not s:
return ''
return ' '.join(reversed(s.split()))
================================================
FILE: lintcode/540_zigzag_iterator.py
================================================
"""
Your ZigzagIterator object will be instantiated and called as such:
solution, result = ZigzagIterator(v1, v2), []
while solution.hasNext(): result.append(solution.next())
Output result
"""
class ZigzagIterator:
"""
@param: v1: A 1d vector
@param: v2: A 1d vector
"""
def __init__(self, v1, v2):
self.g = (v1, v2)
self.x = 0
self.y = 0
self.max_y = max(len(vec) for vec in self.g)
"""
@return: An integer
"""
def next(self):
if not self.hasNext():
return -1
x = self.x
y = self.y
self.x += 1
return self.g[x][y]
"""
@return: True if has next
"""
def hasNext(self):
while self.y < self.max_y:
if (
self.x < len(self.g) and
self.y < len(self.g[self.x])
):
return True
if self.x >= len(self.g):
self.x = 0
self.y += 1
if self.y >= len(self.g[self.x]):
self.x += 1
return False
class ZigzagIterator:
"""
@param: v1: A 1d vector
@param: v2: A 1d vector
"""
def __init__(self, v1, v2):
self.queue = [vec for vec in (v1, v2) if vec]
"""
@return: An integer
"""
def next(self):
if not self.hasNext():
return -1
vec = self.queue.pop(0)
val = vec.pop(0)
if vec:
self.queue.append(vec)
return val
"""
@return: True if has next
"""
def hasNext(self):
return bool(self.queue)
================================================
FILE: lintcode/541_zigzag_iterator_ii.py
================================================
"""
Your ZigzagIterator2 object will be instantiated and called as such:
solution, result = ZigzagIterator2(vecs), []
while solution.hasNext(): result.append(solution.next())
Output result
"""
class ZigzagIterator2:
"""
@param: vecs: a list of 1d vectors
"""
def __init__(self, vecs):
self.g = vecs
self.x = 0
self.y = 0
self.max_y = max(len(vec) for vec in vecs)
"""
@return: An integer
"""
def next(self):
if not self.hasNext():
return -1
x = self.x
y = self.y
self.x += 1
return self.g[x][y]
"""
@return: True if has next
"""
def hasNext(self):
while self.y < self.max_y:
if (
self.x < len(self.g) and
self.y < len(self.g[self.x])
):
return True
if self.x >= len(self.g):
self.x = 0
self.y += 1
if self.y >= len(self.g[self.x]):
self.x += 1
return False
class ZigzagIterator2:
"""
@param: vecs: a list of 1d vectors
"""
def __init__(self, vecs):
self.queue = [vec for vec in vecs if vec]
"""
@return: An integer
"""
def next(self):
if not self.hasNext():
return -1
vec = self.queue.pop(0)
val = vec.pop(0)
if vec:
self.queue.append(vec)
return val
"""
@return: True if has next
"""
def hasNext(self):
return bool(self.queue)
================================================
FILE: lintcode/543_kth_largest_in_n_arrays.py
================================================
import heapq
class Solution:
"""
@param: arrays: a list of array
@param: k: An integer
@return: an integer, K-th largest element in N arrays
"""
def KthInArrays(self, arrays, k):
res = []
for arr in arrays: res += arr
if k > len(res):
return 'error'
res.sort()
return res[-k]
================================================
FILE: lintcode/544_top_k_largest_numbers.py
================================================
from heapq import heappush, heappop
class Solution:
"""
@param: nums: an integer array
@param: k: An integer
@return: the top k largest numbers in array
"""
def topk(self, nums, k):
"""
min heap (normal case in heapq, max heap needs to times -1)
"""
if not nums:
return
ans = []
for num in nums:
heappush(ans, num)
if len(ans) > k:
heappop(ans)
ans.sort(reverse=True)
return ans
================================================
FILE: lintcode/545_top_k_largest_numbers_ii.py
================================================
from heapq import heappop, heappush
class Solution:
"""
@param: k: An integer
"""
def __init__(self, k):
self.k = k
self.tops = []
"""
@param: num: Number to be added
@return: nothing
"""
def add(self, num):
# push in first, since we cannot ensure
# the incoming num will stay in tops
heappush(self.tops, num)
if len(self.tops) > self.k:
heappop(self.tops)
"""
@return: Top k element
"""
def topk(self):
return sorted(self.tops, reverse=True)
================================================
FILE: lintcode/547_intersection_of_two_arrays.py
================================================
class Solution:
def intersection(self, a, b):
"""
:type a: List[int]
:type b: List[int]
:rtype: List[int]
"""
ans = []
if not a or not b:
return ans
s = set(a)
t = set(b)
for x in s:
if x in t:
ans.append(x)
return ans
class Solution:
def intersection(self, a, b):
"""
:type a: List[int]
:type b: List[int]
:rtype: List[int]
"""
ans = []
if not a or not b:
return ans
a.sort()
b.sort()
m, n = len(a), len(b)
i = j = 0
while i < m and j < n:
if a[i] == b[j]:
if not ans or a[i] != ans[-1]:
ans.append(a[i])
i += 1
j += 1
elif a[i] < b[j]:
i += 1
else:
j += 1
return ans
================================================
FILE: lintcode/548_intersection_of_two_arrays_ii.py
================================================
class Solution:
def intersect(self, a, b):
"""
:type a: List[int]
:type b: List[int]
:rtype: List[int]
"""
ans = []
if not a or not b:
return ans
freq = {}
for x in a:
freq[x] = freq.get(x, 0) + 1
for x in b:
if not freq.get(x):
continue
freq[x] -= 1
ans.append(x)
return ans
class Solution:
def intersect(self, a, b):
"""
:type a: List[int]
:type b: List[int]
:rtype: List[int]
"""
ans = []
if not a or not b:
return ans
a.sort()
b.sort()
m, n = len(a), len(b)
i = j = 0
while i < m and j < n:
if a[i] == b[j]:
ans.append(a[i])
i += 1
j += 1
elif a[i] < b[j]:
i += 1
else:
j += 1
return ans
================================================
FILE: lintcode/54_string_to_integer_ii.py
================================================
class Solution:
"""
@param: s: A string
@return: An integer
"""
def atoi(self, s):
NOT_FOUND = 0
if not s:
return NOT_FOUND
n = len(s)
is_negative = False
left, right = 0, n - 1
while left < n and s[left] == ' ':
left += 1
while right >= 0 and s[right] == ' ':
right -= 1
if left < n and s[left] in ('+', '-'):
is_negative = (s[left] == '-')
left += 1
if left > right:
return NOT_FOUND
ans = 0
zero = ord('0')
nine = ord('9')
INT_MAX = 0x7FFFFFFF
INT_MIN = -0x80000000
while left <= right and zero <= ord(s[left]) <= nine:
ans = ans * 10 + ord(s[left]) - zero
left += 1
if is_negative:
ans *= -1
if ans > INT_MAX:
return INT_MAX
if ans < INT_MIN:
return INT_MIN
return ans
================================================
FILE: lintcode/551_nested_list_weight_sum.py
================================================
"""
This is the interface that allows for creating nested lists.
You should not implement it, or speculate about its implementation
class NestedInteger(object):
def isInteger(self):
# @return {boolean} True if this NestedInteger holds a single integer,
# rather than a nested list.
def getInteger(self):
# @return {int} the single integer that this NestedInteger holds,
# if it holds a single integer
# Return None if this NestedInteger holds a nested list
def getList(self):
# @return {NestedInteger[]} the nested list that this NestedInteger holds,
# if it holds a nested list
# Return None if this NestedInteger holds a single integer
"""
class Solution(object):
# @param {NestedInteger[]} L a list of NestedInteger Object
# @return {int} an integer
def depthSum(self, L):
return self.dfs(L, 1)
def dfs(self, L, depth):
_sum = 0
for obj in L:
if obj.isInteger():
_sum += depth * obj.getInteger()
continue
_sum += self.dfs(obj.getList(), depth + 1)
return _sum
class Solution(object):
# @param {NestedInteger[]} L a list of NestedInteger Object
# @return {int} an integer
def depthSum(self, L):
ans = 0
if not L:
return ans
queue = L
depth = 0
while queue:
_queue = []
depth += 1
for obj in queue:
if obj.isInteger():
ans += depth * obj.getInteger()
continue
_queue.extend(obj.getList())
queue = _queue
return ans
================================================
FILE: lintcode/552_create_maximum_number.py
================================================
class Solution:
def maxNumber(self, a, b, k):
"""
:type a: list[int]
:type b: list[int]
:type k: int, k <= m + n
:rtype: list[int]
"""
ans = []
for size in range(
max(0, k - len(a)),
min(k, len(b)) + 1
):
res = self.merge(
self.get_max(a, k - size),
self.get_max(b, size)
)
ans = max(ans, res)
return ans
def get_max(self, a, size):
res = []
n = len(a)
for i in range(n):
while (
res and
len(res) + n - i > size and
res[-1] < a[i]
):
res.pop()
if len(res) < size:
res.append(a[i])
return res
def merge(self, a, b):
return [
max(a, b).pop(0)
for _ in range(len(a) + len(b))
]
================================================
FILE: lintcode/553_bomb_enemy.py
================================================
"""
Cache sharing nodes
Main Concept:
1. the number of enemies killed is same between the two walls in line
y
0 1 2 3 4
x 0 W
1 k 0 k W l
2 n
3 W
4 m
the number of enemies killed is `n + k` at (1, 1)
2. since the iteration order, we can optimize
the space complexity in row
x y 0 1 2 3
0 [[ | 0, | E, | 0, | 0 ], --1-->
1 [ | E, | 0, v W, | E ], --2-->
2 [ v 0, v E, v 0, v 0 ]] --3-->
"""
class Solution:
WALL = 'W'
ENEMY = 'E'
EMPTY = '0'
def maxKilledEnemies(self, grid):
"""
:type grid: list[list[str]]
:rtype: int
"""
ans = 0
if not grid or not grid[0]:
return ans
m, n = len(grid), len(grid[0])
row, cols = 0, [0] * n
for x in range(m):
for y in range(n):
# calculate bomb in cur section [x, 'WALL' | m) in col
if x == 0 or grid[x - 1][y] == self.WALL:
cols[y] = 0
for i in range(x, m):
if grid[i][y] == self.WALL:
break
if grid[i][y] == self.ENEMY:
cols[y] += 1
# calculate bomb in cur section [y, 'WALL' | n) in row
if y == 0 or grid[x][y - 1] == self.WALL:
row = 0
for i in range(y, n):
if grid[x][i] == self.WALL:
break
if grid[x][i] == self.ENEMY:
row += 1
if grid[x][y] == self.EMPTY and row + cols[y] > ans:
ans = row + cols[y]
return ans
"""
TLE
time: O(m^2 * n^2)
"""
class Solution:
WALL = 'W'
ENEMY = 'E'
EMPTY = '0'
def maxKilledEnemies(self, grid):
"""
:type grid: list[list[str]]
:rtype: int
"""
ans = 0
if not grid or not grid[0]:
return ans
for x in range(len(grid)):
for y in range(len(grid[0])):
if grid[x][y] == self.EMPTY:
ans = max(
ans,
self.get_killed_cnt(grid, x, y)
)
return ans
def get_killed_cnt(self, grid, i, j):
m, n = len(grid), len(grid[0])
cnt = 0
# up
for x in range(i, -1, -1):
if grid[x][j] == self.WALL:
break
if grid[x][j] == self.ENEMY:
cnt += 1
# down
for x in range(i, m):
if grid[x][j] == self.WALL:
break
if grid[x][j] == self.ENEMY:
cnt += 1
# left
for y in range(j, -1, -1):
if grid[i][y] == self.WALL:
break
if grid[i][y] == self.ENEMY:
cnt += 1
# right
for y in range(j, n):
if grid[i][y] == self.WALL:
break
if grid[i][y] == self.ENEMY:
cnt += 1
return cnt
================================================
FILE: lintcode/555_counting_bloom_filter.py
================================================
from random import randint
class HashFunc:
def __init__(self, cap, seed):
self.cap = cap
self.seed = seed
def hash(self, key):
code = 0
if not key:
return code
for char in key:
code = (self.seed * code + ord(char)) % self.cap
return code
class CountingBloomFilter:
def __init__(self, k):
"""
:type k: int
"""
CAP = 20000
self.bits = [0] * CAP
self.hashs = []
for i in range(k):
self.hashs.append(HashFunc(
randint(CAP // 2, CAP),
i * 2 + 3
))
def add(self, word):
"""
:type word: str
:rtype: None
"""
for f in self.hashs:
index = f.hash(word)
self.bits[index] += 1
def remove(self, word):
"""
:type word: str
:rtype: None
"""
for f in self.hashs:
index = f.hash(word)
self.bits[index] -= 1
def contains(self, word):
"""
:type word: str
:rtype: bool
"""
for f in self.hashs:
index = f.hash(word)
if self.bits[index] <= 0:
return False
return True
================================================
FILE: lintcode/556_standard_bloom_filter.py
================================================
from random import randint
class HashFunc:
def __init__(self, cap, seed):
self.cap = cap
self.seed = seed
def hash(self, key):
code = 0
if not key:
return code
for char in key:
code = (self.seed * code + ord(char)) % self.cap
return code
class StandardBloomFilter:
def __init__(self, k):
"""
:type k: int
"""
CAP = 20000
self.bits = [0] * CAP
self.hashs = []
for i in range(k):
self.hashs.append(HashFunc(
randint(CAP // 2, CAP),
i * 2 + 3
))
def add(self, word):
"""
:type word: str
:rtype: None
"""
for f in self.hashs:
index = f.hash(word)
self.bits[index] = 1
def contains(self, word):
"""
:type word: str
:rtype: bool
"""
for f in self.hashs:
index = f.hash(word)
if self.bits[index] == 0:
return False
return True
================================================
FILE: lintcode/559_trie_service.py
================================================
"""
Definition of TrieNode:
class TrieNode:
def __init__(self):
# :
self.children = collections.OrderedDict()
self.top10 = []
"""
class TrieService:
def __init__(self):
self.root = TrieNode()
def get_root(self):
# Return root of trie root, and
# lintcode will print the tree struct.
return self.root
# @param {str} word a string
# @param {int} frequency an integer
# @return nothing
def insert(self, word, frequency):
if not word or len(word) < 1 \
or not frequency:
return
parent = self.root
for char in word:
if char not in parent.children:
parent.children[char] = TrieNode()
parent = parent.children[char]
# To handle top10
parent.top10.append(frequency)
parent.top10.sort(reverse=True)
if len(parent.top10) > 10:
parent.top10.pop()
================================================
FILE: lintcode/560_friendship_service.py
================================================
class FriendshipService:
def __init__(self):
self.followers = {}
self.followings = {}
def getFollowers(self, user_id):
return self.get_followers(user_id)
def getFollowings(self, user_id):
return self.get_followings(user_id)
"""
@param: user_id: An integer
@return: all followers and sort by user_id
"""
def get_followers(self, user_id):
if user_id in self.followers:
return sorted(self.followers[user_id])
return []
"""
@param: user_id: An integer
@return: all followings and sort by user_id
"""
def get_followings(self, user_id):
if user_id in self.followings:
return sorted(self.followings[user_id])
return []
"""
@param: to_id: An integer
@param: from_id: An integer
@return: nothing
"""
def follow(self, to_id, from_id):
if from_id not in self.followings:
self.followings[from_id] = set()
self.followings[from_id].add(to_id)
if to_id not in self.followers:
self.followers[to_id] = set()
self.followers[to_id].add(from_id)
"""
@param: to_id: An integer
@param: from_id: An integer
@return: nothing
"""
def unfollow(self, to_id, from_id):
if from_id in self.followings:
self.followings[from_id].discard(to_id)
if to_id in self.followers:
self.followers[to_id].discard(from_id)
================================================
FILE: lintcode/563_backpack_v.py
================================================
"""
DP: MLE
"""
class Solution:
"""
@param: A: an integer array and all positive numbers
@param: target: An integer
@return: An integer
"""
def backPackV(self, A, target):
if not A:
return 0
n = len(A)
dp = [[0] * (target + 1) for _ in range(n + 1)]
dp[0][0] = 1
for i in range(1, n + 1):
for j in range(target + 1):
dp[i][j] = dp[i - 1][j]
if j >= A[i - 1]:
dp[i][j] += dp[i - 1][j - A[i - 1]]
return dp[n][target]
"""
DP: optimized space complexity
"""
class Solution:
"""
@param: A: an integer array and all positive numbers
@param: target: An integer
@return: An integer
"""
def backPackV(self, A, target):
if not A:
return 0
n = len(A)
dp = [0] * (target + 1)
dp[0] = 1
for i in range(n):
for j in range(target, A[i] - 1, -1):
dp[j] += dp[j - A[i]]
return dp[target]
================================================
FILE: lintcode/564_backpack_vi.py
================================================
class Solution:
"""
@param: A: an integer array and all positive numbers, no duplicates
@param: target: An integer
@return: An integer
"""
def backPackVI(self, A, target):
if not A:
return 0
n = len(A)
dp = [0] * (target + 1)
dp[0] = 1
for i in range(1, target + 1):
for j in range(n):
if i >= A[j]:
dp[i] += dp[i - A[j]]
return dp[target]
================================================
FILE: lintcode/565_heart_beat.py
================================================
class HeartBeat:
def __init__(self):
self.slaves_ip_list = {}
"""
@param: slaves_ip_list: a list of slaves'ip addresses
@param: k: An integer
@return: nothing
"""
def initialize(self, slaves_ip_list, k):
self.slaves_ip_list.update(dict.fromkeys(slaves_ip_list, 0))
self.ttl = 2 * k
"""
@param: timestamp: current timestamp in seconds
@param: slave_ip: the ip address of the slave server
@return: nothing
"""
def ping(self, timestamp, slave_ip):
if slave_ip in self.slaves_ip_list:
self.slaves_ip_list[slave_ip] = timestamp
"""
@param: timestamp: current timestamp in seconds
@return: a list of slaves'ip addresses that died
"""
def getDiedSlaves(self, timestamp):
if not timestamp:
return []
return [ ip
for ip, t0 in self.slaves_ip_list.items()
if timestamp - t0 >= self.ttl
]
================================================
FILE: lintcode/566_gfs_client.py
================================================
'''
Definition of BaseGFSClient
class BaseGFSClient:
def readChunk(self, filename, chunkIndex):
# Read a chunk from GFS
def writeChunk(self, filename, chunkIndex, content):
# Write a chunk to GFS
'''
class GFSClient(BaseGFSClient):
"""
@param: chunkSize: An integer
"""
def __init__(self, chunkSize):
BaseGFSClient.__init__(self)
self.chunk_size = chunkSize
self.chunk_num = {}
"""
@param: filename: a file name
@return: conetent of the file given from GFS
"""
def read(self, filename):
if filename not in self.chunk_num:
return
i, content = 0, ''
while i < self.chunk_num[filename]:
content += self.readChunk(filename, i)
i += 1
return content
"""
@param: filename: a file name
@param: content: a string
@return: nothing
"""
def write(self, filename, content):
i, j, n = 0, 0, len(content)
while j < n:
self.writeChunk(filename, i, content[j : j + self.chunk_size])
i += 1
j += self.chunk_size
self.chunk_num[filename] = i
================================================
FILE: lintcode/56_two_sum.py
================================================
"""
time: O(n)
space: O(n)
it works even if it's not sorted
"""
class Solution:
"""
@param: A: An array of Integer
@param: target: target = A[index1] + A[index2]
@return: [index1, index2] (index1 < index2)
"""
def twoSum(self, A, target):
NOT_FOUND = [-1, -1]
if not A or len(A) < 2:
return NOT_FOUND
remaining = {}
for i in range(len(A)):
if A[i] in remaining:
return [
remaining[A[i]],
i,
]
remaining[target - A[i]] = i
return NOT_FOUND
"""
time: O(nlogn)
space: O(n)
needs to sort in advance
"""
class Solution:
"""
@param: A: An array of Integer
@param: target: target = A[index1] + A[index2]
@return: [index1, index2] (index1 < index2)
"""
def twoSum(self, A, target):
NOT_FOUND = [-1, -1]
if not A or len(A) < 2:
return NOT_FOUND
n = len(A)
A = [(A[i], i) for i in range(n)]
A.sort()
left, right = 0, n - 1
while left < right:
_sum = A[left][0] + A[right][0]
if _sum == target:
return sorted([
A[left][1],
A[right][1],
])
if _sum < target:
left += 1
else:
right -= 1
return NOT_FOUND
================================================
FILE: lintcode/573_build_post_office_ii.py
================================================
"""
Note:
- You cannot pass through wall and house, but can pass through empty.
- You only build post office on an empty.
"""
import collections
class Solution:
"""
BFS
1. for each house, use `BFS` in level traversal
to count the distance if the cell is empty and reachable
2. find the minimum of the total distance in each empty cell,
and it must be reachable for each house
"""
EMPTY = 0
HOUSE = 1
WALL = 2
def shortestDistance(self, grid):
"""
:type grid: list[list[int]]
:rtype: int
"""
if not grid or not grid[0]:
return -1
m, n = len(grid), len(grid[0])
cnt = 0
times = collections.defaultdict(int)
steps = collections.defaultdict(int)
for x in range(m):
for y in range(n):
if grid[x][y] == self.HOUSE:
cnt += 1
self.bfs(grid, x, y, times, steps)
ans = INF = float('inf')
for (x, y), t in times.items():
if t == cnt and steps[x, y] < ans:
ans = steps[x, y]
return ans if ans < INF else -1
def bfs(self, grid, x, y, times, steps):
m, n = len(grid), len(grid[0])
queue, _queue = [(x, y)], []
visited = set(queue)
step = 0
while queue:
step += 1
for x, y in queue:
for dx, dy in (
(-1, 0), (1, 0),
(0, -1), (0, 1),
):
_x = x + dx
_y = y + dy
if not (0 <= _x < m and 0 <= _y < n):
continue
if grid[_x][_y] != self.EMPTY:
continue
if (_x, _y) in visited:
continue
visited.add((_x, _y))
_queue.append((_x, _y))
steps[_x, _y] += step
times[_x, _y] += 1
queue, _queue = _queue, []
import collections
class Solution:
"""
DFS: TLE
If use DFS, need to consider update min dist
so may visit a node many times
"""
EMPTY = 0
HOUSE = 1
WALL = 2
def shortestDistance(self, grid):
"""
:type grid: list[list[int]]
:rtype: int
"""
if not grid or not grid[0]:
return -1
m, n = len(grid), len(grid[0])
cnt = 0
ids = collections.defaultdict(set) # record house ids
steps = collections.defaultdict(int) # total steps for all houses
for x in range(m):
for y in range(n):
if grid[x][y] != self.HOUSE:
continue
cnt += 1
step = collections.defaultdict(int) # steps for current house
self.dfs(grid, x, y, cnt, ids, steps, step)
ans = INF = float('inf')
for (x, y), hids in ids.items():
if len(hids) == cnt and steps[x, y] < ans:
ans = steps[x, y]
return ans if ans < INF else -1
def dfs(self, grid, x, y, id, ids, steps, step):
m, n = len(grid), len(grid[0])
for dx, dy in (
(-1, 0), (1, 0),
(0, -1), (0, 1),
):
_x = x + dx
_y = y + dy
if not (0 <= _x < m and 0 <= _y < n):
continue
if grid[_x][_y] != self.EMPTY:
continue
if step[x, y] + 1 >= step[_x, _y] > 0: # > 0 means visited, since its defaultdict
continue
ids[_x, _y].add(id)
steps[_x, _y] -= step[_x, _y]
step[_x, _y] = step[x, y] + 1
steps[_x, _y] += step[_x, _y]
self.dfs(grid, _x, _y, id, ids, steps, step)
================================================
FILE: lintcode/574_build_post_office.py
================================================
"""
Note:
- You can pass through house and empty.
- You only build post office on an empty.
"""
class Solution:
"""
Prefix Sum + Binary Searching
http://yuanyuanzhangcs.blogspot.hk/2017/02/build-post-office.html
for `X = [1, 2, 3, 4], x = 3`
and we want to get the sum of distances between `X[i]` and `x`
=> d = (3 - 1) + (3 - 2) + (3 - 3) + (4 - 3)
so we can use binary search to find the `i` of `x` in `X`
=> n = 4, i = 2 (since 2 < x == 3)
=> d = x * i - (1 + 2) + (3 + 4) - (n - i) * x
and we building prefix sum of `X`
=> `S = [0, 1, 3, 6, 10]`
=> d = x * i - S[i] + (S[n] - S[i]) - x * (n - i)
"""
EMPTY = 0
HOUSE = 1
def shortestDistance(self, grid):
"""
:type grid: list[list[int]]
:rtype: int
"""
if not grid or not grid[0]:
return -1
m, n = len(grid), len(grid[0])
xs, ys = [], []
for x in range(m):
for y in range(n):
if grid[x][y] != self.HOUSE:
continue
xs.append(x)
ys.append(y)
if not xs or len(xs) == m * n:
return -1
ys.sort()
k = len(xs) + 1
psx, psy = [0] * k, [0] * k # prefix sum
for i in range(1, k):
psx[i] = psx[i - 1] + xs[i - 1]
psy[i] = psy[i - 1] + ys[i - 1]
ans = INF = float('inf')
for x in range(m):
for y in range(n):
if grid[x][y] != self.EMPTY:
continue
step = self.get_step(psx, xs, x) + self.get_step(psy, ys, y)
if step < ans:
ans = step
return ans if ans < INF else -1
def get_step(self, ps, axis, pos):
n = len(axis)
if axis[0] > pos:
return ps[n] - pos * n
if axis[-1] < pos:
return pos * n - ps[n]
left, right = 0, n - 1
while left + 1 < right:
mid = (left + right) // 2
if axis[mid] < pos:
left = mid
else:
right = mid
return sum((
pos * right - ps[right],
ps[n] - ps[right] - pos * (n - right),
))
class Solution:
"""
BFS: TLE
time: O(mn)
find the center of the shape composed of houses
and then do bfs
"""
EMPTY = 0
HOUSE = 1
def shortestDistance(self, grid):
"""
:type grid: list[list[int]]
:rtype: int
"""
if not grid or not grid[0]:
return -1
m, n = len(grid), len(grid[0])
houses = []
xc = yc = 0 # the center of the shape composed of houses
for x in range(m):
for y in range(n):
if grid[x][y] == self.HOUSE:
houses.append((x, y))
xc += x
yc += y
xc //= len(houses)
yc //= len(houses)
ans = INF = float('inf')
queue = [(xc, yc)]
visited = set(queue)
for x, y in queue:
if grid[x][y] == self.EMPTY:
ans = min(ans, self.get_step(houses, x, y))
for dx, dy in (
(-1, 0), (1, 0),
(0, -1), (0, 1),
):
_x = x + dx
_y = y + dy
if not (0 <= _x < m and 0 <= _y < n):
continue
if (_x, _y) in visited:
continue
visited.add((_x, _y))
queue.append((_x, _y))
return ans if ans < INF else -1
def get_step(self, houses, x, y):
step = 0
for _x, _y in houses:
step += abs(_x - x) + abs(_y - y)
return step
class Solution:
"""
BFS: TLE
time: O((mn)^2)
brute force to bfs
"""
EMPTY = 0
HOUSE = 1
def shortestDistance(self, grid):
"""
:type grid: list[list[int]]
:rtype: int
"""
if not grid or not grid[0]:
return -1
m, n = len(grid), len(grid[0])
steps = [[0] * n for _ in range(m)]
for x in range(m):
for y in range(n):
if grid[x][y] == self.HOUSE:
self.bfs(grid, x, y, steps)
ans = INF = float('inf')
for x in range(m):
for y in range(n):
if grid[x][y] != self.EMPTY:
continue
if steps[x][y] < ans:
ans = steps[x][y]
return ans if ans < INF else -1
def bfs(self, grid, x, y, steps):
m, n = len(grid), len(grid[0])
queue, _queue = [(x, y)], []
visited = set(queue)
step = 0
while queue:
step += 1
for x, y in queue:
for dx, dy in (
(-1, 0), (1, 0),
(0, -1), (0, 1),
):
_x = x + dx
_y = y + dy
if not (0 <= _x < m and 0 <= _y < n):
continue
if (_x, _y) in visited:
continue
visited.add((_x, _y))
steps[_x][_y] += step
_queue.append((_x, _y))
queue, _queue = _queue, []
================================================
FILE: lintcode/575_expression_expand.py
================================================
class Solution:
def expressionExpand(self, s):
"""
:type s: str
:rtype: str
"""
if not s:
return ''
times = 0
stack = []
for c in s:
if c.isdigit():
times = times * 10 + int(c)
elif c == '[':
stack.append(times)
times = 0
elif c == ']':
part = []
while stack and isinstance(stack[-1], str):
part.append(stack.pop())
cnt = int(stack.pop()) if stack else 1
stack.append(cnt * ''.join(reversed(part)))
else:
stack.append(c)
return ''.join(stack)
================================================
FILE: lintcode/578_lowest_common_ancestor_iii.py
================================================
"""
Notice:
node A or node B may not exist in tree.
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
this.val = val
this.left, this.right = None, None
"""
class Solution:
def lowestCommonAncestor3(self, root, a, b):
"""
:type root: TreeNode
:type a: TreeNode
:type b: TreeNode
:rtype: TreeNode
"""
if not root:
return
lca, has_a, has_b = self.divide_conquer(root, a, b)
return lca if has_a and has_b else None
def divide_conquer(self, node, a, b):
if not node:
return None, False, False
left, a_in_left, b_in_left = self.divide_conquer(node.left, a, b)
right, a_in_right, b_in_right = self.divide_conquer(node.right, a, b)
has_a = a_in_left or a_in_right or node is a
has_b = b_in_left or b_in_right or node is b
if node is a or node is b:
return node, has_a, has_b
if left and right:
return node, has_a, has_b
if left:
return left, has_a, has_b
if right:
return right, has_a, has_b
return None, has_a, has_b
================================================
FILE: lintcode/57_3sum.py
================================================
class Solution:
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
ans = []
if not nums or len(nums) < 3:
return ans
n = len(nums)
nums.sort()
for a in range(n - 2):
if a > 0 and nums[a] == nums[a - 1]:
continue
b, c = a + 1, n - 1
while b < c:
total = nums[a] + nums[b] + nums[c]
if total == 0:
ans.append([nums[a], nums[b], nums[c]])
b += 1
c -= 1
while b < c and nums[b] == nums[b - 1]:
b += 1
while b < c and nums[c] == nums[c + 1]:
c -= 1
elif total < 0:
b += 1
else:
c -= 1
return ans
================================================
FILE: lintcode/582_word_break_ii.py
================================================
class Solution:
"""
dfs/dp: optimized by memory searching
"""
def wordBreak(self, s, words):
"""
:type s: str
:type words: list[str]
:rtype: list[str]
"""
return self.dfs(s, set(words), {})
def dfs(self, s, words, memo):
if s in memo:
return memo[s]
res = []
if not s:
return res
n = len(s)
for size in range(1, n + 1):
prefix = s[:size]
if prefix not in words:
continue
if size == n:
res.append(prefix)
continue
for word in self.dfs(s[size:], words, memo):
res.append('{0} {1}'.format(prefix, word))
memo[s] = res
return res
class Solution:
"""
dfs: TLE
bad in edge case:
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
["a","aa","aaa","aaaa","aaaaa","aaaaaa","aaaaaaa","aaaaaaaa","aaaaaaaaa","aaaaaaaaaa"]
"""
def wordBreak(self, s, words):
"""
:type s: str
:type words: list[str]
:rtype: list[str]
"""
ans = []
if not words:
return ans
self.dfs(s, words, ans, [])
return ans
def dfs(self, s, words, ans, path):
if not s:
ans.append(' '.join(path))
return
for word in words:
if not word or s.find(word) != 0:
# 1. no word
# 2. current word must be the first in s passed in prev
continue
path.append(word)
self.dfs(s[len(word):], words, ans, path)
path.pop()
================================================
FILE: lintcode/584_drop_eggs_ii.py
================================================
"""
REF: http://massivealgorithms.blogspot.jp/2014/07/dynamic-programming-set-11-egg-dropping.html
"""
class Solution:
"""
@param: m: the number of eggs
@param: n: the number of floors
@return: the number of drops in the worst case
"""
def dropEggs2(self, m, n):
if not m or not n:
return 0
INFINITY = float('inf')
"""
`dp[i][j]` means the minimum drops to verify
the worst case in `j` floors with `i` eggs
"""
dp = [[0] * (n + 1) for _ in range(m + 1)]
"""
only one egg
"""
for i in range(1, m + 1):
dp[i][1] = 1
"""
only one floor
"""
for j in range(1, n + 1):
dp[1][j] = j
for i in range(2, m + 1):
for j in range(2, n + 1):
dp[i][j] = INFINITY
for k in range(1, j + 1):
"""
backtracking to drop one egg on arbitrary floor `k`
there is two cases, if previous egg is:
1. broken: reduce to subproblem (m - 1, k - 1)
dp[i - 1][k - 1] + 1 drop
2. not broken: reduce to subproblem (m, n - k)
dp[i][j - k] + 1 drop
"""
_worst = max(dp[i - 1][k - 1], dp[i][j - k]) + 1
"""
find the minimum worst case
"""
if _worst < dp[i][j]:
dp[i][j] = _worst
return dp[m][n]
================================================
FILE: lintcode/585_maximum_number_in_mountain_sequence.py
================================================
class Solution:
"""
@param: nums: a mountain sequence which increase firstly and then decrease
@return: then mountain top
"""
def mountainSequence(self, nums):
if not nums:
return -1
l, m, r = 0, 0, len(nums) - 1
while l + 1 < r:
m = l + (r - l) // 2
"""
`m+1` will not out of range
if len(nums) == 1 || 2, the code in this loop will not execute
"""
if nums[m] > nums[m+1]:
r = m
else:
l = m
return max(nums[l], nums[r])
================================================
FILE: lintcode/586_sqrtx_ii.py
================================================
class Solution:
"""
@param: x: a double
@return: the square root of x
"""
def sqrt(self, x):
if not x:
return x
left = 0
right = x if x > 1 else 1
eps = 1e-10 # the precision needs `1e-8`, check more two digits
while right - left > eps:
mid = (left + right) / 2.0
if mid * mid < x:
left = mid
else:
right = mid
return left
================================================
FILE: lintcode/587_two_sum_unique_pairs.py
================================================
class Solution:
"""
@param: nums: an array of integer
@param: target: An integer
@return: An integer
"""
def twoSum6(self, nums, target):
ans = 0
if not nums:
return ans
nums.sort()
left, right = 0, len(nums) - 1
_sum = 0
while left < right:
_sum = nums[left] + nums[right]
if _sum == target:
ans += 1
left += 1
right -= 1
while left < right and nums[right] == nums[right + 1]:
right -= 1
while left < right and nums[left] == nums[left - 1]:
left -= 1
continue
if _sum > target:
right -= 1
else:
left += 1
return ans
================================================
FILE: lintcode/589_connecting_graph.py
================================================
class ConnectingGraph:
"""
@param: n: An integer
"""
def __init__(self, n):
if not n:
return
self.N = {}
for i in range(1, n + 1):
self.N[i] = i
def find(self, a):
if self.N[a] == a:
return a
self.N[a] = self.find(self.N[a])
return self.N[a]
"""
@param: a: An integer
@param: b: An integer
@return: nothing
"""
def connect(self, a, b):
_a = self.find(a)
_b = self.find(b)
if _a != _b:
self.N[_a] = _b
"""
@param: a: An integer
@param: b: An integer
@return: A boolean
"""
def query(self, a, b):
_a = self.find(a)
_b = self.find(b)
return _a == _b
================================================
FILE: lintcode/58_4sum.py
================================================
class Solution:
def fourSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[List[int]]
"""
ans = []
if not nums or len(nums) < 4 or target is None:
return ans
n = len(nums)
nums.sort()
for a in range(n - 3):
if a > 0 and nums[a] == nums[a - 1]:
continue
for b in range(a + 1, n - 2):
if b > a + 1 and nums[b] == nums[b - 1]:
continue
c, d = b + 1, n - 1
while c < d:
total = nums[a] + nums[b] + nums[c] + nums[d]
if total == target:
ans.append([nums[a], nums[b], nums[c], nums[d]])
c += 1
d -= 1
while c < d and nums[c] == nums[c - 1]:
c += 1
while c < d and nums[d] == nums[d + 1]:
d -= 1
elif total < target:
c += 1
else:
d -= 1
return ans
================================================
FILE: lintcode/590_connecting_graph_ii.py
================================================
class ConnectingGraph2:
"""
@param: n: An integer
"""
def __init__(self, n):
if n < 1:
return
self.nodes = {}
self.count = {}
for i in range(n):
self.nodes[i + 1] = i + 1
self.count[i + 1] = 1
def find(self, a):
if self.nodes[a] == a:
return a
self.nodes[a] = self.find(self.nodes[a])
return self.nodes[a]
"""
@param: a: An integer
@param: b: An integer
@return: nothing
"""
def connect(self, a, b):
root_a = self.find(a)
root_b = self.find(b)
if root_a != root_b:
# Assign a as b's child set
self.nodes[root_a] = root_b
self.count[root_b] += self.count[root_a]
"""
@param: a: An integer
@return: An integer
"""
def query(self, a):
root_a = self.find(a)
return self.count[root_a]
================================================
FILE: lintcode/591_connecting_graph_iii.py
================================================
class ConnectingGraph3:
"""
@param: n: An integer
"""
def __init__(self, n):
if n < 1:
return
self.nodes = {}
self.count = n
for i in range(n):
self.nodes[i + 1] = i + 1
def find(self, a):
if self.nodes[a] == a:
return a
self.nodes[a] = self.find(self.nodes[a])
return self.nodes[a]
"""
@param: a: An integer
@param: b: An integer
@return: nothing
"""
def connect(self, a, b):
root_a = self.find(a)
root_b = self.find(b)
if root_a != root_b:
self.nodes[root_a] = root_b
self.count -= 1
"""
@return: An integer
"""
def query(self):
return self.count
================================================
FILE: lintcode/594_strstr_ii.py
================================================
"""
Rabin-Karp algorithm
https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm
Main Concept:
given S == 'abcde' and T == 'bcd'
1. convert T into hashcode `tcode`
2. iterate S every `n` chars, and compare with `tcode`,
continue to add `S[i]` and kick out `S[i - n]`,
a b c d e
a b c
b c d
c d e
3. so we need to reduce `ord(S[i - n]) * MG ** (n - 1)` every turns
=> calculate `MG ** (n - 1)` in advance to save time
4. if became negative, add `MOD` to back to positive
Test Case:
"abcde"
"e"
"abcdef"
"bcd"
"""
class Solution:
def strStr2(self, S, T):
"""
:type S: List[str]
:type T: List[str]
:rtype: int
"""
NOT_FOUND = -1
if S is not None and T is '':
return 0
if not S or not T:
return NOT_FOUND
m, n = len(S), len(T)
if n > m:
return NOT_FOUND
MOD = 1000000 # hashsize to mod
MG = 31 # magic number
A = ord('a')
p = 1 # `p == MG ** (n - 1)`
tcode = 0 # the code of T
for i in range(n):
tcode = (tcode * MG + ord(T[i]) - A) % MOD
if i == 0:
continue
"""
continue here since p only need `n - 1` times
"""
p = (p * MG) % MOD
_code = 0
for i in range(m):
"""
kick out `S[i - n]`
"""
if i >= n:
_code = (_code - (ord(S[i - n]) - A) * p) % MOD
if _code < 0:
_code += MOD
"""
Add `S[i]`
"""
_code = (_code * MG + ord(S[i]) - A) % MOD
if _code == tcode and S[i - n + 1:i + 1] == T:
return i - n + 1
return NOT_FOUND
================================================
FILE: lintcode/595_binary_tree_longest_consecutive_sequence.py
================================================
"""
The longest consecutive path need to be from parent to child (cannot be the reverse).
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
"""
Bottom Up
"""
def longestConsecutive(self, root):
"""
:type root: TreeNode
:rtype: int
"""
if not root:
return 0
return self.divide_conquer(root)[0]
def divide_conquer(self, node):
if not node:
return 0, 0
size = 1
down = 0
for branch in ('left', 'right'):
child = getattr(node, branch)
if not child:
continue
_size, _down = self.divide_conquer(child)
if child.val - 1 == node.val and _down + 1 > down:
down = _down + 1
if _size > size:
size = _size
if down + 1 > size:
size = down + 1
return size, down
class Solution:
"""
Top Down
"""
def longestConsecutive(self, root):
"""
:type root: TreeNode
:rtype: int
"""
if not root:
return 0
return self.divide_conquer(root, 0, 0)
def divide_conquer(self, node, parent_val, _size):
if not node:
return 0
size = 1
if parent_val + 1 == node.val:
size += _size
left = self.divide_conquer(node.left, node.val, size)
right = self.divide_conquer(node.right, node.val, size)
return max(size, left, right)
================================================
FILE: lintcode/596_minimum_subtree.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
this.val = val
this.left, this.right = None, None
"""
class Solution:
min_sum = float('inf')
node = None
"""
@param: root: the root of binary tree
@return: the root of the minimum subtree
"""
def findSubtree(self, root):
self._traversal(root)
return self.node
def _traversal(self, node):
if not node:
return 0
left_sum = self._traversal(node.left)
right_sum = self._traversal(node.right)
subtree_sum = left_sum + right_sum + node.val
if subtree_sum < self.min_sum:
self.min_sum = subtree_sum
self.node = node
return subtree_sum
================================================
FILE: lintcode/597_subtree_with_maximum_average.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
this.val = val
this.left, this.right = None, None
"""
"""
Test Case:
{1,-5,11,1,2,4,-2}
: subtree_avg = subtree_sum / subtree_size -> subtree_avg = subtree_sum * 1.0 / subtree_size
{-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16}
: max_avg = 0 -> max_avg = float('-inf')
"""
class Solution:
max_avg = float('-inf')
max_node = None
"""
@param: root: the root of binary tree
@return: the root of the maximum average of subtree
"""
def findSubtree2(self, root):
self._traversal(root)
return self.max_node
def _traversal(self, node):
if not node:
return 0, 0
left_sum, left_size = self._traversal(node.left)
right_sum, right_size = self._traversal(node.right)
subtree_sum = left_sum + right_sum + node.val
subtree_size = left_size + right_size + 1
subtree_avg = subtree_sum * 1.0 / subtree_size
if subtree_avg > self.max_avg:
self.max_avg = subtree_avg
self.max_node = node
return subtree_sum, subtree_size
================================================
FILE: lintcode/598_zombie_in_matrix.py
================================================
class Solution:
PEOPLE = 0
ZOMBIE = 1
WALL = 2
"""
@param: grid: a 2D integer grid
@return: an integer
"""
def zombie(self, grid):
if not grid:
return -1
vector = (
( 0, -1),
( 0, 1),
(-1, 0),
( 1, 0),
)
m, n = len(grid), len(grid[0])
queue, _queue = [], None
days = -1
for x in range(m):
for y in range(n):
if grid[x][y] == self.ZOMBIE:
queue.append((x, y))
while queue:
days += 1
_queue = []
for x, y in queue:
for dx, dy in vector:
_x = x + dx
_y = y + dy
if 0 <= _x < m and 0 <= _y < n \
and grid[_x][_y] == self.PEOPLE:
grid[_x][_y] = self.ZOMBIE
_queue.append((_x, _y))
queue = _queue
for x in range(m):
for y in range(n):
if grid[x][y] == self.PEOPLE:
return -1
return days
================================================
FILE: lintcode/599_insert_into_a_cyclic_sorted_list.py
================================================
"""
Definition of ListNode
class ListNode(object):
def __init__(self, val, next=None):
self.val = val
self.next = next
"""
class Solution:
"""
@param: node: a list node in the list
@param: x: An integer
@return: the inserted new list node
"""
def insert(self, node, x):
if not node:
node = ListNode(x)
node.next = node
return node
pre, cur = None, node
while True:
pre = cur
cur = cur.next
# in the list
if pre.val <= x <= cur.val:
break
# at the boundary between minimum and maximum
if (pre.val > cur.val) and (x < cur.val or x > pre.val):
break
# if `cur` have already traversed the list once
if cur is node:
break
new_node = ListNode(x)
new_node.next = cur
pre.next = new_node
return new_node
================================================
FILE: lintcode/59_3sum_closest.py
================================================
class Solution:
def threeSumClosest(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
if not nums or len(nums) < 3 or target is None:
return -1
ans = INF = float('inf')
n = len(nums)
nums.sort()
for a in range(n - 2):
if a > 0 and nums[a] == nums[a - 1]:
continue
b, c = a + 1, n - 1
while b < c:
total = nums[a] + nums[b] + nums[c]
if total == target:
return total
if abs(total - target) < abs(ans - target):
ans = total
if total < target:
b += 1
else:
c -= 1
return ans if ans < INF else -1
================================================
FILE: lintcode/5_kth_largest_element.py
================================================
class Solution:
def kthLargestElement(self, k, nums):
nums.sort()
return nums[-k]
================================================
FILE: lintcode/600_smallest_rectangle_enclosing_black_pixels.py
================================================
class Solution:
def minArea(self, image, x, y):
"""
:type image: list[str]
:type x: int
:type y: int
:rtype: int
"""
if not image or not image[0]:
return 0
m, n = len(image), len(image[0])
top = self.binary_search(image, 0, x, self.is_empty_row)
bottom = self.binary_search(image, m - 1, x, self.is_empty_row)
left = self.binary_search(image, 0, y, self.is_empty_col)
right = self.binary_search(image, n - 1, y, self.is_empty_col)
return (bottom - top + 1) * (right - left + 1)
def binary_search(self, image, start, end, is_empty):
check = None
if start < end:
check = lambda start, end: start + 1 < end
else:
check = lambda start, end: start - 1 > end
while check(start, end):
mid = (start + end) // 2
if is_empty(image, mid):
start = mid
else:
end = mid
return end if is_empty(image, start) else start
def is_empty_row(self, image, x):
for col in image[x]:
if col == '1':
return False
return True
def is_empty_col(self, image, y):
for row in image:
if row[y] == '1':
return False
return True
================================================
FILE: lintcode/601_flatten_2d_vector.py
================================================
"""
Your Vector2D object will be instantiated and called as such:
i, v = Vector2D(vec2d), []
while i.hasNext(): v.append(i.next())
"""
class Vector2D:
# @param vec2d {List[List[int]]}
def __init__(self, vec2d):
self.g = vec2d
self.x = 0
self.y = 0
# @return {int} a next element
def next(self):
if not self.hasNext():
return -1
x = self.x
y = self.y
self.y += 1
return self.g[x][y]
# @return {boolean} true if it has next element
# or false
def hasNext(self):
while self.x < len(self.g):
if self.y < len(self.g[self.x]):
return True
self.x += 1
self.y = 0
return False
================================================
FILE: lintcode/602_russian_doll_envelopes.py
================================================
"""
DP: time => O(nlogn)
REF: https://leetcode.com/problems/russian-doll-envelopes/discuss/82763
"""
from bisect import bisect_left
class Solution:
"""
@param: E: a number of envelopes with widths and heights
@return: the maximum number of envelopes
"""
def maxEnvelopes(self, E):
if not E or not E[0] or len(E[0]) != 2:
return 0
E.sort(key=lambda e: (e[0], -e[1]))
dp = [0] * len(E)
size = 0
for _, h in E:
i = bisect_left(dp, h, 0, size)
dp[i] = h
if i == size:
size += 1
return size
"""
DP: TLE
"""
class Solution:
"""
@param: E: a number of envelopes with widths and heights
@return: the maximum number of envelopes
"""
def maxEnvelopes(self, E):
ans = 0
if not E:
return ans
n = len(E)
"""
`dp[i]` means the maximum number of envelopes
if the `i`th envelope is outermost
"""
dp = [1] * n
E.sort()
for i in range(n):
for j in range(i):
if (E[j][0] < E[i][0] and E[j][1] < E[i][1] and
dp[j] + 1 > dp[i]):
dp[i] = dp[j] + 1
if dp[i] > ans:
ans = dp[i]
return ans
================================================
FILE: lintcode/603_largest_divisible_subset.py
================================================
class Solution:
def largestDivisibleSubset(self, A):
"""
:type A: List[int]
:rtype: List[int]
"""
ans = []
if not A:
return ans
n = len(A)
if n == 1:
return A
A.sort()
"""
this problem similar with LIS
`dp[i]` means the maximum size of subset end at `i`
note that there is size, so init with `1`
`pi[i]` records the previous num in ans, and allow to backtrack
to find all num in ans
`pe` means path end, to record the LDS end
"""
dp = [1] * n
pi = [0] * n
pe = max_size = 0
for i in range(n):
for j in range(i):
"""
backtracking
`A[i]` is larger than `A[j]`,
so check `A[i] % A[j]` if its zero
"""
if A[i] % A[j] == 0 and dp[j] + 1 > dp[i]:
dp[i] = dp[j] + 1
pi[i] = j
if dp[i] > max_size:
max_size = dp[i]
pe = i
ans = [0] * max_size
for i in range(max_size - 1, -1, -1):
ans[i] = A[pe]
pe = pi[pe]
return ans
================================================
FILE: lintcode/604_window_sum.py
================================================
class Solution:
"""
@param: A: a list of integers.
@param: k: length of window.
@return: the sum of the element inside the window at each moving.
"""
def winSum(self, A, k):
ans = []
if (not A or not k or k <= 0 or
len(A) < k):
return ans
_sum = 0
for i in range(len(A)):
_sum += A[i]
if i < k - 1:
continue
ans.append(_sum)
_sum -= A[i - k + 1]
return ans
================================================
FILE: lintcode/605_sequence_reconstruction.py
================================================
"""
Main Concept:
Topological Sort
1. record `edges` and `indegrees` from `seqs`, and only allow `num` in `org`
2. iterate `org` in order, check if there is only one path to get `org[-1]`
"""
class Solution:
def sequenceReconstruction(self, org, seqs):
"""
:type org: list[int]
:type seqs: list[list[int]]
:rtype: bool
"""
if org is None or seqs is None:
return False
n = len(org)
edges = dict.fromkeys(org)
indeg = dict.fromkeys(org, 0)
cnt = 0
for seq in seqs:
if not seq:
continue
cnt += len(seq)
for i in range(len(seq)):
if not (1 <= seq[i] <= n):
return False
if not edges[seq[i]]:
edges[seq[i]] = set()
# dedup same edge
if i > 0 and seq[i] not in edges[seq[i - 1]]:
indeg[seq[i]] += 1
edges[seq[i - 1]].add(seq[i])
# for case: org == [1], seqs == [[], []]
if cnt < n:
return False
for i in range(n - 1):
if indeg[org[i]] != 0:
return False
if org[i + 1] not in edges[org[i]]:
return False
indeg[org[i + 1]] -= 1
return True
================================================
FILE: lintcode/606_kth_largest_element_ii.py
================================================
from heapq import heappop, heappush
class Solution:
"""
@param: nums: an integer unsorted array
@param: k: an integer from 1 to n
@return: the kth largest element
"""
def kthLargestElement2(self, nums, k):
if not nums or not k:
return -1
heap = []
for num in nums:
heappush(heap, num)
if len(heap) > k:
heappop(heap)
return heappop(heap)
================================================
FILE: lintcode/607_two_sum_data_structure_design.py
================================================
class TwoSum:
count = {}
"""
@param: number: An integer
@return: nothing
"""
def add(self, number):
if number in self.count:
self.count[number] += 1
else:
self.count[number] = 1
"""
@param: value: An integer
@return: Find if there exists any pair of numbers which sum is equal to the value.
"""
def find(self, value):
for num in self.count:
remaining = value - num
if remaining not in self.count:
continue
if remaining != num:
return True
if remaining == num and self.count[num] > 1:
return True
return False
================================================
FILE: lintcode/608_two_sum_input_array_is_sorted.py
================================================
"""
time: O(n)
space: O(n)
"""
class Solution:
"""
@param: A: an array of Integer
@param: target: target = A[index1] + A[index2]
@return: [index1 + 1, index2 + 1] (index1 < index2)
"""
def twoSum(self, A, target):
NOT_FOUND = [-1, -1]
if not A:
return NOT_FOUND
remaining = {}
for i in range(len(A)):
if A[i] in remaining:
return [
remaining[A[i]] + 1,
i + 1,
]
remaining[target - A[i]] = i
return NOT_FOUND
"""
time: O(n)
space: O(1)
"""
class Solution:
"""
@param: A: an array of Integer
@param: target: target = A[index1] + A[index2]
@return: [index1 + 1, index2 + 1] (index1 < index2)
"""
def twoSum(self, A, target):
NOT_FOUND = [-1, -1]
if not A:
return NOT_FOUND
left, right = 0, len(A) - 1
while left < right:
_sum = A[left] + A[right]
if _sum == target:
return [left + 1, right + 1]
if _sum < target:
left += 1
else:
right -= 1
return NOT_FOUND
================================================
FILE: lintcode/609_two_sum_less_than_or_equal_to_target.py
================================================
class Solution:
"""
@param: nums: an array of integer
@param: target: an integer
@return: an integer
"""
def twoSum5(self, nums, target):
ans = 0
if not nums or len(nums) < 2:
return ans
nums.sort()
left, right = 0, len(nums) - 1
while left < right:
if nums[left] + nums[right] <= target:
# the count of connections from `left` to `right`
# e.g, from 1 to 4, 1-2, 1-3, 1-4, got 3 connections
ans += right - left
left += 1
else:
right -= 1
return ans
================================================
FILE: lintcode/610_two_sum_difference_equals_to_target.py
================================================
"""
time: O(n)
space: O(n)
it works even if it's not sorted
"""
class Solution:
"""
@param: A: an array of Integer
@param: target: an integer
@return: [index1 + 1, index2 + 1] (index1 < index2)
"""
def twoSum7(self, A, target):
NOT_FOUND = [-1, -1]
if not A or len(A) < 2:
return NOT_FOUND
remaining = {}
for i in range(len(A)):
"""
if a - b = t
=> a = b + t
"""
if A[i] + target in remaining:
return [
remaining[A[i] + target] + 1,
i + 1
]
"""
if b - a = t
=> a = b - t
"""
if A[i] - target in remaining:
return [
remaining[A[i] - target] + 1,
i + 1
]
remaining[A[i]] = i
return NOT_FOUND
"""
time: O(n)
space: O(n)
needs to sort in advance
"""
class Solution:
"""
@param: A: an array of Integer
@param: target: an integer
@return: [index1 + 1, index2 + 1] (index1 < index2)
"""
def twoSum7(self, A, target):
NOT_FOUND = [-1, -1]
if not A or len(A) < 2:
return NOT_FOUND
if target < 0:
target = -1 * target
n = len(A)
A = [(A[i], i) for i in range(n)]
A.sort()
left = 0
for right in range(1, n):
while left + 1 < right and A[right][0] - A[left][0] > target:
left += 1
if A[right][0] - A[left][0] == target:
return sorted([
A[left][1] + 1,
A[right][1] + 1
])
return NOT_FOUND
================================================
FILE: lintcode/611_knight_shortest_path.py
================================================
"""
Definition for a point.
class Point:
def __init__(self, a=0, b=0):
self.x = a
self.y = b
"""
class Solution:
V = (
(-2, -1),
( 2, 1),
(-2, 1),
( 2, -1),
(-1, -2),
( 1, 2),
(-1, 2),
( 1, -2),
)
"""
@param: G: a chessboard included 0 (false) and 1 (true)
@param: S: a point
@param: T: a point
@return: the shortest path
"""
def shortestPath(self, G, S, T):
if not G or not S or not T:
return -1
INFINITY = float('inf')
m, n = len(G), len(G[0])
min_steps = [[INFINITY] * n for _ in range(m)]
queue = [S]
_queue = None
_x = _y = steps = 0
while queue:
_queue = []
steps += 1
for P in queue:
for dx, dy in self.V:
_x = P.x + dx
_y = P.y + dy
if (0 <= _x < m and 0 <= _y < n and
not G[_x][_y] and
steps < min_steps[_x][_y]):
if _x == T.x and _y == T.y:
return steps
min_steps[_x][_y] = steps
_queue.append(Point(_x, _y))
queue = _queue
return -1
================================================
FILE: lintcode/612_k_closest_points.py
================================================
"""
Definition for a point.
class Point:
def __init__(self, a=0, b=0):
self.x = a
self.y = b
"""
import heapq
class Solution:
"""
max heap
"""
def kClosest(self, points, origin, k):
"""
:type points: list[Point]
:type origin: Point
:type k: int
:rtype: list[Point]
"""
ans = []
if not points or not origin or not k:
return ans
for i in range(len(points)):
distance = self.get_distance(origin, points[i])
heapq.heappush(ans, (-distance, i))
if len(ans) > k:
heapq.heappop(ans)
ans.sort(key=lambda a: (-a[0], points[a[1]].x, points[a[1]].y))
return [points[i] for _, i in ans]
def get_distance(self, p, q):
dx = p.x - q.x
dy = p.y - q.y
return dx * dx + dy * dy
import heapq
class Solution:
"""
min heap
"""
def kClosest(self, points, origin, k):
"""
:type points: list[Point]
:type origin: Point
:type k: int
:rtype: list[Point]
"""
ans = []
if not points or not origin or not k:
return ans
heap = []
for i in range(len(points)):
distance = self.get_distance(origin, points[i])
heapq.heappush(heap, (distance, i))
for _ in range(k):
distance, i = heapq.heappop(heap)
ans.append((distance, points[i]))
ans.sort(key=lambda a: (a[0], a[1].x, a[1].y))
return [p for _, p in ans]
def get_distance(self, p, q):
dx = p.x - q.x
dy = p.y - q.y
return dx * dx + dy * dy
================================================
FILE: lintcode/613_high_five.py
================================================
"""
Definition for a Record
class Record:
def __init__(self, id, score):
self.id = id
self.score = score
"""
from heapq import heappop, heappush
class Solution:
# @param {Record[]} results a list of
# @return {dict(id, average)} find the average of 5 highest scores for each person
# (student_id, average_score)
def highFive(self, results):
ans = {}
if not results:
return ans
k = 5
top_k = {}
for r in results:
if r.id not in top_k:
top_k[r.id] = []
heappush(top_k[r.id], r.score)
if len(top_k[r.id]) > k:
heappop(top_k[r.id])
_sum = 0
for id, scores in top_k.items():
_sum = 0
for score in scores:
_sum += score
ans[id] = _sum * 1.0 / k
return ans
================================================
FILE: lintcode/614_binary_tree_longest_consecutive_sequence_ii.py
================================================
"""
The path could be start and end at any node in the tree
If use top down in this case, still need return the result from bottom
=> just use bottom up
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
"""
Bottom Up
"""
def longestConsecutive2(self, root):
"""
:type root: TreeNode
:rtype: int
"""
if not root:
return 0
return self.divide_conquer(root)[0]
def divide_conquer(self, node):
if not node:
return 0, 0, 0
size = 1
up = down = 0
for branch in ('left', 'right'):
child = getattr(node, branch)
if not child:
continue
_size, _up, _down = self.divide_conquer(child)
if child.val + 1 == node.val and _up + 1 > up:
up = _up + 1
if child.val - 1 == node.val and _down + 1 > down:
down = _down + 1
if _size > size:
size = _size
if up + down + 1 > size:
size = up + down + 1
return size, up, down
================================================
FILE: lintcode/615_course_schedule.py
================================================
class Solution:
def canFinish(self, n, P):
"""
:type n: int
:type P: List[List[int]]
:rtype: bool
"""
indeg = [0] * n
edges = [[] for _ in range(n)]
for c, p in P:
indeg[c] += 1
edges[p].append(c)
queue = [i for i in range(n) if indeg[i] == 0]
for p in queue:
for c in edges[p]:
indeg[c] -= 1
if indeg[c] == 0:
queue.append(c)
return len(queue) == n
================================================
FILE: lintcode/616_course_schedule_ii.py
================================================
class Solution:
def findOrder(self, n, prerequisites):
"""
:type n: int
:type prerequisites: list[list[int]]
:rtype: list[int]
"""
ans = []
if not n:
return ans
indeg = [0] * n
edges = [[] for _ in range(n)]
for c, p in prerequisites:
indeg[c] += 1
edges[p].append(c)
queue = [c for c in range(n) if indeg[c] == 0]
for p in queue:
for c in edges[p]:
indeg[c] -= 1
if indeg[c] == 0:
queue.append(c)
if len(queue) != n:
return []
return queue
================================================
FILE: lintcode/617_maximum_average_subarray.py
================================================
"""
REF: https://stackoverflow.com/questions/13093602/finding-subarray-with-maximum-sum-number-of-elements#answer-13094527
You can use binary search.
For a searched value `avg`, consider the array `B[i] = A[i] - avg`.
Now find the maximum sum subarray of length at least `k`.
This works because the average of a subarray of length `k` is
`(A[i] + ... + A[i + k - 1]) / k`.
So we have:
`(A[i] + ... + A[i + k - 1]) / k >= avg`
=> `A[i] + ... + A[i + k - 1] >= avg * k`
=> `(A[i] - avg) + ... + (A[i + k - 1] - avg) >= 0`
So, if you binary search for the average, by substracting it from each element,
if you can find a non-negative sum subarray of length at least k,
that is, find the maximum one and check if it's non-negative.
then avg is a valid answer,
continue to search in [avg, max_avg] to see if you can find a better one.
If not, reduce search to [0, avg].
ans-2c ans-c ans ans+c ans+2c
valid T T T F F
"""
class Solution:
def maxAverage(self, nums, k):
"""
:type nums: list[int]
:type k: int
:rtype: float
"""
if not nums or not k:
return 0.0
EPS = 1e-5
# ans MUST between `min(nums)` and `max(nums)`
left = right = nums[0]
for num in nums:
if num < left:
left = num
if num > right:
right = num
# prefix sum
s = [0] * (len(nums) + 1)
while right - left > EPS:
mid = (left + right) / 2.0
if self.is_valid(nums, k, mid, s):
left = mid
else:
right = mid
return left
def is_valid(self, nums, k, mid, s):
s[0] = smin = 0
for i in range(1, len(nums) + 1):
s[i] = s[i - 1] + nums[i - 1] - mid
if i < k:
continue
"""
if there is a non-negative sum subarray of length at least k
=> it's valid even if just only one, return True immediately
"""
if s[i] >= smin: # s[i] - smin >= 0
return True
if s[i - k + 1] < smin:
smin = s[i - k + 1]
return False
================================================
FILE: lintcode/618_search_graph_nodes.py
================================================
"""
Test Case:
{1}
[0]
1
0
"""
"""
Definition for a undirected graph node
class UndirectedGraphNode:
def __init__(self, x):
self.label = x
self.neighbors = []
"""
class Solution:
"""
@param {UndirectedGraphNode[]} graph a list of undirected graph node
@param {dict} values a dict,
@param {UndirectedGraphNode} node an Undirected graph node
@param {int} target an integer
@return {UndirectedGraphNode} a node
"""
def searchNode(self, graph, values, node, target):
if not graph:
return
queue = [node]
visited = {_node: False for _node in graph}
for _node in queue:
visited[_node] = True
if values[_node] == target:
return _node
for _neighbor in _node.neighbors:
if not visited[_neighbor]:
queue.append(_neighbor)
================================================
FILE: lintcode/619_binary_tree_longest_consecutive_sequence_iii.py
================================================
"""
The path could be start and end at any node in the tree
Definition for a multi tree node.
class MultiTreeNode(object):
def __init__(self, x):
self.val = x
children = [] # children is a list of MultiTreeNode
"""
class Solution:
"""
Bottom Up
"""
def longestConsecutive3(self, root):
"""
:type root: MultiTreeNode
:rtype: int
"""
if not root:
return 0
return self.divide_conquer(root)[0]
def divide_conquer(self, node):
if not node:
return 0, 0, 0
size = 1
up = down = 0
for child in node.children:
if not child:
continue
_size, _up, _down = self.divide_conquer(child)
if child.val + 1 == node.val and _up + 1 > up:
up = _up + 1
if child.val - 1 == node.val and _down + 1 > down:
down = _down + 1
if _size > size:
size = _size
if up + down + 1 > size:
size = up + down + 1
return size, up, down
================================================
FILE: lintcode/61_search_for_a_range.py
================================================
class Solution:
"""
@param: A: an integer sorted array
@param: target: an integer to be inserted
@return: a list of length 2, [index1, index2]
"""
def searchRange(self, A, target):
NOT_FOUND = [-1, -1]
if not A:
return NOT_FOUND
n = len(A)
left, mid, right = 0, 0, n - 1
while left + 1 < right:
mid = left + (right - left) // 2
if A[mid] < target:
left = mid
else:
right = mid
start = left if A[left] == target else right
left, mid, right = 0, 0, n - 1
while left + 1 < right:
mid = left + (right - left) // 2
if A[mid] <= target:
left = mid
else:
right = mid
end = right if A[right] == target else left
if start <= end:
return [start, end]
return NOT_FOUND
================================================
FILE: lintcode/620_maximum_subarray_iv.py
================================================
class Solution:
"""
@param: A: an array of integer
@param: k: an integer
@return: the largest sum
"""
def maxSubarray4(self, A, k):
if not A:
return 0
n = len(A)
if n < k:
return 0
ans = float('-inf')
Smin = 0
S = [0] * (n + 1)
for i in range(1, n + 1):
S[i] = S[i - 1] + A[i - 1]
"""
in prefix sum, `i` means the size,
and ignoring that iteration if `i` < `k`
"""
if i < k:
continue
if S[i] - Smin > ans:
ans = S[i] - Smin
"""
to get the segment sum of `k` children and ended at `i`
=> [i - k + 1, i]
"""
if S[i - k + 1] < Smin:
Smin = S[i - k + 1]
return ans
================================================
FILE: lintcode/621_maximum_subarray_v.py
================================================
"""
main concept:
since the size of subarrays in [k1, k2] of `A`
so when the iteration ended at `i` in `S`
the start index should be in [i - k2, i - k1] in `S`,
that is, [i - k2 + 1, i - k1 + 1] in `A`
and then we using a queue to record the minimum of the index
the ans is `S[i] - S[queue[0]]`
"""
from collections import deque
class Solution:
"""
@param: A: an array of integers
@param: k1: An integer
@param: k2: An integer
@return: the largest sum
"""
def maxSubarray5(self, A, k1, k2):
if not A or not k2 or len(A) < k1:
return 0
n = len(A)
S = [0] * (n + 1)
queue = deque()
ans = float('-inf')
for i in range(1, n + 1):
S[i] = S[i - 1] + A[i - 1]
"""
if the minimum of index is less than `i - k2`
kicked it off
"""
if queue and queue[0] < i - k2:
queue.popleft()
if i < k1:
continue
"""
if the recent children are great than `S[i - k1]`,
means the min sum is impossible to occur here
"""
while queue and S[queue[-1]] > S[i - k1]:
queue.pop()
queue.append(i - k1)
if queue and S[i] - S[queue[0]] > ans:
ans = S[i] - S[queue[0]]
return ans
================================================
FILE: lintcode/622_frog_jump.py
================================================
"""
BFS
"""
class Solution:
def canCross(self, stones):
"""
:type stones: List[int]
:rtype: bool
"""
if not stones:
return False
if len(stones) < 2:
return True
if stones[1] != 1:
return False
xs = set(stones) # to check in O(1)
queue = [(stones[0], 0)]
visited = set(queue)
for pos, k in queue:
for x in (k - 1, k, k + 1):
if x <= 0 or pos + x not in xs:
continue
if (pos + x, x) in visited:
continue
if pos + x == stones[-1]:
return True
visited.add((pos + x, x))
queue.append((pos + x, x))
return False
"""
DP
"""
class Solution:
def canCross(self, stones):
"""
:type stones: List[int]
:rtype: bool
"""
if not stones:
return False
if len(stones) < 2:
return True
if stones[1] != 1:
return False
dp = {pos: set() for pos in stones}
dp[stones[0]].add(0)
for pos in stones:
for k in dp[pos]:
# k - 1 > 0
if k > 1 and pos + k - 1 in dp:
dp[pos + k - 1].add(k - 1)
if pos + k in dp:
dp[pos + k].add(k)
if pos + k + 1 in dp:
dp[pos + k + 1].add(k + 1)
return bool(dp[stones[-1]])
================================================
FILE: lintcode/623_k_edit_distance.py
================================================
class TrieNode:
def __init__(self):
self.end_of = None
self.children = {}
class Trie:
def __init__(self):
self.root = TrieNode()
def put(self, word):
if not isinstance(word, str):
return
node = self.root
for c in word:
if c not in node.children:
node.children[c] = TrieNode()
node = node.children[c]
node.end_of = word
class Solution:
def kDistance(self, words, target, k):
"""
:type words: list[str]
:type target: str
:type k: int
:rtype: list[str]
"""
trie = Trie()
for word in words:
trie.put(word)
ans = []
dp = [i for i in range(len(target) + 1)]
self.dfs(trie.root, k, target, ans, dp)
return ans
def dfs(self, node, k, target, ans, pre):
n = len(target)
if node.end_of is not None and pre[n] <= k:
ans.append(node.end_of)
dp = [0] * (n + 1)
for c in node.children:
dp[0] = pre[0] + 1
for i in range(1, n + 1):
if target[i - 1] == c:
dp[i] = min(
dp[i - 1] + 1,
pre[i] + 1,
pre[i - 1]
)
else:
dp[i] = min(
dp[i - 1] + 1,
pre[i] + 1,
pre[i - 1] + 1
)
self.dfs(node.children[c], k, target, ans, dp)
================================================
FILE: lintcode/624_remove_substrings.py
================================================
class Solution:
"""
@param: s: a string
@param: D: a set of n substrings
@return: the minimum length
"""
def minLength(self, s, D):
if not s:
return 0
_min = len(s)
queue = [s]
visited = set([s])
"""
bfs
s
/----/---\----\
s-d1 s-d2 s-d3 s-d4
... ... ... ...
"""
for s in queue:
for d in D:
found = s.find(d)
while found != -1:
_s = s[:found] + s[found + len(d):]
found = s.find(d, found + 1)
if _s in visited:
continue
if len(_s) < _min:
_min = len(_s)
queue.append(_s)
visited.add(_s)
return _min
================================================
FILE: lintcode/625_partition_array_ii.py
================================================
"""
Rainbow Sort
"""
class Solution:
"""
@param: A: an integer array
@param: low: An integer
@param: high: An integer
@return:
"""
def partition2(self, A, low, high):
if not A:
return
left, right = 0, len(A) - 1
i = 0
while i < right:
if A[i] < low:
A[left], A[i] = A[i], A[left]
left += 1
i += 1
elif A[i] > high:
A[right], A[i] = A[i], A[right]
right -= 1
else:
i += 1
================================================
FILE: lintcode/630_knight_shortest_path_ii.py
================================================
"""
DFS
"""
class Solution:
"""
@param: G: a chessboard included 0 and 1
@return: the shortest path
"""
def shortestPath2(self, G):
NOT_FOUND = -1
if not G or not G[0] or G[0][0] == 1:
return NOT_FOUND
V = (
(-1, 2),
( 1, 2),
(-2, 1),
( 2, 1),
)
m, n = len(G), len(G[0])
queue = [(0, 0)]
turns = {(0, 0): 0}
for x, y in queue:
for dx, dy in V:
_x = x + dx
_y = y + dy
if 0 <= _x < m and 0 <= _y < n and G[_x][_y] == 0:
if (_x, _y) in turns:
continue
turns[_x, _y] = turns[x, y] + 1
if _x == m - 1 and _y == n - 1:
return turns[_x, _y]
queue.append((_x, _y))
return NOT_FOUND
"""
DP
"""
class Solution:
"""
@param: G: a chessboard included 0 and 1
@return: the shortest path
"""
def shortestPath2(self, G):
if not G or not G[0] or G[0][0] == 1:
return -1
INFINITY = float('inf')
m, n = len(G), len(G[0])
dp = [[INFINITY] * 3 for _ in range(m)]
pre2 = pre1 = curr = 0
dp[0][curr] = 0
"""
x and y is changed since its need from left to right
"""
for y in range(1, n):
pre2, pre1 = pre1, curr
curr = y % 3
for x in range(m):
dp[x][curr] = INFINITY
if G[x][y] == 1:
continue
if (x >= 2 and y >= 1 and dp[x - 2][pre1] < INFINITY and
dp[x - 2][pre1] + 1 < dp[x][curr]):
dp[x][curr] = dp[x - 2][pre1] + 1
if (x >= 1 and y >= 2 and dp[x - 1][pre2] < INFINITY and
dp[x - 1][pre2] + 1 < dp[x][curr]):
dp[x][curr] = dp[x - 1][pre2] + 1
if (x + 1 < m and y >= 2 and dp[x + 1][pre2] < INFINITY and
dp[x + 1][pre2] + 1 < dp[x][curr]):
dp[x][curr] = dp[x + 1][pre2] + 1
if (x + 2 < m and y >= 1 and dp[x + 2][pre1] < INFINITY and
dp[x + 2][pre1] + 1 < dp[x][curr]):
dp[x][curr] = dp[x + 2][pre1] + 1
return dp[m - 1][curr] if dp[m - 1][curr] < INFINITY else -1
================================================
FILE: lintcode/633_find_the_duplicate_number.py
================================================
"""
Binary Searching
`a`: the duplicated num
`cnt(b)`: the cnt of children less than or equal `b`
for each `num < a`: `cnt(num) == num`
for each `num >= a`: `cnt(num) > num`
"""
class Solution:
def findDuplicate(self, A):
"""
:type A: List[int]
:rtype: int
"""
if not A:
return -1
left, right = 1, len(A) - 1
while left + 1 < right:
mid = (left + right) // 2
if self.after_dup(A, mid):
right = mid
else:
left = mid
if self.after_dup(A, left):
return left
if self.after_dup(A, right):
return right
return -1
def after_dup(self, A, mid):
cnt = 0
for a in A:
if a <= mid:
cnt += 1
if cnt > mid:
return True
return False
"""
Floyd Circle Detection
example: [5,4,4,3,2,1]
i 0, 1, 2, 3, 4, 5
a 5, 4, 4, 3, 2, 1
-------------------
s f
f s
s f
f s
sf
-------------------
f s
s f
f s
sf
"""
class Solution:
"""
@param: A: an array containing n + 1 integers which is between 1 and n
@return: the duplicate one
"""
def findDuplicate(self, A):
if not A:
return -1
slow = A[0]
fast = A[A[0]]
while slow != fast:
slow = A[slow]
fast = A[A[fast]]
fast = 0
while slow != fast:
slow = A[slow]
fast = A[fast]
return slow
================================================
FILE: lintcode/634_word_squares.py
================================================
"""
Trie + DFS
"""
"""
TLE: DFS
"""
class Solution:
def wordSquares(self, words):
"""
:type words: list[str]
:rtype: list[list[str]]
"""
ans = []
if not words:
return ans
self.dfs(words, len(words[0]), ans, [])
return ans
def dfs(self, words, n, ans, path):
if (len(path) == n and
self.is_valid(path)):
ans.append(path[:])
return
if len(path) >= n:
return
for i in range(len(words)):
path.append(words[i])
self.dfs(words, n, ans, path)
path.pop()
def is_valid(self, path):
if not path or len(path) != len(path[0]):
return False
for i in range(1, len(path)):
for j in range(i):
if path[i][j] != path[j][i]:
return False
return True
================================================
FILE: lintcode/635_boggle_game.py
================================================
class TrieNode:
def __init__(self):
self.children = {}
self.end_of = None
class Solution:
def boggleGame(self, board, words):
"""
:type board: list[list[str]]
:type words: list[str]
:rtype: int
"""
self.ans = 0
if not board or not board[0]:
return self.ans
root = TrieNode()
for word in words:
self.put(root, word)
m, n = len(board), len(board[0])
visited = set()
for x in range(m):
for y in range(n):
self.dfs(board, x, y, root, visited, 0)
return self.ans
def dfs(self, board, i, j, root, visited, cnt):
m, n = len(board), len(board[0])
for x in range(i, m):
for y in range(j, n):
next_words = []
self.find_next_words(board, x, y, visited, cnt, root, next_words, [])
for pos in next_words:
for p in pos:
visited.add(p)
self.dfs(board, x, y, root, visited, cnt + 1)
for p in pos:
visited.discard(p)
j = 0
def find_next_words(self, board, x, y, visited, cnt, node, next_words, path):
if (x, y) in visited or board[x][y] not in node.children:
return
m, n = len(board), len(board[0])
node = node.children[board[x][y]]
path.append((x, y))
visited.add((x, y))
if node.end_of is not None:
next_words.append(path[:])
self.ans = max(self.ans, cnt + 1)
else:
for dx, dy in (
(-1, 0), (1, 0),
(0, -1), (0, 1),
):
_x = x + dx
_y = y + dy
if not (0 <= _x < m and 0 <= _y < n):
continue
self.find_next_words(board, _x, _y, visited, cnt, node, next_words, path)
path.pop()
visited.discard((x, y))
def put(self, root, word):
node = root
for c in word:
if c not in node.children:
node.children[c] = TrieNode()
node = node.children[c]
node.end_of = word
================================================
FILE: lintcode/636_132_pattern.py
================================================
class Solution:
"""
@param: A: a list of n integers
@return: true if there is a 132 pattern or false
"""
def find132pattern(self, A):
if not A:
return False
stack = [float('-inf')]
for i in range(len(A) - 1, -1, -1):
if A[i] < stack[-1]:
return True
v = None
while stack and A[i] > stack[-1]:
v = stack.pop()
stack.append(A[i])
if v is not None:
stack.append(v)
return False
================================================
FILE: lintcode/646_first_position_unique_character.py
================================================
class Solution:
"""
@param: s: a string
@return: it's index
"""
def firstUniqChar(self, s):
if not s:
return -1
D = {}
for c in s:
D[c] = D.get(c, 0) + 1
i = 0
for c in s:
if D[c] == 1:
return i
i += 1
return -1
================================================
FILE: lintcode/647_substring_anagrams.py
================================================
"""
REF: https://leetcode.com/problems/find-all-anagrams-in-a-string/discuss/92007/
"""
class Solution:
def findAnagrams(self, s, t):
"""
:type s: str
:type t: str
:rtype: List[int]
"""
ans = []
if not s or not t or len(t) > len(s):
return ans
F = {}
for c in t:
F[c] = F.get(c, 0) + 1
n, m, cnt = len(s), len(t), len(F)
left = right = 0
while right < n:
if s[right] in F:
F[s[right]] -= 1
if F[s[right]] == 0:
cnt -= 1
right += 1
while cnt == 0:
if s[left] in F:
F[s[left]] += 1
if F[s[left]] == 1:
cnt += 1
if right - left == m:
ans.append(left)
left += 1
return ans
================================================
FILE: lintcode/64_merge_sorted_array.py
================================================
class Solution:
def mergeSortedArray(self, a, m, b, n):
"""
:type a: List[int]
:type m: int
:type b: List[int]
:type n: int
:rtype: void Do not return anything, modify nums1 in-place instead.
"""
i, j = m - 1, n - 1
k = m + n - 1
while i >= 0 and j >= 0:
if a[i] > b[j]:
a[k] = a[i]
i -= 1
else:
a[k] = b[j]
j -= 1
k -= 1
while j >= 0:
a[k] = b[j]
j -= 1
k -= 1
================================================
FILE: lintcode/654_sparse_matrix_multiplication.py
================================================
class Solution:
"""
@param A: a sparse matrix
@param B: a sparse matrix
@return: the result of A * B
"""
def multiply(self, A, B):
if not A or not B or len(A[0]) != len(B):
return []
m, n = len(A), len(B[0])
l = len(B)
ans = [[0] * n for _ in range(m)]
for i in range(m):
for j in range(n):
for k in range(l):
ans[i][j] += A[i][k] * B[k][j]
return ans
================================================
FILE: lintcode/655_big_integer_addition.py
================================================
class Solution:
def addStrings(self, a, b):
"""
:type a: str
:type b: str
:rtype: str
"""
if not a and not b:
return ''
if not a:
return b
if not b:
return a
m, n = len(a), len(b)
idx = max(m, n)
ans = [''] * (idx + 1)
i = m - 1
j = n - 1
carry = 0
zero = ord('0')
while i >= 0 and j >= 0:
carry += ord(a[i]) + ord(b[j]) - 2 * zero
ans[idx] = str(carry % 10)
carry //= 10
idx -= 1
i -= 1
j -= 1
while i >= 0:
carry += ord(a[i]) - zero
ans[idx] = str(carry % 10)
carry //= 10
idx -= 1
i -= 1
while j >= 0:
carry += ord(b[j]) - zero
ans[idx] = str(carry % 10)
carry //= 10
idx -= 1
j -= 1
if carry:
ans[0] = str(carry)
else:
ans = ans[1:]
return ''.join(ans)
================================================
FILE: lintcode/656_big_integer_multiplication.py
================================================
class Solution:
def multiply(self, a, b):
"""
:type a: str
:type b: str
:rtype: str
"""
if not a or not b or a == '0' or b == '0':
return '0'
m, n = len(a), len(b)
tmp = [0] * (m + n)
for i in range(m - 1, -1, -1):
carry = 0
for j in range(n - 1, -1, -1):
carry += tmp[i + j + 1] + int(a[i]) * int(b[j])
tmp[i + j + 1] = carry % 10
carry //= 10
tmp[i] = carry
i = 0
while tmp[i] == 0:
i += 1
return ''.join([str(j) for j in tmp[i:]])
================================================
FILE: lintcode/65_median_of_two_sorted_arrays.py
================================================
"""
Heap
"""
import heapq
class Solution:
def findMedianSortedArrays(self, a, b):
"""
:type a: list
:type b: list
:rtype: float
"""
heap = []
n = 0
for nums in (a, b):
if not nums:
continue
n += len(nums)
heapq.heappush(heap, (nums[0], nums, 0))
if n == 0:
return 0.0
num = 0
for _ in range((n + 1) // 2):
num, nums, i = heapq.heappop(heap)
i += 1
if i < len(nums):
heapq.heappush(heap, (nums[i], nums, i))
if n & 1 == 1:
return num * 1.0
_num = heapq.heappop(heap)[0]
return (num + _num) / 2.0
"""
Decreasing Approaching
"""
class Solution:
"""
@param: A: An integer array
@param: B: An integer array
@return: a double whose format is *.5 or *.0
"""
def findMedianSortedArrays(self, A, B):
n = len(A) + len(B)
median = self.find_kth(A, 0, B, 0, n // 2 + 1)
if n % 2 == 1:
return median
_median = self.find_kth(A, 0, B, 0, n // 2)
return (median + _median) / 2.0
def find_kth(self, A, i, B, j, k):
"""
example: A: [1, 2, 3, 4, 5, 6]
B: [2, 3, 4, 5]
m + n = 10, median is `(5th + 6th) / 2`
the process below is to find the `6th`
r1/ k = 6
1 2 |3| 4 5 6
2 3 |4| 5
_a = _b = 2
a = 3, b = 4
a < b
r2/ k = 3
-1--2--3-|4| 5 6
|2| 3 4 5
_a = 3, _b = 0
a = 4, b = 2
a > b
r3/ k = 2
-1--2--3-|4| 5 6
-2-|3| 4 5
_a = 3, _b = 1
a = 4, b = 3
a > b
r4/ k = 1
-1--2--3-|4| 5 6
-2--3-|4| 5
since k == 1
return min(4, 4) = `4`
"""
if i >= len(A):
return B[j + k - 1]
if j >= len(B):
return A[i + k - 1]
if k == 1:
return min(A[i], B[j])
_a = i + k // 2 - 1
_b = j + k // 2 - 1
a = A[_a] if _a < len(A) else float('inf')
b = B[_b] if _b < len(B) else float('inf')
if a < b:
return self.find_kth(A, i + k // 2, B, j, k - k // 2)
else:
return self.find_kth(A, i, B, j + k // 2, k - k // 2)
================================================
FILE: lintcode/662_guess_number_game.py
================================================
"""
The guess API is already defined for you.
@param num, your guess
@return -1 if my number is lower, 1 if my number is higher, otherwise return 0
you can call Guess.guess(num)
"""
class Solution:
def guessNumber(self, n):
"""
:type n: int
:rtype: int
"""
if not n or n <= 1:
return 1
left, right = 1, n
while left + 1 < right:
mid = (left + right) // 2
res = Guess.guess(mid)
if res == 0:
return mid
elif res == 1:
left = mid
else:
right = mid
return left if Guess.guess(left) == 0 else right
================================================
FILE: lintcode/664_counting_bits.py
================================================
class Solution:
"""
@param: num: a non negative integer number
@return: an array represent the number of 1's in their binary
"""
def countBits(self, num):
if not num:
return [0]
upper_bound = num + 1
F = [0] * upper_bound
for i in range(1, upper_bound):
"""
1. `i & (i - 1)` must be less than `i`, since `0 & n` is `0`
=> `F[i & (i - 1)]` must have been calculated
2. `+ 1` means the removed `1` in bit
"""
F[i] = F[i & (i - 1)] + 1
return F
================================================
FILE: lintcode/667_longest_palindromic_subsequence.py
================================================
class Solution:
"""
@param: s: the maximum length of s is 1000
@return: the longest palindromic subsequence's length
"""
def longestPalindromeSubseq(self, s):
if not s:
return 0
n = len(s)
if n == 1:
return 1
# `dp[i][j]` means the length of the longest subsequence
# included in `s[i:j+1]`
dp = [[0] * n for _ in range(n)]
ans = 0
start = end = 0
for end in range(n):
dp[end][end] = 1
if n < 1:
continue
start = end - 1
if s[start] == s[end]:
dp[start][end] = 2
else:
dp[start][end] = 1
for size in range(3, n + 1):
for start in range(n - size + 1):
end = start + size - 1
dp[start][end] = max(dp[start][end - 1], dp[start + 1][end])
if s[start] == s[end]:
dp[start][end] = max(
dp[start][end],
dp[start + 1][end - 1] + 2
)
if dp[start][end] > ans:
ans = dp[start][end]
return ans
================================================
FILE: lintcode/668_ones_and_zeroes.py
================================================
"""
optimized space complexity
"""
class Solution:
"""
@param: strs: an array with strings include only 0 and 1
@param: m: An integer
@param: n: An integer
@return: find the maximum number of strings
"""
def findMaxForm(self, strs, m, n):
if not strs:
return 0
"""
`dp[j][k]` means the current str can be made up of
`j` 0s and `k` 1s
"""
dp = [[0] * (n + 1) for _ in range(m + 1)]
c0 = c1 = 0
for s in strs:
c0 = s.count('0')
c1 = len(s) - c0
for j in range(m, c0 - 1, -1):
for k in range(n, c1 - 1, -1):
"""
case 1: included current `strs[i - 1]`
case 2: not included current `strs[i - 1]`, same as previous
"""
dp[j][k] = max(
dp[j][k],
dp[j - c0][k - c1] + 1
)
return dp[m][n]
"""
origin
"""
class Solution:
"""
@param: strs: an array with strings include only 0 and 1
@param: m: An integer
@param: n: An integer
@return: find the maximum number of strings
"""
def findMaxForm(self, strs, m, n):
if not strs:
return 0
l = len(strs)
"""
`dp[i][j][k]` means the pre- `i`th strs can be made up of
`j` 0s and `k` 1s
dp[0][j][k] = 0
"""
dp = [[[0] * (n + 1) for _ in range(m + 1)] for _ in range(l + 1)]
c0 = c1 = 0
for i in range(1, l + 1):
c0 = strs[i - 1].count('0')
c1 = len(strs[i - 1]) - c0
for j in range(m + 1):
for k in range(n + 1):
"""
case 1: included current `strs[i - 1]`
"""
if j >= c0 and k >= c1:
dp[i][j][k] = dp[i - 1][j - c0][k - c1] + 1
"""
case 2: not included current `strs[i - 1]`, same as previous
"""
if dp[i - 1][j][k] > dp[i][j][k]:
dp[i][j][k] = dp[i - 1][j][k]
return dp[l][m][n]
================================================
FILE: lintcode/669_coin_change.py
================================================
class Solution:
"""
BFS
"""
def coinChange(self, coins, amount):
"""
:type coins: List[int]
:type amount: int
:rtype: int
"""
ans = 0
if not coins or not amount:
return ans
queue, _queue = [0], []
visited = set(queue)
while queue:
ans += 1
for a in queue:
for c in coins:
_a = a + c
if _a == amount:
return ans
if _a > amount or _a in visited:
continue
visited.add(_a)
_queue.append(_a)
queue, _queue = _queue, []
return -1
class Solution:
"""
DP: TLE
"""
def coinChange(self, coins, amount):
"""
:type coins: List[int]
:type amount: int
:rtype: int
"""
if not coins or not amount:
return 0
INF = float('inf')
dp = [INF] * (amount + 1)
dp[0] = 0
for c in coins:
for a in range(c, amount + 1):
# if a < c: continue
dp[a] = min(dp[a], dp[a - c] + 1)
return dp[amount] if dp[amount] < INF else -1
================================================
FILE: lintcode/66_binary_tree_preorder_traversal.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
"""
@param: root: A Tree
@return: Preorder in ArrayList which contains node values.
"""
def preorderTraversal(self, root):
ans = []
if not root:
return ans
self._traversal(root, ans)
return ans
def _traversal(self, node, res):
if not node:
return
res.append(node.val)
self._traversal(node.left, res)
self._traversal(node.right, res)
================================================
FILE: lintcode/676_decode_ways_ii.py
================================================
class Solution:
"""
@param s: a message being encoded
@return: an integer
"""
def numDecodings(self, s):
if not s or s == '0':
return 0
n = len(s)
MOD = 10 ** 9 + 7
dp = [0] * (n + 1)
dp[0] = 1
if s[0] == '*':
dp[1] = 9 # 9 * dp[0]
elif s[0] != '0':
dp[1] = 1 # dp[0]
for i in range(2, n + 1):
if s[i - 1] == '*':
dp[i] += 9 * dp[i - 1]
if s[i - 2] == '*':
dp[i] += 15 * dp[i - 2]
elif s[i - 2] == '1':
dp[i] += 9 * dp[i - 2]
elif s[i - 2] == '2':
dp[i] += 6 * dp[i - 2]
dp[i] %= MOD
continue
if s[i - 1] != '0':
dp[i] += dp[i - 1]
if s[i - 2] == '*':
if int(s[i - 1]) <= 6:
dp[i] += 2 * dp[i - 2]
else:
dp[i] += dp[i - 2]
elif 10 <= int(s[i - 2:i]) <= 26:
dp[i] += dp[i - 2]
dp[i] %= MOD
return dp[n]
================================================
FILE: lintcode/67_binary_tree_inorder_traversal.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
"""
@param: root: A Tree
@return: Inorder in ArrayList which contains node values.
"""
def inorderTraversal(self, root):
ans = []
if not root:
return ans
self._traversal(root, ans)
return ans
def _traversal(self, node, res):
if not node:
return
self._traversal(node.left, res)
res.append(node.val)
self._traversal(node.right, res)
================================================
FILE: lintcode/689_two_sum_bst_edtion.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
"""
@param: : the root of tree
@param: : the target sum
@return: two numbers from tree which sum is n
"""
def twoSum(self, root, n):
self.left = self.right = None
self.head = self.tail = root
self.pre()
self.nxt()
while self.left != self.right:
_sum = self.left.val + self.right.val
if _sum == n:
return [self.left.val, self.right.val]
if _sum < n:
self.nxt()
else:
self.pre()
def pre(self):
while self.tail:
cur = self.tail.right
if cur and cur != self.right:
while cur.left and cur.left != self.tail:
cur = cur.left
if cur.left == self.tail:
self.right = self.tail
cur.left = None
self.tail = self.tail.left
break
else:
cur.left = self.tail
self.tail = self.tail.right
else:
self.right = self.tail
self.tail = self.tail.left
break
def nxt(self):
while self.head:
cur = self.head.left
if cur and cur != self.left:
while cur.right and cur.right != self.head:
cur = cur.right
if cur.right == self.head:
self.left = self.head
cur.right = None
self.head = self.head.right
break
else:
cur.right = self.head
self.head = self.head.left
else:
self.left = self.head
self.head = self.head.right
break
================================================
FILE: lintcode/68_binary_tree_postorder_traversal.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
"""
@param: root: A Tree
@return: Postorder in ArrayList which contains node values.
"""
def postorderTraversal(self, root):
ans = []
if not root:
return ans
stack = []
node = last_visit = root
while node or stack:
while node:
stack.append(node)
node = node.left
node = stack[-1]
if (not node.right or
last_visit is node.right):
stack.pop()
ans.append(node.val)
last_visit = node
node = None
else:
node = node.right
return ans
================================================
FILE: lintcode/69_binary_tree_level_order_traversal.py
================================================
"""
Main Concept:
1. Use `_queue` to collect node in current level
2. Use `level_values` to collect the val of node in current level
3. After traverse the current level,
append `level_values` to answer
and reset `queue` as `_queue`
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
def levelOrder(self, root):
"""
:type root: TreeNode
:rtype: List[List[int]]
"""
ans = []
if not root:
return ans
queue, _queue = [root], []
while queue:
ans.append([])
for node in queue:
if node.left:
_queue.append(node.left)
if node.right:
_queue.append(node.right)
ans[-1].append(node.val)
queue, _queue = _queue, []
return ans
================================================
FILE: lintcode/6_merge_two_sorted_arrays.py
================================================
class Solution:
"""
@param: A: sorted integer array A
@param: B: sorted integer array B
@return: A new sorted integer array
"""
def mergeSortedArray(self, A, B):
if not A:
return B
if not B:
return A
m, n = len(A), len(B)
ans = [0] * (m + n)
i = j = index = 0
while i < m and j < n:
if A[i] < B[j]:
ans[index] = A[i]
i += 1
else:
ans[index] = B[j]
j += 1
index += 1
while i < m:
ans[index] = A[i]
i += 1
index += 1
while j < n:
ans[index] = B[j]
j += 1
index += 1
return ans
================================================
FILE: lintcode/70_binary_tree_level_order_traversal_ii.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
"""
@param: root: A tree
@return: buttom-up level order a list of lists of integer
"""
def levelOrderBottom(self, root):
ans = []
if not root:
return ans
preorder = [(root, 1)]
self.dfs(root, ans, preorder, 0)
"""
cannot do append in dfs,
since it may be the deepest node is at right child
"""
height = len(ans)
for node, level in preorder:
ans[height - level].append(node.val)
return ans
def dfs(self, node, ans, preorder, parent_at):
if len(ans) < preorder[parent_at][1]:
ans.append([])
depth = preorder[parent_at][1] + 1
if node.left:
preorder.append((node.left, depth))
self.dfs(node.left, ans, preorder, len(preorder) - 1)
if node.right:
preorder.append((node.right, depth))
self.dfs(node.right, ans, preorder, len(preorder) - 1)
================================================
FILE: lintcode/717_tree_longest_path_with_same_value.py
================================================
"""
REF: https://blog.csdn.net/zhaohengchuan/article/details/78833501
"""
class Solution:
def LongestPathWithSameValue(self, a, e):
"""
:type a: list[int]
:type e: list[int]
:rtype: int
"""
if not a or len(a) <= 1:
return 0
neibs = [[] for _ in range(len(a) + 1)] # neighbors
# to build the node connection
for i in range(0, len(e), 2):
neibs[e[i]].append(e[i + 1])
neibs[e[i + 1]].append(e[i])
self.ans = 0
res = self.dfs(0, 1, a, neibs)
return max(self.ans, res)
def dfs(self, root, curr, a, neibs):
tmp = []
for neib in neibs[curr]:
# ignore if the neib is curr's parent
if neib == root:
continue
res = self.dfs(curr, neib, a, neibs)
# incr the res and append to tmp
# if both `neib` and `curr` have same value
if a[neib - 1] == a[curr - 1]:
tmp.append(res + 1)
# to prevent len(tmp) == 0
tmp.extend((0, 0))
tmp.sort(reverse=True)
self.ans = max(self.ans, tmp[0] + tmp[1])
return tmp[0]
================================================
FILE: lintcode/718_repeat_string.py
================================================
class Solution:
def repeatedString(self, a, b):
"""
:type a: str
:type b: str
:rtype: int
"""
if len(b) <= len(a) and b in a:
return 1
if not a or not b:
return -1
ans = b.count(a)
c = b.split(a)
if c[0] and a.endswith(c[0]):
ans += 1
if c[-1] and a.startswith(c[-1]):
ans += 1
return ans if a.startswith(c[-1]) and a.endswith(c[0]) else -1
================================================
FILE: lintcode/71_binary_tree_zigzag_level_order_traversal.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
"""
@param: root: A Tree
@return: A list of lists of integer include the zigzag level order traversal of its nodes' values.
"""
def zigzagLevelOrder(self, root):
ans = []
if not root:
return ans
queue = [root]
while queue:
_queue = []
ans.append([])
for node in queue:
if node.left:
_queue.append(node.left)
if node.right:
_queue.append(node.right)
ans[-1].append(node.val)
if len(ans) % 2 == 0:
ans[-1].reverse()
queue = _queue
return ans
================================================
FILE: lintcode/724_minimum_partition.py
================================================
class Solution:
def findMin(self, nums):
"""
:type nums: list[int]
:rtype: int
"""
if not nums:
return 0
target = sum(nums)
dp = [False] * (target + 1)
dp[0] = True
ans = float('inf')
for num in nums:
for i in range(target, num - 1, -1):
dp[i] = dp[i] or dp[i - num]
if dp[i]:
ans = min(
ans,
abs(target - i * 2)
)
return ans
================================================
FILE: lintcode/725_boolean_parenthesization.py
================================================
"""
REF: https://blog.csdn.net/zhaohengchuan/article/details/78937943
"""
class Solution:
def countParenth(self, symb, oper):
"""
:type symb: list[str]
:type oper: list[str]
:rtype: int
"""
if not symb or not oper:
return 0
n = len(symb)
"""
`t[l][r]` means the ways to evaluate True in `symb[i:j]`
"""
t = [[0] * n for _ in range(n)]
f = [[0] * n for _ in range(n)]
for i in range(n):
if symb[i] == 'T':
t[i][i] = 1
else:
f[i][i] = 1
for r in range(n):
for l in range(r - 1, -1, -1):
t[l][r] = 0
f[l][r] = 0
for i in range(l, r):
if oper[i] == '&':
t[l][r] += t[l][i] * t[i + 1][r]
f[l][r] += (
(t[l][i] + f[l][i]) *
(t[i + 1][r] + f[i + 1][r]) -
t[l][i] * t[i + 1][r]
)
elif oper[i] == '|':
t[l][r] += (
(t[l][i] + f[l][i]) *
(t[i + 1][r] + f[i + 1][r]) -
f[l][i] * f[i + 1][r]
)
f[l][r] += f[l][i] * f[i + 1][r]
elif oper[i] == '^':
t[l][r] += t[l][i] * f[i + 1][r] + f[l][i] * t[i + 1][r]
f[l][r] += t[l][i] * t[i + 1][r] + f[l][i] * f[i + 1][r]
return t[0][n - 1]
================================================
FILE: lintcode/729_last_digit_by_factorial_divide.py
================================================
class Solution:
def computeLastDigit(self, a, b):
"""
:type a: int
:type b: int
:rtype: int
"""
if not a or not b:
return 0
if a == b:
return 1
ans = 1
for num in range(a + 1, b + 1):
if ans == 0:
return 0
ans *= num % 10
ans %= 10
return ans
================================================
FILE: lintcode/745_palindromic_ranges.py
================================================
class Solution:
def PalindromicRanges(self, left, right):
"""
:type left: int
:type right: int
:rtype: int
"""
if left > right:
return 0
if left == right:
return 1
dp = [0] * (right - left + 2) # n + 1, n = right - left + 1
# dp[0] = 0
for num in range(left, right + 1):
if self.is_palindrome(num):
dp[num - left + 1] = dp[num - left] + 1
else:
dp[num - left + 1] = dp[num - left]
ans = 0
for i in range(1, right - left + 2):
for j in range(i):
if ((dp[i] - dp[j]) & 1 == 0):
ans += 1
return ans
def is_palindrome(self, num):
if num // 10 == 0:
return True
s = str(num)
left, right = 0, len(s) - 1
while left < right:
if s[left] != s[right]:
return False
left += 1
right -= 1
return True
================================================
FILE: lintcode/74_first_bad_version.py
================================================
"""
Main Concept
ver. | 1 2 3 4 5
good | T T F F F
class SVNRepo:
@classmethod
def isBadVersion(cls, id)
# Run unit tests to check whether verison `id` is a bad version
# return true if unit tests passed else false.
You can use SVNRepo.isBadVersion(10) to check whether version 10 is a
bad version.
"""
class Solution:
def findFirstBadVersion(self, n):
"""
:type n: int
:rtype: int
"""
if not n:
return 0
left, right = 1, n
while left + 1 < right:
mid = (left + right) // 2
if isBadVersion(mid):
right = mid
else:
left = mid
return left if isBadVersion(left) else right
================================================
FILE: lintcode/752_rogue_knight_sven.py
================================================
class Solution:
def getNumberOfWays(self, n, m, limit, cost):
"""
:type n: int
:type m: int
:type limit: int
:type cost: List[int]
:rtype: int
"""
"""
`dp[i][j]` means the ways to reach planet `i` and still keep `j` coins
"""
dp = [[0] * (m + 1) for _ in range(n + 1)]
dp[0][m] = 1
for i in range(1, n + 1):
for j in range(m + 1):
for k in range(max(0, i - limit), i):
if j + cost[i] > m:
continue
dp[i][j] += dp[k][j + cost[i]]
return sum(dp[n])
================================================
FILE: lintcode/75_find_peak_element.py
================================================
class Solution:
def findPeak(self, nums):
"""
:type nums: list
:rtype: int
"""
if not nums:
return -1
left, right = 0, len(nums) - 1
while left + 1 < right:
mid = (left + right) // 2
if nums[mid] < nums[mid + 1]:
left = mid
else:
right = mid
return right if nums[left] < nums[right] else left
================================================
FILE: lintcode/76_longest_increasing_subsequence.py
================================================
"""
Binary Searching
"""
class Solution:
def longestIncreasingSubsequence(self, A):
"""
:type A: List[int]
:rtype: int
the iteration of B:
[-inf, 0, inf, inf, inf, inf, inf, inf, inf]
[-inf, 0, 8, inf, inf, inf, inf, inf, inf]
[-inf, 0, 4, inf, inf, inf, inf, inf, inf]
[-inf, 0, 4, 12, inf, inf, inf, inf, inf]
[-inf, 0, 2, 12, inf, inf, inf, inf, inf]
[-inf, 0, 2, 10, inf, inf, inf, inf, inf]
[-inf, 0, 2, 6, inf, inf, inf, inf, inf]
[-inf, 0, 2, 6, 14, inf, inf, inf, inf]
lis_size = 4
"""
if not A:
return 0
INFINITY = float('inf')
n = len(A)
P = [-INFINITY] + [INFINITY] * n
for i in range(n):
j = self.binary_search(P, A[i])
P[j] = A[i]
for i in range(n, -1, -1):
if P[i] < INFINITY:
return i
return 0
def binary_search(self, P, a):
left, right = 0, len(P) - 1
while left + 1 < right:
mid = (left + right) // 2
if P[mid] < a:
left = mid
else:
right = mid
return right
"""
DP + Print Paths
"""
class Solution:
def longestIncreasingSubsequence(self, A):
"""
:type A: List[int]
:rtype: int
"""
lis_size = 0
if not A:
return lis_size
n = len(A)
"""
`dp[i]` means the maximum size of LIS end at `i`
note that there is size, so init with `1`
"""
dp = [1] * n
# pi = [0] * n
# end_at = -1
for i in range(n):
for j in range(i):
"""
`dp[j]` the existing subseq end at `j`
`+ 1` means included `A[i]`
"""
if A[j] < A[i] and dp[j] + 1 > dp[i]:
dp[i] = dp[j] + 1
# pi[i] = j
if dp[i] > lis_size:
lis_size = dp[i]
# end_at = i
# paths = [0] * lis_size
# for i in range(lis_size - 1, -1, -1):
# paths[i] = A[end_at]
# end_at = pi[end_at]
# print(paths)
return lis_size
================================================
FILE: lintcode/772_group_anagrams.py
================================================
class Solution:
"""
@param s: the given array of strings
@return: The anagrams which have been divided into groups
"""
def groupAnagrams(self, s):
if not s:
return []
group = {}
for w in s:
key = ''.join(sorted(w))
if key not in group:
group[key] = []
group[key].append(w)
return sorted([sorted(g) for g in group.values()])
================================================
FILE: lintcode/775_palindrome_pairs.py
================================================
"""
REF: https://leetcode.com/problems/palindrome-pairs/discuss/79199/150-ms-45-lines-JAVA-solution
Main Concept:
1. The <= in for (int j=0; j<=words[i].length(); j++) is aimed to handle empty string in the input. Consider the test case of [“a”, “”];
2. Since we now use <= in for (int j=0; j<=words[i].length(); j++) instead of <. There may be duplicates in the output (consider test case [“abcd”, “dcba”]). Therefore I put a str2.length()!=0 to avoid duplicates.
"""
class Solution:
def palindromePairs(self, words):
"""
:type words: list[str]
:rtype: list[list[int]]
"""
ans = []
if not words:
return ans
n = len(words)
w2i = {}
for i in range(n):
w2i[words[i]] = i
for i in range(n):
for j in range(len(words[i]) + 1):
s = words[i][:j]
t = words[i][j:]
_s = ''.join(reversed(s))
_t = ''.join(reversed(t))
if (self.is_palindrome(s) and
_t in w2i and
w2i[_t] != i
):
ans.append([w2i[_t], i])
if (self.is_palindrome(t) and
len(t) != 0 and # since len(word) + 1, may empty here
_s in w2i and
w2i[_s] != i
):
ans.append([i, w2i[_s]])
return ans
def is_palindrome(self, word):
n = len(word)
left, right = 0, n - 1
while left < right:
if word[left] != word[right]:
return False
left += 1
right -= 1
return True
"""
TLE: Brute Force
"""
class Solution:
def palindromePairs(self, words):
"""
:type words: list[str]
:rtype: list[list[int]]
"""
ans = []
if not words:
return ans
n = len(words)
for i in range(n):
for j in range(i):
if self.is_palindrome(words, i, j):
ans.append([i, j])
if self.is_palindrome(words, j, i):
ans.append([j, i])
return ans
def is_palindrome(self, words, i, j):
s, t = words[i], words[j]
a, b = len(s), len(t)
n = a + b
left, right = 0, n - 1
while left < right:
if left >= a and t[left - a] != t[right - a]:
return False
elif right < a and s[left] != s[right]:
return False
elif left < a and right >= a and s[left] != t[right - a]:
return False
left += 1
right -= 1
return True
"""
TLE: Brute Force
"""
class Solution:
def palindromePairs(self, words):
"""
:type words: list[str]
:rtype: list[list[int]]
"""
ans = []
if not words:
return ans
n = len(words)
for i in range(n):
for j in range(n):
if i == j:
continue
if self.is_palindrome(words[i] + words[j]):
ans.append([i, j])
return ans
def is_palindrome(self, s):
n = len(s)
left, right = 0, n - 1
while left < right:
if s[left] != s[right]:
return False
left += 1
right -= 1
return True
================================================
FILE: lintcode/776_strobogrammatic_number_ii.py
================================================
class Solution:
def findStrobogrammatic(self, n):
"""
:type n: int
:rtype: list[str]
"""
ans = ['']
if not n:
return ans
if n & 1:
ans = ['0', '1', '8']
n -= 1
while n:
queue = []
for s in ans:
if n != 2:
queue.append('0' + s + '0')
queue.append('1' + s + '1')
queue.append('8' + s + '8')
queue.append('6' + s + '9')
queue.append('9' + s + '6')
ans = queue
n -= 2
return ans
================================================
FILE: lintcode/77_longest_common_subsequence.py
================================================
class Solution:
"""
@param: A: A string
@param: B: A string
@return: The length of longest common subsequence of A and B
"""
def longestCommonSubsequence(self, A, B):
"""
remove the single line comment to print paths
"""
if not A or not B:
return 0
m, n = len(A), len(B)
"""
`dp[i][j]` means the size of LCS, consisting of
the substr end at `A[i - 1]` and the substr end at `B[j - 1]`
dp[0][j] = 0
dp[i][0] = 0
"""
dp = [[0] * (n + 1) for _ in range(2)]
# pi = [[0] * (n + 1) for _ in range(m + 1)]
prev = curr = 0
for i in range(1, m + 1):
prev = curr
curr = 1 - curr
for j in range(1, n + 1):
"""
case 1: `A[i]` is not one of pairs
case 2: `B[j]` is not one of pairs
case 3: `A[i]` and `B[j]` is just a pair
"""
dp[curr][j] = max(dp[prev][j], dp[curr][j - 1])
# if dp[curr][j] == dp[prev][j]:
# pi[i][j] = 1
# else:
# pi[i][j] = 2
if A[i - 1] == B[j - 1]:
dp[curr][j] = max(dp[curr][j], dp[prev][j - 1] + 1)
# if dp[curr][j] == dp[prev][j - 1] + 1:
# pi[i][j] = 3
# path = [None] * dp[curr][n]
# i, j, k = m, n, dp[curr][n] - 1
# while i > 0 and j > 0:
# if pi[i][j] == 1:
# i -= 1
# elif pi[i][j] == 2:
# j -= 1
# else:
# path[k] = A[i - 1] # or B[j - 1], its same
# k -= 1
# i -= 1
# j -= 1
# print(path)
return dp[curr][n]
================================================
FILE: lintcode/784_the_longest_common_prefix_ii.py
================================================
class Solution:
"""
@param D: the n strings
@param target: the target string
@return: The ans
"""
def theLongestCommonPrefix(self, D, target):
ans = 0
for word in D:
i = 0
for c in word:
if c != target[i]:
break
i += 1
if i > ans:
ans = i
return ans
================================================
FILE: lintcode/790_parser.py
================================================
class Solution:
"""
@param S: Generating set of rules.
@param s: Start symbol.
@param e: Symbol string.
@return: Return true if the symbol string can be generated, otherwise return false.
"""
def canBeGenerated(self, S, start, end):
if not start:
start = ''
N = {}
for s in S:
cur, nxt = s.split(' -> ')
if cur not in N:
N[cur] = set()
N[cur].add(nxt)
return self.dfs(N, end, start)
def dfs(self, N, end, s):
if len(s) > len(end):
return False
if s == end:
return True
for i in range(len(s)):
if (not ord('A') <= ord(s[i]) <= ord('Z') or
s[i] not in N):
continue
for _s in N[s[i]]:
res = self.dfs(N, end, s[:i] + _s + s[i + 1:])
if res:
return True
return False
================================================
FILE: lintcode/791_merge_number.py
================================================
from heapq import heappush, heappop
class Solution:
"""
@param A: the numbers
@return: the minimum cost
"""
def mergeNumber(self, A):
ans = 0
if not A:
return ans
heap = []
for a in A:
heappush(heap, a)
while len(heap) > 1:
_sum = heappop(heap) + heappop(heap)
ans += _sum
heappush(heap, _sum)
return ans
================================================
FILE: lintcode/792_kth_prime_number.py
================================================
class Solution:
"""
@param n: the number
@return: the rank of the number
"""
def kthPrime(self, n):
if not n or n < 3:
return 1
is_prime = [True] * n
is_prime[0] = is_prime[1] = False
for i in range(2, int(n ** 0.5) + 1):
if not is_prime[i]:
continue
for j in range(i * i, n, i):
is_prime[j] = False
return sum(is_prime) + 1
================================================
FILE: lintcode/793_intersection_of_arrays.py
================================================
class Solution:
"""
@param A: the arrays
@return: the number of the intersection of the arrays
"""
def intersectionOfArrays(self, A):
ans = 0
if not A:
return ans
n = len(A)
C = {}
for i in range(n):
if not A[i]:
return ans
for a in A[i]:
if a not in C:
C[a] = set()
C[a].add(i)
for a, S in C.items():
if len(S) == n:
ans += 1
return ans
================================================
FILE: lintcode/7_binary_tree_serialization.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
EMPTY = '#'
def serialize(self, root):
"""Encodes a tree to a single string.
:type root: TreeNode
:rtype: str
"""
TEMPLATE = '{{{}}}' # {{, }} is to escape brackets
if not root:
return TEMPLATE.format('')
vals = []
queue = [root]
for node in queue:
if not node:
vals.append(self.EMPTY)
continue
vals.append(str(node.val))
queue.append(node.left)
queue.append(node.right)
while vals[-1] == self.EMPTY:
vals.pop()
return TEMPLATE.format(','.join(vals))
def deserialize(self, data):
"""Decodes your encoded data to tree.
:type data: str
:rtype: TreeNode
"""
if (not data or
data[0] != '{' or
data[-1] != '}' or
len(data) < 3 or
data[1] == '#'
):
return
vals = data[1:-1].split(',')
n = len(vals)
i = 0
root = TreeNode(int(vals[i]))
queue = [root]
for node in queue:
for branch in ('left', 'right'):
i += 1
if i >= n:
break
if vals[i] == self.EMPTY:
continue
setattr(node, branch, TreeNode(int(vals[i])))
queue.append(getattr(node, branch))
return root
================================================
FILE: lintcode/813_find_anagram_mappings.py
================================================
"""
Find last pos
"""
class Solution:
def anagramMappings(self, a, b):
"""
:type a: list[int]
:type b: list[int]
:rtype: list[int]
"""
if not a or not b or len(a) != len(b):
return []
n = len(a)
ans = [-1] * n
b2i = {}
for i in range(n):
b2i[b[i]] = i
for i in range(n):
if a[i] not in b2i:
return []
ans[i] = b2i[a[i]]
return ans
"""
Strict
"""
class Solution:
def anagramMappings(self, a, b):
"""
:type a: list[int]
:type b: list[int]
:rtype: list[int]
"""
if not a or not b or len(a) != len(b):
return []
n = len(a)
ans = [-1] * n
b2i = {}
for i in range(n):
if b[i] not in b2i:
b2i[b[i]] = []
b2i[b[i]].append(i)
for i in range(n):
if not b2i.get(a[i]):
# a[i] not in b2i
# b2i[a[i]] is empty list
return []
ans[i] = b2i[a[i]].pop()
return ans
================================================
FILE: lintcode/81_data_stream_median.py
================================================
import heapq
class Solution:
def medianII(self, nums):
"""
:type nums: list[int]
:rtype: list[int]
"""
ans = []
if not nums:
return ans
minheap = []
maxheap = []
median = 0
for num in nums:
if num < median:
heapq.heappush(maxheap, -num)
else:
heapq.heappush(minheap, num)
while len(minheap) > len(maxheap):
heapq.heappush(maxheap, -heapq.heappop(minheap))
while len(maxheap) > len(minheap) + 1:
heapq.heappush(minheap, -heapq.heappop(maxheap))
if maxheap:
median = -maxheap[0]
else:
median = 0
ans.append(median)
return ans
================================================
FILE: lintcode/823_input_stream.py
================================================
"""
Merge Sort
time: O(n)
space: O(1)
"""
class Solution:
def inputStream(self, a, b):
"""
:type a: str
:type b: str
:rtype: str, 'NO' or 'YES'
"""
RES = ('NO', 'YES')
if a == b == '':
return RES[1]
BACK = '<'
m, n = len(a), len(b)
i, j = m - 1, n - 1
acnt = bcnt = 0 # count the backspace in both a and b
while i >= 0 and j >= 0:
while i >= 0 and (a[i] == BACK or acnt):
acnt += 1 if a[i] == BACK else -1
i -= 1
while j >= 0 and (b[j] == BACK or bcnt):
bcnt += 1 if b[j] == BACK else -1
j -= 1
if a[i] != b[j]:
return RES[0]
i -= 1
j -= 1
while i >= 0 and (a[i] == BACK or acnt):
acnt += 1 if a[i] == BACK else -1
i -= 1
while j >= 0 and (b[j] == BACK or bcnt):
bcnt += 1 if b[j] == BACK else -1
j -= 1
return RES[int(i == j)]
"""
Stack
time: O(n)
space: O(n)
"""
class Solution:
def inputStream(self, a, b):
"""
:type a: str
:type b: str
:rtype: str, 'NO' or 'YES'
"""
RES = ('NO', 'YES')
if a == '' and b == '':
return RES[1]
if a is None or b is None:
return RES[0]
RM = '<'
stack = []
for c in a:
if c != RM:
stack.append(c)
elif stack:
# c == RM
stack.pop()
_stack = []
for c in b:
if c != RM:
_stack.append(c)
elif _stack:
# c == RM
_stack.pop()
return RES[int(stack == _stack)]
================================================
FILE: lintcode/824_single_number_iv.py
================================================
class Solution:
def getSingleNumber(self, nums):
"""
:type nums: list[int]
:rtype: int
"""
if not nums:
return -1
if len(nums) == 1:
return nums[0]
n = len(nums)
left, right = 0, n - 1
while left + 1 < right:
mid = (left + right) // 2
if mid > 0 and nums[mid] == nums[mid - 1]:
if mid & 1 == 1:
left = mid
else:
right = mid
else:
if mid & 1 == 1:
right = mid
else:
left = mid
for mid in (left, right):
if mid > 0 and nums[mid] == nums[mid - 1]:
continue
if mid + 1 < n and nums[mid] == nums[mid + 1]:
continue
return nums[mid]
return -1
================================================
FILE: lintcode/826_computer_maintenance.py
================================================
class Solution:
"""
@param m: the rows of matrix
@param n: the cols of matrix
@param P: the bad computers
@return: The answer
"""
def maintenance(self, m, n, P):
if not m or not n or not P:
return 0
dp = [[0, 0] for _ in range(m)]
G = [[0] * n for _ in range(m)]
for p in P:
G[p.x][p.y] = 1
for i in range(m):
left = right = -1
for j in range(n):
if G[i][j] == 0:
continue
left = max(left, n - 1 - j)
right = max(right, j)
if i == 0:
if right == -1:
dp[i][0] = 0
dp[i][1] = n - 1
else:
dp[i][0] = 2 * right
dp[i][1] = n - 1
else:
if right == -1:
dp[i][0] = dp[i - 1][0] + 1
dp[i][1] = dp[i - 1][1] + 1
else:
dp[i][0] = 1 + min(
dp[i - 1][0] + 2 * right,
dp[i - 1][1] + n - 1
)
dp[i][1] = 1 + min(
dp[i - 1][1] + 2 * left,
dp[i - 1][0] + n - 1
)
return min(dp[m - 1][0], dp[m - 1][1])
================================================
FILE: lintcode/830_string_sort.py
================================================
class Solution:
"""
@param s: the string that needs to be sorted
@return: sorted string
"""
def stringSort(self, s):
if not s:
return ''
return ''.join(sorted(s, key=lambda c: (-s.count(c), c)))
================================================
FILE: lintcode/831_3sum_ii.py
================================================
class Solution:
"""
@param n: an integer
@return: the number of solutions
"""
def threeSum2(self, n):
ans = 0
nums = []
for a in range(n + 1):
if a * a > n:
break
nums.append(a * a)
nums.append(a * a)
nums.append(a * a)
m = len(nums)
for a in range(m - 2):
if a > 0 and nums[a] == nums[a - 1]:
continue
b = a + 1
c = m - 1
while b < c:
_sum = nums[a] + nums[b] + nums[c]
if _sum < n:
b += 1
elif _sum > n:
c -= 1
else:
ans += 1
b += 1
c -= 1
while b < c and nums[b] == nums[b - 1]:
b += 1
while b < c and nums[c] == nums[c + 1]:
c -= 1
return ans
class Solution:
"""
@param n: an integer
@return: the number of solutions
"""
def threeSum2(self, n):
ans = 0
m = int(n ** 0.5)
for a in range(m + 1):
target = n - a * a
b, c = a, m
while b <= c:
_sum = b * b + c * c
if _sum < target:
b += 1
elif _sum > target:
c -= 1
else:
ans += 1
b += 1
return ans
================================================
FILE: lintcode/832_count_negative_number.py
================================================
class Solution:
"""
@param g: the sorted matrix
@return: the number of Negative Number
"""
def countNumber(self, g):
ans = 0
if not g or not g[0]:
return ans
m, n = len(g), len(g[0])
for i in range(m):
left, right = 0, n - 1
while left + 1 < right:
mid = (left + right) // 2
if g[i][mid] < 0:
left = mid
else:
right = mid
if g[i][left] >= 0:
continue
ans += left + 1 if g[i][right] >= 0 else right + 1
return ans
================================================
FILE: lintcode/833_process_sequence.py
================================================
class Solution:
"""
@param logs: Sequence of processes
@param queries: Sequence of queries
@return: Return the number of processes
"""
def numberOfProcesses(self, logs, queries):
time = []
# 0: end, 1: start, 2: in progress
for log in logs:
time.append((log.start, 1))
time.append((log.end + 1, 0))
for t in queries:
time.append((t, 2))
time.sort()
cnt = 0
time2cnt = {}
for t, status in time:
if status == 0:
cnt -= 1
elif status == 1:
cnt += 1
time2cnt[t] = cnt
return [time2cnt[t] for t in queries]
================================================
FILE: lintcode/85_insert_node_in_a_binary_search_tree.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
# recursion
class Solution:
"""
@param: root: The root of the binary search tree.
@param: node: insert this node into the binary search tree
@return: The root of the new binary search tree.
"""
def insertNode(self, root, node):
if not root:
return node
if node.val < root.val:
root.left = self.insertNode(root.left, node)
else:
root.right = self.insertNode(root.right, node)
return root
# iteration
class Solution:
"""
@param: root: The root of the binary search tree.
@param: node: insert this node into the binary search tree
@return: The root of the new binary search tree.
"""
def insertNode(self, root, node):
if not root:
return node
curr = root
while curr is not node:
if node.val < curr.val:
if curr.left is None:
curr.left = node
curr = curr.left
else:
if curr.right is None:
curr.right = node
curr = curr.right
return root
================================================
FILE: lintcode/86_binary_search_tree_iterator.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
Example of iterate a tree:
iterator = BSTIterator(root)
while iterator.hasNext():
node = iterator.next()
do something for node
"""
class BSTIterator:
"""
@param: root: The root of binary tree.
"""
def __init__(self, root):
self.stack = []
self.node = root
"""
@return: True if there has next node, or false
"""
def hasNext(self):
return self.node or self.stack
"""
@return: return next node
"""
def next(self):
node = self.node
stack = self.stack
while node:
stack.append(node)
node = node.left
node = stack.pop()
nxt = node
self.node = node.right
return nxt
================================================
FILE: lintcode/87_remove_node_in_binary_search_tree.py
================================================
"""
Main Concept:
https://discuss.leetcode.com/topic/65792/recursive-easy-to-understand-java-solution
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
"""
@param: root: The root of the binary search tree.
@param: target: Remove the node with given value.
@return: The root of the binary search tree after removal.
"""
def removeNode(self, root, target):
if not root:
return root
if root.val == target:
if not root.left:
return root.right
if not root.right:
return root.left
min_node = self.find_min(root.right)
root.val = min_node.val
root.right = self.removeNode(root.right, root.val)
return root
if target < root.val:
root.left = self.removeNode(root.left, target)
else:
root.right = self.removeNode(root.right, target)
return root
def find_min(self, node):
if not node:
return node
while node.left:
node = node.left
return node
================================================
FILE: lintcode/88_lowest_common_ancestor.py
================================================
"""
Notice:
Assume two nodes are exist in tree.
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
def lowestCommonAncestor(self, root, a, b):
"""
:type root: TreeNode
:type a: TreeNode
:type b: TreeNode
:rtype: TreeNode
"""
if not root or root is a or root is b:
return root
left = self.lowestCommonAncestor(root.left, a, b)
right = self.lowestCommonAncestor(root.right, a, b)
if left and right:
return root
if left:
return left
if right:
return right
================================================
FILE: lintcode/89_k_sum.py
================================================
class Solution:
"""
@param A: An integer array
@param K: A positive integer (K <= length(A))
@param target: An integer
@return: An integer
"""
def kSum(self, A, K, target):
n = len(A)
"""
`dp[i][j][k]` means the ways we can take `j` in previous `i` nums and its sum equals `k`
"""
dp = [[[0] * (target + 1) for _ in range(K + 1)] for _ in range(n + 1)]
for i in range(n + 1):
dp[i][0][0] = 1
for i in range(1, n + 1):
for j in range(1, min(K, i) + 1):
for k in range(1, target + 1):
if k >= A[i - 1]:
dp[i][j][k] += dp[i - 1][j - 1][k - A[i - 1]]
dp[i][j][k] += dp[i - 1][j][k]
return dp[n][K][target]
================================================
FILE: lintcode/8_rotate_string.py
================================================
class Solution:
"""
@param: S: An array of char
@param: x: An integer
@return: nothing
"""
def rotateString(self, S, x):
if not S or not x:
return S
n = len(S)
x %= n
self.reverse(S, 0, n - x - 1)
self.reverse(S, n - x, n - 1)
self.reverse(S, 0, n - 1)
def reverse(self, S, start, end):
while start < end:
S[start], S[end] = S[end], S[start]
start += 1
end -= 1
================================================
FILE: lintcode/900_closest_binary_search_tree_value.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
def closestValue(self, root, target):
"""
:type root: TreeNode
:type target: float
:rtype: int
"""
if not root:
return float('-inf')
if root.left and target < root.val:
left = self.closestValue(root.left, target)
if abs(left - target) < abs(root.val - target):
return left
if root.right and target > root.val:
right = self.closestValue(root.right, target)
if abs(right - target) < abs(root.val - target):
return right
return root.val
================================================
FILE: lintcode/901_closest_binary_search_tree_value_ii.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
def closestKValues(self, root, target, k):
"""
:type root: TreeNode
:type target: float
:type k: int
"""
ans = []
if not root:
return ans
vals = []
self.inorder_traverse(root, vals)
n = len(vals)
i = 0
while i < n and vals[i] < target:
i += 1
i, j = i - 1, i
while k and i >= 0 and j < n:
if target - vals[i] < vals[j] - target:
ans.append(vals[i])
i -= 1
else:
ans.append(vals[j])
j += 1
k -= 1
while k and i >= 0:
ans.append(vals[i])
i -= 1
k -= 1
while k and j < n:
ans.append(vals[j])
j += 1
k -= 1
return ans
def inorder_traverse(self, root, vals):
if not root:
return
self.inorder_traverse(root.left, vals)
vals.append(root.val)
self.inorder_traverse(root.right, vals)
================================================
FILE: lintcode/919_meeting_rooms_ii.py
================================================
"""
Definition of Interval.
class Interval(object):
def __init__(self, start, end):
self.start = start
self.end = end
"""
class Solution:
def minMeetingRooms(self, intervals):
"""
:type intervals: list[Interval]
:rtype: int
"""
ans = 0
if not intervals:
return ans
time = []
for x in intervals:
time.append((x.start, True))
time.append((x.end, False))
time.sort()
cnt = 0
for t, is_start in time:
if is_start:
cnt += 1
else:
cnt -= 1
if cnt > ans:
ans = cnt
return ans
================================================
FILE: lintcode/920_meeting_rooms.py
================================================
"""
Definition of Interval.
class Interval:
def __init__(self, start, end):
self.start = start
self.end = end
"""
"""
Sweep Line
time: O(n)
space: O(n)
"""
class Solution:
def canAttendMeetings(self, intervals):
"""
:type intervals: list[Interval]
:rtype: bool
"""
timeline = []
for interval in intervals:
timeline.append((interval.start, True))
timeline.append((interval.end, False))
timeline.sort()
cnt = 0
for time, is_start in timeline:
if is_start:
cnt += 1
else:
cnt -= 1
if cnt > 1:
return False
return True
"""
Sorting
time: O(nlogn)
space: O(1)
"""
class Solution:
def canAttendMeetings(self, intervals):
"""
:type intervals: list[Interval]
:rtype: bool
"""
intervals.sort(key=lambda x: (x.start, x.end))
for i in range(1, len(intervals)):
if intervals[i].start < intervals[i - 1].end:
return False
return True
================================================
FILE: lintcode/92_backpack.py
================================================
class Solution:
# @param m: An integer m denotes the size of a backpack
# @param A: Given n items with size A[i]
# @return: The maximum size
def backPack(self, m, A):
if not A:
return 0
n = len(A)
dp = [[False] * (m + 1) for _ in range(n + 1)]
dp[0][0] = True
for i in range(1, n + 1):
for j in range(m + 1):
dp[i][j] = dp[i - 1][j]
if (j >= A[i - 1]
and dp[i - 1][j - A[i - 1]]):
dp[i][j] = True
for j in range(m, -1, -1):
if dp[n][j]:
return j
return 0
================================================
FILE: lintcode/93_balanced_binary_tree.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
"""
@param: root: The root of binary tree.
@return: True if this Binary tree is Balanced, or false.
"""
def isBalanced(self, root):
return self._divide_conquer(root)[0]
def _divide_conquer(self, node):
if not node:
return True, 0
is_balanced_left, maxdepth_left = self._divide_conquer(node.left)
if not is_balanced_left:
return False, 0
is_balanced_right, maxdepth_right = self._divide_conquer(node.right)
if not is_balanced_right:
return False, 0
return abs(maxdepth_left - maxdepth_right) <= 1, \
max(maxdepth_left, maxdepth_right) + 1
================================================
FILE: lintcode/95_validate_binary_search_tree.py
================================================
"""
Main Concept:
since the fact,
if we said a tree is NOT a binary search tree,
and then the values we got by inorder traversal
is must be a non-descending sequence, that is, A[i+1] >= A[i].
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
def isValidBST(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
stack = []
node = root
pre = None
while node or stack:
while node:
stack.append(node)
node = node.left
node = stack.pop()
if pre and node.val <= pre.val:
return False
pre = node
node = node.right
return True
class Solution:
ans = True
pre = None
def isValidBST(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
if not root:
return self.ans
self.isValidBST(root.left)
if self.pre and root.val <= self.pre.val:
self.ans = False
return self.ans
self.pre = root
self.isValidBST(root.right)
return self.ans
================================================
FILE: lintcode/96_partition_list.py
================================================
"""
Definition of ListNode
class ListNode(object):
def __init__(self, val, next=None):
self.val = val
self.next = next
"""
class Solution:
"""
@param: head: The first node of linked list
@param: x: An integer
@return: A ListNode
"""
def partition(self, head, x):
if not head:
return
left_dummy = left_tail = ListNode(-1)
right_dummy = right_tail = ListNode(-1)
while head:
node = ListNode(head.val)
if head.val < x:
left_tail.next = node
left_tail = node
else:
right_tail.next = node
right_tail = node
head = head.next
left_tail.next = right_dummy.next
return left_dummy.next
================================================
FILE: lintcode/97_maximum_depth_of_binary_tree.py
================================================
"""
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
"""
@param root: The root of binary tree.
@return: An integer
"""
def maxDepth(self, root):
if not root:
return 0
return 1 + max(
self.maxDepth(root.left),
self.maxDepth(root.right)
)
================================================
FILE: lintcode/98_sort_list.py
================================================
"""
Definition of ListNode
class ListNode(object):
def __init__(self, val, next=None):
self.val = val
self.next = next
"""
class Solution:
"""
@param: head: The head of linked list.
@return: You should return the head of the sorted linked list, using constant space complexity.
"""
def sortList(self, head):
return self.merge_sort(head)
def quick_sort(self, head):
if not head or not head.next:
return head
mid = self.find_middle(head)
left_dummy = left_tail = ListNode(0)
mid_dummy = mid_tail = ListNode(0)
right_dummy = right_tail = ListNode(0)
while head:
if head.val < mid.val:
left_tail.next = head
left_tail = head
elif head.val > mid.val:
right_tail.next = head
right_tail = head
else:
mid_tail.next = head
mid_tail = head
head = head.next
left_tail.next = mid_tail.next = right_tail.next = None
left_dummy.next = self.quick_sort(left_dummy.next)
right_dummy.next = self.quick_sort(right_dummy.next)
dummy = tail = ListNode(0)
for node in [left_dummy, mid_dummy, right_dummy]:
tail.next = node.next
tail = self.get_tail(tail)
return dummy.next
def merge_sort(self, head):
if not head or not head.next:
return head
left = head
mid = self.find_middle(head)
right = mid.next
mid.next = None
left = self.merge_sort(left)
right = self.merge_sort(right)
dummy = tail = ListNode(0)
while left and right:
if left.val < right.val:
tail.next = left
left = left.next
else:
tail.next = right
right = right.next
tail = tail.next
if left:
tail.next = left
else:
tail.next = right
return dummy.next
def find_middle(self, head):
slow, fast = head, head.next
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow
def get_tail(self, head):
if not head:
return
while head.next:
head = head.next
return head
================================================
FILE: lintcode/9_fizz_buzz.py
================================================
class Solution:
def fizzBuzz(self, n):
"""
:type n: int
:rtype: List[str]
"""
ans = []
if not n:
return ans
for i in range(1, n + 1):
if i % 3 == 0 and i % 5 == 0:
ans.append('FizzBuzz')
elif i % 3 == 0:
ans.append('Fizz')
elif i % 5 == 0:
ans.append('Buzz')
else:
ans.append(str(i))
return ans
class Solution:
def fizzBuzz(self, n):
"""
:type n: int
:rtype: List[str]
"""
ans = []
if not n:
return ans
a = i3 = i5 = 1
while a <= n:
while a <= n and a < i3 * 3 and a < i5 * 5:
ans.append(str(a))
a += 1
if a <= n and a == i3 * 3 and a == i5 * 5:
ans.append('fizz buzz')
a += 1
i3 += 1
i5 += 1
continue
while a <= n and a == i3 * 3:
ans.append('fizz')
a += 1
i3 += 1
while a <= n and a == i5 * 5:
ans.append('buzz')
a += 1
i5 += 1
return ans
================================================
FILE: other/anti_queue_reconstruction_by_height.py
================================================
"""
This question is related with `leetcode/406_queue_reconstruction_by_height`
"""
def reorder(nums):
"""
:type nums: list[int]
:rtype: list[int]
>>> reorder([0, 1, 2, 1, 0])
[4, 2, 1, 3, 5]
"""
if not nums:
return []
n = len(nums)
ans = [0] * n
cands = [i for i in range(1, n + 1)]
for i in range(n - 1, -1, -1):
ans[i] = cands.pop(i - nums[i])
return ans
================================================
FILE: other/binary_tree_maximum_path_product.py
================================================
"""
Testing:
>>> class TreeNode:
... def __init__(self, val):
... self.val = val
... self.left = self.right = None
>>> trees = []
>>> tree_infos = [
... ((
... (-3, None, None),
... ), -3),
... ((
... (3, 4, 5),
... (4, None, None),
... (5, None, None),
... ), 60),
... ((
... (3, 4, -5),
... (4, None, None),
... (-5, None, None),
... ), 12),
... ((
... (3, 4, -5),
... (4, -6, 7),
... (-5, 2, -9),
... (-6, None, None),
... (7, None, None),
... (2, None, None),
... (-9, None, None),
... ), 3780),
... ((
... (0, 4, -5),
... (4, -6, 7),
... (-5, 2, -9),
... (-6, None, None),
... (7, None, None),
... (2, None, None),
... (-9, None, None),
... ), 90),
... ]
>>> for info, _ in tree_infos:
... nodes = {node[0]: TreeNode(node[0]) for node in info}
...
... for val, left, right in info:
... if left:
... nodes[val].left = nodes[left]
... if right:
... nodes[val].right = nodes[right]
...
... trees.append(nodes[info[0][0]])
>>> gotcha = []
>>> s = Solution()
>>> for i in range(len(trees)):
... res = s.maxPathProd(trees[i])
... if res != tree_infos[i][1]: print(res, tree_infos[i])
... gotcha.append(res == tree_infos[i][1])
>>> bool(gotcha) and all(gotcha)
True
"""
class Solution:
def maxPathProd(self, root):
"""
:type root: TreeNode
:rtype: int
"""
if not root:
return 0
ans, _, _ = self.divide_conquer(root)
return ans
def divide_conquer(self, node):
if not node:
return float('-inf'), 1, 1
res_left, max_left, min_left = self.divide_conquer(node.left)
res_right, max_right, min_right = self.divide_conquer(node.right)
a = node.val * max(max_left, max_right)
b = node.val * min(min_left, min_right)
res = max(
# ignoring current (0)
res_left,
res_right,
# only current (1)
node.val,
# half path (2)
a, b,
# go through current (3)
node.val * max_left * max_right,
node.val * min_left * min_right,
)
return res, max(a, b), min(a, b)
================================================
FILE: other/candy_crush.py
================================================
"""
Design a algorithm to initialize the board of Candy Crush Saga.
With M x N board, Q types of candies.
Rules:
1. with randomization
2. no 3 for run after initialization
3. must contain at least one valid move at the beginning
Thought:
1. for random candies and no 3 for run at begining
- random generate, and re-gen if invalid
2. prevent dead loop while checking valid
- [x] iterate by order
=> dead loop may occur when visiting the cells arround prefilled
- [v] BFS from the prefilled cell to boundary
3. at least one valid move at the beginning
- just define the patterns (3 for run => 12 patterns)
prefill to board, and mark as unchangable
REF:
1. LC 723
2. http://massivetechinterview.blogspot.com/2015/11/how-to-initialize-board-of-candy-crush.html
TODO:
1. implement `move` to move candy to D/R/U/L, and
- remove the connected candy
- the candy will drop if there is empty below
2. count score
3. end up game if no more valid move or the game is finished
Testing:
>>> gotcha = []
>>> for params in (
... (7, 7, 3), (10, 10, 3), (20, 20, 3),
... (7, 7, 5), (10, 10, 4), (20, 20, 6),
... ):
... game = CandyCrush(*params)
... for _ in range(5):
... game.reset_board()
... valid = _check_board_valid(game.get_board())
... if not valid: print(game._print_board())
... gotcha.append(valid)
>>> bool(gotcha) and all(gotcha)
True
"""
import random
def _check_board_valid(board):
# for testing
m, n = len(board), len(board[0])
for x in range(2, m):
for y in range(n):
if board[x][y] == board[x - 1][y] == board[x - 2][y]:
return False
for y in range(2, n):
for x in range(m):
if board[x][y] == board[x][y - 1] == board[x][y - 2]:
return False
return True
class CandyCrush:
def __init__(self, m, n, q):
"""
:type m: int
:type n: int
:type q: int
"""
self.width = n
self.height = m
self.__board = None
self.__types = q
self.__patterns = ( # with rotating, we can find up to 12 patterns
(-1, -1, 1, 0), # /--
(-1, 1, 1, 0), # --\
(-1, -1, 1, -1), # /-\
)
self.reset_board()
def reset_board(self):
"""
:rtype: void
"""
m, n = self.height, self.width
b = self.__board = [[-1] * n for _ in range(m)]
x, y = random.randint(1, m - 2), random.randint(1, n - 2)
d = random.choice(self.__patterns) # dx1, dy1, dx2, dy2
q = random.randrange(self.__types)
queue = [(x + d[0], y + d[1]), (x, y), (x + d[2], y + d[3])]
visited = set(queue)
for x, y in queue:
b[x][y] = q
for x, y in queue:
for dx, dy in (
(0, -1), (0, 1),
(-1, 0), (1, 0),
):
_x = x + dx
_y = y + dy
if not (0 <= _x < m and 0 <= _y < n):
continue
if (_x, _y) in visited:
continue
visited.add((_x, _y))
queue.append((_x, _y))
while not self._check_cell_valid(_x, _y):
b[_x][_y] = random.randrange(self.__types)
def get_board(self):
"""
:rtype: list[list[int]]
"""
return self.__board
def _check_cell_valid(self, x, y):
"""
:type x: int
:type y: int
:rtype: bool
"""
b = self.__board
m, n = self.height, self.width
if b[x][y] == -1:
return False
for x1, y1, x2, y2 in (
(x - 2, y, x - 1, y), (x + 1, y, x + 2, y), # most up, down
(x, y - 2, x, y - 1), (x, y + 1, x, y + 2), # most left, right
(x - 1, y, x + 1, y), (x, y - 1, x, y + 1), # cross middle
):
if not (
0 <= x1 < m and 0 <= y1 < n and
0 <= x2 < m and 0 <= y2 < n
):
continue
if b[x][y] == b[x1][y1] == b[x2][y2]:
return False
return True
def _print_board(self):
# for testing
print('\n'.join(str(r) for r in self.__board))
================================================
FILE: other/card_shuffler.py
================================================
"""
>>> CASE = (
... (['AB', [1, 0]], 2),
... (['ABCD', [1, 2, 3, 0]], 4),
... (['ABCDE', [4, 3, 2, 0, 1]], 4),
... (['ABCDE', [0, 3, 4, 0, 2]], -1),
... )
>>> all(card_shuffler(*inpt) == oupt for inpt, oupt in CASE)
True
"""
def card_shuffler(cards, shuffles):
"""
:type cards: Iterable[str]
:type shuffles: list[int]
:rtype: int
"""
n = len(cards)
offsets = [0] * n
for i in range(n):
offsets[i] = get_offset(i, shuffles)
if offsets[i] == -1:
return -1
return get_lcm(*offsets)
def get_offset(start, shuffles):
visited = set()
i = start
offset = 0
while i not in visited:
visited.add(i)
i = shuffles[i]
offset += 1
return offset if i == start else -1
def get_lcm(*nums):
lcm = nums[0]
for i in range(1, len(nums)):
lcm = lcm // get_gcd(lcm, nums[i]) * nums[i]
return lcm
def get_gcd(a, b):
while b:
a, b = b, a % b
return a
================================================
FILE: other/deep_fetch.js
================================================
function deepFetch(target, path, defaultValue = undefined) {
if (!target || !Array.isArray(path)) {
return defaultValue
}
return path.reduce((level, key) => (
(level && level[key]) ? level[key] : defaultValue
), target)
}
module.exports = deepFetch
================================================
FILE: other/deep_fetch.test.js
================================================
const deepFetch = require('./deep_fetch')
test('It could deeply fetch props from target', () => {
const target = {
a: 1,
b: 2,
c: {
d: 3,
e: {
f: 4,
g: 5,
},
},
}
expect(deepFetch(target, ['b'])).toBe(2)
expect(deepFetch(target, ['c', 'd'])).toBe(3)
expect(deepFetch(target, ['c', 'e', 'f'])).toBe(4)
expect(deepFetch(target, ['c', 'e', 'h'])).toBe(undefined)
expect(deepFetch(target, ['c', 'e', 'h'], -1)).toBe(-1)
})
================================================
FILE: other/find_treasure_in_maze.py
================================================
"""
http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=82
Description:
一个叫ACM的寻宝者找到了一个藏宝图,它根据藏宝图找到了一个迷宫,
这是一个很特别的迷宫,迷宫里有N个编过号的门(N<=5),
它们分别被编号为A,B,C,D,E.为了找到宝藏,ACM必须打开门,
但是,开门之前必须在迷宫里找到这个打开这个门所需的所有钥匙(每个门都至少有一把钥匙),
例如:现在A门有三把钥匙,ACM就必须找全三把钥匙才能打开A门。
现在请你编写一个程序来告诉ACM,他能不能顺利的得到宝藏。
每组测试数据的第一行包含了两个整数M,N(1>> s = Solution()
>>> gotcha = []
>>> for _in, _out in (
... (['S.X.', 'a.X.', '..XG', '....'], True),
... (['S.Xa', '.aXB', 'b.AG'], False),
... (['aX.S', 'bXAB', 'cXCD', 'dXGX'], False),
... (['SbAdX.', 'a.BD.G', 'cBCdaX'], True),
... (['S.Aa.X.', 'a.Xc.C.', 'b.X..DG', '.cB..Xd'], True),
... ):
... res = s.find_treasure_in_maze(_in)
... if res != _out: print(_in, res)
... gotcha.append(res == _out)
>>> bool(gotcha) and all(gotcha)
True
"""
class Solution:
START = 'S'
GOLD = 'G'
OBSTACLE = 'X'
EMPTY = '.'
DOORS = 'ABCDE'
KEYS = 'abcde'
def find_treasure_in_maze(self, maze):
"""
:type maze: list[str]
:rtype: bool
"""
if not maze or not maze[0]:
return False
m, n = len(maze), len(maze[0])
k = len(self.DOORS)
keys = [0] * k # all keys count
has_gold = False
queue = []
doors = [None] * k # doors meet when bfs
holds = [0] * k # keys meet when bfs
visited = set() # visited cell when bfs
for x in range(m):
for y in range(n):
if maze[x][y] == self.START:
queue.append((x, y))
elif maze[x][y] == self.GOLD:
has_gold = True
elif maze[x][y] in self.KEYS:
i = ord(maze[x][y]) - ord('a')
keys[i] += 1
if not has_gold or not queue:
return False
while queue or self.is_possible(maze, keys, holds, doors):
if self.bfs(maze, queue, keys, holds, doors, visited):
return True
return False
def bfs(self, maze, queue, keys, holds, doors, visited):
"""
return True if got gold, otherwise False
:type maze: list[str]
:type queue: list[tuple[int]]
:type keys: list[int]
:type holds: list[int]
:type doors: list[tuple[int]]
:type visited: set[tuple[int]]
:rtype: bool
"""
m, n = len(maze), len(maze[0])
for x, y in queue:
for dx, dy in (
(0, -1), (0, 1),
(-1, 0), (1, 0),
):
_x = x + dx
_y = y + dy
if not (0 <= _x < m and 0 <= _y < n):
continue
if (_x, _y) in visited or maze[_x][_y] == self.OBSTACLE:
continue
if maze[_x][_y] == self.GOLD:
return True
if maze[_x][_y] in self.DOORS:
i = ord(maze[_x][_y]) - ord('A')
if holds[i] < keys[i]:
doors[i] = (_x, _y)
continue
doors[i] = None
if maze[_x][_y] in self.KEYS:
i = ord(maze[_x][_y]) - ord('a')
holds[i] += 1
visited.add((_x, _y))
queue.append((_x, _y))
queue.clear()
return False
def is_possible(self, maze, keys, holds, doors):
"""
:type maze: list[str]
:type keys: list[int]
:type holds: list[int]
:type doors: list[tuple[int]]
:rtype: bool
"""
for door in doors:
if not door:
continue
x, y = door
i = ord(maze[x][y]) - ord('A')
if holds[i] >= keys[i]:
return True
return False
================================================
FILE: other/find_ways_in_board_game.py
================================================
"""
>>> gotcha = []
>>> for _in, _out in (
... (0, 1), (1, 1),
... (2, 2), (3, 4),
... (4, 8), (5, 16),
... (6, 32), (7, 63),
... (8, 125), (9, 248),
... ):
... res = find_ways_in_board_game(_in)
... if res != _out: print(_in, res)
... gotcha.append(res == _out)
>>> bool(gotcha) and all(gotcha)
True
"""
def find_ways_in_board_game(n):
if not n or n < 2:
return 1
dp = [0] * (n + 1)
dp[0] = 1
for i in range(1, min(n + 1, 6)):
for j in range(i):
dp[i] += dp[j]
for i in range(6, n + 1):
dp[i] = sum((
dp[i - 1],
dp[i - 2],
dp[i - 3],
dp[i - 4],
dp[i - 5],
dp[i - 6],
))
return dp[n]
================================================
FILE: other/freq_iterator.py
================================================
"""
Testing:
>>> gotcha = []
>>> for _in, _out in (
... (
... ['foo', 'foo', 'bar', 'foo'],
... [['foo', 2], ['bar', 1], ['foo', 1]]
... ),
... (
... ['a', 'a', 'a', 'b', 'b', 'c', 'a', 'b', 'b'],
... [['a', 3], ['b', 2], ['c', 1], ['a', 1], ['b', 2]]
... ),
... (
... ['a', 'a', 'a', 'b', 'b', 'c', 'a', 'b', 'c'],
... [['a', 3], ['b', 2], ['c', 1], ['a', 1], ['b', 1], ['c', 1]]
... ),
... (
... ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'],
... [['a', 1], ['b', 1], ['c', 1],
... ['d', 1], ['e', 1], ['f', 1],
... ['g', 1], ['h', 1], ['i', 1]]
... ),
... ):
... origin_iterator = ListIterator(_in)
... res = [origin_iterator.next() for _ in range(len(_in))]
... if _in != res: print(_in, res)
... gotcha.append(_in == res)
... gotcha.append(origin_iterator.has_next() is False)
... gotcha.append(origin_iterator.next() is None)
...
... res = []
... origin_iterator = ListIterator(_in)
... freq_iterator = FreqIterator(origin_iterator)
... while freq_iterator.has_next():
... res.append(freq_iterator.next())
...
... if res != _out: print(_in, res)
... gotcha.append(res == _out)
>>> bool(gotcha) and all(gotcha)
True
"""
class FreqIterator:
def __init__(self, iterator):
if not iterator or not iterator.has_next():
return
self.iterator = iterator
self.pre = None
self.word = iterator.next()
def next(self):
if not self.has_next():
return
cnt = 1
nxt = None
while self.iterator.has_next():
nxt = self.iterator.next()
if nxt != self.word:
break
cnt += 1
self.pre = self.word
self.word = nxt
return [self.pre, cnt]
def has_next(self):
return self.pre != self.word and self.word is not None
class ListIterator:
def __init__(self, words):
self.words = words
self.i = 0
def next(self):
if not self.has_next():
return
res = self.words[self.i]
self.i += 1
return res
def has_next(self):
if self.i < len(self.words):
return True
return False
================================================
FILE: other/gcd_and_lcm.py
================================================
def get_gcd(a, b):
"""
:type a: int
:type b: int
:rtype: int
>>> get_gcd(10, 2)
2
>>> get_gcd(500, 375)
125
"""
while b:
a, b = b, a % b
return a
def get_lcm(a, b):
"""
:type a: int
:type b: int
:rtype: int
>>> get_lcm(10, 2)
10
>>> get_lcm(500, 375)
1500
"""
return (a * b) // get_gcd(a, b)
================================================
FILE: other/get_most_popular_word.py
================================================
"""
Question:
Given an integer W that represents number of words and unlimited space complexity.
Write functions insert(word) and getMostPopularWord()
such that getMostPopularWord() will always return the most popular word in the last W number of words.
Create two solutions that will optimize run-time complexity for either function
while sacrificing the run-time for the other function.
example1:
let W = 2
insert('A')
getMostPopularWord() => 'A'
insert('B')
getMostPopularWord() => 'B'
example2:
let W = 3
insert('A')
insert('A')
getMostPopularWord() => 'A'
insert('B')
getMostPopularWord() => 'A'
insert('B')
getMostPopularWord() => 'B' // since the first inserted 'A' is out of the scope of the last 3 words
follow-up1: insert => O(1), getMostPopularWord => O(W)
follow-up2: insert => O(W), getMostPopularWord => O(1)
follow-up3: insert => O(1), getMostPopularWord => O(1)
=> like LFU
- linked list: save words order
- 2d linked list: level1 is freqs list, level2 is words list
=> bucket sort
Testing:
>>> gotcha = []
>>> for (freq, words), expects in (
... ((0, 'AB'), ''),
... ((0, 'AABB'), ''),
... ((1, 'AB'), 'AB'),
... ((1, 'AABB'), 'AABB'),
... ((2, 'AB'), 'AB'),
... ((2, 'AABB'), 'AABB'),
... ((4, 'AB'), 'AB'),
... ((4, 'AABB'), 'AAAB'),
... ((8, 'AB'), 'AB'),
... ((8, 'AABB'), 'AAAB'),
... ((8, 'ABABCCCBAC'), 'ABABBCCBBC'),
... ):
... s = Solution(freq)
... for i in range(len(words)):
... gotcha.append(s.insert(words[i]) is None)
... res = s.get_most_popular_word()
... if freq == 0:
... if res != '': print(freq, words, i, '', res)
... gotcha.append(res == '')
... else:
... if res != expects[i]: print(freq, words, i, expects[i], res)
... gotcha.append(res == expects[i])
>>> bool(gotcha) and all(gotcha)
True
"""
class Solution:
def __init__(self, W):
self.cap = W
self.size = 0
self.words = (WordNode(''), WordNode('')) # D, d for words list
self.words[0].nxt = self.words[1]
self.words[1].pre = self.words[0]
self.nodes = {}
self.freqs = (FreqNode(0), FreqNode(0)) # D, d for freqs list
self.freqs[0].nxt = self.freqs[1]
self.freqs[1].pre = self.freqs[0]
def insert(self, word):
if not word or self.cap <= 0:
return
# needs to evict first,
# to ensure the coming word will be added at the tail
while self.size >= self.cap:
self.size -= 1
self._evict_word()
self.size += 1
self._add_word(word)
def get_most_popular_word(self):
return self.freqs[1].pre.words[1].pre.word
def _add_word(self, word):
# update self.words
node = WordNode(word)
node.link(self.words[1].pre, self.words[1])
# update self.freqs
node = from_freq = to_freq = None
if self.nodes.get(word):
node = self.nodes[word]
from_freq = node.freq_node
node.unlink()
else:
node = WordNode(word)
from_freq = self.freqs[0]
self.nodes[word] = node
if from_freq.nxt.freq == from_freq.freq + 1:
to_freq = from_freq.nxt
else:
to_freq = FreqNode(from_freq.freq + 1)
from_freq.after(to_freq)
to_freq.append_tail(node)
if from_freq.freq != 0 and from_freq.is_empty():
from_freq.unlink()
def _evict_word(self):
# update self.words
node = self.words[0].nxt
node.unlink()
word = node.word
# update self.freqs
node = self.nodes[word]
from_freq = node.freq_node
to_freq = None
node.unlink()
if from_freq.freq == 1:
self.nodes[word] = None
if from_freq.is_empty():
from_freq.unlink()
return
if from_freq.pre.freq == from_freq.freq - 1:
to_freq = from_freq.pre
else:
to_freq = FreqNode(from_freq.freq - 1)
from_freq.before(to_freq)
to_freq.append_tail(node)
if from_freq.is_empty():
from_freq.unlink()
class WordNode:
def __init__(self, word, freq_node=None, pre=None, nxt=None):
self.word = word
self.freq_node = freq_node
self.pre = pre
self.nxt = nxt
def link(self, pre, nxt):
self.pre = pre
self.nxt = nxt
pre.nxt = self
nxt.pre = self
def unlink(self):
self.pre.nxt = self.nxt
self.nxt.pre = self.pre
self.pre = self.nxt = self.freq_node = None
class FreqNode:
def __init__(self, freq, pre=None, nxt=None):
self.freq = freq
self.pre = pre
self.nxt = nxt
self.words = (WordNode(''), WordNode('')) # D, d for words list
self.words[0].nxt = self.words[1]
self.words[1].pre = self.words[0]
def unlink(self):
self.pre.nxt = self.nxt
self.nxt.pre = self.pre
self.pre = self.nxt = self.words = None
def before(self, freq_node):
freq_node.pre = self.pre
freq_node.nxt = self
self.pre.nxt = freq_node
self.pre = freq_node
def after(self, freq_node):
freq_node.pre = self
freq_node.nxt = self.nxt
self.nxt.pre = freq_node
self.nxt = freq_node
def is_empty(self):
return self.words[0].nxt is self.words[1]
def append_tail(self, word_node):
word_node.freq_node = self
word_node.pre = self.words[1].pre
word_node.nxt = self.words[1]
self.words[1].pre.nxt = word_node
self.words[1].pre = word_node
if __name__ == '__main__':
s = Solution(8)
_in, _out = 'ABABCCCBAC', 'ABABBCCBBC'
gotcha = []
for i in range(len(_in)):
gotcha.append(s.insert(_in[i]) is None)
res = s.get_most_popular_word()
if res != _out[i]: print(_in, i, _in[i], res)
gotcha.append(res == _out[i])
print(bool(gotcha) and all(gotcha))
================================================
FILE: other/guess_secret.py
================================================
"""
>>> gotcha = []
>>> for words, _out in (
... (['apple', 'price', 'tuple', 'agile'], 'apple'),
... (['apple', 'apple', 'apple', 'apple'], 'apple'),
... (['aa', 'ab', 'ac', 'ad', 'ae', 'af', 'ag'], 'ae'),
... (['aa', 'ab', 'ac', 'ad', 'ae', 'af', 'ag'], 'ag'),
... (['aa', 'ab', 'ab', 'ac'], 'ab'),
... ):
... secret = Secret(_out)
... res = find_secret(secret, words)
... if res != _out: print(words, res, secret.score)
... gotcha.append(res == _out)
>>> bool(gotcha) and all(gotcha)
True
"""
def find_secret(secret, words):
if not isinstance(secret, Secret):
return ''
while len(words) > 1:
has_got, correct_cnt = secret.guess(words[-1])
guess_word = words.pop()
if has_got:
return guess_word
_words = []
n = len(guess_word)
for word in words:
cnt = 0
for i in range(n):
if word[i] == guess_word[i]:
cnt += 1
if cnt == len(word):
return word
if cnt == correct_cnt:
_words.append(word)
words = _words
return words[0]
class Secret:
def __init__(self, word):
self.secret = word
self.score = 0
def guess(self, word):
if not word or len(word) != len(self.secret):
return
cnt = 0
for i in range(len(word)):
if word[i] == self.secret[i]:
cnt += 1
self.score -= 1
return [cnt == len(word), cnt]
================================================
FILE: other/inorder_non_threaded_binary_tree_traversal.py
================================================
"""
Inorder Traversal in Binary Tree
without Recursion or Stack or Modification
Recursion: The simplest way
Iteration: With Stack
Morris Traversal: With Modification
And the following code is for the 4th way
to inorder traverse binary tree
if we have parent pointers available to us.
REF: Inorder Non-threaded Binary Tree Traversal without Recursion or Stack
https://www.geeksforgeeks.org/inorder-non-threaded-binary-tree-traversal-without-recursion-or-stack/
Node Structure:
>>> class TreeNode:
... def __init__(self, val, left=None, right=None, parent=None):
... self.val = val
... self.left = left
... self.right = right
... self.parent = parent
Process:
0. go_left = True
1. keep moving to the most left
2. print val
3. if has right child, move on it. and go_left = True to repeat (1)
4. keep moving to the most parent
if no right and its just the right of its parent
5. move one more to its parent and go_left = False
Testing:
>>> trees = []
>>> tree_infos = [
... ((
... (1, None, None, 2),
... (2, 1, None, None),
... ), '1,2'),
... ((
... (20, None, 8, 22),
... (8, 20, 4, 12),
... (22, 20, None, None),
... (4, 8, None, None),
... (12, 8, 10, 14),
... (10, 12, None, None),
... (14, 12, None, None),
... ), '4,8,10,12,14,20,22'),
... ((
... (10, None, 5, 100),
... (5, 10, None, None),
... (100, 10, 80, 120),
... (80, 100, None, None),
... (120, 100, None, None),
... ), '5,10,80,100,120'),
... ((
... (1, None, 2, 3),
... (2, 1, 4, None),
... (3, 1, None, 5),
... (4, 2, None, 6),
... (5, 3, None, None),
... (6, 4, 7, 8),
... (7, 6, None, None),
... (8, 6, None, None),
... ), '4,7,6,8,2,1,3,5'),
... ((
... (1, None, None, 2),
... (2, 1, None, 3),
... (3, 2, None, 4),
... (4, 3, None, 5),
... (5, 4, None, 6),
... (6, 5, None, 7),
... (7, 6, None, None),
... ), '1,2,3,4,5,6,7'),
... ]
>>> for info, _ in tree_infos:
... nodes = {node[0]: TreeNode(node[0]) for node in info}
...
... for val, parent, left, right in info:
... if parent:
... nodes[val].parent = nodes[parent]
... if left:
... nodes[val].left = nodes[left]
... if right:
... nodes[val].right = nodes[right]
...
... trees.append(nodes[info[0][0]])
>>> gotcha = []
>>> for i in range(len(trees)):
... res = []
... inorder_traverse(trees[i], callback=lambda val: res.append(str(val)))
... gotcha.append(','.join(res) == tree_infos[i][1])
>>> bool(gotcha) and all(gotcha)
True
"""
def inorder_traverse(root, *, callback):
go_left = True
while root:
while go_left and root.left:
root = root.left
callback(root.val)
if root.right:
root = root.right
go_left = True
continue
# if no right child,
# and its just a right child of its parent
while root.parent and root is root.parent.right:
root = root.parent
root = root.parent
go_left = False
================================================
FILE: other/is_valid_relation.py
================================================
"""
Question:
The rules look like this:
'A NE B' - means this means point A is located northeast of point B.
'A SW C' - means that point A is southwest of C.
'A N D' - means that point A is north of D but maybe true north, northeast, or northwest.
Given a list of rules, check if the sum of the rules validates. For example:
['A N B', 'B NE C', 'C N A'], returns False
['A N B', 'B NE C', 'C S A'], returns True
Testing:
>>> gotcha = []
>>> for _in, _out in (
... ([], False),
... ([''], False),
... (['A N B', 'B NE C', 'C N A'], False),
... (['A N B', 'B NW C', 'C N C'], False),
... (['A N B', 'B N C', 'C N B'], False),
... (['A N B', 'B NE C', 'C S A'], True),
... (['A N B', 'B NW C', 'C S A'], True),
... (['A E B', 'B E C', 'C S D', 'D S E'], True),
... (['A N B', 'B S A', 'C E D', 'D W C'], True),
... ):
... res = is_valid_relation(_in)
... if res != _out: print(_in, res)
... gotcha.append(res == _out)
>>> bool(gotcha) and all(gotcha)
True
"""
import collections
def is_valid_relation(strs):
"""
:type strs: list[str]
:rtype: bool
"""
if not strs:
return False
# egraph => scan from east to west
egraph = collections.defaultdict(set)
wgraph = collections.defaultdict(set)
ngraph = collections.defaultdict(set)
sgraph = collections.defaultdict(set)
for s in strs:
if not s:
return False
dst, d, src = s.split()
if 'E' in d:
egraph[dst].add(src)
wgraph[src].add(dst)
elif 'W' in d:
wgraph[dst].add(src)
egraph[src].add(dst)
if 'N' in d:
ngraph[dst].add(src)
sgraph[src].add(dst)
elif 'S' in d:
sgraph[dst].add(src)
ngraph[src].add(dst)
for graph in (egraph, wgraph, ngraph, sgraph):
for node in graph.keys():
if dfs(graph, node, set()):
return False
return True
def dfs(graph, node, visited):
"""
returns True if there is cycle in graph
:type graph: dict{str: set}
:type node: str
:type visited: set
:rtype: bool
"""
if node not in graph:
return False
if node in visited:
return True
visited.add(node)
for nxt in graph[node]:
if dfs(graph, nxt, visited):
return True
return False
================================================
FILE: other/merge_mail.py
================================================
def merge_mail(data):
"""group owners if their mails got a connection
>>> merge_mail({
... 'A1': ['a1@gmail.com', 'a2@gmail.com'],
... 'A2': ['b1@gmail.com', 'a2@gmail.com'],
... 'A3': ['c1@gmail.com'],
... 'A4': ['c1@gmail.com', 'd1@gmail.com'],
... 'A5': ['b1@gmail.com', 'e1@gmail.com'],
... })
[['A1', 'A2', 'A5'], ['A3', 'A4']]
>>> merge_mail({
... 'A1': ['a1@gmail.com', 'a2@gmail.com'],
... 'A2': ['b1@gmail.com', 'a2@gmail.com'],
... 'A3': ['b1@gmail.com', 'c1@gmail.com'],
... 'A4': ['c1@gmail.com', 'd1@gmail.com'],
... 'A5': ['b1@gmail.com', 'e1@gmail.com'],
... })
[['A1', 'A2', 'A3', 'A4', 'A5']]
"""
owners = {} # owners
mail2owners = {} # mail to owners
for owner, mails in data.items():
find(owners, owner)
for mail in mails:
if mail not in mail2owners:
mail2owners[mail] = owner
continue
mail2owners[mail] = connect(
owners, owner, mail2owners[mail]
)
ans = {}
for k, v in owners.items():
if v not in ans:
ans[v] = []
ans[v].append(k)
return list(ans.values())
def connect(nodes, a, b):
_a = find(nodes, a)
_b = find(nodes, b)
if _a is not _b:
nodes[_a] = _b
return _b
def find(nodes, a):
if a not in nodes:
nodes[a] = a
return a
if nodes[a] is a:
return a
nodes[a] = find(nodes, nodes[a])
return nodes[a]
================================================
FILE: other/reservation.py
================================================
"""
OO Design
设计一个剧院的订座系统,user一次可以query不多于5个座位,
这个系统要能return相应数量并尽可能相连的座位。
用户可以选择订或不订这些位子,订的话这些位子就不available了,
没订的话位子就都还available,
但要求这位用户下次query还是return这些位子(防止用户不停刷系统以拿到他们想要的位子),
除非下次query前这些位子里k个被别人订了,那系统再生成k个available的位子。
My understanding
r1 => 尽可能相连的座位 => MUST be in same row
r2 => requery => same seats if seats are also available,
otherwise find new available seats and still follow (r1)
"""
class Reservation:
def __init__(self, m, n):
pass
def get_seats(self, user_id, n=0):
pass
================================================
FILE: other/robot_cleaner.py
================================================
"""
Given a robot cleaner in a room modeled as a grid.
Each cell in the grid can be empty or blocked.
The robot cleaner with 4 given APIs can move forward, turn left or turn right.
When it tries to move into a blocked cell,
its bumper sensor detects the obstacle and it stays on the current cell.
The 4 APIs are:
clean(): clean the current location.
turnleft(k=1): turn left k*90 degrees.
turnrigt(k=1): turn right k*90 degrees.
move(direction=None): move forward for 1 position, return False if that’s not possible.
- How do you clean the entire space?
- How long will it take? (1 step == 1 time unit)
- Can you show paths?
Need to ask:
- is there an api to detect the cell need to clean?
- are both the robot's direction and coordinate need to same as room?
- once we called the `move` api and passed a direction.
will the robot turn its face in that direction?
and move forward with 1 unit?
REF:
- http://www.hoangvancong.com/2016/10/28/bfs-backtrack-robot-don-dep-cleaning-robot
- https://blog.csdn.net/aozil_yang/article/details/52177644
- http://www.1point3acres.com/bbs/thread-289514-1-1.html
- http://www.1point3acres.com/bbs/thread-345555-1-1.html
Testing:
1. is the mock api working?
>>> room = Room([
... [0, 0, 0, 0, 0, 0, 0, 0],
... [0, 0, 0, 1, 0, 0, 0, 0],
... [0, 2, 0, 0, 0, 0, 0, 0],
... [2, 2, 2, 0, 2, 2, 2, 2],
... [0, 0, 0, 0, 0, 0, 0, 3],
... ])
>>> robot = Robot(room)
>>> all((
... room._get_robot() == (4, 7),
... robot._get_face() == Dirs.DOWN,
... robot.move() is False,
... robot.turnleft() is None,
... robot.move() is False,
... robot.turnrigt() is None,
... robot.move() is False,
... robot.turnleft(3) is None,
... robot.move() is True,
... room._get_robot() == (4, 6),
... ))
True
>>> all((
... robot.move() is True,
... robot.move(Dirs.LEFT) is True,
... robot.turnleft(2) is None,
... robot.turnrigt(2) is None,
... robot.move() is True,
... room._get_robot() == (4, 3),
... ))
True
>>> all((
... robot._get_face() == Dirs.LEFT,
... robot.move(Dirs.UP) is True,
... robot.turnleft() is None,
... robot.move() is False,
... robot.turnrigt(2) is None,
... robot.move() is False,
... robot.turnleft() is None,
... ))
True
>>> all((
... robot.move() is True,
... robot.turnleft() is None,
... robot.move() is True,
... robot.move() is False,
... robot.move() is False,
... robot.move() is False,
... robot.turnrigt() is None,
... robot.move() is True,
... robot.turnrigt() is None,
... ))
True
>>> all((
... room._get_robot() == (1, 2),
... robot.move() is True,
... room.is_clear() is False,
... robot.clean() is None,
... room.is_clear() is True,
... robot.clean() is None,
... room.is_clear() is True,
... room.is_clear() is True,
... ))
True
2. test cleaner
>>> CASES = (
... (
... (3, 0, 0, 0, 0, 0, 1, 0),
... (0, 0, 0, 0, 0, 0, 0, 0),
... (0, 2, 0, 0, 0, 0, 0, 0),
... (2, 2, 2, 0, 2, 2, 2, 2),
... (0, 0, 1, 0, 0, 0, 1, 0),
... ),
... (
... (0, 0, 0, 0, 0, 0, 0, 0),
... (0, 0, 1, 0, 2, 0, 0, 0),
... (1, 2, 0, 0, 2, 1, 0, 0),
... (2, 2, 2, 0, 2, 2, 2, 2),
... (1, 0, 0, 0, 0, 0, 0, 3),
... ),
... (
... (1, 0, 0, 0, 0, 0, 0, 1),
... (0, 0, 0, 1, 0, 0, 0, 0),
... (0, 2, 0, 0, 0, 0, 1, 0),
... (2, 2, 2, 0, 2, 2, 2, 2),
... (0, 1, 0, 3, 0, 1, 0, 1),
... ),
... (
... (0, 0, 0, 0, 0, 0, 0, 1),
... (0, 0, 0, 1, 2, 0, 3, 0),
... (0, 2, 0, 0, 2, 1, 0, 0),
... (2, 2, 2, 1, 2, 2, 2, 2),
... (0, 1, 0, 0, 1, 0, 0, 1),
... ),
... )
>>> cleaners = (
... RobotCleanerDFS(),
... RobotCleanerDFS2(),
... # RobotCleanerBFS(),
... )
>>> gotcha = []
>>> for grid in CASES:
... for cleaner in cleaners:
... room = Room([list(r) for r in grid])
... robot = Robot(room)
...
... gotcha.append(not room.is_clear())
... cleaner.clean_room(robot)
...
... if not room.is_clear(): room._print_room(); print(cleaner)
... gotcha.append(room.is_clear())
>>> bool(gotcha) and all(gotcha)
True
"""
class Dirs:
"""
The directions should be in order
to make turnleft/right in Robot more convient
if need 8-dirs, the order becomes:
D, DR, R, UR, U, UL, L, DL
"""
DOWN = 0
RIGHT = 1
UP = 2
LEFT = 3
DELTA = (
( 1, 0),
( 0, 1),
(-1, 0),
( 0, -1),
)
class Room:
EMPTY = 0
CLEANUP = 1
OBSTACLE = 2
ROBOT = 3
def __init__(self, grid):
"""
:type grid: list[list[int]]
"""
self.__room = grid
self.__cleanups = 0
self.__robot_at = (0, 0)
m, n = len(grid), len(grid[0])
for x in range(m):
for y in range(n):
if grid[x][y] == self.CLEANUP:
self.__cleanups += 1
elif grid[x][y] == self.ROBOT:
grid[x][y] = self.EMPTY
self.__robot_at = (x, y)
def is_clear(self):
"""
:rtype: bool
"""
return self.__cleanups == 0
def move_robot(self, direction):
"""
:type direction: int, defined in Dirs
:rtype: bool
"""
m, n = len(self.__room), len(self.__room[0])
x, y = self.__robot_at
dx, dy = Dirs.DELTA[direction]
_x, _y = x + dx, y + dy
if not (0 <= _x < m and 0 <= _y < n):
return False
if self.__room[_x][_y] == self.OBSTACLE:
return False
self.__robot_at = (_x, _y)
return True
def clean(self, robot):
"""
:type robot: Robot
:rtype: void
"""
if not isinstance(robot, Robot):
return
x, y = self.__robot_at
if self.__room[x][y] == self.CLEANUP:
self.__room[x][y] = self.EMPTY
self.__cleanups -= 1
def _get_robot(self):
# for testing
return self.__robot_at
def _print_room(self):
# for testing
print(
'\n'.join(str(r) for r in self.__room),
'\nRobot at: ', self.__robot_at,
'\nCleanups: ', self.__cleanups,
'\n'
)
class Robot:
def __init__(self, room):
"""
:type room: Room
"""
self.__room = room
self.__face = Dirs.DOWN
def move(self, direction=None):
"""
:type direction: int, defined in Dirs
:rtype: bool
"""
if direction in range(len(Dirs.DELTA)):
self.__face = direction
return self.__room.move_robot(self.__face) is True
def turnleft(self, k=1):
"""
:type k: int
:rtype: void
"""
n = len(Dirs.DELTA)
self.__face = (self.__face + k) % n
def turnrigt(self, k=1):
"""
:type k: int
:rtype: void
"""
# note that, -1 % 4 == 3 in Python, or just (x - k + n) % n
n = len(Dirs.DELTA)
self.__face = (self.__face - k) % n
def clean(self):
"""
:rtype: void
"""
self.__room.clean(self)
def _get_face(self):
# for testing
return self.__face
class RobotCleanerDFS:
"""
this approach is for we need to adjust `dir` manually
the `robot.move()` only can move forward with 1 step
"""
def clean_room(self, robot):
"""
:type robot: Robot
"""
if not isinstance(robot, Robot):
return
"""
robot's direction and coord no needs to same as room
just start as (0, 0),
and face 0 (this 0 just ref of dirs, no needs to treat it as Dirs.DOWN)
Dirs.DELTA => D, R, U, L
(1, 0), (0, 1), (-1, 0), (0, -1)
"""
self.dfs(0, 0, 0, robot, set())
def dfs(self, x, y, to_dir, robot, visited):
robot.clean()
visited.add((x, y))
# down
d = to_dir
_x = x + Dirs.DELTA[d][0]
_y = y + Dirs.DELTA[d][1]
if (_x, _y) not in visited and robot.move():
self.dfs(_x, _y, d, robot, visited)
robot.turnrigt()
else:
robot.turnleft()
# right
d = (to_dir + 1) % len(Dirs.DELTA)
_x = x + Dirs.DELTA[d][0]
_y = y + Dirs.DELTA[d][1]
if (_x, _y) not in visited and robot.move():
self.dfs(_x, _y, d, robot, visited)
else:
robot.turnleft(2)
# left
d = (to_dir + 3) % len(Dirs.DELTA)
_x = x + Dirs.DELTA[d][0]
_y = y + Dirs.DELTA[d][1]
if (_x, _y) not in visited and robot.move():
self.dfs(_x, _y, d, robot, visited)
robot.turnleft()
else:
robot.turnrigt()
# up
d = (to_dir + 2) % len(Dirs.DELTA)
_x = x + Dirs.DELTA[d][0]
_y = y + Dirs.DELTA[d][1]
if (_x, _y) not in visited and robot.move():
self.dfs(_x, _y, d, robot, visited)
robot.turnrigt(2)
# move robot when the recursion is back
robot.move()
class RobotCleanerDFS2:
"""
this approach is for we can just pass `dir` into `robot.move(dir)`
"""
def clean_room(self, robot):
"""
:type robot: Robot
"""
if not isinstance(robot, Robot):
return
"""
robot's direction and coord no needs to same as room
just start as (0, 0),
and face 0 (this 0 just ref of dirs, no needs to treat it as Dirs.DOWN)
"""
self.dfs(0, 0, 0, robot, set())
def dfs(self, x, y, from_dir, robot, visited):
# is there a api to detect the cell need to clean?
robot.clean()
visited.add((x, y))
for to_dir in range(len(Dirs.DELTA)):
if to_dir == from_dir:
continue
# to_dir is index and also the direction defined in Dirs
dx, dy = Dirs.DELTA[to_dir]
_x = x + dx
_y = y + dy
if (_x, _y) in visited:
continue
if robot.move(to_dir):
self.dfs(_x, _y, (to_dir + 2) % len(Dirs.DELTA), robot, visited)
else:
visited.add((_x, _y))
robot.move(from_dir)
class RobotCleanerBFS:
def clean_room(self, robot):
"""
:type robot: Robot
"""
if not isinstance(robot, Robot):
return
def bfs(self):
pass
if __name__ == '__main__':
# for debugging
room = Room([
[1, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 1, 0, 0, 0, 0],
[0, 2, 0, 0, 0, 0, 1, 0],
[2, 2, 2, 0, 2, 2, 2, 2],
[0, 1, 0, 3, 0, 1, 0, 1],
])
robot = Robot(room)
s = RobotCleanerDFS()
room._print_room()
s.clean_room(robot)
room._print_room()
================================================
FILE: other/snake_and_ladder_problem.py
================================================
"""
Problem: https://www.geeksforgeeks.org/snake-ladder-problem-2/
"""
def get_min_dice_throws(moves):
"""
:type moves: list[int]
:type n: int
:rtype: int
>>> moves = [-1] * 30
>>> for i, val in (
... # ladders
... ( 2, 21),
... ( 4, 7),
... (10, 25),
... (19, 28),
... # snakes
... (26, 0),
... (20, 8),
... (16, 3),
... (18, 6),
... ):
... moves[i] = val
>>> get_min_dice_throws(moves)
3
"""
if not moves:
return -1
n = len(moves)
queue, _queue = [0], []
steps = {0: 0} # min steps to get i
while queue:
for i in queue:
for j in range(i + 1, min(i + 7, n)):
# 6-faced dice => max 6 steps but up to n
if j == n - 1:
return steps[i]
if j in steps:
# is visited
continue
# next cell
k = moves[j] if moves[j] != -1 else j
steps[k] = steps[i] + 1
_queue.append(k)
queue, _queue = _queue, []
return -1
================================================
FILE: other/stock_stream.py
================================================
class StockStream:
def __init__(self, prices):
pass
def update(self, timestamp, price):
"""
:type timestamp: int
:type price: int
:rtype: void
"""
pass
def get_highest(self):
"""
:rtype: int
"""
pass
def get_lowest(self):
"""
:rtype: int
"""
pass
def get_latest(self):
"""
:rtype: int
"""
pass
================================================
FILE: other/string_abbreviation.py
================================================
"""
This problem should be discussed, and there might be 3 ways to solve
REF: http://www.1point3acres.com/bbs/thread-218909-1-1.html
1. add specific char to separate. '@'
2. encode number anyway
3. cheating, like '1aaaaa' -> '1a4xa'
>>> CASE = (
... ('ff', 'ff'),
... ('fff', 'fff'),
... ('ffff', '4xf'),
... ('1xt', '1x1xt'),
... ('5xt', '1x5xt'),
... ('1aaaaa', '1x15xa'),
... ('3aaaaa', '1x35xa'),
... ('123aaaa', '1x11x21x34xa'),
... ('aaabbbbcccc', 'aaa4xb4xc'),
... )
>>> all(decode(encode(inpt)) == inpt for inpt, _ in CASE)
True
>>> all(encode(inpt) == oupt for inpt, oupt in CASE)
True
>>> all(decode(oupt) == inpt for inpt, oupt in CASE)
True
"""
def encode(s):
"""
:type s: str
:rtype: str
"""
if not s:
return ''
res = []
cnt = 1
char = s[0]
for i in range(1, len(s)):
if s[i] == s[i - 1]:
cnt += 1
continue
# s[i] != s[i - 1]
if char.isdigit() or cnt > 3:
res.append(str(cnt))
res.append('x')
res.append(char)
else:
res.append(char * cnt)
cnt = 1
char = s[i]
if char.isdigit() or cnt > 3:
res.append(str(cnt))
res.append('x')
res.append(char)
else:
res.append(char * cnt)
return ''.join(res)
def decode(s):
"""
:type s: str
:rtype: str
"""
if not s:
return ''
res = []
i = 0
n = len(s)
while i < n:
is_digit = s[i].isdigit()
if is_digit and i + 2 >= n:
return ''
if is_digit:
res.append(int(s[i]) * s[i + 2])
i += 3
else:
res.append(s[i])
i += 1
return ''.join(res)
================================================
FILE: other/the_point_inside_polygon.py
================================================
"""
Questions:
1. Choose a random point from one single rectangle.
=> get_random_point_inside_rectangle
2. Choose a random point from multiple rectangles, if there is no overlapping among them.
=> get_random_point_inside_rectangle2
3. Choose a random point from multiple rectangles, if there is overlapping among them.
=> get_random_point_inside_rectangle3
4. Is a point inside polygon?
=> is_point_inside_polygon
* p.s. overlapping means the point
- was shared between two recs
- even the point is a vertex or on edge.
"""
import random
def get_random_point_inside_rectangle(rectangle):
"""
:type rectangle: list[int]
:rtype: list[int]
"""
if not rectangle:
return []
x1, y1, x2, y2 = rectangle
return [
random.randint(x1, x2),
random.randint(y2, y1),
]
# ======
def get_random_point_inside_rectangle2(rectangles):
"""
:type rectangles: list[list[int]]
:rtype: list[int]
"""
if not rectangles:
return []
k = total = 0
for i in range(len(rectangles)):
x1, y1, x2, y2 = rectangles[i]
area = (x2 - x1) * (y1 - y2)
if random.randrange(total + area) >= total:
k = i
total += area
return get_random_point_inside_rectangle(rectangles[k])
# ======
def get_random_point_inside_rectangle3(rectangles):
"""
:type rectangles: list[list[int]]
:rtype: list[int]
"""
if not rectangles:
return []
# ======
def is_point_inside_polygon(polygon, point):
"""
:type polygon: list[list[int]]
:type point: list[int]
:rtype: bool
"""
if not polygon or not point or len(polygon) < 3:
return False
# ======
if __name__ == '__main__':
import collections
gotcha = []
# single-rec
freq = collections.defaultdict(int)
for _ in range(10000):
x, y = get_random_point_inside_rectangle((0, 2, 3, 0))
freq[x, y] += 1
gotcha.append(len(freq) == 12)
gotcha.append(all(633 <= fq <= 1033 for fq in freq.values()))
# multi-recs, no overlapping
freq = collections.defaultdict(int)
for _ in range(10000):
x, y = get_random_point_inside_rectangle2((
(0, 2, 1, 0),
(2, 2, 3, 0),
))
freq[x, y] += 1
gotcha.append(len(freq) == 12)
gotcha.append(all(633 <= fq <= 1033 for fq in freq.values()))
# # multi-recs, overlapping
# freq = collections.defaultdict(int)
# for _ in range(100000):
# x, y = get_random_point_inside_rectangle3((
# (3, 3, 7, -4),
# (-4, 5, 1, -2),
# (-2, 7, 5, 1),
# ))
# freq[x, y] += 1
# gotcha.append(len(freq) == 115)
# gotcha.append(all(469 <= fq <= 1269 for fq in freq.values()))
# # multi-recs, overlapping
# freq = collections.defaultdict(int)
# for _ in range(100000):
# x, y = get_random_point_inside_rectangle3((
# (-6, 2, 0, -4),
# (-2, 4, 4, -2),
# (-4, 6, 2, 0),
# ))
# freq[x, y] += 1
# gotcha.append(len(freq) == 101)
# gotcha.append(all(590 <= fq <= 1390 for fq in freq.values()))
# for _in, _out in (
# (([[0, 0], [10, 0], [10, 10], [0, 10]], [20, 20]), False),
# (([[0, 0], [10, 0], [10, 10], [0, 10]], [5, 5]), True),
# (([[0, 0], [5, 5], [5, 0]], [3, 3]), False),
# (([[0, 0], [5, 5], [5, 0]], [5, 1]), False),
# (([[0, 0], [5, 5], [5, 0]], [8, 1]), False),
# (([[0, 0], [10, 0], [10, 10], [0, 10]], [-1, 10]), False),
# (([[2, 1], [3, 2], [2, 3]], [1, 2]), False),
# (([[0, 0], [5, 0], [10, 10], [5, 10]], [3, 3]), True),
# (([[0, 0], [5, 0], [10, 10], [5, 10]], [4, 10]), False),
# (([[0, 0], [-5, 0], [-10, -10]], [0, -2]), False),
# (([[2, 5], [5, 0], [8, 5], [5, 10]], [0, 0]), False),
# (([[2, 5], [5, 0], [8, 5], [5, 10]], [5, 5]), True),
# ):
# res = is_point_inside_polygon(*_in)
# if res != _out: print(_in, res)
# gotcha.append(res == _out)
print(bool(gotcha) and all(gotcha))
================================================
FILE: other/uneven_random_get.py
================================================
"""
TODO:
1. do expectations like `chi-square calculator`
"""
import random
def uneven_random_get(options, rate):
"""unevenly fetch the option according to the corresponding rate
:type options: list[str]
:type rate: list[num]
:rtype: str
"""
if not options or not rate or len(options) != len(rate):
return ''
num = 0
rand = random.randint(1, sum(rate))
for i in range(len(rate)):
num += rate[i]
if num >= rand:
return options[i]
return options[0]
def uneven_random_get2(options, rate):
"""unevenly fetch the option according to the corresponding rate
:type options: list[str]
:type rate: list[num]
:rtype: str
"""
if not options or not rate or len(options) != len(rate):
return ''
k = total = 0
for i in range(len(rate)):
if random.randrange(total + rate[i]) >= total:
k = i
total += rate[i]
return options[k]
if __name__ == '__main__':
gotcha = []
options = 'abc'
for uneven_get in (uneven_random_get, uneven_random_get2):
freq = dict.fromkeys(options, 0)
for _ in range(10000):
c = uneven_get(options, (10, 10, 10))
freq[c] += 1
gotcha.append(all(2833 <= freq[c] <= 3833 for c in options))
freq = dict.fromkeys(options, 0)
for _ in range(10000):
c = uneven_get(options, (1, 1, 1))
freq[c] += 1
gotcha.append(all(2833 <= freq[c] <= 3833 for c in options))
freq = dict.fromkeys(options, 0)
for _ in range(10000):
c = uneven_get(options, (8, 1, 1))
freq[c] += 1
gotcha.append(all((
7500 <= freq['a'] <= 8500,
500 <= freq['b'] <= 1500,
500 <= freq['c'] <= 1500,
)))
print(bool(gotcha) and all(gotcha))
================================================
FILE: other/unique_paths_with_followups.py
================================================
"""
给定一个矩形的宽和长,求所有可能的路径数量
Rules:
1. 从左上角走到右上角
2. 要求每一步只能向正右、右上或右下走,即 →↗↘
followup1: 优化空间复杂度至 O(n)
followup2: 给定矩形里的三个点,判断是否存在遍历这三个点的路经
followup3: 给定矩形里的三个点,找到遍历这三个点的所有路径数量
followup4: 给定一个下界 (x == H),找到能经过给定下界的所有路径数量 (x >= H)
followup5: 起点和终点改成从左上到左下,每一步只能 ↓↘↙,求所有可能的路径数量
"""
def find_unique_paths(m, n):
"""
:type m: int
:type n: int
:rtype: int
>>> gotcha = [
... find_unique_paths(*_in) == _out
... for _in, _out in (
... ((2, 2), 1), ((2, 3), 2), ((3, 3), 2),
... ((5, 5), 9), ((7, 6), 21), ((6, 7), 51),
... )
... ]
>>> bool(gotcha) and all(gotcha)
True
"""
if not m or not n:
return 0
dp = [[0] * n for _ in range(m)]
dp[0][0] = 1
for y in range(1, n):
for x in range(m):
dp[x][y] = dp[x][y - 1]
if x > 0:
dp[x][y] += dp[x - 1][y - 1]
if x + 1 < m:
dp[x][y] += dp[x + 1][y - 1]
return dp[0][n - 1]
def find_unique_paths1(m, n):
"""
:type m: int
:type n: int
:rtype: int
>>> gotcha = [
... find_unique_paths1(*_in) == _out
... for _in, _out in (
... ((2, 2), 1), ((2, 3), 2), ((3, 3), 2),
... ((5, 5), 9), ((7, 6), 21), ((6, 7), 51),
... )
... ]
>>> bool(gotcha) and all(gotcha)
True
"""
if not m or not n:
return 0
dp = [0] * m
dp[0] = 1
pre = cur = 0
for y in range(1, n):
pre = cur = 0
for x in range(m):
pre, cur = cur, dp[x]
if x > 0:
dp[x] += pre
if x + 1 < m:
dp[x] += dp[x + 1]
return dp[0]
def find_unique_paths2(m, n, points):
"""
:type m: int
:type n: int
:type points: list[list[int]]
:rtype: bool
>>> gotcha = [
... find_unique_paths2(*_in) == _out
... for _in, _out in (
... ((2, 3, [[1, 0], [1, 1], [1, 2]]), False),
... ((3, 3, [[1, 0], [2, 1], [1, 2]]), False),
... ((3, 3, [[1, 0], [1, 1], [1, 2]]), False),
... ((5, 5, [[0, 1], [2, 2], [1, 3]]), False),
... ((5, 5, [[1, 1], [2, 2], [1, 3]]), True),
... ((5, 5, [[2, 2], [1, 1], [1, 3]]), True),
... ((6, 8, [[0, 0], [4, 3], [0, 7]]), False),
... ((8, 6, [[1, 1], [0, 4], [1, 3]]), True),
... ((8, 6, [[1, 1], [0, 4], [2, 3]]), False),
... )
... ]
>>> bool(gotcha) and all(gotcha)
True
"""
if not m or not n or not points or len(points) < 3:
return False
path = [(0, 0), (0, n - 1)]
path.extend(tuple(p) for p in points)
path.sort(key=lambda p: (p[1], p[0]))
for i in range(1, len(path)):
x, y = path[i]
_x, _y = path[i - 1]
delta = y - _y
if not (x - delta <= _x <= x + delta):
return False
return True
def find_unique_paths3(m, n, points):
"""
:type m: int
:type n: int
:type points: list[list[int]]
:rtype: int
>>> gotcha = [
... find_unique_paths3(*_in) == _out
... for _in, _out in (
... ((2, 3, [[1, 0], [1, 1], [1, 2]]), 0),
... ((3, 3, [[1, 0], [2, 1], [1, 2]]), 0),
... ((3, 3, [[1, 0], [1, 1], [1, 2]]), 0),
... ((5, 5, [[0, 1], [2, 2], [1, 3]]), 0),
... ((5, 5, [[1, 1], [2, 2], [1, 3]]), 1),
... ((5, 5, [[2, 2], [1, 1], [1, 3]]), 1),
... ((6, 8, [[0, 0], [4, 3], [0, 7]]), 0),
... ((8, 6, [[0, 0], [0, 5], [0, 4]]), 9),
... ((8, 6, [[1, 1], [0, 4], [2, 3]]), 0),
... )
... ]
>>> bool(gotcha) and all(gotcha)
True
"""
NOT_FOUND = 0
if not m or not n or not points:
return NOT_FOUND
points.sort(key=lambda p: (p[1], p[0]))
dp = [[0] * n for _ in range(m)]
dp[0][0] = 1
k = len(points)
i = 0
while points[i][1] == 0:
i += 1
if i >= k:
return NOT_FOUND
for y in range(1, n):
for x in range(m):
dp[x][y] = dp[x][y - 1]
if x > 0:
dp[x][y] += dp[x - 1][y - 1]
if x + 1 < m:
dp[x][y] += dp[x + 1][y - 1]
if i < k and y == points[i][1]:
for x in range(m):
if x != points[i][0]:
dp[x][y] = 0
i += 1
return dp[0][n - 1] if i == k else NOT_FOUND
def find_unique_paths4(m, n, h):
"""
:type m: int
:type n: int
:type h: int
:rtype: int
>>> gotcha = [
... find_unique_paths4(*_in) == _out
... for _in, _out in (
... ((2, 3, 1), 1), ((3, 3, 1), 1), ((3, 3, 2), 0),
... ((4, 4, 0), 4), ((4, 4, 1), 3), ((4, 4, 2), 0),
... ((6, 7, 0), 51), ((6, 7, 1), 50), ((6, 7, 2), 19),
... ((6, 7, 3), 1), ((6, 7, 4), 0), ((6, 7, 5), 0)
... )
... ]
>>> bool(gotcha) and all(gotcha)
True
"""
if not m or not n:
return 0
dp = [[0] * n for _ in range(m)]
dp[0][0] = 1
for y in range(1, n):
for x in range(m):
dp[x][y] = dp[x][y - 1]
if x > 0:
dp[x][y] += dp[x - 1][y - 1]
if x + 1 < m:
dp[x][y] += dp[x + 1][y - 1]
if h < 1:
return dp[0][n - 1]
for y in range(n):
for x in range(h):
dp[x][y] = 0
for y in range(1, n):
for x in range(h - 1, -1, -1):
dp[x][y] = dp[x][y - 1]
if x > 0:
dp[x][y] += dp[x - 1][y - 1]
if x + 1 < m:
dp[x][y] += dp[x + 1][y - 1]
return dp[0][n - 1]
def find_unique_paths5(m, n):
"""
:type m: int
:type n: int
:rtype: int
>>> gotcha = [
... find_unique_paths5(*_in) == _out
... for _in, _out in (
... ((2, 2), 1), ((2, 3), 1), ((3, 3), 2),
... ((5, 5), 9), ((7, 6), 51), ((6, 7), 21),
... )
... ]
>>> bool(gotcha) and all(gotcha)
True
"""
if not m or not n:
return 0
dp = [[0] * n for _ in range(m)]
dp[0][0] = 1
for x in range(1, m):
for y in range(n):
dp[x][y] = dp[x - 1][y]
if y > 0:
dp[x][y] += dp[x - 1][y - 1]
if y + 1 < n:
dp[x][y] += dp[x - 1][y + 1]
return dp[m - 1][0]
================================================
FILE: other/unique_word_abbreviation_ii.py
================================================
"""
This question is the follow-up of `leetcode/288_unique_word_abbreviation`
1. test `WordIterator`
>>> gotcha = []
>>> for _in, _out in (
... ('', []),
... ('a', ['a']),
... ('dog', ['3', 'd2', '2g', 'dog']),
... ('abcd', ['4', 'a3', '3d', 'ab2', 'a2d', '2cd', 'abcd']),
... ('price', ['5', 'p4', '4e', 'pr3', 'p3e', '3ce', 'pri2', 'pr2e', 'p2ce', '2ice', 'price']),
... ):
... res = [w for w in WordIterator(_in)]
... if res != _out: print(_in, res)
... gotcha.append(res == _out)
>>> bool(gotcha) and all(gotcha)
True
2. test `find_unique_abbreviations`
>>> gotcha = []
>>> for _in, _out in (
... (
... ['internationalization', 'localization', 'dog', 'accessibility', 'automatically'],
... ['20', '12', '3', 'ac11', 'au11']
... ),
... (
... ['aaabcdefg', 'aaabbcdef', 'aaabbbcde'],
... ['8g', '8f', '8e'],
... ),
... (
... ['abcdef', 'abcdef', 'abcdef'],
... ['6', '6', '6'],
... ),
... (
... ['abcdef', 'abdeef', 'abefef'],
... ['abc3', 'abd3', 'abe3'],
... ),
... ):
... res = find_unique_abbreviations(_in)
... if res != _out: print(_in, res)
... gotcha.append(res == _out)
>>> bool(gotcha) and all(gotcha)
True
"""
def find_unique_abbreviations(words):
iterators = {} # {abbr: iterator}
for word in set(words):
iterator = (WordIterator(word).__iter__(), word)
abbr = next(iterator[0], None)
if not abbr:
continue
if abbr in iterators:
iterators[abbr].append(iterator)
else:
iterators[abbr] = [iterator]
collisions = [key for key, iters in iterators.items() if len(iters) > 1]
while collisions:
for key in collisions:
iters = iterators[key]
del iterators[key]
for iterator in iters:
abbr = next(iterator[0], None)
if not abbr:
continue
if abbr in iterators:
iterators[abbr].append(iterator)
else:
iterators[abbr] = [iterator]
collisions = [key for key, iters in iterators.items() if len(iters) > 1]
reversed_index = {iters[0][1]: key for key, iters in iterators.items() if len(iters) == 1}
return [reversed_index[word] for word in words]
class WordIterator:
def __init__(self, word):
self.word = word
self.TMPL = '{}{}{}'
def __iter__(self):
if not self.word:
return
n = len(self.word)
if n > 1:
yield str(n)
for i in range(1, n - 1):
for j in range(i + 1):
yield self.TMPL.format(
self.word[:i - j],
n - i,
self.word[-j:] if j > 0 else ''
)
yield self.word
================================================
FILE: other/upside_down_numbers.py
================================================
def find_upside_down_numbers(n):
"""
:type n: int
:rtype: list[int]
>>> gotcha = []
>>> for _in, _out in (
... (10, [1, 6, 8, 9]),
... (100, [1, 6, 8, 9, 16, 18, 19, 61, 66, 68, 81, 86, 89, 91, 98, 99]),
... (500, [1, 6, 8, 9, 16, 18, 19, 61, 66, 68, 81, 86, 89, 91, 98, 99, 161, 191]),
... (700, [1, 6, 8, 9, 16, 18, 19, 61, 66, 68, 81, 86, 89, 91, 98, 99, 161, 169, 189, 191, 199, 611, 661, 669, 681]),
... ):
... res = find_upside_down_numbers(_in)
... if res != _out: print(_in, res)
... gotcha.append(res == _out)
>>> bool(gotcha) and all(gotcha)
True
"""
ans = []
if not n or n <= 0:
return ans
cands = '01689'
queue, _queue = ['1', '6', '8', '9'], []
up_down_nums = {
'0': '0',
'1': '1',
'6': '9',
'8': '8',
'9': '6',
}
while queue:
ans.extend(queue)
for num in queue:
for nxt in cands:
res = num + nxt
rev = get_reversed_number(res, up_down_nums)
if rev[0] == '0' or res == rev or int(rev) > n:
continue
if int(res) > n:
ans.extend(_queue)
return [int(s) for s in ans]
_queue.append(res)
queue, _queue = _queue, []
return [int(s) for s in ans]
def get_reversed_number(s, up_down_nums):
res = ''
for i in range(len(s) - 1, -1, -1):
res += up_down_nums[s[i]]
return res
================================================
FILE: pramp/array_index_and_element_equality.py
================================================
def index_equals_value_search(nums):
if not nums:
return -1
left, right = 0, len(nums) - 1
while left + 1 < right:
mid = (left + right) // 2
if nums[left] == left: # lowest index
return left
if nums[mid] == mid:
return mid
if nums[mid] < mid:
left = mid
else:
right = mid
for mid in (left, right):
if nums[mid] == mid:
return mid
return -1
================================================
FILE: pramp/array_of_array_products.py
================================================
"""
thought:
=> nums = [a, b, c, d]
1. fill `ans` with `1`
=> ans = [1, 1, 1, 1]
2. scan from left to right, and do multiply continuously
=> ans = [1, a, a * b, a * b * c]
3. scan from right to left, and do multiply continuously but with a temp var
=> prod = b * c * d, c * d, d
=> ans = [b * c * d, a * c * d, a * b * d, a * b * c]
"""
def array_of_array_products(nums):
"""
:type nums: list[int]
:rtype: list[int]
"""
if not nums or len(nums) <= 1:
return []
n = len(nums)
ans = [1] * n
# 1st scan [1, a, a * b, a * b * c]
for i in range(1, n):
ans[i] *= ans[i - 1] * nums[i - 1]
# 2nd scan
prod = 1
for i in range(n - 2, -1, -1):
prod *= nums[i + 1]
ans[i] *= prod
return ans
================================================
FILE: pramp/award_budget_cuts.py
================================================
"""
Main Concept:
budget = 190
[2, 100, 35, 120, 1000]
=> sort, [1000, 120, 100, 35, 2]
=> total = sum(arr) - budget
=> just treat it as bar chart
and iterate from start to end to remove the area between `a` and `b`
from total below
|-| -> a
|=|=| -> b
| | |-|
=> untill total < 0 => reach the `cap` line
=> remove the remaining area from total
>>> EPS = 1 ** -3
>>> gotcha = []
>>> for _in, _out in (
... (([2, 4], 3), 1.5),
... (([2, 4, 6], 3), 1.0),
... (([2, 100, 50, 120, 167], 400), 128.0),
... (([2, 100, 50, 120, 1000], 190), 47.0),
... (([2, 100, 35, 120, 1000], 190), 51.0),
... (([21, 100, 50, 120, 130, 110], 140), 23.8),
... (([210, 200, 150, 193, 130, 110, 209, 342, 117], 1530), 211.0),
... ):
... res = find_grants_cap(*_in)
... if abs(res - _out) >= EPS: print(_in, res)
... gotcha.append(abs(res - _out) < EPS)
>>> bool(gotcha) and all(gotcha)
True
"""
def find_grants_cap(grantsArray, newBudget):
"""
:type grantsArray: list[int]
:type newBudget: int
:rtype: float
"""
n = len(grantsArray)
area = sum(grantsArray) - newBudget
grantsArray.sort(reverse=True)
i = segment_sum = 0
while i < n:
if i == n - 1:
# (i + 1) * (grantsArray[i] - 0)
segment_sum = n * grantsArray[i]
else:
segment_sum = (i + 1) * (grantsArray[i] - grantsArray[i + 1])
if area < segment_sum:
break
area -= segment_sum
i += 1
return grantsArray[i] - area / float(i + 1)
================================================
FILE: pramp/bracket_match.py
================================================
def bracket_match(text):
ans = 0
if not text:
return ans
cnt = 0
for c in text:
if c == '(':
cnt += 1
elif c == ')':
cnt -= 1
if cnt < 0:
cnt = 0
ans += 1
if cnt > 0:
ans += cnt
return ans
================================================
FILE: pramp/busiest_time_in_the_mall.py
================================================
def find_busiest_period(data):
timestamp = -1
if not data:
return timestamp
if len(data) == 1: # data[0][2] always 1
return data[0][0]
n = len(data)
cnt = maxi = 0
for i in range(len(data)):
if data[i][2] == 1:
cnt += data[i][1]
else:
cnt -= data[i][1]
if (i == n - 1 or data[i][0] != data[i + 1][0]) and cnt > maxi:
maxi = cnt
timestamp = data[i][0]
return timestamp
================================================
FILE: pramp/decrypt_message.py
================================================
"""
Encryption Rules:
1. add 1 to the ascii of first letter
2. add the ascii of prev letter, form second to last
3. keep substract 26 from every letter until it is in [ord('a'), ord('z')]
Testing:
>>> gotcha = []
>>> for origin, encryption in (
... ('crime', 'dnotq'),
... ('encyclopedia', 'flgxswdliefy'),
... ('qqqqq', 'rajsb'),
... ('abcdefghijklmnopqrstuvwxyz', 'bvqmjhgghjmqvbiqzjugthwmdv'),
... ('drugtrafficking', 'eobamwpnlmhklrq'),
... ('', ''),
... ):
... res = encrypt(origin)
... if res != encryption: print('L18', origin, res)
... gotcha.append(res == encryption)
...
... res = decrypt(encryption)
... if res != origin: print('L22', encryption, res)
... gotcha.append(res == origin)
...
... res = decrypt(encrypt(origin))
... if res != origin: print('L26', origin, res)
... gotcha.append(res == origin)
...
... res = decrypt2(encryption)
... if res != origin: print('L30', encryption, res)
... gotcha.append(res == origin)
...
... res = decrypt2(encrypt(origin))
... if res != origin: print('L34', origin, res)
... gotcha.append(res == origin)
>>> bool(gotcha) and all(gotcha)
True
"""
def encrypt(word):
if not word:
return ''
a = ord('a')
ans = [ord(c) for c in word]
cumul = 1
for i in range(len(word)):
cumul += ans[i]
ans[i] = chr((cumul - a) % 26 + a)
return ''.join(ans)
def decrypt(word):
if not word:
return ''
a = ord('a')
ans = [ord(c) for c in word]
for i in range(len(word) - 1, -1, -1):
if i == 0:
ans[i] = chr((ans[i] - 1 - a) % 26 + a)
else:
ans[i] = chr((ans[i] - ans[i - 1] - a) % 26 + a)
return ''.join(ans)
def decrypt2(word):
if not word:
return ''
a = ord('a')
ans = [ord(c) for c in word]
cumul = 1
for i in range(len(word)):
# substract the cumul sum
ans[i] -= cumul
# keep adding 26 to make ans[i] in [ord('a'), ord('z')]
steps = (a - ans[i]) // 26
if (a - ans[i]) % 26:
# (a - ans[i]) % 26 != 0
# add 1 more
steps += 1
ans[i] += steps * 26
# update cumul
cumul += ans[i]
ans[i] = chr(ans[i])
return ''.join(ans)
================================================
FILE: pramp/deletion_distance.py
================================================
"""
>>> gotcha = []
>>> for _in, _out in (
... (('', ''), 0), (('', 'hit'), 3), (('neat', ''), 4),
... (('heat', 'hit'), 3), (('hot', 'not'), 2), (('some', 'thing'), 9),
... (('abc', 'adbc'), 1), (('awesome', 'awesome'), 0), (('ab', 'ba'), 2),
... ):
... for get_distance in (deletion_distance, deletion_distance2):
... res = get_distance(*_in)
... if res != _out: print(_in, res)
... gotcha.append(res == _out)
>>> bool(gotcha) and all(gotcha)
True
"""
def deletion_distance(s, t):
if s == t:
return 0
if not s and not t:
return 0
if not s:
return len(t)
if not t:
return len(s)
m, n = len(s), len(t)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(1, m + 1):
dp[i][0] = i
for j in range(1, n + 1):
dp[0][j] = j
for i in range(1, m + 1):
for j in range(1, n + 1):
if s[i - 1] == t[j - 1]:
dp[i][j] = dp[i - 1][j - 1]
else:
dp[i][j] = 1 + min(
dp[i - 1][j],
dp[i][j - 1]
)
return dp[m][n]
def deletion_distance2(s, t):
if s == t:
return 0
if not s and not t:
return 0
if not s:
return len(t)
if not t:
return len(s)
m, n = len(s), len(t)
dp = [[0] * (n + 1) for _ in range(2)]
pre = cur = 0
for j in range(1, n + 1):
dp[cur][j] = j
for i in range(1, m + 1):
pre, cur = cur, 1 - cur
dp[cur][0] = i
for j in range(1, n + 1):
if s[i - 1] == t[j - 1]:
dp[cur][j] = dp[pre][j - 1]
else:
dp[cur][j] = 1 + min(
dp[pre][j],
dp[cur][j - 1]
)
return dp[cur][n]
================================================
FILE: pramp/drone_flight_planner.py
================================================
def calc_drone_min_energy(route):
ans = 0
if not route or len(route) < 2:
return ans
delta = 0
max_z = route[0][2]
for i in range(1, len(route)):
delta += route[i][2] - route[i - 1][2]
if route[i][2] > max_z:
max_z = route[i][2]
ans = delta
return ans
================================================
FILE: pramp/find_the_duplicates.py
================================================
def find_duplicates(arr1, arr2):
ans = []
if not arr1 or not arr2:
return ans
m, n = len(arr1), len(arr2)
i = j = 0
while i < m and j < n:
if arr1[i] < arr2[j]:
i += 1
elif arr1[i] > arr2[j]:
j += 1
else:
if not ans or arr1[i] != ans[-1]:
ans.append(arr1[i])
i += 1
j += 1
return ans
def find_duplicates2(arr1, arr2):
ans = []
if not arr1 or not arr2:
return ans
vals = {}
for num in arr1:
vals[num] = False
for num in arr2:
if num in vals and vals[num] is False:
vals[num] = True
ans.append(num)
return ans
def find_duplicates3(arr1, arr2):
if not arr1 or not arr2:
return []
val1 = set(arr1)
val2 = set(arr2)
return list(val1 & val2)
if __name__ == '__main__':
print(find_duplicates([1, 2, 3, 5, 6, 7], [3, 6, 7, 8, 20]))
print(find_duplicates([1, 2, 3, 3, 5, 6, 7], [3, 3, 6, 7, 8, 20]))
print(find_duplicates2([1, 2, 3, 5, 6, 7], [3, 6, 7, 8, 20]))
print(find_duplicates2([1, 2, 3, 3, 5, 6, 7], [3, 3, 6, 7, 8, 20]))
print(find_duplicates3([1, 2, 3, 5, 6, 7], [3, 6, 7, 8, 20]))
print(find_duplicates3([1, 2, 3, 3, 5, 6, 7], [3, 3, 6, 7, 8, 20]))
================================================
FILE: pramp/flatten_a_dictionary.py
================================================
def flatten_dictionary(dictionary):
ans = {}
if not dictionary:
return ans
dfs(dictionary, [], ans)
return ans
def dfs(dictionary, keys, ans):
if not isinstance(dictionary, dict):
key = '.'.join(keys)
ans[key] = dictionary
return
for key in dictionary:
if key:
keys.append(key)
dfs(dictionary[key], keys, ans)
if key:
keys.pop()
================================================
FILE: pramp/h_tree_construction.py
================================================
def draw_line(x1, y1, x2, y2):
pass
def draw_h_tree(x, y, length, depth):
"""iteration
:type x: float
:type y: float
:type length: float
:type depth: float
:rtype: void
"""
queue, _queue = [(x, y)], []
for _ in range(depth):
for x, y in queue:
x1 = x - length / 2
y1 = y + length / 2
x2 = x + length / 2
y2 = y - length / 2
draw_line(x1, y1, x1, y2)
draw_line(x2, y1, x2, y2)
draw_line(x1, y, x2, y)
_queue.append((x1, y1))
_queue.append((x1, y2))
_queue.append((x2, y1))
_queue.append((x2, y2))
queue, _queue = _queue, []
length /= 2 ** 0.5
def draw_h_tree2(x, y, length, depth):
"""recursion
:type x: float
:type y: float
:type length: float
:type depth: float
:rtype: void
"""
if depth == 0:
return
x1 = x - length / 2
y1 = y + length / 2
x2 = x + length / 2
y2 = y - length / 2
draw_line(x1, y1, x1, y2)
draw_line(x2, y1, x2, y2)
draw_line(x1, y, x2, y)
_length = length / (2 ** 0.5)
_depth = depth - 1
draw_h_tree2(x1, y1, _length, _depth)
draw_h_tree2(x1, y2, _length, _depth)
draw_h_tree2(x2, y1, _length, _depth)
draw_h_tree2(x2, y2, _length, _depth)
================================================
FILE: pramp/k_messed_array_sort.py
================================================
import heapq
def sort_k_messed_array(nums, k):
"""
Given an array of integers arr where each element is at most k places away from its sorted position.
time: O(n * log(k))
space: O(k)
"""
if not nums or not k:
return nums
n = len(nums)
heap = []
for i in range(k + 1):
heapq.heappush(heap, nums[i])
for i in range(k + 1, n):
nums[i - k - 1] = heapq.heappop(heap)
heapq.heappush(heap, nums[i])
for i in range(n - k - 1, n):
nums[i] = heapq.heappop(heap)
return nums
================================================
FILE: pramp/largest_smaller_bst_node.py
================================================
def largest_smaller_bst_node(root, target):
ans = -1
if not root:
return ans
while root:
if root.val < target:
ans = root.val
root = root.right
else:
root = root.left
return ans
================================================
FILE: pramp/number_of_paths.py
================================================
def num_of_paths_to_dest(n):
if not n or n == 0:
return 0
if n == 1:
return 1
dp = [[0] * n for _ in range(n)]
for i in range(n):
dp[i][0] = 1
for i in range(1, n):
for j in range(1, i + 1):
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
return dp[n - 1][n - 1]
================================================
FILE: pramp/pairs_with_specific_difference.py
================================================
def find_pairs_with_given_difference(arr, k):
ans = []
if not arr or not isinstance(k, int):
return ans
n = len(arr)
sums = {}
for i in range(n):
sums[arr[i] - k] = i
for j in range(n):
if arr[j] in sums:
i = sums[arr[j]]
ans.append([arr[i], arr[j]])
return ans
================================================
FILE: pramp/pancake_sort.py
================================================
"""
Main Concept:
1. find the max and do `flip` in [0,max_idx]
2. the max is already at `0`, `flip` it to the correct pos
3. repeat (1)
[1,5,2,4,3]
=> [5,1,2,4,3]
=> [3,4,2,1,5]
=> [4,3,2,1,5]
=> [1,2,3,4,5]
Testing:
>>> gotcha = []
>>> for _in, _out in (
... (([1, 2, 3, 4, 5], 3), [3, 2, 1, 4, 5]),
... (([1, 2, 3, 4, 5], 0), [1, 2, 3, 4, 5]),
... (([1, 2, 3, 4, 5], 5), [5, 4, 3, 2, 1]),
... (([1, 2, 3, 4, 5], 6), [5, 4, 3, 2, 1]),
... ):
... flip(*_in)
... if _in[0] != _out: print(_in, _out)
... gotcha.append(_in[0] == _out)
>>> bool(gotcha) and all(gotcha)
True
>>> gotcha = []
>>> for _in, _out in (
... ([1, 5, 4, 3, 2], [1, 2, 3, 4, 5]),
... ([5, 1, 3, 4, 2], [1, 2, 3, 4, 5]),
... ([1, 4, 2, 3, 5], [1, 2, 3, 4, 5]),
... ([5, 10, 1, 20, 4], [1, 4, 5, 10, 20]),
... ([5, 1, 4, 20, 10], [1, 4, 5, 10, 20]),
... ):
... res = pancake_sort(_in)
... if res != _out: print(_in, res)
... gotcha.append(res == _out)
>>> bool(gotcha) and all(gotcha)
True
"""
def pancake_sort(nums):
"""using `flip` to sort input in order
:type nums: list[int]
:rtype: list[int]
"""
for j in range(len(nums) - 1, -1, -1):
i = get_max_index(nums, j)
flip(nums, i + 1)
flip(nums, j + 1)
return nums
def flip(nums, k):
"""reverses the order of the first `k` elements in the array `nums` place
:type nums: list[int]
:type k: int
:rtype: void
"""
if not nums:
return
if not k or k <= 1:
return
k = min(k, len(nums))
i, j = 0, k - 1
while i < j:
nums[i], nums[j] = nums[j], nums[i]
i += 1
j -= 1
def get_max_index(nums, i):
"""return the index of the maximum in [0,i] of `nums`
:type nums: list[int]
:type i: int
:rtype: int
"""
k = 0
for j in range(1, i + 1):
if nums[j] > nums[k]:
k = j
return k
================================================
FILE: pramp/root_of_number.py
================================================
"""
>>> gotcha = []
>>> for _in, _out in (
... ((4, 2), 2),
... ((9, 3), 2.08),
... ((7, 3), 1.913),
... ((11, 4), 1.821),
... ):
... res = root(*_in)
... valid = abs(res - _out) < 0.001
... if not valid: print(_in, res)
... gotcha.append(valid)
>>> bool(gotcha) and all(gotcha)
True
"""
def root(x, n):
if not x or x in (0, 1):
return x
left = 0
right = max(1, x)
while right - left > 1e-4:
mid = (left + right) / 2.0
product = get_product(mid, n)
if product < x:
left = mid
elif product > x:
right = mid
else:
return mid
return (left + right) / 2.0
def get_product(x, n):
res = 1
for _ in range(n):
res *= x
return res
================================================
FILE: pramp/sales_path.py
================================================
"""
1. find the minimum cost for the path from root through leaf
=> get_cheapest_cost
2. find all paths which have the minimum cost
=> get_cheapest_cost_paths
=> how to find all paths by one-pass?
Node Structure:
>>> class TreeNode:
... def __init__(self, cost):
... self.cost = cost
... self.children = []
Testing:
>>> trees = []
>>> TEST_CASE = (
... ((
... (0, 0, [1, 2, 3]),
... (1, 5, [4]),
... (2, 3, [5]),
... (3, 6, [6]),
... (4, 4, []),
... (5, 2, []),
... (6, 1, []),
... ), 5, [[0, 3, 2]]),
... ((
... (0, 0, [1, 2, 3]),
... (1, 5, [4]),
... (2, 3, [5, 6]),
... (3, 6, [7, 8]),
... (4, 4, []),
... (5, 2, [9]),
... (6, 0, [10]),
... (7, 1, []),
... (8, 5, []),
... (9, 1, [11]),
... (10, 10, []),
... (11, 1, []),
... ), 7, [[0, 3, 2, 1, 1], [0, 6, 1]]),
... )
>>> for case, _, _ in TEST_CASE:
... nodes = {}
... for id, cost, _ in case:
... nodes[id] = TreeNode(cost)
... for id, _, children in case:
... nodes[id].children = [nodes[id] for id in children]
... trees.append(nodes[0])
1. test get_cheapest_cost:
>>> gotcha = []
>>> for i in range(len(TEST_CASE)):
... res = get_cheapest_cost(trees[i])
... if res != TEST_CASE[i][1]: print(i, res)
... gotcha.append(res == TEST_CASE[i][1])
>>> bool(gotcha) and all(gotcha)
True
2. test get_cheapest_cost_paths:
>>> gotcha = []
>>> for i in range(len(TEST_CASE)):
... res = get_cheapest_cost_paths(trees[i])
... if res != TEST_CASE[i][2]: print(i, res)
... gotcha.append(res == TEST_CASE[i][2])
>>> bool(gotcha) and all(gotcha)
True
"""
def get_cheapest_cost(root):
if not root:
return 0
if not root.children:
return root.cost
res = float('inf')
for child in root.children:
tmp = get_cheapest_cost(child)
if tmp < res:
res = tmp
return res + root.cost
def get_cheapest_cost_paths(root):
ans = []
if not root:
return ans
min_val = get_cheapest_cost(root)
_dfs(root, min_val, ans, [root.cost])
return ans
def _dfs(root, target, ans, path):
if not root:
return
if not root.children and target == 0:
ans.append(path[:])
return
if not root.children:
return
for child in root.children:
if target < child.cost:
continue
path.append(child.cost)
_dfs(child, target - child.cost, ans, path)
path.pop()
================================================
FILE: pramp/sentence_reverse.py
================================================
"""
Test Case:
>>> gotcha = []
>>> for _in, _out in (
... ([' ', ' '], [' ', ' ']),
... (['a', ' ', ' ', 'b'], ['b', ' ', ' ', 'a']),
... (['h', 'e', 'l', 'l', 'o'], ['h', 'e', 'l', 'l', 'o']),
... (
... ['p', 'e', 'r', 'f', 'e', 'c', 't', ' ', 'm', 'a', 'k', 'e', 's', ' ', 'p', 'r', 'a', 'c', 't', 'i', 'c', 'e'],
... ['p', 'r', 'a', 'c', 't', 'i', 'c', 'e', ' ', 'm', 'a', 'k', 'e', 's', ' ', 'p', 'e', 'r', 'f', 'e', 'c', 't']
... ),
... (
... ['y', 'o', 'u', ' ', 'w', 'i', 't', 'h', ' ', 'b', 'e', ' ', 'f', 'o', 'r', 'c', 'e', ' ', 't', 'h', 'e', ' ', 'm', 'a', 'y'],
... ['m', 'a', 'y', ' ', 't', 'h', 'e', ' ', 'f', 'o', 'r', 'c', 'e', ' ', 'b', 'e', ' ', 'w', 'i', 't', 'h', ' ', 'y', 'o', 'u']
... ),
... (
... ['g', 'r', 'e', 'a', 't', 'e', 's', 't', ' ', 'n', 'a', 'm', 'e', ' ', 'f', 'i', 'r', 's', 't', ' ', 'e', 'v', 'e', 'r', ' ', 'n', 'a', 'm', 'e', ' ', 'l', 'a', 's', 't'],
... ['l', 'a', 's', 't', ' ', 'n', 'a', 'm', 'e', ' ', 'e', 'v', 'e', 'r', ' ', 'f', 'i', 'r', 's', 't', ' ', 'n', 'a', 'm', 'e', ' ', 'g', 'r', 'e', 'a', 't', 'e', 's', 't']
... ),
... ([' ', ' ', 'a', ' ', 'b', 'c'], ['b', 'c', ' ', 'a', ' ', ' ']),
... (['b', 'c', ' ', 'a', ' ', ' '], [' ', ' ', 'a', ' ', 'b', 'c']),
... ([' ', ' ', 'a', ' ', 'b', 'c', ' '], [' ', 'b', 'c', ' ', 'a', ' ', ' ']),
... ):
... for test_func in (reverse_words, reverse_words2):
... res = test_func(_in)
... if res != _out: print(_in, res)
... gotcha.append(res == _out)
>>> bool(gotcha) and all(gotcha)
True
"""
"""
Approach 1: use built-in function
"""
def reverse_words(arr):
return list(' '.join(reversed(''.join(arr).split(' '))))
"""
Approach 2: swap children and modify in place
"""
def reverse_words2(arr):
if not arr:
return []
n = len(arr)
reverse_in_range(arr, 0, n - 1)
left = 0
while left < n and arr[left] == ' ':
left += 1
for right in range(n):
if arr[right] != ' ':
continue
reverse_in_range(arr, left, right - 1)
left = right + 1
right = n - 1
while right >= 0 and arr[right] == ' ':
right -= 1
reverse_in_range(arr, left, right)
return arr
def reverse_in_range(arr, i, j):
"""
to reverse arr[i:j + 1]
"""
while i < j:
arr[i], arr[j] = arr[j], arr[i]
i += 1
j -= 1
================================================
FILE: pramp/smallest_substring_of_all_characters.py
================================================
def get_shortest_unique_substring(strs, s):
if not strs or not s:
return ''
freq = dict.fromkeys(strs, 0)
left = cnt = 0
start = 0
size = INF = float('inf')
for right in range(len(s)):
if s[right] in freq:
if freq[s[right]] == 0:
cnt += 1
freq[s[right]] += 1
while cnt == len(freq):
if right - left + 1 < size:
start = left
size = right - left + 1
if s[left] in freq:
freq[s[left]] -= 1
if freq[s[left]] == 0:
cnt -= 1
left += 1
return s[start:start + size] if size < INF else ''
================================================
FILE: pramp/time_planner.py
================================================
def meeting_planner(slots1, slots2, duration):
if not slots1 or not slots2 or not duration:
return []
m, n = len(slots1), len(slots2)
i = j = 0
while i < m and j < n:
start = max(slots1[i][0], slots2[j][0])
end = min(slots1[i][1], slots2[j][1])
if start + duration <= end:
return [start, start + duration]
if slots1[i][1] < slots2[j][1]:
i += 1
else:
j += 1
return []
================================================
FILE: pramp/word_count_engine.py
================================================
"""
>>> gotcha = []
>>> for _in, _out in (
... (
... "Practice makes perfect. you'll only get Perfect by practice. just practice!",
... [['practice', 3], ['perfect', 2], ['makes', 1], ['youll', 1], ['only', 1], ['get', 1], ['by', 1], ['just', 1]],
... ),
... (
... "Practice makes perfect. just practice! you'll only get Perfect by practice.",
... [['practice', 3], ['perfect', 2], ['makes', 1], ['just', 1], ['youll', 1], ['only', 1], ['get', 1], ['by', 1]],
... ),
... (
... "Practice makes perfect. you'll only get Perfect by practice. just practice by yourself!",
... [['practice', 3], ['perfect', 2], ['by', 2], ['makes', 1], ['youll', 1], ['only', 1], ['get', 1], ['just', 1], ['yourself', 1]],
... ),
... ):
... res = word_count_engine(_in)
... if res != _out: print(_in, res)
... gotcha.append(res == _out)
>>> bool(gotcha) and all(gotcha)
True
"""
def word_count_engine(document):
"""
:type document: str
:rtype: list[list[str]]
count and sort
time: O(n logn)
"""
ans = []
document = document and document.strip()
if not document:
return ans
document = ''.join(c for c in document if c.isalnum() or c == ' ')
word2idx = {}
for word in document.lower().strip().split():
if not word:
continue
if word not in word2idx:
ans.append([word, 0])
word2idx[word] = len(ans) - 1
i = word2idx[word]
ans[i][1] += 1
ans.sort(key=lambda x: x[1], reverse=True)
return ans
def word_count_engine2(document):
"""
:type document: str
:rtype: list[list[str]]
# TODO
LRU
time: O(n)
"""
pass
================================================
FILE: topic/README.md
================================================
The time and space complexity of common algorithms and data-structures
======
- Inspired by [Big-O Algorithm Complexity Cheat Sheet @ericdrowell](http://bigocheatsheet.com)
- For the complexity representation
- 1 param: $\Theta(avg.)$
- 2 params: $\Theta(avg.)$ / $O(worst)$
- 3 params: $\Omega(best)$ / $\Theta(avg.)$ / $O(worst)$
## Data Structures
| Name | 结构名 | Space | Access | Search | Insert | Delete | Note |
| --- | --- | --- | --- | --- | --- | --- | --- |
| [Array](http://en.wikipedia.org/wiki/Array_data_structure) | 数组 | $O(n)$ | $\Theta(1)$ / $O(1)$ | $\Theta(n)$ / $O(n)$ | $\Theta(n)$ / $O(n)$ | $\Theta(n)$ / $O(n)$ | Swap with `a[-1]` => $O(1)$ in insert, delete |
| [Queue](https://en.wikipedia.org/wiki/Queue_(abstract_data_type)) | 队列 | $O(n)$ | $\Theta(n)$ / $O(n)$ | $\Theta(n)$ / $O(n)$ | $\Theta(1)$ / $O(1)$ | $\Theta(1)$ / $O(1)$ | |
| [Stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type)) | 栈 | $O(n)$ | $\Theta(n)$ / $O(n)$ | $\Theta(n)$ / $O(n)$ | $\Theta(1)$ / $O(1)$ | $\Theta(1)$ / $O(1)$ | |
| [Heap](https://en.wikipedia.org/wiki/Heap_(data_structure)) | 堆 | | | | | | Check it in [Heap](./#heap) Section |
| [Graph](https://en.wikipedia.org/wiki/Graph_(abstract_data_type)) | 图 | | | | | | Check it in [Graph](./#graph) Section |
| [Singly-Linked List](https://en.wikipedia.org/wiki/Linked_list#Singly_linked_lists) | 单向链表 | $O(n)$ | $\Theta(n)$ / $O(n)$ | $\Theta(n)$ / $O(n)$ | $\Theta(1)$ / $O(1)$ | $\Theta(1)$ / $O(1)$ | |
| [Doubly-Linked List](https://en.wikipedia.org/wiki/Doubly_linked_list) | 双向链表 | $O(n)$ | $\Theta(n)$ / $O(n)$ | $\Theta(n)$ / $O(n)$ | $\Theta(1)$ / $O(1)$ | $\Theta(1)$ / $O(1)$ | |
| [Skip List](https://en.wikipedia.org/wiki/Skip_list) | 跳跃表 | $O(n\log{n})$ | $\Theta(\log{n})$ / $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | |
| [Hash Table](https://en.wikipedia.org/wiki/Hash_table) | 哈希表 | $O(n)$ | - | $\Theta(1)$ / $O(n)$ | $\Theta(1)$ / $O(n)$ | $\Theta(1)$ / $O(n)$ | |
| [Binary Search Tree](https://en.wikipedia.org/wiki/Binary_search_tree) | 二叉查找树 | $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | |
| [Cartesian Tree](https://en.wikipedia.org/wiki/Cartesian_tree) | 笛卡尔树 | $O(n)$ | - | $\Theta(\log{n})$ / $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | |
| [B-Tree](https://en.wikipedia.org/wiki/B-tree) | B 树 | $O(n)$ | $\Theta(\log{n})$ / $O(\log{n})$ | $\Theta(\log{n})$ / $O(\log{n})$ | $\Theta(\log{n})$ / $O(\log{n})$ | $\Theta(\log{n})$ / $O(\log{n})$ | |
| [Red-Black Tree](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree) | 红黑树 | $O(n)$ | $\Theta(\log{n})$ / $O(\log{n})$ | $\Theta(\log{n})$ / $O(\log{n})$ | $\Theta(\log{n})$ / $O(\log{n})$ | $\Theta(\log{n})$ / $O(\log{n})$ | |
| [Splay Tree](https://en.wikipedia.org/wiki/Splay_tree) | 伸展树 | $O(n)$ | - | $\Theta(\log{n})$ / $O(\log{n})$ | $\Theta(\log{n})$ / $O(\log{n})$ | $\Theta(\log{n})$ / $O(\log{n})$ | |
| [AVL Tree](https://en.wikipedia.org/wiki/AVL_tree) | AVL 树 | $O(n)$ | $\Theta(\log{n})$ / $O(\log{n})$ | $\Theta(\log{n})$ / $O(\log{n})$ | $\Theta(\log{n})$ / $O(\log{n})$ | $\Theta(\log{n})$ / $O(\log{n})$ | |
| [KD Tree](https://en.wikipedia.org/wiki/K-d_tree) | K 维树 | $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | |
### Heap
| Implementation | 结构名 | Heapify | Access Top | Pop Top | Increase Key | Insert | Delete | Merge |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| Linked List (Sorted) | 链表(已排序) | - | $\Theta(1)$ | $\Theta(1)$ | $\Theta(n)$ | $\Theta(n)$ | $\Theta(1)$ | $\Theta(m+n)$ |
| Linked List (Unsorted) | 链表(未排序) | - | $\Theta(n)$ | $\Theta(n)$ | $\Theta(1)$ | $\Theta(1)$ | $\Theta(1)$ | $\Theta(1)$ |
| [Binary Heap](https://en.wikipedia.org/wiki/Binary_heap) | 二叉堆 | $O(n)$ | $\Theta(1)$ | $\Theta(\log{n})$ | $\Theta(\log{n})$ | $\Theta(\log{n})$ | $\Theta(\log{n})$ | $\Theta(m+n)$ |
| [Binomial Heap](https://en.wikipedia.org/wiki/Binomial_heap) | 二项堆 | - | $\Theta(1)$ | $\Theta(\log{n})$ | $\Theta(\log{n})$ | $\Theta(1)$ | $\Theta(\log{n})$ | $\Theta(\log{n})$ |
| [Fibonacci Heap](https://en.wikipedia.org/wiki/Fibonacci_heap) | 斐波那契堆 | - | $\Theta(1)$ | $\Theta(\log{n})$ | $\Theta(1)$ | $\Theta(1)$ | $\Theta(\log{n})$ | $\Theta(1)$ |
### Graph
- Graph with $\lvert{V}\rvert$ vertices and $\lvert{E}\rvert$ edges
| Vertex / Edge Management | 结构名 | Storage | Add Vertex | Add Edge | Remove Vertex | Remove Edge | Search |
| --- | --- | --- | --- | --- | --- | --- | --- |
| [Adjacency List](http://reference.wolfram.com/language/ref/AdjacencyList.html) | 邻接表 | $O(\lvert{V}\rvert+\lvert{E}\rvert)$ | $\Theta(1)$ | $\Theta(1)$ | $\Theta(\lvert{V}\rvert+\lvert{E}\rvert)$ | $\Theta(\lvert{E}\rvert)$ | $\Theta(\lvert{V}\rvert)$ |
| [Incidence List](http://reference.wolfram.com/language/ref/IncidenceList.html) | 关联表 | $O(\lvert{V}\rvert+\lvert{E}\rvert)$ | $\Theta(1)$ | $\Theta(1)$ | $\Theta(\lvert{E}\rvert)$ | $\Theta(\lvert{E}\rvert)$ | $\Theta(\lvert{E}\rvert)$ |
| [Adjacency Matrix](http://reference.wolfram.com/language/ref/AdjacencyMatrix.html) | 邻接矩阵 | $O(\lvert{V}\rvert^2)$ | $\Theta(\lvert{V}\rvert^2)$ | $\Theta(1)$ | $\Theta(\lvert{V}\rvert^2)$ | $\Theta(1)$ | $\Theta(1)$ |
| [Incidence Matrix](http://reference.wolfram.com/language/ref/IncidenceMatrix.html) | 关联矩阵 | $O(\lvert{V}\rvert\cdot\lvert{E}\rvert)$ | $\Theta(\lvert{V}\rvert\cdot\lvert{E}\rvert)$ | $\Theta(\lvert{V}\rvert\cdot\lvert{E}\rvert)$ | $\Theta(\lvert{V}\rvert\cdot\lvert{E}\rvert)$ | $\Theta(\lvert{V}\rvert\cdot\lvert{E}\rvert)$ | $\Theta(\lvert{E}\rvert)$ |
## Algorithms
### Sorting Algorithms
| Name | 算法名 | Space | Time | Note |
| --- | --- | --- | --- | --- |
| [Quick Sort](https://en.wikipedia.org/wiki/Quicksort) | 快速排序 | $O(\log{n})$ | $\Omega(n\log{n})$ / $\Theta(n\log{n})$ / $O(n^2)$ | Basic
Unstable |
| [Merge Sort](https://en.wikipedia.org/wiki/Merge_sort) | 归并排序 | $O(n)$ | $\Omega(n\log{n})$ / $\Theta(n\log{n})$ / $O(n\log{n})$ | Basic
Stable |
| [Heap Sort](https://en.wikipedia.org/wiki/Heapsort) | 堆排序 | $O(1)$ | $\Omega(n\log{n})$ / $\Theta(n\log{n})$ / $O(n\log{n})$ | Basic
Unstable |
| [Bubble Sort](https://en.wikipedia.org/wiki/Bubble_sort) | 冒泡排序 | $O(1)$ | $\Omega(n)$ / $\Theta(n^2)$ / $O(n^2)$ | Basic
Stable |
| [Radix Sort](https://en.wikipedia.org/wiki/Radix_sort) | 基数排序 | $O(n+k)$ | $\Omega(nk)$ / $\Theta(nk)$ / $O(nk)$ | Basic
Stable |
| [Selection Sort](https://en.wikipedia.org/wiki/Selection_sort) | 选择排序 | $O(1)$ | $\Omega(n^2)$ / $\Theta(n^2)$ / $O(n^2)$ | Basic
Unstable |
| [Shell Sort](https://en.wikipedia.org/wiki/Shellsort) | 希尔排序 | $O(1)$ | $\Omega(n\log{n})$ / $\Theta(n(\log{n})^2)$ / $O(n(\log{n})^2)$ | Basic
Unstable |
| [Insertion Sort](https://en.wikipedia.org/wiki/Insertion_sort) | 插入排序 | $O(1)$ | $\Omega(n)$ / $\Theta(n^2)$ / $O(n^2)$ | Basic
Stable |
| [Tim Sort](https://en.wikipedia.org/wiki/Timsort) | 提姆排序 | $O(n)$ | $\Omega(n)$ / $\Theta(n\log{n})$ / $O(n\log{n})$ | Stable |
| [Bucket Sort](https://en.wikipedia.org/wiki/Bucket_sort) | 桶排序 | $O(n)$ | $\Omega(n+k)$ / $\Theta(n+k)$ / $O(n^2)$ | Stable |
| [Tree Sort](https://en.wikipedia.org/wiki/Tree_sort) | 树排序 | $O(n)$ | $\Omega(n\log{n})$ / $\Theta(n\log{n})$ / $O(n^2)$ | |
| [Counting Sort](https://en.wikipedia.org/wiki/Counting_sort) | 计数排序 | $O(k)$ | $\Omega(n+k)$ / $\Theta(n+k)$ / $O(n+k)$ | Stable |
| [Cube Sort](https://en.wikipedia.org/wiki/Cubesort) | 立方排序 | $O(n)$ | $\Omega(n)$ / $\Theta(n\log{n})$ / $O(n\log{n})$ | Stable |
### Searching Algorithms
- Graph with $\lvert{V}\rvert$ vertices and $\lvert{E}\rvert$ edges
| Name | 算法名 | Space | Time | Note |
| --- | --- | --- | --- | --- |
| [Depth First Search](https://en.wikipedia.org/wiki/Depth-first_search) | 深度优先搜索 | $O(\lvert{V}\rvert)$ | - / $O(\lvert{V}\rvert+\lvert{E}\rvert)$ | |
| [Breadth First Search](https://en.wikipedia.org/wiki/Breadth-first_search) | 广度优先搜索 | $O(\lvert{V}\rvert)$ | - / $O(\lvert{V}\rvert+\lvert{E}\rvert)$ | |
| [Binary Search](https://en.wikipedia.org/wiki/Binary_search_algorithm) | 二分搜索 | $O(1)$ | $\Theta(\log{n})$ / $O(\log{n})$ | Sorted array of n elements |
| [Brute Force](https://en.wikipedia.org/wiki/Brute-force_search) | 穷举搜索 | $O(1)$ | $\Theta(n)$ / $O(n)$ | Array |
| [Shortest path by Bellman-Ford](https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm) | | $O(\lvert{V}\rvert)$ | $\Theta(\lvert{V}\rvert\cdot\lvert{E}\rvert)$ / $O(\lvert{V}\rvert\cdot\lvert{E}\rvert)$ | |
| [Shortest path by Dijkstra (Min-heap)](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm) | | $O(\lvert{V}\rvert)$ | $\Theta((\lvert{V}\rvert+\lvert{E}\rvert)\cdot\log{\lvert{V}\rvert})$ / $O((\lvert{V}\rvert+\lvert{E}\rvert)\cdot\log{\lvert{V}\rvert})$ | |
| Shortest path by Dijkstra (Unordered Array) | | $O(\lvert{V}\rvert)$ | $\Theta(\lvert{V}\rvert^2)$ / $O(\lvert{V}\rvert^2)$ | |
================================================
FILE: topic/_test/python/__init__.py
================================================
from _test.python.test_base import TestBase
================================================
FILE: topic/_test/python/test_base.py
================================================
import unittest
import datetime
class TaskTimer:
def __init__(self):
self.reset_time()
def reset_time(self):
self.timestamp = datetime.datetime.now()
def print_duration(self):
duration = datetime.datetime.now() - self.timestamp
print(duration.microseconds // 10 / 100.0, 'ms')
class TestBase(unittest.TestCase):
MSG_TEMPLATE = '{status}: {msg}'
@classmethod
def setUpClass(cls):
print(cls.MSG_TEMPLATE.format(
status=cls.__name__,
msg='Starting tests...'
))
@classmethod
def tearDownClass(cls):
print(cls.MSG_TEMPLATE.format(
status=cls.__name__,
msg='Finished tests.\n'
))
def setUp(self):
self.task_timer = TaskTimer()
def tearDown(self):
self.task_timer.print_duration()
================================================
FILE: topic/balanced_search_tree/python/__init__.py
================================================
================================================
FILE: topic/balanced_search_tree/python/_helper.py
================================================
================================================
FILE: topic/balanced_search_tree/python/red_black_tree.py
================================================
# TODO
================================================
FILE: topic/bit_manipulation/README.md
================================================
Bit Manipulation
======
## Modulo
```python
>>> a & 1 == a % 2
True
```
## Division
```python
>>> a >> 1 == a // 2
True
```
## 0 -> 1, 1 -> 0
output 1 if input 0
```python
>>> True ^ 1
0
>>> 1 ^ 1
0
>>> False ^ 1
1
>>> 0 ^ 1
1
```
## To check a number is power of 2
if a number is power of 2, then there is only 1 in the bit format in the number.
```python
>>> [(num & (num - 1)) == 0 for num in (4, 5, 11, 16, 21, 29, 32)]
[True, False, False, True, False, False, True]
```
================================================
FILE: topic/bit_manipulation/python/__init__.py
================================================
from bit_manipulation.python.calculator_in_bit import Calculator
================================================
FILE: topic/bit_manipulation/python/calculator_in_bit.py
================================================
"""
1. Addition
- go through example: 1 + 5, that is 0001 + 0101
step1/ no carry(xor): 0001 ^ 0101 == 0100
step2/ considering carry(and + shift): (0001 & 0101) << 1 == 0010
the carry only occurs when the same digit is 1
step3/ repeat (1) and (2) til the result in (2) is 0
1 + 5 -> 0001 + 0101
r1: 0001 ^ 0101 == 0100, (0001 & 0101) << 1 == 0010
r2: 0100 ^ 0010 == 0110, (0100 & 0010) << 1 == 0000
got 0110 == 6
- deal with negative values
if a < 0 and b == 0,
=> a wont be `&` (cannot enter iteration)
=> a >> 31 == -1
=> a ^ ~INT_RANGE got wrong number
so the simplest way is
`a >> 31 == 0` -> `a >> 31 <= 0`
2. Subtraction
the simplest way is `a + (-b)`
to get the complement of `b`, that is `-b`
-b = ~b + 1
3. Multiplication
in oct | in bin
a 12 | a 1100
x b 10 | x b 1010
-------- | ----------
00 | 0000
12 | 1100
-------- | 0000
120 | 1100
| ----------
| 1111000
r1/ 1100 * 0 = 0000
r2/ 11000 * 1 = 11000
r3/ 110000 * 0 = 000000
r4/ 1100000 * 1 = 1100000
4. Division
if a // (2 ** i) >= b:
=> quotient += 2 ** i
=> a -= b * (2 ** i) for next iteration
"""
class Calculator:
INT_RANGE = 0xFFFFFFFF
@classmethod
def _plus(cls, a, b):
"""
recursion
"""
if b == 0:
return a if a >> 31 <= 0 else a ^ ~cls.INT_RANGE
return cls._plus((a ^ b) & cls.INT_RANGE, (a & b) << 1)
@classmethod
def plus(cls, a, b):
"""
iteration
"""
while b != 0:
a, b = a ^ b, (a & b) << 1
a &= cls.INT_RANGE
return a if a >> 31 <= 0 else a ^ ~cls.INT_RANGE
@classmethod
def minus(cls, a, b):
return cls.plus(a, cls.plus(~b, 1))
@classmethod
def times(cls, a, b):
if not a or not b:
return 0
x = a if a > 0 else cls.plus(~a, 1)
y = b if b > 0 else cls.plus(~b, 1)
product = 0
while y:
if y & 1:
product = cls.plus(product, x)
x = x << 1
y = y >> 1
if a ^ b >= 0:
return product
return cls.plus(~product, 1)
@classmethod
def divide(cls, a, b):
if not b:
return float('inf') if a >= 0 else float('-inf')
if not a:
return 0
x = a if a > 0 else cls.plus(~a, 1)
y = b if b > 0 else cls.plus(~b, 1)
quotient = 0
for i in range(31, -1, -1):
if x >> i >= y:
quotient = cls.plus(quotient, 1 << i)
x = cls.minus(x, y << i)
if a ^ b >= 0:
return quotient
if x == 0:
return cls.plus(~quotient, 1)
return ~quotient
================================================
FILE: topic/bit_manipulation/python/calculator_in_bit__test.py
================================================
from _test.python import *
from bit_manipulation.python import *
class TestBitCalculator(TestBase):
CASES = (
(-1, -1), (-1, 0), (-1, 1),
( 0, -1), ( 0, 0), ( 0, 1),
( 1, -1), ( 1, 0), ( 1, 1),
# (-0x80000000, -1),
# (-0x80000000, 1),
# ( 0x7FFFFFFF, -1),
# ( 0x7FFFFFFF, 1),
# (-1, -0x80000000),
# (-1, 0x7FFFFFFF),
# ( 1, -0x80000000),
# ( 1, 0x7FFFFFFF),
(-9, -7), (-9, 7),
( 9, -7), ( 9, 7),
(-7, -9), (-7, 9),
( 7, -9), ( 7, 9),
(-123, 0),
( 123, 0),
( 0, -123),
( 0, 123),
)
def test_plus_recursion(self):
for a, b in self.CASES:
self.assertEqual(a + b, Calculator._plus(a, b))
def test_plus_iteration(self):
for a, b in self.CASES:
self.assertEqual(a + b, Calculator.plus(a, b))
def test_minus(self):
for a, b in self.CASES:
self.assertEqual(a - b, Calculator.minus(a, b))
def test_times(self):
for a, b in self.CASES:
self.assertEqual(a * b, Calculator.times(a, b))
def test_divide(self):
for a, b in self.CASES:
if not b:
c = float('inf') if a >= 0 else float('-inf')
elif not a:
c = 0
else:
c = a // b
self.assertEqual(c, Calculator.divide(a, b))
================================================
FILE: topic/complexity_analysis.md
================================================
Complexity Analysis
======
## Case #1
- time: `O(N)`
- REF: https://en.wikipedia.org/wiki/1/2_%2B_1/4_%2B_1/8_%2B_1/16_%2B_%E2%8B%AF
`N/2 + N/4 + N/8 + ... = N`
```java
int count = 0;
for (int i = N; i > 0; i /= 2) {
for (int j = 0; j < i; j++) {
count += 1;
}
}
```
## Case #2
If X will always be a better choice for **large inputs**,
then we say that **an algorithm X is asymptotically more efficient than Y**.
## Case #3
- time: `O(N)`
- the `j` go through that array only once.
```java
int j = 0;
for(int i = 0; i < n; ++i) {
while(j < n && arr[i] < arr[j]) {
j++;
}
}
```
## Case #4
- time: `O(2 ^ N)`
- REF: https://math.stackexchange.com/questions/177405/prove-by-induction-2n-cn-0-cn-1-cdots-cn-n
`C(n, 0) + C(n, 1) + ... + C(n, n) = 2 ^ n`
================================================
FILE: topic/dynamic_programming/README.md
================================================
Dynamic Programming
======
## Steps
1. what is the meaning of `dp[i]`
2. how to do initialization
3. how you iterate that `dp`
4. what is the returning
## Space Optimization
- rolling array, e.g., [lintcode/115_unique_paths_ii.py](../../lintcode/115_unique_paths_ii.py)
- cache previous cell, e.g., [other/unique_paths_with_followups.py](../../other/unique_paths_with_followups.py)
## Print Paths
- 1D example: [leetcode/300_longest_increasing_subsequence.py](../../leetcode/300_longest_increasing_subsequence.py)
- 2D example: [lintcode/168_burst_balloons.py](../../lintcode/168_burst_balloons.py)
================================================
FILE: topic/graph/README.md
================================================
Graph
======
## Graph Searching
- Cyclic Graph: maintain a `seen` to record visited nodes.
- Undirected Acyclic Graph: pick any point to start searching.
- Directed Acyclic Graph: count `indegree` and do `Topological Sorting`.
================================================
FILE: topic/graph/python/__init__.py
================================================
from graph.python.union_find import UnionFind
================================================
FILE: topic/graph/python/union_find.py
================================================
class UnionFind:
def __init__(self):
self.nodes = {}
def union(self, u, v):
a = self.find(u)
b = self.find(v)
if a is not b:
self.nodes[b] = a
return a
def find(self, u):
if u not in self.nodes:
self.nodes[u] = u
return u
if self.nodes[u] is u:
return u
self.nodes[u] = self.find(self.nodes[u])
return self.nodes[u]
================================================
FILE: topic/graph/python/union_find__test.py
================================================
from _test.python import *
from graph.python import UnionFind
class TestUnionFind(TestBase):
def test_init(self):
uf = UnionFind()
self.assertEqual(uf.nodes, {})
def test_union(self):
uf = UnionFind()
for i in range(1, 6):
query = uf.union(0, i)
self.assertEqual(query, 0)
for i in range(1, 6):
self.assertEqual(uf.nodes[i], 0)
def test_compression(self):
uf = UnionFind()
uf.union(2, 3)
uf.union(1, 2)
self.assertEqual(uf.nodes[3], 2)
self.assertEqual(uf.nodes[2], 1)
query = uf.find(3)
self.assertEqual(query, 1)
self.assertEqual(uf.nodes[3], 1)
================================================
FILE: topic/hash/python/__init__.py
================================================
from hash.python.hashtable import HashTable
from hash.python.geohash import GeoHash
================================================
FILE: topic/hash/python/geohash.py
================================================
"""
REF:
- lintcode/529_geohash.py
- lintcode/530_geohash_ii.py
Main Concept: for encoding
1. use binary search to convert `lng` and `lat` to `bincode`
1: up or right
0: down or left
2. merge `lngcode` and `latcode` alternately into one `bincode`
`lngcode[0] + latcode[0] + lngcode[1] + ...`
3. pick every 5 digit from `bincode` and convert that to base32 chars
"""
class GeoHash:
def __init__(self):
ignored_chars = {ord(i) for i in 'ailo'}
base32 = [str(i) for i in range(10)]
base32.extend(
chr(i)
for i in range(ord('a'), ord('z') + 1)
if i not in ignored_chars
)
base32i = {base32[i]: i for i in range(len(base32))}
self.base32 = base32
self.base32i = base32i
def encode(self, latitude, longitude, precision=5):
digits = precision * 5 // 2 + 1
lngcode = self._pos_to_bin(longitude, digits, -180, 180)
latcode = self._pos_to_bin( latitude, digits, -90, 90)
bincode = []
for i in range(digits):
bincode.append(lngcode[i])
bincode.append(latcode[i])
geohash = [
self.base32[int(''.join(bincode[i:i + 5]), 2)]
for i in range(0, len(bincode), 5)
]
return ''.join(geohash[:precision])
def decode(self, geohash):
bincode = []
for i in geohash:
if i not in self.base32i:
return []
code = bin(self.base32i[i])[2:].rjust(5, '0')
bincode.extend(list(code))
n = len(bincode)
latcode = [bincode[i] for i in range(1, n, 2)]
lngcode = [bincode[i] for i in range(0, n, 2)]
return [
self._bin_to_pos(latcode, -90, 90),
self._bin_to_pos(lngcode, -180, 180),
]
def _bin_to_pos(self, bincode, start, end):
for i in bincode:
mid = (start + end) / 2.0
if i == '1':
start = mid
else:
end = mid
return (start + end) / 2.0
def _pos_to_bin(self, position, digits, start, end):
bincode = []
for _ in range(digits):
mid = (start + end) / 2.0
if mid < position:
bincode.append('1')
start = mid
else:
bincode.append('0')
end = mid
return bincode
================================================
FILE: topic/hash/python/geohash__test.py
================================================
from _test.python import *
from hash.python import GeoHash
class TestGeoHash(TestBase):
CASES = (
((39.92816697, 116.38954991), 'wx4g0s8q3jf9'),
((51.5171437, -0.1337183), 'gcpvhfqth5sk'),
((51.5160862, -0.1294528), 'gcpvj41wzw8b'),
((37.4846102, -122.1516928), '9q9jhrggemb1'),
((37.4219999, -122.0862462), '9q9hvu7wbq2s'),
((-90, -180), '000000000000'),
((-90, 0), '5bpbpbpbpbpb'),
((-90, 180), 'pbpbpbpbpbpb'),
((0, -180), '2pbpbpbpbpbp'),
((0, 0), '7zzzzzzzzzzz'),
((0, 180), 'rzzzzzzzzzzz'),
((90, -180), 'bpbpbpbpbpbp'),
((90, 0), 'gzzzzzzzzzzz'),
((90, 180), 'zzzzzzzzzzzz'),
)
def test_encode(self):
gh = GeoHash()
for i, o in self.CASES:
self.assertEqual(gh.encode(*i, len(o)), o)
def test_decode(self):
EPS = 1e-6
gh = GeoHash()
for (lat1, lng1), i in self.CASES:
lat2, lng2 = gh.decode(i)
self.assertTrue(abs(lat1 - lat2) < EPS)
self.assertTrue(abs(lng1 - lng2) < EPS)
================================================
FILE: topic/hash/python/hashtable.py
================================================
class HashTable:
def __init__(self, cap=4000, power=31):
self.cap = cap
self.power = power
self.size = 0
self.table = [None] * self.cap
def __repr__(self):
return '{{{}}}'.format(
', '.join(
repr(key) + ': ' + repr(val)
for key, val in self.items()
)
)
def __len__(self):
return self.size
def __setitem__(self, key, val):
self.set(key, val)
def __getitem__(self, key):
return self.get(key)
def __delitem__(self, key):
self.remove(key)
def __iter__(self):
for key, _ in self.items():
yield key
def keys(self):
for key, _ in self.items():
yield key
def values(self):
for _, val in self.items():
yield val
def items(self):
for head in self.table:
node = head
while node:
yield node.key, node.val
node = node.nxt
def set(self, key, val):
code = self._encode(key)
if not self.table[code]:
self.size += 1
self.table[code] = ListNode(key, val)
return
node = self.table[code]
while node and node.nxt and node.key != key:
node = node.nxt
if node.key == key:
node.val = val
else:
self.size += 1
node.nxt = ListNode(key, val)
def get(self, key):
code = self._encode(key)
if not self.table[code]:
raise KeyError(key)
node = self.table[code]
while node and node.key != key:
node = node.nxt
if node and node.key == key:
return node.val
raise KeyError(key)
def remove(self, key):
code = self._encode(key)
if not self.table[code]:
raise KeyError(key)
if self.table[code].key == key:
self.size -= 1
self.table[code] = self.table[code].nxt
return
node = self.table[code]
while node and node.nxt and node.nxt.key != key:
node = node.nxt
if node and node.nxt and node.nxt.key == key:
self.size -= 1
node.nxt = node.nxt.nxt
return
raise KeyError(key)
def _encode(self, key):
if isinstance(key, int):
return key % self.cap
code = 0
for c in key:
code = (code * self.power + ord(c)) % self.cap
return code
class ListNode:
def __init__(self, key, val, nxt=None):
self.key = key
self.val = val
self.nxt = nxt
================================================
FILE: topic/hash/python/hashtable__test.py
================================================
from _test.python import *
from hash.python import HashTable
class TestHashTable(TestBase):
def test_operation(self):
d = HashTable()
self.assertEqual(len(d), 0)
d[1] = 1
self.assertEqual(d[1], 1)
self.assertEqual(len(d), 1)
d[2] = 2
self.assertEqual(d[2], 2)
self.assertEqual(len(d), 2)
d['ab'] = 3
self.assertEqual(d['ab'], 3)
self.assertEqual(len(d), 3)
d['ba'] = 4
self.assertEqual(d['ba'], 4)
self.assertEqual(len(d), 4)
self.assertEqual(d['ab'], 3)
self.assertEqual(len(d), 4)
del d['ab']
self.assertEqual(len(d), 3)
with self.assertRaises(KeyError):
d['ab']
def test_collision(self):
d = HashTable(4000, 31)
self.assertEqual(len(d), 0)
self.assertEqual(d._encode('ab'), d._encode('bC'))
self.assertEqual(d._encode('ab'), d._encode('c$'))
d['ab'] = 1
self.assertEqual(d['ab'], 1)
self.assertEqual(len(d), 1)
d['bC'] = 2
self.assertEqual(d['bC'], 2)
self.assertEqual(len(d), 2)
d['c$'] = 3
self.assertEqual(d['c$'], 3)
self.assertEqual(len(d), 3)
del d['bC']
self.assertEqual(len(d), 2)
with self.assertRaises(KeyError):
d['bC']
del d['c$']
self.assertEqual(len(d), 1)
with self.assertRaises(KeyError):
d['c$']
d['bC']
del d['ab']
self.assertEqual(len(d), 0)
with self.assertRaises(KeyError):
d['ab']
d['c$']
d['bC']
================================================
FILE: topic/heap/python/__init__.py
================================================
from heap.python.binary_hash_heap import BinaryHashHeap
from heap.python.binary_heap import BinaryHeap
from heap.python.lazy_removable_heapq import LazyRemovableHeapq
from heap.python.removable_heapq import RemovableHeapq
================================================
FILE: topic/heap/python/binary_hash_heap.py
================================================
import collections
class BinaryHashHeap:
MSG_EMPTY_HEAP = 'access element from empty heap'
def __init__(self, iterable=None):
self.__heap = [0]
self.__size = 0
self.__idxs = collections.defaultdict(set)
if iterable:
self.heapify(iterable)
def __len__(self):
return self.__size
def __bool__(self):
return self.__size > 0
def heapify(self, iterable):
if not isinstance(iterable, collections.Iterable):
return
for val in iterable:
self.push(val)
def push(self, val):
self.__heap.append(val)
self.__size += 1
self.__idxs[val].add(self.__size)
self._siftdown(self.__size)
def pop(self):
if self.__size < 1:
raise IndexError(self.MSG_EMPTY_HEAP)
heap = self.__heap
idxs = self.__idxs
val = heap[1]
idxs[val].discard(1)
idxs[heap[-1]].discard(self.__size)
idxs[heap[-1]].add(1)
heap[1] = heap[-1]
heap.pop()
self.__size -= 1
self._siftup(1)
return val
def remove(self, val):
if not self.__idxs.get(val):
raise KeyError(val)
heap = self.__heap
idxs = self.__idxs
i = idxs[val].pop()
idxs[heap[-1]].discard(self.__size)
idxs[heap[-1]].add(i)
heap[i] = heap[-1]
heap.pop()
self.__size -= 1
self._siftup(i)
def top(self):
if self.__size < 1:
raise IndexError(self.MSG_EMPTY_HEAP)
return self.__heap[1]
def _siftdown(self, i):
heap = self.__heap
while i // 2 > 0:
j = i // 2
if heap[i] < heap[j]:
self._swap(i, j)
i = j
def _siftup(self, i):
heap = self.__heap
size = self.__size
while i * 2 <= size:
j = i * 2
if j + 1 <= size and heap[j + 1] < heap[j]:
j += 1
if heap[i] > heap[j]:
self._swap(i, j)
i = j
def _swap(self, i, j):
heap = self.__heap
idxs = self.__idxs
idxs[heap[i]].discard(i)
idxs[heap[j]].discard(j)
idxs[heap[i]].add(j)
idxs[heap[j]].add(i)
heap[i], heap[j] = heap[j], heap[i]
================================================
FILE: topic/heap/python/binary_heap.py
================================================
import collections
class BinaryHeap:
MSG_EMPTY_HEAP = 'access element from empty heap'
def __init__(self, iterable=None):
self.__heap = [0]
self.__size = 0
if iterable:
self.heapify(iterable)
def __len__(self):
return self.__size
def __bool__(self):
return self.__size > 0
def heapify(self, iterable):
if not isinstance(iterable, collections.Iterable):
return
for val in iterable:
self.push(val)
def push(self, val):
self.__heap.append(val)
self.__size += 1
self._siftdown(self.__size)
def pop(self):
if self.__size < 1:
raise IndexError(self.MSG_EMPTY_HEAP)
heap = self.__heap
val = heap[1]
heap[1] = heap[-1]
heap.pop()
self.__size -= 1
self._siftup(1)
return val
def top(self):
if self.__size < 1:
raise IndexError(self.MSG_EMPTY_HEAP)
return self.__heap[1]
def _siftdown(self, i):
heap = self.__heap
while i // 2 > 0:
j = i // 2
if heap[i] < heap[j]:
heap[i], heap[j] = heap[j], heap[i]
i = j
def _siftup(self, i):
heap = self.__heap
size = self.__size
while i * 2 <= size:
j = i * 2
if j + 1 <= size and heap[j + 1] < heap[j]:
j += 1
if heap[i] > heap[j]:
heap[i], heap[j] = heap[j], heap[i]
i = j
================================================
FILE: topic/heap/python/heap__test.py
================================================
from _test.python import *
from heap.python import *
class TestHeap(TestBase):
def _test_heapify(self, Heap):
h = Heap([i for i in range(5)] * 2)
self.assertEqual(len(h), 10)
self.assertEqual(h.top(), 0)
self.assertEqual(
[h.pop() for _ in range(10)],
[0, 0, 1, 1, 2, 2, 3, 3, 4, 4]
)
self.assertEqual(len(h), 0)
with self.assertRaises(IndexError):
h.pop()
h.pop()
h.pop()
def _test_push(self, Heap):
CASES = (1, 2, 3, 1, 3, 2, 0, 1, 4)
h = Heap()
self.assertEqual(len(h), 0)
for i in range(len(CASES)):
self.assertIsNone(h.push(CASES[i]))
self.assertEqual(len(h), i + 1)
self.assertEqual(
[h.pop() for _ in range(len(CASES))],
[0, 1, 1, 1, 2, 2, 3, 3, 4]
)
self.assertEqual(len(h), 0)
with self.assertRaises(IndexError):
h.pop()
h.pop()
h.pop()
def _test_pop(self, Heap):
CASES = list(range(10))
h = Heap(CASES)
self.assertEqual(len(h), 10)
for i in range(len(CASES)):
self.assertEqual(h.pop(), i)
self.assertEqual(len(h), len(CASES) - i - 1)
self.assertEqual(len(h), 0)
with self.assertRaises(IndexError):
h.pop()
h.pop()
h.pop()
def _test_remove(self, Heap):
CASES = (1, 2, 3, 1, 3, 2, 0, 1, 4)
h = Heap(CASES)
self.assertEqual(len(h), 9)
self.assertIsNone(h.remove(1))
self.assertEqual(len(h), 8)
self.assertIsNone(h.remove(1))
self.assertEqual(len(h), 7)
self.assertIsNone(h.remove(1))
self.assertEqual(len(h), 6)
with self.assertRaises(KeyError):
h.remove(1)
h.remove(5)
h.remove(1)
self.assertEqual(h.pop(), 0)
self.assertEqual(len(h), 5)
self.assertEqual(h.top(), 2)
self.assertEqual(len(h), 5)
self.assertIsNone(h.push(1))
self.assertEqual(len(h), 6)
self.assertEqual(h.top(), 1)
self.assertEqual(len(h), 6)
self.assertIsNone(h.remove(1))
self.assertEqual(h.top(), 2)
self.assertEqual(len(h), 5)
self.assertEqual(h.pop(), 2)
self.assertEqual(len(h), 4)
self.assertIsNone(h.remove(3))
self.assertEqual(len(h), 3)
self.assertIsNone(h.remove(2))
self.assertEqual(len(h), 2)
self.assertIsNone(h.remove(3))
self.assertEqual(len(h), 1)
self.assertEqual(h.pop(), 4)
self.assertEqual(len(h), 0)
with self.assertRaises(KeyError):
h.remove(-1)
h.remove(0)
h.remove(1)
h.remove(2)
h.remove(3)
h.remove(4)
h.remove(5)
def _test_top(self, Heap):
CASES = (1, 2, 3, 1, 3, 2, 0, 1, 4)
h = Heap(CASES)
self.assertEqual(len(h), 9)
for _ in range(len(CASES)):
self.assertEqual(h.top(), h.pop())
self.assertEqual(len(h), 0)
with self.assertRaises(IndexError):
h.top()
h.top()
h.top()
def _run_heap_test(self, Heap):
self._test_heapify(Heap)
self._test_push(Heap)
self._test_pop(Heap)
self._test_top(Heap)
def _run_removable_heap_test(self, Heap):
self._test_heapify(Heap)
self._test_push(Heap)
self._test_pop(Heap)
self._test_remove(Heap)
self._test_top(Heap)
def test_binary_hash_heap(self):
self._run_removable_heap_test(BinaryHashHeap)
def test_binary_heap(self):
self._run_heap_test(BinaryHeap)
def test_lazy_removable_heapq(self):
self._run_removable_heap_test(LazyRemovableHeapq)
def test_removable_heapq(self):
self._run_removable_heap_test(RemovableHeapq)
================================================
FILE: topic/heap/python/lazy_removable_heapq.py
================================================
import collections
import heapq
class LazyRemovableHeapq:
MSG_EMPTY_HEAP = 'access element from empty heap'
def __init__(self, iterable=None):
self.__heap = []
self.__size = 0
self.__cnts = collections.defaultdict(int)
if iterable:
self.heapify(iterable)
def __len__(self):
return self.__size
def __bool__(self):
return self.__size > 0
def heapify(self, iterable):
if not isinstance(iterable, collections.Iterable):
return
for val in iterable:
self.push(val)
def push(self, val):
heapq.heappush(self.__heap, val)
self.__size += 1
self.__cnts[val] += 1
def pop(self):
if self._is_empty():
raise IndexError(self.MSG_EMPTY_HEAP)
val = heapq.heappop(self.__heap)
self.__size -= 1
self.__cnts[val] -= 1
return val
def remove(self, val):
if self._is_empty() or self.__cnts.get(val, 0) < 1:
raise KeyError(val)
self.__size -= 1
self.__cnts[val] -= 1
def top(self):
if self._is_empty():
raise IndexError(self.MSG_EMPTY_HEAP)
return self.__heap[0]
def _is_empty(self):
while self.__heap and self.__cnts.get(self.__heap[0]) == 0:
heapq.heappop(self.__heap)
return self.__size == 0
================================================
FILE: topic/heap/python/removable_heapq.py
================================================
"""
in `remove`, we cannot record index in hashmap to speed up
since the index in `heapq` changed all the time
"""
import collections
import heapq
class RemovableHeapq:
MSG_EMPTY_HEAP = 'access element from empty heap'
def __init__(self, iterable=None):
self.__heap = []
if iterable:
self.heapify(iterable)
def __len__(self):
return len(self.__heap)
def __bool__(self):
return bool(self.__heap)
def heapify(self, iterable):
if not isinstance(iterable, collections.Iterable):
return
for val in iterable:
self.push(val)
def push(self, val):
heapq.heappush(self.__heap, val)
def pop(self):
if not self.__heap:
raise IndexError(self.MSG_EMPTY_HEAP)
return heapq.heappop(self.__heap)
def remove(self, val):
if not self.__heap:
raise KeyError(val)
i = 0
n = len(self.__heap)
while i < n and self.__heap[i] != val:
i += 1
if i >= n:
raise KeyError(val)
if i == n - 1:
self.__heap.pop()
else:
self.__heap[i] = self.__heap[-1]
self.__heap.pop()
heapq._siftup(self.__heap, i)
def top(self):
if not self.__heap:
raise IndexError(self.MSG_EMPTY_HEAP)
return self.__heap[0]
================================================
FILE: topic/language/javascript/README.md
================================================
JavaScript Syntax Note
======
## `Object`
### To check a key is in object
```js
// `obj.hasOwnProperty`
// check only the obj instance
> obj.hasOwnProperty('key')
// `in` operator
// check the whole prototype chain
> 'key' in obj
> const someKey = 'key'
> someKey in obj
```
## `Array`
### To init array with fixed length
Note that, the inited value passed into `fill` must be **IMMUTABLE**,
otherwise we can use `map` to init with mutable value.
```js
> new Array(3).fill(0)
[ 0, 0, 0 ]
> new Array(3).fill(0).map(_ => new Array(3).fill(0))
[ [ 0, 0, 0 ], [ 0, 0, 0 ], [ 0, 0, 0 ] ]
```
Explanation:
```js
> let nums
// bad
> nums = new Array(3).fill(new Array(3).fill(0))
[ [ 0, 0, 0 ], [ 0, 0, 0 ], [ 0, 0, 0 ] ]
> nums[0][0] = 1
1
> nums
[ [ 1, 0, 0 ], [ 1, 0, 0 ], [ 1, 0, 0 ] ]
// good
> nums = new Array(3).fill(0).map(_ => new Array(3).fill(0))
[ [ 0, 0, 0 ], [ 0, 0, 0 ], [ 0, 0, 0 ] ]
> nums[0][0] = 1
1
> nums
[ [ 1, 0, 0 ], [ 0, 0, 0 ], [ 0, 0, 0 ] ]
```
### To sort
The default sorting is by **alphabetical order**.
You can also indicate a comparator if you want to sort it by custom order.
```js
> const nums = [1, 2, 4, 6, 4, 2, 2, 5, 1, 11, 22, 2, 3, 44]
undefined
> nums.sort()
[ 1, 1, 11, 2, 2, 2, 2, 22, 3, 4, 4, 44, 5, 6 ]
> nums.sort((a, b) => a - b)
[ 1, 1, 2, 2, 2, 2, 3, 4, 4, 5, 6, 11, 22, 44 ]
> nums.sort((a, b) => b - a)
[ 44, 22, 11, 6, 5, 4, 4, 3, 2, 2, 2, 2, 1, 1 ]
```
### To override a existing array
```js
> let nums
undefined
> nums = [2, 1, 3, 4, 1, 2]
[ 2, 1, 3, 4, 1, 2 ]
> nums.splice(0, nums.length, ...[5, 6, 7, 1, 2, 3])
[ 2, 1, 3, 4, 1, 2 ]
> nums
[ 5, 6, 7, 1, 2, 3 ]
> nums.splice(0, nums.length)
[ 5, 6, 7, 1, 2, 3 ]
> nums
[]
```
## `String`
### To convert a string-number to a real number
```js
> +'123'
123
> +'-123'
-123
> -'123'
-123
> -'+123'
-123
> +'0'
0
> +'1'
1
> +'0.123'
0.123
> +'0.123a'
NaN
> +'0.123/2'
NaN
```
## `Boolean`
### To convert a boolean to `1` or `0`
```js
> +true
1
> +false
0
```
## `Set`
## `Map`
================================================
FILE: topic/language/javascript/traversal.md
================================================
Traversal
======
## To simulate a queue
It's also a good example to show how the pointer works for the loop.
```js
> const queue = [1]
undefined
> for (let i = 0; i < queue.length; i++) {
... if (i === 5) break
... queue.push(i)
... }
undefined
> queue
[ 1, 0, 1, 2, 3, 4 ]
```
```js
> const queue = [1]
undefined
> let i = 0
undefined
> queue.forEach(num => {
... if (i === 5) return
... queue.push(i)
... })
undefined
> queue
[ 1, 0 ]
```
## To do BFS in level
```js
> const queue = [[0, 0]]
undefined
> const _queue = []
undefined
> const delta = [
... [0, -1], [0, 1],
... [-1, 0], [1, 0],
... ]
undefined
>
> const visited = {'0,0': 0}
undefined
> let steps = 0
undefined
> let i, x, y, _x, _y, key
undefined
>
> while (queue.length && steps < 2) {
... steps += 1
...
... for (i = 0; i < queue.length; i++) {
... [x, y] = queue[i]
...
... delta.forEach(([dx, dy]) => {
... _x = x + dx
... _y = y + dy
... key = `${_x},${_y}`
...
... if (visited[key] < steps) return
...
... visited[key] = steps
... _queue.push([_x, _y])
... })
... }
...
... queue.splice(0, queue.length, ..._queue)
... _queue.splice(0, _queue.length)
... }
[ [ 0, -2 ], [ -1, -1 ], [ 1, -1 ], [ 0, 2 ],
[ -1, 1 ], [ 1, 1 ], [ -1, -1 ], [ -1, 1 ],
[ -2, 0 ], [ 1, -1 ], [ 1, 1 ], [ 2, 0 ] ]
>
> queue
[ [ 0, -2 ], [ -1, -1 ], [ 1, -1 ], [ 0, 2 ],
[ -1, 1 ], [ 1, 1 ], [ -1, -1 ], [ -1, 1 ],
[ -2, 0 ], [ 1, -1 ], [ 1, 1 ], [ 2, 0 ] ]
> visited
{ '0,0': 0, '0,-1': 1, '0,1': 1, '-1,0': 1,
'1,0': 1, '0,-2': 2, '-1,-1': 2, '1,-1': 2,
'0,2': 2, '-1,1': 2, '1,1': 2, '-2,0': 2, '2,0': 2 }
```
================================================
FILE: topic/language/php/README.md
================================================
PHP Syntax Note
======
## General
- A PHP script starts with ``. You can also use the shorthand PHP tags, `` and `?>`, as long as they're supported by the server.
- PHP statements end with semicolons `;`.
- Comment: single-line `//`, multi-line begins with `/*` and ends with `*/`.
## Variable and Constant
```php
$variable_name = 'value';
echo $variable_name; // 'value'
$var1 = 'var2';
$var2 = 'value';
echo $$var1; // 'value'
```
- A variable name can only contain alpha-numeric characters and underscores. (`A-z`, `0-9`, and `_`)
- A variable name must start with a letter or an underscore.
- Variable names are case-sensitive. (`$name` and `$NAME` would be two different variables)
```php
define(CONSTANT_NAME, `value`, `case-insensitive`);
echo CONSTANT_NAME;
```
- Constants are similar to variables except that they cannot be changed or undefined after they've been defined.
- name: Specifies the name of the constant.
- value: Specifies the value of the constant.
- case-insensitive: Specifies whether the constant name should be case-insensitive. Default is `false`.
## Operators
| Operator | Example | Note |
| ------------ | ------------- | ------------------------------------------------------------ |
| `+` | `$a + $b` | Addition. Supports `$a += $b`. |
| `-` | `$a - $b` | Subtraction. Supports `$a -= $b`. |
| `*` | `$a * $b` | Multiplication. Supports `$a *= $b`. |
| `/` | `$a / $b` | Division (**NOT** Truncation Division). Supports `$a /= $b`. `10 / 3 == 3.3333...` |
| `%` | `$a % $b` | Modulus. Supports `$a %= $b`. If you use floating point numbers with the modulus operator, they will be converted to **integers** before the operation. |
| `++` | `$a++; ++$a;` | post-increment / pre-increment. The difference is that the post-increment returns the original value **before** it changes the variable, while the pre-increment changes the variable first and then returns the value. |
| `--` | `$a--; --$a;` | post-decrement / pre-decrement. |
| `==` | `$a == $b` | Returns `true` if `$a` is equal to `$b`. |
| `===` | `$a === $b` | Returns `true` if `$a` is equal to `$b` and they are same type. |
| `!=` | `$a != $b` | Returns `true` if `$a` is **NOT** equal to `$b`. Same to `$a <> $b`. |
| `!==` | `$a !== $b` | Returns `true` if `$a` is **NOT** equal to `$b` **OR** they are **NOT** same type. |
| `>` / `>=` | `$a > $b` | Greater than (or equal to). |
| `<` / `<=` | `$a < $b` | Less than (or equal to). |
| `&&` / `and` | `$a && $b` | Returns `true` if **both** `$a` and `$b` are `true`. |
| `||` / `or` | `$a || $b` | Returns `true` if **either** `$a` and `$b` is `true`. |
| `xor` | `$a xor $b` | Returns `true` if **either** `$a` and `$b` is `true`, but **NOT both**. |
| `!` | `!$a` | Returns `true` if `$a` is falsy. |
## Template
- `include`: continue if file was not found. `require`: throw error if file was not found.
## `String`
- `""` supports escape character and html tag, but `''` do NOT.
## `Array`
- Numeric Array: generic array; Associative Array: like a hashtable.
### Inited by values
```php
$arr = array('a', 'b', 'c');
echo $arr[0]; // 'a'
$arr[0] = 'd';
echo $arr[0]; // 'd'
$arr = array('a' => 1, 'b' => 2, 'c' => 3);
echo $arr['a']; // 1
$arr['a'] = 4;
echo $arr['a']; // 4
```
### Inited by size
```php
// 1d array
$arr = array_fill(0, 10, 1);
echo implode(',', $arr); // 1,1,1,1,1,1,1,1,1,1
// 2d array
$arr = array_fill(0, 5, array_fill(0, 5, 1));
echo $arr[0][0]; // 1
$arr[0][0] = 2;
echo $arr[0][0]; // 2
echo implode(',', $arr[0]); // 2,1,1,1,1
echo implode(',', $arr[1]); // 1,1,1,1,1
echo implode(',', $arr[2]); // 1,1,1,1,1
echo implode(',', $arr[3]); // 1,1,1,1,1
echo implode(',', $arr[4]); // 1,1,1,1,1
```
================================================
FILE: topic/language/python/README.md
================================================
Python Syntax Note
======
## General
### Difference between `is` and `==`
`is` will return True if two variables point to the same object,
`==` if the objects referred to by the variables are equal.
- example in `int` and `float`
note that, negative `int`/`float` is special case.
```python
>>> 10 != 10
False
>>> 10 is not 10
False
>>> 0 != 0
False
>>> 0 is not 0
False
>>> -9 != -9
False
>>> -9 is not -9
True
```
- example in `str` and `bytes`
```python
# Python `2.7.13`, `2.7.14`
>>> u'test' == 'test'
True
>>> u'test' is 'test'
False
# Python `3.6.0`, `3.6.3`
>>> u'test' == 'test'
True
>>> u'test' is 'test'
True
```
- example in `list`
```python
>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> a == b
True
>>> a is b
False
```
### Always ensures that shared vars are **IMMUTABLE**
in class
```python
# bad
>>> class TestClass:
... x = {}
>>> a = TestClass()
>>> b = TestClass()
>>> a.x
{}
>>> a.x[1] = 2
>>> b.x
{1: 2}
# good
>>> class TestClass:
... x = None
... def __init__(self):
... self.x = {}
>>> a = TestClass()
>>> b = TestClass()
>>> a.x
{}
>>> a.x[1] = 2
>>> b.x
{}
```
in func
```python
# bad
>>> def test_func(x=[]):
... x.append(1)
... return x
>>> test_func()
[1]
>>> test_func()
[1, 1]
# good
>>> def test_func(x=None):
... if x is None:
... x = []
... x.append(1)
... return x
>>> test_func()
[1]
>>> test_func()
[1]
```
### Private prop in `class`
```python
>>> class Test:
... def __init__(self):
... self.a = 1
... self._b = 2
... self.__c = 3
...
... def get_c(self):
... return self.__c
>>> t = Test()
>>> t.a
1
>>> t._b
2
>>> t.__c
AttributeError: 'Test' object has no attribute '__c'
>>> t.get_c()
3
```
### Hoisting
```python
# hoisting is in `for`, `if`, `while`
>>> a
NameError: name 'a' is not defined
>>> i
NameError: name 'i' is not defined
>>> for i in range(10):
... a = 123
>>> a
123
>>> i
9
# hoisting is NOT in list comprehensions
>>> b
NameError: name 'b' is not defined
>>> [b for b in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> b
NameError: name 'b' is not defined
# hoisting is NOT in `class`, `function`
>>> c
NameError: name 'c' is not defined
>>> def test(a = 1):
... c = 2
>>> test()
>>> a
NameError: name 'a' is not defined
>>> c
NameError: name 'c' is not defined
```
## String `str`, `bytes`
### To find index
```python
>>> 'abc'.find('b')
1
>>> 'abc'.find('z')
-1
```
### To split by multiple delimiters
```python
>>> import re
>>> re.split(',\s|\s', "Jimmy has an apple, it is on the table")
['Jimmy', 'has', 'an', 'apple', 'it', 'is', 'on', 'the', 'table']
```
## Number `int`, `float`
### Using cascade comparison
```python
>>> 0 <= 2 < 3
True
>>> 0 <= 4 < 3
False
```
### Be careful of the division
```python
# Python 2
>>> 3 // 2
1
>>> 3 / 2
1
# to parse the result as float
>>> 3 / 2.0
1.5
# Python 3
>>> 3 // 2
1
>>> 3 / 2
1.5
```
conclusion:
```python
# need int
>>> 3 // 2
1
# need float
>>> 3 / 2.0
1.5
```
### Infinity
```python
# positive infinite
>>> float('inf')
inf
# negative infinite
>>> float('-inf')
-inf
```
## List `list`
### Iteration
```python
>>> A = [['a', 'b'], ['c', 'd']]
>>> A
[['a', 'b'], ['c', 'd']]
>>> B = map(''.join, A)
>>> B