651. 4 Keys Keyboard đ
Problem Description
You have a special keyboard with four possible operations:
- A: Print one 'A' on the screen
- Ctrl-A: Select all text currently on the screen
- Ctrl-C: Copy the selected text to a buffer
- Ctrl-V: Paste the buffer content to the screen (appending it after existing text)
Given an integer n
representing the maximum number of key presses allowed, determine the maximum number of 'A' characters you can print on the screen.
Each operation counts as one key press. For example:
- Pressing 'A' uses 1 key press and adds 1 'A'
- The copy-paste sequence (Ctrl-A, Ctrl-C, Ctrl-V) uses 3 key presses total and doubles the current text
- Multiple Ctrl-V operations can be performed after a single copy operation
The challenge is to find the optimal combination of direct printing ('A') and copy-paste operations to maximize the total number of 'A' characters within n
key presses.
For instance, with n = 7
key presses, you could:
- Press 'A' 7 times to get 7 'A's
- Or press 'A' 3 times, then Ctrl-A, Ctrl-C, Ctrl-V, Ctrl-V to get 9 'A's (3 initial, then paste twice for 3+3+3)
The solution uses dynamic programming where dp[i]
represents the maximum number of 'A's achievable with i
key presses. For each position, it considers all possible points where a copy-paste sequence could start to maximize the output.
Intuition
The key insight is recognizing when copy-paste becomes more efficient than simply pressing 'A' repeatedly. For small values of n
, pressing 'A' directly is optimal. However, as n
grows, strategically using copy-paste operations can multiply our output.
Think about what happens when we perform a copy-paste sequence:
- Ctrl-A, Ctrl-C, Ctrl-V takes 3 key presses minimum
- This sequence doubles what we currently have on screen
- We can continue pressing Ctrl-V to keep pasting (each additional paste costs only 1 key press)
The crucial observation is that once we decide to start a copy operation at some point, we should consider using all remaining key presses for pasting. If we have x
'A's on screen and perform Ctrl-A, Ctrl-C followed by k
Ctrl-V operations, we get x * (k + 1)
total 'A's.
This leads to a dynamic programming approach where for each position i
, we ask: "What if I stopped pressing 'A' at some earlier point j
and used the remaining key presses for copy-paste operations?"
Breaking down the math:
- At position
j-1
, we havedp[j-1]
'A's on screen - Position
j
starts our Ctrl-A (select all) - Position
j+1
is Ctrl-C (copy) - Positions
j+2
throughi
are all Ctrl-V (paste) - Number of pastes:
i - j - 1
- Total copies of the original text:
i - j
(including the original) - Final result:
dp[j-1] * (i - j)
By trying all possible "switch points" j
where we transition from typing to copying, we can find the optimal strategy. The formula dp[i] = max(dp[i], dp[j-1] * (i-j))
captures this relationship, where we compare keeping the current value versus the result of starting a copy sequence at position j
.
Learn more about Math and Dynamic Programming patterns.
Solution Approach
We use dynamic programming to solve this problem. The approach builds up the solution incrementally, determining the maximum number of 'A's achievable for each number of key presses from 1 to n
.
Initialization:
- Create a DP array
dp
wheredp[i]
represents the maximum number of 'A's achievable with exactlyi
key presses - Initialize
dp[i] = i
for all positions, representing the base case of pressing 'A'i
times
Dynamic Programming Transition:
For each position i
from 3 to n
:
- We maintain the current value
dp[i]
(pressing 'A' directlyi
times) - For each potential copy starting point
j
from 2 toi-2
:- Calculate the result if we start copying at position
j
- The formula is:
dp[j-1] * (i - j)
- Where
dp[j-1]
is the number of 'A's before starting the copy sequence - And
(i - j)
is the multiplication factor from the copy-paste operations - Update
dp[i]
with the maximum value
- Calculate the result if we start copying at position
Why the loop bounds?
- We start from
i = 3
because we need at least 3 operations for a meaningful copy-paste sequence - For
j
, we use range(2, i-1)
:j >= 2
ensures we have at least one 'A' to copy (at positionj-1
)j < i-1
ensures we have at least 3 positions for Ctrl-A, Ctrl-C, and one Ctrl-V
Time Complexity: O(n²)
due to the nested loops
Space Complexity: O(n)
for the DP array
The algorithm effectively considers all possible strategies:
- Never copy (just press 'A'
n
times) - Copy at various points and use remaining presses for pasting
By comparing all these strategies, we find the optimal number of 'A's achievable with n
key presses, which is stored in dp[n]
or dp[-1]
.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with n = 7
key presses to find the maximum number of 'A's we can print.
Step 1: Initialize DP array
dp = [0, 1, 2, 3, 4, 5, 6, 7]
Each dp[i]
initially represents pressing 'A' directly i
times.
Step 2: Process each position starting from i = 3
When i = 3:
- Current:
dp[3] = 3
(pressing 'A' three times) - Try j = 2:
- At position 1, we have
dp[1] = 1
'A' - Operations: position 2 (Ctrl-A), position 3 (Ctrl-C)
- No room for Ctrl-V, so this doesn't improve our result
- At position 1, we have
- Result:
dp[3] = 3
When i = 4:
- Current:
dp[4] = 4
- Try j = 2:
dp[1] * (4-2) = 1 * 2 = 2
(not better than 4)
- Result:
dp[4] = 4
When i = 5:
- Current:
dp[5] = 5
- Try j = 2:
dp[1] * (5-2) = 1 * 3 = 3
(not better)
- Try j = 3:
dp[2] * (5-3) = 2 * 2 = 4
(not better)
- Result:
dp[5] = 5
When i = 6:
- Current:
dp[6] = 6
- Try j = 2:
dp[1] * (6-2) = 1 * 4 = 4
(not better)
- Try j = 3:
dp[2] * (6-3) = 2 * 3 = 6
(equal, keep 6)
- Try j = 4:
dp[3] * (6-4) = 3 * 2 = 6
(equal, keep 6)
- Result:
dp[6] = 6
When i = 7:
- Current:
dp[7] = 7
- Try j = 2:
dp[1] * (7-2) = 1 * 5 = 5
(not better)
- Try j = 3:
dp[2] * (7-3) = 2 * 4 = 8
â (better!)- This means: Press 'A' twice (2 'A's), then Ctrl-A, Ctrl-C, Ctrl-V, Ctrl-V, Ctrl-V
- Result: 2 + 2 + 2 + 2 = 8 'A's
- Try j = 4:
dp[3] * (7-4) = 3 * 3 = 9
â (even better!)- This means: Press 'A' three times (3 'A's), then Ctrl-A, Ctrl-C, Ctrl-V, Ctrl-V
- Result: 3 + 3 + 3 = 9 'A's
- Try j = 5:
dp[4] * (7-5) = 4 * 2 = 8
(not better than 9)
- Result:
dp[7] = 9
Final DP array:
dp = [0, 1, 2, 3, 4, 5, 6, 9]
The maximum number of 'A's with 7 key presses is 9, achieved by pressing 'A' three times, then copying and pasting that text twice.
Solution Implementation
1class Solution:
2 def maxA(self, n: int) -> int:
3 # dp[i] represents the maximum number of 'A's that can be produced with i keystrokes
4 dp = list(range(n + 1)) # Initialize: dp[i] = i (typing 'A' i times)
5
6 # For each number of keystrokes from 3 to n
7 for i in range(3, n + 1):
8 # Try different positions to perform Ctrl+A, Ctrl+C, then multiple Ctrl+V
9 # j represents the position after which we start the copy-paste sequence
10 for j in range(2, i - 1):
11 # dp[j - 1]: max 'A's before starting copy operation
12 # (i - j): number of times we can paste after copy
13 # We need 2 keystrokes for Ctrl+A and Ctrl+C, then (i - j - 1) for pastes
14 # This gives us dp[j - 1] * (i - j) total 'A's
15 dp[i] = max(dp[i], dp[j - 1] * (i - j))
16
17 # Return the maximum number of 'A's achievable with n keystrokes
18 return dp[-1]
19
1class Solution {
2 /**
3 * Finds the maximum number of 'A's that can be printed using n keystrokes.
4 * Available operations:
5 * 1. Print 'A' (1 keystroke)
6 * 2. Ctrl-A: Select all (1 keystroke)
7 * 3. Ctrl-C: Copy (1 keystroke)
8 * 4. Ctrl-V: Paste (1 keystroke)
9 *
10 * @param n The total number of keystrokes allowed
11 * @return The maximum number of 'A's that can be printed
12 */
13 public int maxA(int n) {
14 // dp[i] represents the maximum number of 'A's achievable with i keystrokes
15 int[] dp = new int[n + 1];
16
17 // Base case: with i keystrokes, we can print at most i 'A's directly
18 for (int i = 0; i <= n; i++) {
19 dp[i] = i;
20 }
21
22 // For each number of keystrokes from 3 to n
23 for (int i = 3; i <= n; i++) {
24 // Try different positions to start the copy-paste sequence
25 // j represents the position after which we do Ctrl-A, Ctrl-C
26 for (int j = 2; j < i - 1; j++) {
27 // dp[j - 1]: number of 'A's before starting copy sequence
28 // After position j-1, we use 2 keystrokes for Ctrl-A and Ctrl-C
29 // Remaining keystrokes (i - j - 1) are used for Ctrl-V
30 // Total pastes: (i - j - 1), plus the original makes (i - j) copies
31 dp[i] = Math.max(dp[i], dp[j - 1] * (i - j));
32 }
33 }
34
35 return dp[n];
36 }
37}
38
1class Solution {
2public:
3 int maxA(int n) {
4 // dp[i] represents the maximum number of 'A's that can be printed with i key presses
5 vector<int> dp(n + 1);
6
7 // Initialize dp array: with i key presses, we can print at most i 'A's by pressing 'A' i times
8 iota(dp.begin(), dp.end(), 0);
9
10 // For each number of key presses from 3 to n
11 for (int i = 3; i <= n; ++i) {
12 // Try different positions to start the copy-paste sequence
13 // j represents the position after which we perform Ctrl-A, Ctrl-C, then paste multiple times
14 for (int j = 2; j < i - 1; ++j) {
15 // dp[j - 1]: maximum 'A's we can get with (j - 1) key presses
16 // After position (j - 1), we use 2 keys for Ctrl-A and Ctrl-C
17 // Then we have (i - j - 1) key presses left for Ctrl-V (paste)
18 // Each paste multiplies the copied content, so total multiplier is (i - j)
19 dp[i] = max(dp[i], dp[j - 1] * (i - j));
20 }
21 }
22
23 // Return the maximum number of 'A's that can be printed with n key presses
24 return dp[n];
25 }
26};
27
1function maxA(n: number): number {
2 // dp[i] represents the maximum number of 'A's that can be printed with i key presses
3 const dp: number[] = new Array(n + 1);
4
5 // Initialize dp array: with i key presses, we can print at most i 'A's by pressing 'A' i times
6 for (let i = 0; i <= n; i++) {
7 dp[i] = i;
8 }
9
10 // For each number of key presses from 3 to n
11 for (let i = 3; i <= n; i++) {
12 // Try different positions to start the copy-paste sequence
13 // j represents the position after which we perform Ctrl-A, Ctrl-C, then paste multiple times
14 for (let j = 2; j < i - 1; j++) {
15 // dp[j - 1]: maximum 'A's we can get with (j - 1) key presses
16 // After position (j - 1), we use 2 keys for Ctrl-A and Ctrl-C
17 // Then we have (i - j - 1) key presses left for Ctrl-V (paste)
18 // Each paste multiplies the copied content, so total multiplier is (i - j)
19 dp[i] = Math.max(dp[i], dp[j - 1] * (i - j));
20 }
21 }
22
23 // Return the maximum number of 'A's that can be printed with n key presses
24 return dp[n];
25}
26
Time and Space Complexity
Time Complexity: O(n²)
The code uses a nested loop structure:
- The outer loop runs from
i = 3
ton
, which isO(n)
iterations - The inner loop runs from
j = 2
toi - 1
, which in the worst case (wheni = n
) runs approximatelyn - 2
times - Inside the inner loop, we perform constant time operations (
max
comparison and multiplication) - Therefore, the total time complexity is
O(n) * O(n) * O(1) = O(n²)
Space Complexity: O(n)
The space complexity is determined by:
- The
dp
array which storesn + 1
elements, requiringO(n)
space - Only a constant amount of additional variables (
i
,j
) are used - Therefore, the total space complexity is
O(n)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Understanding of Copy-Paste Multiplication
Pitfall: A common mistake is misunderstanding how the copy-paste sequence multiplies the text. Some might think that after Ctrl-A, Ctrl-C, each Ctrl-V adds the copied content once, resulting in a formula like dp[j-1] * (i - j - 2 + 1)
or similar incorrect calculations.
Why it happens: The confusion arises from not recognizing that:
- Ctrl-A, Ctrl-C takes 2 operations
- After copying, we have
(i - j - 1)
operations left for pasting - Each paste adds the original content, so with k pastes, we get (k+1) times the original content
Correct Understanding:
- At position j-1: We have
dp[j-1]
A's - Operations j and j+1: Ctrl-A and Ctrl-C (select all and copy)
- Operations j+2 to i: We have
(i - j - 1)
paste operations - Total A's = original + (number of pastes Ă original) =
dp[j-1] * (i - j)
2. Off-by-One Errors in Loop Boundaries
Pitfall: Using incorrect loop boundaries like for j in range(1, i)
or for j in range(2, i)
instead of for j in range(2, i-1)
.
Why it matters:
- If j is too small (j=1): We'd be trying to copy when we have 0 A's
- If j is too large (j=i-1): We'd only have room for Ctrl-A, Ctrl-C with no paste operation
- If j=i: We'd have no operations left after starting at position j
Solution: Ensure j ranges from 2 to i-2 (inclusive), which in Python is range(2, i-1)
. This guarantees:
- At least one 'A' exists before copying (j-1 >= 1)
- At least three operations remain for the copy-paste sequence (i - j >= 2)
3. Not Considering the Base Case Properly
Pitfall: Forgetting that for small values of n (n < 3), the optimal strategy is always to just press 'A' n times. Some implementations might try to apply copy-paste logic even when n is too small.
Solution: Initialize the DP array properly with dp[i] = i
and start the optimization loop from i=3. This ensures:
- dp[0] = 0 (no keystrokes = no A's)
- dp[1] = 1 (one keystroke = one A)
- dp[2] = 2 (two keystrokes = two A's)
4. Integer Overflow in Large Test Cases
Pitfall: For large values of n, the number of A's can grow exponentially through repeated copy-paste operations, potentially causing integer overflow in languages with fixed-size integers.
Solution:
- In Python, this is generally not an issue due to arbitrary precision integers
- In other languages, consider using appropriate data types (long long in C++, BigInteger in Java)
- Add validation if the problem has constraints on the maximum output value
Example Trace to Avoid Confusion:
For n=7, let's trace through j=4 (copy after 3 A's):
- Positions 1-3: Press 'A' three times â "AAA"
- Position 4: Ctrl-A (select all)
- Position 5: Ctrl-C (copy "AAA")
- Position 6: Ctrl-V (paste) â "AAAAAA" (6 A's)
- Position 7: Ctrl-V (paste) â "AAAAAAAAA" (9 A's)
The formula gives: dp[3] * (7 - 4) = 3 * 3 = 9
â
Which data structure is used to implement priority queue?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Donât Miss This!