Files
codetutor/backend/data/questions/2-keys-keyboard.yaml

170 lines
7.6 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
title: 2 Keys Keyboard
slug: 2-keys-keyboard
difficulty: medium
leetcode_id: 650
leetcode_url: https://leetcode.com/problems/2-keys-keyboard/
categories:
- dynamic-programming
- math
patterns:
- dynamic-programming
description: |
There is only one character `'A'` on the screen of a notepad. You can perform one of two operations on this notepad for each step:
- **Copy All**: You can copy all the characters present on the screen (a partial copy is not allowed).
- **Paste**: You can paste the characters which are copied last time.
Given an integer `n`, return *the minimum number of operations to get the character* `'A'` *exactly* `n` *times on the screen*.
constraints: |
- `1 <= n <= 1000`
examples:
- input: "n = 3"
output: "3"
explanation: "Initially, we have one character 'A'. In step 1, we use Copy All operation. In step 2, we use Paste operation to get 'AA'. In step 3, we use Paste operation to get 'AAA'."
- input: "n = 1"
output: "0"
explanation: "We already have one 'A' on the screen, so no operations are needed."
explanation:
intuition: |
Imagine you're a factory manager trying to manufacture `n` identical items. You start with 1 item. Your only tools are a **copying machine** (which copies everything you have) and a **duplicator** (which produces copies of the last batch you made).
The key insight is that this problem is fundamentally about **prime factorisation**. Here's why:
If `n = 6`, you could:
- Copy 1, Paste 5 times → 6 operations (1 → 2 → 3 → 4 → 5 → 6)
- Or: Copy 1, Paste twice (get 3), Copy 3, Paste once (get 6) → 5 operations
The second approach is better because we're **multiplying** rather than adding. When we copy at `k` A's and paste `p-1` times, we multiply our count by `p` using exactly `p` operations (1 copy + (p-1) pastes).
This means: **to reach `n` A's optimally, we want to factor `n` into primes and "pay" for each prime factor**. If `n = p₁ × p₂ × ... × pₖ`, the minimum operations is `p₁ + p₂ + ... + pₖ`.
Think of it like this: each prime factor represents a "multiplying stage" where we copy once and paste multiple times to multiply our current count.
approach: |
We solve this using **Prime Factorisation**:
**Step 1: Handle the base case**
- If `n == 1`, return `0` — we already have one 'A', no operations needed
&nbsp;
**Step 2: Find prime factors and sum them**
- Start with a divisor `d = 2`
- While `d * d <= n`:
- While `n` is divisible by `d`:
- Add `d` to our operation count
- Divide `n` by `d`
- Increment `d`
- If `n > 1` after the loop, `n` itself is a prime factor — add it to the count
&nbsp;
**Step 3: Return the sum of prime factors**
- The total sum equals the minimum number of operations
&nbsp;
**Why this works**: To multiply your screen count by a factor `p`, you need exactly `p` operations: 1 Copy All + (p-1) Pastes. Since we must reach exactly `n` starting from 1, we're essentially "building up" through multiplication, and the optimal way is to use the prime factors.
common_pitfalls:
- title: Treating This as a Standard DP Problem
description: |
While this *can* be solved with dynamic programming where `dp[i]` = minimum operations to get `i` A's, the mathematical insight about prime factorisation is more elegant and efficient.
The DP approach would require O(n√n) or O(n²) time to fill the table, checking all divisors for each number. The prime factorisation approach runs in O(√n) time.
wrong_approach: "DP with O(n²) divisor checking"
correct_approach: "Prime factorisation in O(√n)"
- title: Forgetting That n=1 Needs Zero Operations
description: |
When `n = 1`, we already have one 'A' on screen. No copy or paste is needed. Make sure to handle this edge case by returning `0`.
wrong_approach: "Returning 1 for n=1"
correct_approach: "Return 0 since we start with one 'A'"
- title: Missing the Last Prime Factor
description: |
After the factorisation loop where `d * d <= n`, if `n > 1`, then `n` itself is a prime factor that must be added.
For example, with `n = 6`: after dividing by 2 (getting 3) and incrementing d to 3, we have `d * d = 9 > 3`, so the loop exits. But 3 is still a prime factor that needs to be counted.
wrong_approach: "Stopping when d*d > n without checking remaining n"
correct_approach: "Add remaining n to sum if n > 1 after loop"
key_takeaways:
- "**Math insight transforms complexity**: Recognising the prime factorisation connection reduces this from a DP problem to a simple factorisation"
- "**Operations as multiplication**: Copy+Paste sequences multiply your count by factors, so optimal operations = sum of prime factors"
- "**Pattern recognition**: Many problems that seem to require DP have elegant mathematical solutions when you analyse the structure"
- "**Similar problems**: This pattern appears in problems involving minimum steps to reach a target through multiplication/division operations"
time_complexity: "O(√n). We iterate up to √n to find all prime factors, and each division reduces n significantly."
space_complexity: "O(1). We only use a few variables to track the divisor and operation count."
solutions:
- approach_name: Prime Factorisation
is_optimal: true
code: |
def min_steps(n: int) -> int:
# Base case: already have 1 'A', no operations needed
if n == 1:
return 0
operations = 0
divisor = 2
# Find all prime factors and sum them
while divisor * divisor <= n:
# While current divisor is a factor, add it
while n % divisor == 0:
operations += divisor
n //= divisor
divisor += 1
# If n > 1, it's a prime factor itself
if n > 1:
operations += n
return operations
explanation: |
**Time Complexity:** O(√n) — We only iterate up to √n to find prime factors.
**Space Complexity:** O(1) — Only a few integer variables used.
The algorithm finds all prime factors of n and sums them. Each prime factor p represents a sequence of 1 Copy + (p-1) Pastes, which multiplies the current count by p using exactly p operations.
- approach_name: Dynamic Programming
is_optimal: false
code: |
def min_steps(n: int) -> int:
# dp[i] = minimum operations to get exactly i 'A's
dp = [0] * (n + 1)
for i in range(2, n + 1):
# Worst case: copy 1 'A' and paste i-1 times
dp[i] = i
# Try all possible divisors
# If j divides i, we can reach i from i//j
# by copying at i//j and pasting j-1 times
j = 2
while j * j <= i:
if i % j == 0:
# j operations to multiply by j
dp[i] = min(dp[i], dp[i // j] + j)
# i//j operations to multiply by i//j
dp[i] = min(dp[i], dp[j] + i // j)
j += 1
return dp[n]
explanation: |
**Time Complexity:** O(n√n) — For each number up to n, we check divisors up to √i.
**Space Complexity:** O(n) — We store the DP array of size n+1.
This approach builds up the solution from smaller subproblems. For each count i, we find the minimum operations by checking all ways to reach i through copy-paste sequences. While correct, it's less efficient than the mathematical approach.