LeetCode 650: 2 Keys Keyboard
A dynamic programming and prime factorization solution for finding the minimum operations needed to produce n characters.
Problem Restatement
We start with one character:
"A"
We can perform two operations:
| Operation | Meaning |
|---|---|
Copy All |
Copy the entire current text |
Paste |
Paste the copied text |
We need to produce exactly n characters "A" using the minimum number of operations.
Return that minimum number.
The initial "A" already exists and does not count as an operation.
Input and Output
| Item | Meaning |
|---|---|
| Input | Integer n |
| Output | Minimum operations needed to produce exactly n "A" characters |
| Initial state | One "A" already exists |
| Allowed operations | Copy All and Paste |
Example function shape:
def minSteps(n: int) -> int:
...
Examples
Example 1:
n = 3
Operations:
| Step | Operation | Text |
|---|---|---|
| Start | none | A |
| 1 | Copy All | A |
| 2 | Paste | AA |
| 3 | Paste | AAA |
Total operations:
3
Example 2:
n = 1
We already have one "A".
No operations are needed.
Output:
0
First Thought: Dynamic Programming by State
A direct idea is:
dp[i]
meaning:
minimum operations needed to produce i characters
Suppose we want to reach i.
If some smaller number j divides i, then:
- Build
jcharacters. - Copy All once.
- Paste
(i / j - 1)times.
That costs:
dp[j] + 1 + (i // j - 1)
which simplifies to:
dp[j] + i // j
This leads to a valid DP solution.
Key Insight
The operations naturally form multiplication.
Suppose we currently have:
j
characters.
After:
1 Copy All
k Paste operations
we get:
j * (k + 1)
characters.
So every construction step multiplies the current amount.
This leads to an important observation:
Breaking n into factors minimizes the total number of operations.
For example:
n = 9
We could do:
1 -> 9
using:
Copy + 8 pastes = 9 operations
But a better plan is:
1 -> 3 -> 9
Cost:
| Step | Operations |
|---|---|
1 -> 3 |
3 |
3 -> 9 |
3 |
Total:
6
The factorization:
9 = 3 * 3
matches the operation cost:
3 + 3 = 6
This pattern generalizes.
Prime Factorization Insight
Suppose:
n = a * b
We can:
- Build
acharacters. - Copy once.
- Paste
b - 1times.
Cost:
cost(a) + b
Then we recursively factor a.
Eventually, the optimal solution becomes:
sum of prime factors of n
For example:
n = 12
Prime factorization:
12 = 2 * 2 * 3
Minimum operations:
2 + 2 + 3 = 7
Construction:
| Step | Operations |
|---|---|
1 -> 2 |
2 |
2 -> 4 |
2 |
4 -> 12 |
3 |
Total:
7
Greedy Prime Factorization Algorithm
We repeatedly divide by the smallest factor.
- Initialize
answer = 0. - Start with divisor
d = 2. - While
d * d <= n:- While
ddividesn:- Add
dto the answer. - Divide
nbyd.
- Add
- Increase
d.
- While
- If
n > 1, add the remaining prime factor. - Return the answer.
Implementation
class Solution:
def minSteps(self, n: int) -> int:
answer = 0
divisor = 2
while divisor * divisor <= n:
while n % divisor == 0:
answer += divisor
n //= divisor
divisor += 1
if n > 1:
answer += n
return answer
Code Explanation
We start with:
answer = 0
divisor = 2
The divisor scans possible prime factors.
Whenever:
n % divisor == 0
the divisor is part of the factorization.
We add that factor to the answer:
answer += divisor
Then remove it from n:
n //= divisor
We keep dividing while the factor still exists.
At the end:
if n > 1:
answer += n
handles the remaining prime factor larger than sqrt(original_n).
Why Prime Factors Give the Minimum
Suppose we want to multiply the current amount by:
x
This requires:
| Operation | Count |
|---|---|
| Copy All | 1 |
| Paste | x - 1 |
Total:
x
operations.
Now suppose:
x = a * b
with:
a > 1
b > 1
Then instead of multiplying by x in one step, we can multiply by a and then by b.
Cost:
a + b
Since:
a + b <= a * b
for integers greater than 1, breaking a composite factor into smaller factors never increases the cost.
Therefore, the minimum cost is achieved when all factors are prime.
Thus the optimal number of operations equals the sum of the prime factors of n.
Correctness
Each sequence of operations can be viewed as repeatedly multiplying the current number of characters.
If we multiply by some factor f, we must perform:
| Operation | Count |
|---|---|
One Copy All |
1 |
Paste operations |
f - 1 |
for a total cost of:
f
Therefore, any construction of n corresponds to a factorization of n, and the total operation count equals the sum of those factors.
If some factor is composite:
f = a * b
then replacing f with a and b changes the cost from:
f
to:
a + b
Since:
a + b <= a * b = f
for integers greater than 1, decomposing composite factors never increases the cost.
Thus the minimum total cost is achieved when all factors are prime.
The algorithm computes exactly the sum of the prime factors of n, including multiplicity, so it returns the minimum number of operations.
Complexity
Let n be the input value.
| Metric | Value | Why |
|---|---|---|
| Time | O(sqrt(n)) |
Trial division up to square root |
| Space | O(1) |
Only a few variables are used |
Alternative Dynamic Programming Solution
We can also use DP directly.
class Solution:
def minSteps(self, n: int) -> int:
dp = [0] * (n + 1)
for i in range(2, n + 1):
dp[i] = i
for j in range(i // 2, 1, -1):
if i % j == 0:
dp[i] = dp[j] + i // j
break
return dp[n]
Explanation:
If:
j divides i
then:
- Build
j. - Copy once.
- Paste until reaching
i.
Cost:
dp[j] + i // j
| Metric | Value |
|---|---|
| Time | O(n²) worst case |
| Space | O(n) |
The prime-factor solution is simpler and faster.
Testing
def run_tests():
s = Solution()
assert s.minSteps(1) == 0
assert s.minSteps(2) == 2
assert s.minSteps(3) == 3
assert s.minSteps(4) == 4
assert s.minSteps(5) == 5
assert s.minSteps(6) == 5
assert s.minSteps(9) == 6
assert s.minSteps(12) == 7
assert s.minSteps(18) == 8
print("all tests passed")
run_tests()
Test meaning:
| Test | Why |
|---|---|
1 |
Already complete |
| Prime numbers | Cost equals the prime itself |
4 = 2 * 2 |
Factorization reduces operations |
6 = 2 * 3 |
Mixed prime factors |
9 = 3 * 3 |
Repeated prime factor |
12 = 2 * 2 * 3 |
Multiple factors |
18 = 2 * 3 * 3 |
Larger composite example |