Input: n =3Output: 3Explanation: 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'.
In worst case we need to copy the character and paste it n-1 times to get the desired result. Now, lets see how we can minimize it, with different number of operations.
2 operations ⇨ copy + paste ⇨ we get AA (i.e. double). The ratio of operations to size is 2:2.
3 operations ⇨ copy + paste + paste ⇨ we get AAA (i.e. triple). The ratio of operations to size is 3:3.
4 operations ⇨ copy + paste + paste + paste ⇨ AAAA (i.e. quadruple). The ratio is 4:4.
5 operations
copy + 4 X paste ⇨ AAAAA (i.e. 5 times)
but, combining the two and three operations sequences can multiply the number of lines by 6 for the same number of operations. i.e. copy + paste ⇨ AA + copy + 2X paste ⇨ AAAAAA
Since we want to reduce the number of operations we need to reduce the paste operations where ever it is possible which means perform the copy operation. Say If n = 50 then we print 25 A’s then by doing copy all and paste will print 50 characters. Now to reach 25, which is multiple of 5 so if we print 5 A’s then copy and paste it 4 times to get 25 A’s and now to print 5 A’s, copy the already printed A and paste it 4 times.
1
2
3
4
n = 50
Copy + 4 X Paste = AAAAA => 5 operations
Copy + 4 X Paste = AA...AA(25 As) => 5 operations, total operations = 10
Copy and paste – printed – AA...AAA (50 As) => 2 operations, total operations = 10 + 2 = 12
publicclassSolution {
publicintminSteps(int n) {
int steps = 0;
int factor = 2;
while (n > 1) {
while (n % factor == 0) { // check if problem can be broken in smaller problems steps += factor; //if yes then add no of smaller problems to result. If number = 25 i = 5 then 5*5 = 25 so add 5 to results n /= factor; // create smaller problem }
factor++;
}
return steps;
}
}
The key insight is that copying isn’t necessary every time; pasting multiple times after a copy is more efficient. For instance, to get 9 ‘A’s, we might:
Start with ‘A’
Copy All (clipboard: ‘A’)
Paste (now ‘AA’)
Copy All (clipboard: ‘AA’)
Paste (now ‘AAAA’)
Paste again (now ‘AAAAAA’)
Copy All (clipboard: ‘AAAAAA’)
Paste (now ‘AAAAAAAAAAAA’)
This example overshoots, highlighting the need for strategic copying and pasting. Thus, experimenting with different copy-paste combinations becomes vital, and recursion helps explore these combinations effectively.
Let’s define our function f(i,j) more formally:
f(i,j) represents the minimum number of operations needed to generate n A’s, starting with i A’s on the screen and j A’s in the clipboard.
Our objective is to find ( f(1, 1) ), which means starting with one ‘A’ on the screen and one ‘A’ in the clipboard.
At each step, we have two options:
Copy All and Paste:
Takes 2 steps (1 Copy, 1 Paste).
Transforms state from (i,j) to (2i, i).
Total operations: 2 + f(2i, i) )
Paste Only:
Takes 1 step.
Transforms state from (i,j) to (i + j, j).
Total operations: 1 + f(i + j, j).
Thus, the recurrence relation is:
$$
f(i, j) = \min(2 + f(2i, i), 1 + f(i + j, j))
$$
This relation captures the essence of choosing the minimum number of operations between the two options at each step.
Base cases:
If ( i = n ), we’ve achieved our goal, so ( f(n, j) = 0 ) for any j.
If ( i > n ), we’ve overshot, so return a very large number (effectively infinity).
publicclassSolution {
privatestaticfinalint INF = 1001; // Define a large number to denote invalid pathspublicintminSteps(int n) {
if (n == 1) {
return 0; // We already have one 'A', so no operations needed }
return 1 + findMinSteps(1, 1, n); // Start with one 'A' on screen and in clipboard }
privateintfindMinSteps(int currLen, int clipboardLen, int n) {
if (currLen == n) {
return 0; // We've reached our goal, no more operations needed }
if (currLen > n) {
return INF; // We've overshot, this path is invalid }
// Try Copy All and Pasteint copyAndPaste = 2 + findMinSteps(currLen * 2, currLen, n);
// Try Paste onlyint pasteOnly = 1 + findMinSteps(currLen + clipboardLen, clipboardLen, n);
// Return the minimum of the two optionsreturn Math.min(copyAndPaste, pasteOnly);
}
}
publicclassSolution {
privatestaticfinalint INF = 1001; // Define a large number to denote invalid pathsprivateint[][] cache;
publicintminSteps(int n) {
if (n == 1) {
return 0; // We already have one 'A', so no operations needed }
this.cache=newint[n + 1][n / 2 + 1];
return 1
+ findMinSteps(1, 1, n); // Start with one 'A' on screen and in clipboard }
privateintfindMinSteps(int currLen, int clipboardLen, int n) {
if (currLen == n) {
return 0; // We've reached our goal, no more operations needed }
if (currLen > n) {
return INF; // We've overshot, this path is invalid }
if (cache[currLen][clipboardLen]!= 0) {
return cache[currLen][clipboardLen];
}
// Try Copy All and Pasteint copyAndPaste = 2 + findMinSteps(currLen * 2, currLen, n);
// Try Paste onlyint pasteOnly = 1 + findMinSteps(currLen + clipboardLen, clipboardLen, n);
// Return the minimum of the two optionsint ans = Math.min(copyAndPaste, pasteOnly);
cache[currLen][clipboardLen]= ans;
return ans;
}
}
In the worst case, we might need to fill out our entire cache. The cache has dimensions n × (n/2), where n is our target length. For each cell, we’re doing constant time operations. Therefore, our time complexity is O(n * n/2) = O(n^2).
🧺 Space complexity: O(n^2) assuming recursion stack and using cache