questions F-L

This commit is contained in:
2025-05-25 11:47:04 +01:00
parent 798e0ba1df
commit 5dbe52df0d
54 changed files with 11235 additions and 0 deletions

View File

@@ -0,0 +1,188 @@
title: Greatest Common Divisor of Strings
slug: greatest-common-divisor-of-strings
difficulty: easy
leetcode_id: 1071
leetcode_url: https://leetcode.com/problems/greatest-common-divisor-of-strings/
categories:
- strings
- math
patterns:
- greedy
description: |
For two strings `s` and `t`, we say "`t` divides `s`" if and only if `s = t + t + t + ... + t` (i.e., `t` is concatenated with itself one or more times).
Given two strings `str1` and `str2`, return *the largest string* `x` *such that* `x` *divides both* `str1` *and* `str2`.
constraints: |
- `1 <= str1.length, str2.length <= 1000`
- `str1` and `str2` consist of English uppercase letters.
examples:
- input: 'str1 = "ABCABC", str2 = "ABC"'
output: '"ABC"'
explanation: '"ABC" divides both strings. "ABCABC" = "ABC" + "ABC" and "ABC" = "ABC".'
- input: 'str1 = "ABABAB", str2 = "ABAB"'
output: '"AB"'
explanation: '"AB" divides both strings. "ABABAB" = "AB" + "AB" + "AB" and "ABAB" = "AB" + "AB".'
- input: 'str1 = "LEET", str2 = "CODE"'
output: '""'
explanation: "There is no string that divides both str1 and str2."
explanation:
intuition: |
This problem cleverly connects string manipulation to a fundamental mathematical concept: the **Greatest Common Divisor (GCD)**.
Think of it like this: if a string `x` can "divide" both `str1` and `str2`, then `x` repeated some number of times equals `str1`, and `x` repeated another number of times equals `str2`. This is exactly analogous to how a number `d` divides both `a` and `b` if `a = d * m` and `b = d * n` for some integers `m` and `n`.
The key insight is that **if a common divisor string exists, the length of the GCD string must be the GCD of the two string lengths**. Why? Because if `x` divides both strings, then `len(str1)` must be a multiple of `len(x)` and `len(str2)` must be a multiple of `len(x)`. The *largest* such length is exactly `gcd(len(str1), len(str2))`.
But there's one more critical check: **not all string pairs have a common divisor**. For example, `"LEET"` and `"CODE"` have no common divisor because they're fundamentally incompatible. The elegant way to check compatibility is: if `str1 + str2 == str2 + str1`, then a common divisor exists. If the strings are "made of the same building block," the order of concatenation doesn't matter.
approach: |
We solve this using a **GCD-based Approach**:
**Step 1: Check if a common divisor exists**
- Concatenate `str1 + str2` and `str2 + str1`
- If these are not equal, the strings have no common divisor — return an empty string
- This check works because if both strings are built from the same repeating pattern, the order of concatenation won't matter
&nbsp;
**Step 2: Calculate the GCD of the string lengths**
- Use the Euclidean algorithm to find `gcd(len(str1), len(str2))`
- This gives us the length of the largest possible common divisor string
&nbsp;
**Step 3: Return the GCD string**
- Return the prefix of `str1` (or `str2`) with length equal to the GCD
- Since we've verified compatibility in Step 1, this prefix is guaranteed to divide both strings
&nbsp;
The mathematical foundation makes this solution both elegant and efficient — we avoid brute-force checking of all possible divisor strings.
common_pitfalls:
- title: Brute Force All Prefixes
description: |
A naive approach might try every possible prefix of the shorter string and check if it divides both strings. For each candidate prefix of length `k`, you'd verify if `str1` and `str2` are composed entirely of that prefix.
While correct, this is unnecessarily slow. With strings up to length 1000, and checking each prefix by iterating through both strings, you could do up to O(n^2) work.
The GCD approach reduces this to O(n) string concatenation checks plus O(log(min(n, m))) for the GCD calculation.
wrong_approach: "Try every prefix and check divisibility"
correct_approach: "Use mathematical GCD on lengths after compatibility check"
- title: Forgetting the Compatibility Check
description: |
You might be tempted to just compute `gcd(len(str1), len(str2))` and return that prefix. But this fails for cases like `str1 = "LEET"`, `str2 = "CODE"`.
The GCD of 4 and 4 is 4, but `"LEET"` does not equal `"CODE"` — there's no common divisor string at all! The `str1 + str2 == str2 + str1` check catches this: `"LEETCODE"` ≠ `"CODELEET"`.
wrong_approach: "Just return str1[:gcd(len(str1), len(str2))]"
correct_approach: "First verify str1 + str2 == str2 + str1"
- title: Checking Wrong String for Prefix
description: |
After finding the GCD length, some might try to construct the result by repeating characters or using complex logic. Simply take a prefix of either string — since we've verified they're compatible, both strings start with the same pattern.
wrong_approach: "Complex construction of the result string"
correct_approach: "Return str1[:gcd_length] directly"
key_takeaways:
- "**Mathematical insight**: String divisibility mirrors integer divisibility — the GCD concept transfers directly"
- "**Compatibility check first**: The `str1 + str2 == str2 + str1` test elegantly verifies that a common pattern exists"
- "**Euclidean algorithm**: The GCD of two numbers can be computed efficiently in O(log(min(a, b))) time"
- "**Pattern recognition**: Look for mathematical analogies when problems involve repetition or divisibility"
time_complexity: "O(n + m). We perform two string concatenations of total length `n + m`, one equality check of length `n + m`, and a GCD calculation in O(log(min(n, m)))."
space_complexity: "O(n + m). We create two concatenated strings of length `n + m` for the compatibility check."
solutions:
- approach_name: GCD of Lengths
is_optimal: true
code: |
from math import gcd
def gcd_of_strings(str1: str, str2: str) -> str:
# Check if a common divisor pattern exists
# If both strings are made of the same repeating unit,
# concatenation order doesn't matter
if str1 + str2 != str2 + str1:
return ""
# The GCD string length must be the GCD of both lengths
gcd_length = gcd(len(str1), len(str2))
# Return the prefix of that length
return str1[:gcd_length]
explanation: |
**Time Complexity:** O(n + m) — String concatenation and comparison dominate.
**Space Complexity:** O(n + m) — For the concatenated strings.
This elegant solution leverages the mathematical relationship between string divisibility and integer GCD. The concatenation equality check `str1 + str2 == str2 + str1` is a brilliant way to verify that both strings share a common building block pattern.
- approach_name: Iterative GCD Check
is_optimal: false
code: |
def gcd_of_strings(str1: str, str2: str) -> str:
def gcd(a: int, b: int) -> int:
# Euclidean algorithm
while b:
a, b = b, a % b
return a
def divides(s: str, t: str) -> bool:
# Check if s divides t (t is s repeated)
if len(t) % len(s) != 0:
return False
times = len(t) // len(s)
return s * times == t
# Find GCD length
gcd_len = gcd(len(str1), len(str2))
candidate = str1[:gcd_len]
# Verify it actually divides both strings
if divides(candidate, str1) and divides(candidate, str2):
return candidate
return ""
explanation: |
**Time Complexity:** O(n + m) — Checking divisibility for both strings.
**Space Complexity:** O(gcd(n, m)) — For the candidate string and repeated copies.
This approach is more explicit: it computes the candidate GCD string, then verifies it actually divides both inputs. While correct and intuitive, it's slightly less elegant than the concatenation trick which handles the compatibility check in one comparison.
- approach_name: Brute Force
is_optimal: false
code: |
def gcd_of_strings(str1: str, str2: str) -> str:
# Try all possible prefix lengths, from largest to smallest
min_len = min(len(str1), len(str2))
for length in range(min_len, 0, -1):
# Skip if lengths aren't divisible
if len(str1) % length != 0 or len(str2) % length != 0:
continue
# Get candidate prefix
candidate = str1[:length]
# Check if candidate divides both strings
times1 = len(str1) // length
times2 = len(str2) // length
if candidate * times1 == str1 and candidate * times2 == str2:
return candidate
return ""
explanation: |
**Time Complexity:** O(min(n, m) * (n + m)) — For each candidate length, we check both strings.
**Space Complexity:** O(n + m) — For the repeated candidate strings.
This brute force approach tries every possible prefix length from largest to smallest. While it works, it's inefficient because it doesn't leverage the mathematical insight that the answer length must be `gcd(n, m)`. Included to illustrate why the GCD approach is superior.