3210. Find the Encrypted String
Problem Description
You are given a string s
and an integer k
. Your task is to encrypt the string by shifting each character in a cyclic manner.
The encryption works as follows:
- For each character in the string, replace it with the character that is
k
positions ahead of it - The shifting is cyclic, meaning when you reach the end of the string, you wrap around to the beginning
For example, if s = "abc"
and k = 1
:
- The character at position 0 ('a') gets replaced by the character at position 1 ('b')
- The character at position 1 ('b') gets replaced by the character at position 2 ('c')
- The character at position 2 ('c') gets replaced by the character at position 0 ('a') due to cyclic wrapping
The solution uses modular arithmetic (i + k) % n
to handle the cyclic nature of the shifting, where i
is the current position, k
is the shift amount, and n
is the length of the string. This ensures that when i + k
exceeds the string length, it wraps back to the beginning.
The algorithm iterates through each position in the string and replaces the character at that position with the character at the calculated shifted position, then returns the encrypted string.
Intuition
When we need to shift characters in a string, the key insight is recognizing this as an index mapping problem. Each character at position i
needs to move to a new position that is k
steps ahead.
The main challenge is handling what happens when we try to shift beyond the string's end. For instance, if we have a string of length 5 and we're at position 3, shifting by 3 positions would take us to position 6, which doesn't exist.
This naturally leads us to think about circular or cyclic behavior - when we go past the end, we should wrap around to the beginning. This is similar to how a clock works: after 12 comes 1, not 13.
The modulo operator %
is perfect for this circular behavior. When we calculate (i + k) % n
:
- If
i + k
is less thann
, the result is justi + k
- If
i + k
equals or exceedsn
, it wraps around by giving us the remainder
For example, with a string of length 3:
- Position 0 shifted by 4 becomes
(0 + 4) % 3 = 1
- Position 2 shifted by 2 becomes
(2 + 2) % 3 = 1
Since we need to replace every character simultaneously (not one by one which would affect subsequent replacements), we create a new character array and fill it with characters from the original string at their shifted positions. This ensures each replacement uses the original string's characters, not the already-modified ones.
Solution Approach
The solution uses a simulation approach to implement the cyclic character shifting. Here's how the implementation works:
-
Create a mutable copy: First, we convert the string
s
into a listcs
usinglist(s)
. This is necessary because strings in Python are immutable, so we need a mutable data structure to build our result. -
Store the string length: We save
n = len(s)
to avoid recalculating the length multiple times. -
Iterate through each position: We loop through each index
i
from0
ton-1
usingfor i in range(n)
. -
Apply the cyclic shift formula: For each position
i
, we calculate the source position using(i + k) % n
:i + k
gives us the positionk
steps ahead% n
ensures we wrap around when the position exceeds the string length- We then fetch the character from this calculated position in the original string
s
-
Update the character: We replace
cs[i]
with the character froms[(i + k) % n]
. Note that we read from the original strings
but write to our copycs
, ensuring all replacements happen simultaneously. -
Join and return: Finally, we use
"".join(cs)
to convert the list of characters back into a string and return it.
The time complexity is O(n)
where n
is the length of the string, as we iterate through each character once. The space complexity is also O(n)
for storing the character list.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the encryption process with s = "dart"
and k = 3
.
Initial Setup:
- String:
"dart"
(length n = 4) - Shift amount: k = 3
- Create a mutable copy:
cs = ['d', 'a', 'r', 't']
Step-by-step character replacement:
Position 0 (currently 'd'):
- Calculate source position:
(0 + 3) % 4 = 3
- Fetch character at position 3 of original string:
s[3] = 't'
- Update:
cs[0] = 't'
- Result so far:
cs = ['t', 'a', 'r', 't']
Position 1 (currently 'a'):
- Calculate source position:
(1 + 3) % 4 = 4 % 4 = 0
- Fetch character at position 0 of original string:
s[0] = 'd'
- Update:
cs[1] = 'd'
- Result so far:
cs = ['t', 'd', 'r', 't']
Position 2 (currently 'r'):
- Calculate source position:
(2 + 3) % 4 = 5 % 4 = 1
- Fetch character at position 1 of original string:
s[1] = 'a'
- Update:
cs[2] = 'a'
- Result so far:
cs = ['t', 'd', 'a', 't']
Position 3 (currently 't'):
- Calculate source position:
(3 + 3) % 4 = 6 % 4 = 2
- Fetch character at position 2 of original string:
s[2] = 'r'
- Update:
cs[3] = 'r'
- Final result:
cs = ['t', 'd', 'a', 'r']
Final Step:
- Join the characters:
"".join(cs) = "tdar"
The encrypted string is "tdar"
. Notice how each character was replaced by the one that was 3 positions ahead in a cyclic manner - when we went beyond position 3, we wrapped around to the beginning of the string.
Solution Implementation
1class Solution:
2 def getEncryptedString(self, s: str, k: int) -> str:
3 """
4 Encrypts a string by shifting each character k positions to the right (cyclically).
5
6 Args:
7 s: The input string to be encrypted
8 k: The number of positions to shift each character
9
10 Returns:
11 The encrypted string after shifting
12 """
13 # Convert string to list for character manipulation
14 encrypted_chars = list(s)
15
16 # Get the length of the string
17 string_length = len(s)
18
19 # Replace each character with the character k positions ahead (with wrap-around)
20 for i in range(string_length):
21 # Use modulo to handle wrap-around when index exceeds string length
22 encrypted_chars[i] = s[(i + k) % string_length]
23
24 # Join the list of characters back into a string
25 return "".join(encrypted_chars)
26
1class Solution {
2 /**
3 * Encrypts a string by shifting each character k positions to the right cyclically.
4 * Each character at position i is replaced by the character at position (i + k) % n.
5 *
6 * @param s the input string to be encrypted
7 * @param k the number of positions to shift each character
8 * @return the encrypted string after shifting
9 */
10 public String getEncryptedString(String s, int k) {
11 // Convert string to character array for modification
12 char[] charArray = s.toCharArray();
13
14 // Get the length of the string
15 int length = charArray.length;
16
17 // Iterate through each position in the character array
18 for (int i = 0; i < length; ++i) {
19 // Replace character at position i with character at position (i + k) % length
20 // The modulo operation ensures cyclic wrapping when k exceeds string length
21 charArray[i] = s.charAt((i + k) % length);
22 }
23
24 // Convert the modified character array back to string and return
25 return new String(charArray);
26 }
27}
28
1class Solution {
2public:
3 string getEncryptedString(string s, int k) {
4 // Get the length of the input string
5 int n = s.length();
6
7 // Create a result string of the same length, initialized with spaces
8 string result(n, ' ');
9
10 // For each character position in the original string
11 for (int i = 0; i < n; ++i) {
12 // Place the character at position (i + k) % n into position i of result
13 // The modulo operation ensures circular rotation
14 result[i] = s[(i + k) % n];
15 }
16
17 // Return the encrypted string
18 return result;
19 }
20};
21
1/**
2 * Encrypts a string by shifting each character k positions to the right (cyclically)
3 * @param s - The input string to be encrypted
4 * @param k - The number of positions to shift each character
5 * @returns The encrypted string after shifting
6 */
7function getEncryptedString(s: string, k: number): string {
8 // Array to store the shifted characters
9 const shiftedCharacters: string[] = [];
10
11 // Get the length of the input string
12 const stringLength = s.length;
13
14 // Iterate through each position in the original string
15 for (let currentIndex = 0; currentIndex < stringLength; ++currentIndex) {
16 // Calculate the source index using modulo for cyclic shifting
17 // Each character at position i gets the character from position (i + k) % n
18 const sourceIndex = (currentIndex + k) % stringLength;
19
20 // Place the character from the calculated source position
21 shiftedCharacters[currentIndex] = s[sourceIndex];
22 }
23
24 // Join all characters to form the final encrypted string
25 return shiftedCharacters.join('');
26}
27
Time and Space Complexity
Time Complexity: O(n)
The algorithm iterates through the string once with a for loop that runs n
times, where n
is the length of the input string s
. Inside the loop, each operation takes constant time:
- Accessing an element from the string using index
(i + k) % n
takesO(1)
- Assigning a character to the list takes
O(1)
Additionally, the "".join(cs)
operation at the end takes O(n)
time to concatenate all characters. Therefore, the overall time complexity is O(n) + O(n) = O(n)
.
Space Complexity: O(n)
The algorithm creates a new list cs
by converting the input string s
to a list, which requires O(n)
space to store all n
characters. The final string created by "".join(cs)
also requires O(n)
space, but since we typically count the output space separately or consider the maximum space used at any point, the space complexity remains O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Modifying the Original String While Reading From It
A common mistake is trying to modify the string in-place while simultaneously reading from it. This creates incorrect results because characters that have already been shifted will be read again for subsequent shifts.
Incorrect approach:
def getEncryptedString(self, s: str, k: int) -> str:
result = s
for i in range(len(s)):
result = result[:i] + s[(i + k) % len(s)] + result[i+1:]
return result
This doesn't work because after updating position 0, when we process position 1, we might be reading from an already modified position if (1 + k) % n == 0
.
Solution: Always read from the original string and write to a separate data structure, as shown in the correct implementation.
2. Not Handling Large Values of k
When k
is much larger than the string length, not using modulo arithmetic efficiently can lead to unnecessary complexity or confusion.
Potential issue:
# If k = 1000000 and len(s) = 3, this still works but is conceptually unclear encrypted_chars[i] = s[(i + k) % string_length]
Solution: Optimize by reducing k
first:
k = k % string_length # Reduce k to its effective value encrypted_chars[i] = s[(i + k) % string_length]
This makes the code clearer and prevents potential integer overflow in other languages.
3. Off-by-One Errors with Cyclic Indexing
Developers might incorrectly implement the wrap-around logic, especially when trying to handle it manually without modulo.
Incorrect approach:
def getEncryptedString(self, s: str, k: int) -> str:
cs = list(s)
n = len(s)
for i in range(n):
new_pos = i + k
if new_pos >= n:
new_pos = new_pos - n # This only works for k < n
cs[i] = s[new_pos]
return "".join(cs)
This fails when k >= n
because subtracting n
once isn't enough.
Solution: Always use modulo operator %
for cyclic operations as it handles all cases correctly.
Problem: Given a list of tasks and a list of requirements, compute a sequence of tasks that can be performed, such that we complete every task once while satisfying all the requirements.
Which of the following method should we use to solve this problem?
Recommended Readings
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
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 assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!