From 05ee26ce40241da6f45dbfc13d4f4f314cbda190 Mon Sep 17 00:00:00 2001 From: RomanSanaye Date: Wed, 14 Oct 2026 20:26:39 +0100 Subject: [PATCH 1/3] optimize find_longest_common_prefix using pre-sorting --- .../common_prefix/common_prefix.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Sprint-2/improve_with_precomputing/common_prefix/common_prefix.py b/Sprint-2/improve_with_precomputing/common_prefix/common_prefix.py index f4839e70..774133a2 100644 --- a/Sprint-2/improve_with_precomputing/common_prefix/common_prefix.py +++ b/Sprint-2/improve_with_precomputing/common_prefix/common_prefix.py @@ -7,12 +7,21 @@ def find_longest_common_prefix(strings: List[str]): In the event that an empty list, a list containing one string, or a list of strings with no common prefixes is passed, the empty string will be returned. """ + if not strings or len(strings) < 2: + return "" + + # Pre-computation: Sort the strings lexicographically. + # The longest common prefix of any two strings in the collection + # will always be shared by at least one pair of adjacent strings in the sorted list. + sorted_strings = sorted(strings) + longest = "" - for string_index, string in enumerate(strings): - for other_string in strings[string_index+1:]: - common = find_common_prefix(string, other_string) - if len(common) > len(longest): - longest = common + + for i in range(len(sorted_strings) - 1): + common = find_common_prefix(sorted_strings[i], sorted_strings[i+1]) + if len(common) > len(longest): + longest = common + return longest From 0ae38d2a94e25a1689fd79605c432fc414189479 Mon Sep 17 00:00:00 2001 From: RomanSanaye Date: Thu, 15 Oct 2026 23:21:15 +0100 Subject: [PATCH 2/3] time complexity comparison explained --- .../common_prefix/common_prefix.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/Sprint-2/improve_with_precomputing/common_prefix/common_prefix.py b/Sprint-2/improve_with_precomputing/common_prefix/common_prefix.py index 774133a2..52a6b9e0 100644 --- a/Sprint-2/improve_with_precomputing/common_prefix/common_prefix.py +++ b/Sprint-2/improve_with_precomputing/common_prefix/common_prefix.py @@ -14,14 +14,14 @@ def find_longest_common_prefix(strings: List[str]): # The longest common prefix of any two strings in the collection # will always be shared by at least one pair of adjacent strings in the sorted list. sorted_strings = sorted(strings) - + longest = "" for i in range(len(sorted_strings) - 1): - common = find_common_prefix(sorted_strings[i], sorted_strings[i+1]) + common = find_common_prefix(sorted_strings[i], sorted_strings[i + 1]) if len(common) > len(longest): longest = common - + return longest @@ -31,3 +31,16 @@ def find_common_prefix(left: str, right: str) -> str: if left[i] != right[i]: return left[:i] return left[:min_length] + + +# Time Complexity Comparison: +# Original version: It used a nested loop to check every possible pair of strings. +# This gave it a slow quadratic time complexity (O(N^2)), which gets very heavy +# and slow as the list grows larger. +# +# New version: We sort the strings alphabetically first. When a list is sorted, +# any two strings with the longest common prefix will always be right next to each other. +# Because of this, we only need a single loop to check neighbor pairs. +# +# Result: This changes the time complexity from O(N^2) to O(N log N), +# making the code much faster and more efficient. From 163219a29bd9fb58fdab23cca0b0a439715c7720 Mon Sep 17 00:00:00 2001 From: RomanSanaye Date: Thu, 15 Oct 2026 23:35:41 +0100 Subject: [PATCH 3/3] refactor(count_letters): reduce time complexity to O(N) using set lookups --- .../count_letters/count_letters.py | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/Sprint-2/improve_with_precomputing/count_letters/count_letters.py b/Sprint-2/improve_with_precomputing/count_letters/count_letters.py index 62c3ec07..7badf008 100644 --- a/Sprint-2/improve_with_precomputing/count_letters/count_letters.py +++ b/Sprint-2/improve_with_precomputing/count_letters/count_letters.py @@ -1,14 +1,32 @@ +from typing import Set + + def count_letters(s: str) -> int: """ - count_letters returns the number of letters which only occur in upper case in the passed string. + Returns the number of alphabetic letters that only occur in upper case in the passed string. + Optimized to O(N) time complexity using sets. """ - only_upper = set() - for letter in s: - if is_upper_case(letter): - if letter.lower() not in s: - only_upper.add(letter) + if not s: + return 0 + + # Collect all unique upper-case and lower-case characters + upper_chars: Set[str] = {char for char in s if char.isupper()} + lower_chars: Set[str] = {char.lower() for char in s if char.islower()} + + # Find upper-case letters whose lower-case versions do not exist in the string + only_upper = {char for char in upper_chars if char.lower() not in lower_chars} + return len(only_upper) -def is_upper_case(letter: str) -> bool: - return letter == letter.upper() +# Time and Space Complexity Comparison: +# +# Original version: +# - Time Complexity: O(N^2). It used a loop to look at every character, and inside the loop, it searched the entire string s (letter.lower() not in s). This nested search makes it quadratic and slow for long texts. +# - Space Complexity: O(N). It stored unique letters in a set. +# +# Refactored version (with Sets): +# - Time Complexity: O(N). It passes through the string to collect uppercase and lowercase characters into sets first. Because checking a set takes O(1) time, looking up the lowercase letter is instant. +# - Space Complexity: O(N). It uses extra memory to store unique characters in sets, but this is a minor trade-off for a massive increase in speed. +# +# Result: The time complexity drops from O(N^2) to O(N), making the code much faster and more efficient for large inputs.