LeetCode 3210 - Find the Encrypted String
The problem asks us to implement a custom string encryption algorithm. We are given a string s and an integer k. For each character in s, we need to replace it with the character that is k positions ahead in the string, in a cyclic manner.
Difficulty: 🟢 Easy
Topics: String
Solution
Problem Understanding
The problem asks us to implement a custom string encryption algorithm. We are given a string s and an integer k. For each character in s, we need to replace it with the character that is k positions ahead in the string, in a cyclic manner. "Cyclic" means that if the index goes beyond the end of the string, we wrap around to the beginning.
The input s is a string of lowercase English letters with a length between 1 and 100. The integer k is between 1 and 10,000. The expected output is the string obtained after performing this cyclic shift for every character.
Important observations include the fact that the string length is relatively small (up to 100), but k can be much larger than the string length. This immediately suggests we need to handle large k by taking k % len(s) to avoid unnecessary computations. Edge cases that could cause naive implementations to fail include when all characters are the same, when k is a multiple of the string length, and when s has length 1.
Approaches
The brute-force approach would iterate over each character, calculate the index of the kth character ahead, and append it to the result. This works correctly but is inefficient if k is much larger than the length of the string because it performs unnecessary repeated shifts.
The optimal approach leverages modular arithmetic. Since the string is cyclic, moving k positions ahead is equivalent to moving k % n positions ahead, where n is the string length. By precomputing this shift and using simple index arithmetic, we can construct the encrypted string in a single pass.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n*k) | O(n) | Directly computes the kth character for each position, inefficient for large k |
| Optimal | O(n) | O(n) | Uses modular arithmetic to compute the new index efficiently |
Algorithm Walkthrough
- Compute the length
nof the input strings. - Compute the effective shift as
shift = k % nto handle cases wherekis larger than the string length. - Initialize an empty list
resultto store the encrypted characters. - Iterate over each index
iin the string:
a. Compute the new index as (i + shift) % n.
b. Append s[new_index] to the result list.
5. Join the list result into a single string.
6. Return the joined string as the encrypted result.
Why it works: Each character is shifted by the effective distance in a cyclic manner. Using modulo ensures that the indexing wraps around correctly. Since we process each character exactly once, the output string is correct and complete.
Python Solution
class Solution:
def getEncryptedString(self, s: str, k: int) -> str:
n = len(s)
shift = k % n
result = []
for i in range(n):
new_index = (i + shift) % n
result.append(s[new_index])
return ''.join(result)
The implementation first computes the effective shift. A loop iterates over each index in the string, calculating the new index after the shift using modular arithmetic. The characters at these indices are collected in a list, which is joined into the final encrypted string.
Go Solution
func getEncryptedString(s string, k int) string {
n := len(s)
shift := k % n
result := make([]byte, n)
for i := 0; i < n; i++ {
newIndex := (i + shift) % n
result[i] = s[newIndex]
}
return string(result)
}
In Go, we use a byte slice to collect the shifted characters since strings are immutable. The modulo operation ensures cyclic behavior. Finally, we convert the byte slice back to a string for the result.
Worked Examples
Example 1: s = "dart", k = 3
| i | Original char | new_index | Encrypted char |
|---|---|---|---|
| 0 | d | (0+3)%4=3 | t |
| 1 | a | (1+3)%4=0 | d |
| 2 | r | (2+3)%4=1 | a |
| 3 | t | (3+3)%4=2 | r |
Encrypted string = "tdar"
Example 2: s = "aaa", k = 1
| i | Original char | new_index | Encrypted char |
|---|---|---|---|
| 0 | a | (0+1)%3=1 | a |
| 1 | a | (1+1)%3=2 | a |
| 2 | a | (2+1)%3=0 | a |
Encrypted string = "aaa"
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | Each character is processed once to compute the encrypted string. |
| Space | O(n) | An auxiliary list or slice of length n is used to store the result. |
The complexity is efficient because we avoid unnecessary repeated shifts by using modulo, and the input size is small enough that O(n) is perfectly acceptable.
Test Cases
# Provided examples
assert Solution().getEncryptedString("dart", 3) == "tdar" # cyclic shift
assert Solution().getEncryptedString("aaa", 1) == "aaa" # all same characters
# Edge cases
assert Solution().getEncryptedString("a", 100) == "a" # single character
assert Solution().getEncryptedString("abc", 3) == "abc" # shift equals length
assert Solution().getEncryptedString("abc", 4) == "bca" # shift greater than length
assert Solution().getEncryptedString("abcd", 0) == "abcd" # zero shift
assert Solution().getEncryptedString("xyz", 10000) == "yzx" # large k
| Test | Why |
|---|---|
| "dart", 3 | Normal case, cyclic shift within string |
| "aaa", 1 | All identical characters |
| "a", 100 | Single character, large k |
| "abc", 3 | Shift equal to string length, string remains same |
| "abc", 4 | Shift greater than string length, modulo used |
| "abcd", 0 | Zero shift, string unchanged |
| "xyz", 10000 | Large k, checks modulo arithmetic correctness |
Edge Cases
One important edge case is when s contains only one character. Any shift should result in the same string because cyclic behavior does not change a single element. Another edge case is when k is a multiple of the string length; this should also result in the same string because the shift effectively wraps around completely. A third edge case is when k is much larger than the string length; naive implementations may attempt repeated shifting, but using modulo ensures efficiency and correctness. All of these edge cases are handled correctly by computing k % len(s) before processing.