1427. Perform String Shifts
Problem Description
In this problem, we are given a string s
that consists of lowercase English letters, and a matrix shift
consisting of pairs [direction, amount]
. We are tasked with performing a series of shift operations on the string according to the instructions provided by each element in the shift
matrix. The direction
indicates whether the shift should be to the left (0
) or to the right (1
) and amount
tells us how many times the shift should occur.
A left shift (direction = 0
) means removing the first character of string s
and appending it to the end. Conversely, a right shift (direction = 1
) means removing the last character of s
and adding it to the beginning. After applying all shifts in the order they appear in the shift
matrix, our goal is to return the resultant string.
To illustrate, if we have string s = "abcde"
:
- A left shift of
1
would change the string to"bcdea"
. - A right shift of
1
would change the string to"eabcd"
.
We need to ensure that we carry out these operations in an efficient manner to determine the final state of the string s
.
Intuition
To efficiently perform the shift operations on the string, we notice that shifts in opposite directions counteract each other. That means if we shift a string to the left by 2
positions and then to the right by 2
positions, we end up with the original string. Furthermore, shifting the string by its length or multiples of its length also results in the same string. Therefore, we can simplify the problem by combining all the shifts into a single effective shift.
To achieve this, we iterate over the shift
matrix and maintain a variable x
to calculate the net effect of all shifts. For left shifts, we can subtract the amount from x
, and for right shifts, we can add the amount to x
. By doing this for each shift and then simplifying the net shifts (x
) modulo the length of the string, we arrive at a single shift value that represents the overall effect of all the given shifts.
Once the effective shift is determined, we can perform the shift in one step. If x
is positive, it indicates a right shift; if negative, a left shift. The implementation in Python takes advantage of string slicing to perform this operation:
s[-x:]
: This slice takes the lastx
characters ofs
, which is what moves to the start of the string in a right shift.s[:-x]
: This slice takes all characters ofs
except the lastx
, which remain in their original order after the shift.
Combining these two slices, we create the new string post shift, s[-x:] + s[:-x]
, effectively performing the necessary left or right shift in a single operation. This approach effectively reduces the complexity of the problem from potentially having to do multiple shifts to simply calculating the net shift and applying it once. This not only simplifies the problem but also ensures that the solution is efficient, even when the number of shift operations is large.
Learn more about Math patterns.
Solution Approach
The provided solution follows a direct algorithmic approach to combine all the individual shift commands into one effective shift operation and then apply it to the string s
. Here is a step-by-step walkthrough of the implementation:
-
Initialize a variable
x
to0
. This will keep track of the net number of shifts needed. When we encounter a left shift, we will decreasex
, and for a right shift, we will increasex
. -
Loop through each shift operation in the
shift
matrix. Each operation is a list containing two elements:direction
andamount
. We check the direction:- If the
direction
is0
(left shift), we convert theamount
to a negative number (by doing-b
) and add it tox
. - If the
direction
is1
(right shift), we simply add theamount
tox
.
In Python, this can be compactly written as:
for a, b in shift: if a == 0: b = -b x += b
- If the
-
After processing all shift operations, we normalize the net total shifts
x
by taking the remainder whenx
is divided by the length ofs
(x %= len(s)
). This is an essential step because shifting the string by its length (or multiples thereof) results in the same string, so shifts beyond that point are redundant.In mathematical form, this step is expressed as:
x %= len(s)
If
x
is positive, it represents a net right shift; if negative, it represents a net left shift. In either case, since the remainder operation normalizesx
within the range from0
tolen(s) - 1
, we have a guaranteed valid index for the slicing operation in the next step. -
Finally, we apply the effective shift to
s
using Python's string slicing feature:s[-x:]
generates a substring consisting of the lastx
characters ins
.s[:-x]
creates a substring of all characters ins
except for the lastx
.
The result is then concatenated to get the shifted string. As
x
has been adjusted to be within the proper index range, these slices will always be valid. The Python code for this operation is:return s[-x:] + s[:-x]
This method uses the string slicing capability of Python, which is very efficient, to perform the shifting operation in constant time, which is much more efficient than performing each shift individually. The space complexity of the solution is constant since it uses a fixed amount of extra space, regardless of the input size, and the time complexity is linear relative to the number of shift operations due to the single pass through the shift
array.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's consider a small example to illustrate the solution approach:
Suppose we have a string s = "abcdef"
and a shift
matrix with the following operations: [[0, 1], [1, 2], [0, 3]]
.
-
We would initialize our variable
x
to0
, which will keep track of the net number of shifts. -
The first operation is a left shift ([0, 1]), so we decrease
x
by 1. Nowx
becomes-1
. -
The second operation is a right shift ([1, 2]), so we increase
x
by 2. Nowx
becomes1
(-1 + 2
). -
The final operation is a left shift ([0, 3]), so we decrease
x
by 3. Nowx
becomes-2
(1 - 3
). -
Once we've processed all operations, we will normalize
x
with the length ofs
which is6
. We computex %= len(s)
, which results inx
becoming4
because-2 % 6
is4
. It means we have a net right shift of4
. -
We now apply this effective shift to string
s
with a single slicing operation. The substring from the lastx
characters will bes[-4:]
, which gives us"cdef"
. The substring from the start to the point before the lastx
characters will bes[:-4]
, giving us"ab"
. -
Combining these two substrings to perform the net right shift operation, we get the transformed string
"cdefab"
.
Thus, after applying an algorithmic approach, we were able to reduce multiple shifts to a single operation and efficiently determine the final state of s
after all the shifting instructions.
Solution Implementation
1from typing import List
2
3class Solution:
4 def stringShift(self, s: str, shifts: List[List[int]]) -> str:
5 # Initialize a variable to keep track of the net shift amount
6 net_shift = 0
7
8 # Loop through each shift command in the shifts list
9 for direction, amount in shifts:
10 # If direction is 0, shift left; otherwise, shift right.
11 # Left shift can be considered as a negative shift in calculations,
12 # so we negate the amount for left shifts.
13 if direction == 0:
14 net_shift -= amount
15 else:
16 net_shift += amount
17
18 # The effective shift is the net shift modulo the string's length
19 # This accounts for shifts that exceed the string length.
20 effective_shift = net_shift % len(s)
21
22 # Apply the effective shift on the string and return the result.
23 # We use negative indexing to wrap around the string.
24 return s[-effective_shift:] + s[:-effective_shift]
25
26# Example usage:
27# solution = Solution()
28# result = solution.stringShift("abcdefg", [[1, 1], [1, 1], [0, 2], [1, 3]])
29# print(result) # Expected output would be the final shifted string.
30
1class Solution {
2
3 // This method takes a String 's' and performs a series of shifts according to 'shifts' array.
4 public String stringShift(String s, int[][] shifts) {
5 int totalShifts = 0; // This variable will accumulate the effective number of shifts.
6
7 // Iterate through each shift operation described by the 'shifts' array.
8 for (int[] shiftOperation : shifts) {
9 // The direction of the shift (0 for left shift, 1 for right shift).
10 int direction = shiftOperation[0];
11 int amount = shiftOperation[1]; // The amount to shift.
12
13 // Convert left shifts into negative values to simplify the calculation.
14 if (direction == 0) {
15 amount = -amount;
16 }
17 // Accumulate the effective shift amount.
18 totalShifts += amount;
19 }
20
21 int stringLength = s.length();
22 // Normalize the effective shift to be within the range of the string length.
23 // This also accounts for the shifts that go beyond the string length.
24 totalShifts = (totalShifts % stringLength + stringLength) % stringLength;
25
26 // Perform the shift by using substring.
27 // Extract the end of the string that comes to the front and concatenate with
28 // the beginning of the string that goes to the end.
29 return s.substring(stringLength - totalShifts) + s.substring(0, stringLength - totalShifts);
30 }
31}
32
1#include <string>
2#include <vector>
3
4using std::string;
5using std::vector;
6
7class Solution {
8public:
9 string stringShift(string s, vector<vector<int>>& shifts) {
10 int totalShift = 0; // Will hold the net number of shifts to apply.
11
12 // Loop through each shift operation in the array.
13 for (const auto& shiftOp : shifts) {
14 int direction = shiftOp[0]; // Direction of shift: 0 for left, 1 for right.
15 int amount = shiftOp[1]; // Amount to shift.
16
17 // If the direction is 'left' (0), we convert amount to a negative value.
18 if (direction == 0) {
19 amount = -amount;
20 }
21
22 // Accumulate the effective shift amount.
23 totalShift += amount;
24 }
25
26 int n = s.size(); // Size of the string to help modulate the shift amount.
27 totalShift = (totalShift % n + n) % n; // Normalize totalShift within the bounds of the string length.
28
29 // Perform the actual shift operation. The substring from (n - totalShift) to the end is moved to the front,
30 // concatenated by the beginning of the string up to (n - totalShift).
31 return s.substr(n - totalShift) + s.substr(0, n - totalShift);
32 }
33};
34
1function stringShift(s: string, shifts: number[][]): string {
2 // Holds the net number of shifts to apply.
3 let totalShift: number = 0;
4
5 // Loop through each shift operation in the array.
6 for (let shiftOp of shifts) {
7 let direction: number = shiftOp[0]; // Direction of shift: 0 for left, 1 for right
8 let amount: number = shiftOp[1]; // Amount to shift
9
10 // If the direction is 'left' (0), we convert amount to a negative value.
11 if (direction === 0) {
12 amount = -amount;
13 }
14
15 // Accumulate the effective shift amount.
16 totalShift += amount;
17 }
18
19 let n: number = s.length; // Size of the string to help modulate the shift amount.
20 // Normalize totalShift within the bounds of the string length.
21 totalShift = ((totalShift % n) + n) % n;
22
23 // Perform the actual shift operation. The substring from (n - totalShift) to the end is moved to the front,
24 // concatenated with the beginning of the string up to (n - totalShift).
25 return s.substring(n - totalShift) + s.substring(0, n - totalShift);
26}
27
Time and Space Complexity
Time Complexity
The time complexity of the given code is O(n + m)
, where n
is the length of the string s
, and m
is the number of shifts in the shift
list. This is because the code loops once over the list of shifts (m
operations) and then applies a single string operation that has a complexity of O(n)
.
Let's break it down:
- The
for
loop processes each element in theshift
array of sizem
, which means it operates inO(m)
time. - The modulo operation (
x %= len(s)
) is done in constant timeO(1)
since the length of the string is calculated just once. - The string concatenation (
s[-x:] + s[:-x]
) isO(n)
because it involves creating two substrings and then concatenating them together, which requires building a new string of lengthn
.
Therefore, when combined we get O(m + n)
for the time complexity of the entire function.
Space Complexity
The space complexity of the code is O(n)
. This is due to the space needed for the output string in the final concatenation step. While x
is calculated in place and does not need additional space proportional to the input, the slicing operation creates new strings which are then concatenated to form the final result, requiring a buffer that can hold the entire string s
.
Here's why:
- The integer
x
is a single counter variable and thus takes upO(1)
space. - The space required to store the input
s
and the shift instructions does not count towards the space complexity as they are considered input. - The final line (
return s[-x:] + s[:-x]
) creates substrings and concatenates them, requiringO(n)
space since the resulting string is of the same length as the original strings
.
In summary, the space complexity is O(n)
.
Learn more about how to find time and space complexity quickly using problem constraints.
Which type of traversal does breadth first search do?
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
LeetCode 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 we
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Want a Structured Path to Master System Design Too? Don’t Miss This!