LeetCode link: 3614. Process String with Special Operations II, difficulty: Hard.
You are given a string s consisting of lowercase English letters and the special characters: '*', '#', and '%'.
You are also given an integer k.
Build a new string result by processing s according to the following rules from left to right:
- If the letter is a lowercase English letter append it to
result. - A
'*'removes the last character fromresult, if it exists. - A
'#'duplicates the currentresultand appends it to itself. - A
'%'reverses the currentresult.
Return the k^{th} character of the final string result. If k is out of the bounds of result, return '.'.
Example 1:
Input: s = "a#b%*", k = 1
Output: "a"
Explanation:
i |
s[i] |
Operation | Current result |
|---|---|---|---|
| 0 | 'a' |
Append 'a' |
"a" |
| 1 | '#' |
Duplicate result |
"aa" |
| 2 | 'b' |
Append 'b' |
"aab" |
| 3 | '%' |
Reverse result |
"baa" |
| 4 | '*' |
Remove the last character | "ba" |
The final result is "ba". The character at index k = 1 is 'a'.
Example 2:
Input: s = "cd%#*#", k = 3
Output: "d"
Explanation:
i |
s[i] |
Operation | Current result |
|---|---|---|---|
| 0 | 'c' |
Append 'c' |
"c" |
| 1 | 'd' |
Append 'd' |
"cd" |
| 2 | '%' |
Reverse result |
"dc" |
| 3 | '#' |
Duplicate result |
"dcdc" |
| 4 | '*' |
Remove the last character | "dcd" |
| 5 | '#' |
Duplicate result |
"dcddcd" |
The final result is "dcddcd". The character at index k = 3 is 'd'.
Example 3:
Input: s = "z*#", k = 0
Output: "."
Explanation:
i |
s[i] |
Operation | Current result |
|---|---|---|---|
| 0 | 'z' |
Append 'z' |
"z" |
| 1 | '*' |
Remove the last character | "" |
| 2 | '#' |
Duplicate the string | "" |
The final result is "". Since index k = 0 is out of bounds, the output is '.'.
Constraints:
1 <= s.length <= 10^5sconsists of only lowercase English letters and special characters'*','#', and'%'.0 <= k <= 10^{15}- The length of
resultafter processingswill not exceed10^{15}.
Topics
Hint 1
Track the length of the string after each operation on s.
Hint 2
Walk backwards through s, undoing each # by using modulus on the tracked lengths, and undoing each % by mirroring across the midpoint, to pinpoint the kth character.
Intuition
Since k can be up to 10^{15}, we cannot simulate the string construction directly as it would result in Memory Limit Exceeded (MLE) or Time Limit Exceeded (TLE).
Instead, we can use a Backward Simulation approach:
- First, compute the length of the string at each step of processing
s. - If the final length is less than or equal to
k, the index is out of bounds, so we return'.'. - Otherwise, we traverse the string backwards. At each step
i:- If
s[i]is a letter: Check if it's the character we are looking for (lens[i] - 1 == k). If so, return it. - If
s[i] == '#'(duplicate): The string was duplicated. Ifkfalls in the second half, it corresponds to the same character in the first half. So we updatek = k % (lens[i] / 2). - If
s[i] == '%'(reverse): The string was reversed. We updatekto its corresponding index before the reverse:k = lens[i] - 1 - k. - If
s[i] == '*'(delete): We don't need to changek. Sincekis a valid index in the current string, and*just removed a character at the very end, our currentknaturally points to a character added before this*.
- If
This logic guarantees that k is always a valid index in the string at the current step.
👋 Hi, I'm the developer behind coder.id — Build your programmer brand. Create your portfolio, resume, and blog in minutes, and land your dream offer!
Step-by-Step Solution
- Create an array
lensof sizento store the length of the string at each step. - Initialize
curr_len = 0. - Iterate through
sfrom left to right:- If
s[i]is a letter, incrementcurr_len. - If
s[i] == '*', decrementcurr_len(minimum 0). - If
s[i] == '#', doublecurr_len. - Store
curr_leninlens[i].
- If
- If
curr_len <= k, return'.'. - Iterate through
sfrom right to left:- If
s[i]is a letter andlens[i] - 1 == k, returns[i]. - If
s[i] == '#'andk >= lens[i] / 2, updatek %= (lens[i] / 2). - If
s[i] == '%', updatek = lens[i] - 1 - k.
- If
Complexity
Time complexity
O(n)
Space complexity
O(n)
Explanation
- Time Complexity:
O(n), wherenis the length of strings. We make two passes over the string. - Space Complexity:
O(n)to store the lengths at each step in thelensarray.
Python #
class Solution:
def processStr(self, s: str, k: int) -> str:
lens = []
curr_len = 0
for ch in s:
if ch.isalpha():
curr_len += 1
elif ch == '*':
curr_len = max(0, curr_len - 1)
elif ch == '#':
curr_len *= 2
elif ch == '%':
pass
lens.append(curr_len)
if curr_len <= k:
return '.'
for i in range(len(s) - 1, -1, -1):
if s[i].isalpha():
if lens[i] - 1 == k:
return s[i]
elif s[i] == '#':
half = lens[i] // 2
if half > 0 and k >= half:
k %= half
elif s[i] == '%':
if lens[i] > 0:
k = lens[i] - 1 - k
return '.'
Java #
class Solution {
public char processStr(String s, long k) {
int n = s.length();
long[] lens = new long[n];
long currLen = 0;
for (int i = 0; i < n; i++) {
char ch = s.charAt(i);
if (ch >= 'a' && ch <= 'z') {
currLen++;
} else if (ch == '*') {
currLen = Math.max(0L, currLen - 1);
} else if (ch == '#') {
currLen *= 2;
}
lens[i] = currLen;
}
if (currLen <= k) {
return '.';
}
for (int i = n - 1; i >= 0; i--) {
char ch = s.charAt(i);
if (ch >= 'a' && ch <= 'z') {
if (lens[i] - 1 == k) {
return ch;
}
} else if (ch == '#') {
long half = lens[i] / 2;
if (half > 0 && k >= half) {
k %= half;
}
} else if (ch == '%') {
if (lens[i] > 0) {
k = lens[i] - 1 - k;
}
}
}
return '.';
}
}
JavaScript #
/**
* @param {string} s
* @param {number} k
* @return {string}
*/
var processStr = function(s, k) {
const n = s.length;
const lens = new Array(n);
let currLen = 0n;
let bigK = BigInt(k);
for (let i = 0; i < n; i++) {
const ch = s[i];
if (ch >= 'a' && ch <= 'z') {
currLen++;
} else if (ch === '*') {
if (currLen > 0n) currLen--;
} else if (ch === '#') {
currLen *= 2n;
}
lens[i] = currLen;
}
if (currLen <= bigK) {
return '.';
}
for (let i = n - 1; i >= 0; i--) {
const ch = s[i];
if (ch >= 'a' && ch <= 'z') {
if (lens[i] - 1n === bigK) {
return ch;
}
} else if (ch === '#') {
const half = lens[i] / 2n;
if (half > 0n && bigK >= half) {
bigK %= half;
}
} else if (ch === '%') {
if (lens[i] > 0n) {
bigK = lens[i] - 1n - bigK;
}
}
}
return '.';
};
C++ #
class Solution {
public:
char processStr(string s, long long k) {
int n = s.length();
vector<long long> lens(n);
long long curr_len = 0;
for (int i = 0; i < n; ++i) {
char ch = s[i];
if (ch >= 'a' && ch <= 'z') {
curr_len++;
} else if (ch == '*') {
curr_len = max(0LL, curr_len - 1);
} else if (ch == '#') {
curr_len *= 2;
}
lens[i] = curr_len;
}
if (curr_len <= k) return '.';
for (int i = n - 1; i >= 0; --i) {
char ch = s[i];
if (ch >= 'a' && ch <= 'z') {
if (lens[i] - 1 == k) return ch;
} else if (ch == '#') {
long long half = lens[i] / 2;
if (half > 0 && k >= half) k %= half;
} else if (ch == '%') {
if (lens[i] > 0) k = lens[i] - 1 - k;
}
}
return '.';
}
};
C# #
public class Solution {
public char ProcessStr(string s, long k) {
int n = s.Length;
long[] lens = new long[n];
long currLen = 0;
for (int i = 0; i < n; i++) {
char ch = s[i];
if (ch >= 'a' && ch <= 'z') {
currLen++;
} else if (ch == '*') {
currLen = Math.Max(0L, currLen - 1);
} else if (ch == '#') {
currLen *= 2;
}
lens[i] = currLen;
}
if (currLen <= k) return '.';
for (int i = n - 1; i >= 0; i--) {
char ch = s[i];
if (ch >= 'a' && ch <= 'z') {
if (lens[i] - 1 == k) return ch;
} else if (ch == '#') {
long half = lens[i] / 2;
if (half > 0 && k >= half) k %= half;
} else if (ch == '%') {
if (lens[i] > 0) k = lens[i] - 1 - k;
}
}
return '.';
}
}
Go #
func processStr(s string, k int64) byte {
n := len(s)
lens := make([]int64, n)
var currLen int64 = 0
for i := 0; i < n; i++ {
ch := s[i]
if ch >= 'a' && ch <= 'z' {
currLen++
} else if ch == '*' {
if currLen > 0 {
currLen--
}
} else if ch == '#' {
currLen *= 2
}
lens[i] = currLen
}
if currLen <= k {
return '.'
}
for i := n - 1; i >= 0; i-- {
ch := s[i]
if ch >= 'a' && ch <= 'z' {
if lens[i] - 1 == k {
return ch
}
} else if ch == '#' {
half := lens[i] / 2
if half > 0 && k >= half {
k %= half
}
} else if ch == '%' {
if lens[i] > 0 {
k = lens[i] - 1 - k
}
}
}
return '.'
}
Ruby #
# @param {String} s
# @param {Integer} k
# @return {String}
def process_str(s, k)
n = s.length
lens = Array.new(n)
curr_len = 0
(0...n).each do |i|
ch = s[i]
if ch >= 'a' && ch <= 'z'
curr_len += 1
elsif ch == '*'
curr_len = [0, curr_len - 1].max
elsif ch == '#'
curr_len *= 2
end
lens[i] = curr_len
end
return '.' if curr_len <= k
(n - 1).downto(0) do |i|
ch = s[i]
if ch >= 'a' && ch <= 'z'
return ch if lens[i] - 1 == k
elsif ch == '#'
half = lens[i] / 2
k %= half if half > 0 && k >= half
elsif ch == '%'
k = lens[i] - 1 - k if lens[i] > 0
end
end
'.'
end
Rust #
impl Solution {
pub fn process_str(s: String, mut k: i64) -> char {
let n = s.len();
let mut lens = vec![0i64; n];
let mut curr_len = 0i64;
let bytes = s.as_bytes();
for i in 0..n {
let ch = bytes[i];
if ch >= b'a' && ch <= b'z' {
curr_len += 1;
} else if ch == b'*' {
if curr_len > 0 { curr_len -= 1; }
} else if ch == b'#' {
curr_len *= 2;
}
lens[i] = curr_len;
}
if curr_len <= k {
return '.';
}
for i in (0..n).rev() {
let ch = bytes[i];
if ch >= b'a' && ch <= b'z' {
if lens[i] - 1 == k {
return ch as char;
}
} else if ch == b'#' {
let half = lens[i] / 2;
if half > 0 && k >= half {
k %= half;
}
} else if ch == b'%' {
if lens[i] > 0 {
k = lens[i] - 1 - k;
}
}
}
'.'
}
}
Kotlin #
class Solution {
fun processStr(s: String, k: Long): Char {
var mutableK = k
val n = s.length
val lens = LongArray(n)
var currLen = 0L
for (i in 0 until n) {
val ch = s[i]
if (ch in 'a'..'z') {
currLen++
} else if (ch == '*') {
if (currLen > 0) currLen--
} else if (ch == '#') {
currLen *= 2
}
lens[i] = currLen
}
if (currLen <= mutableK) return '.'
for (i in n - 1 downTo 0) {
val ch = s[i]
if (ch in 'a'..'z') {
if (lens[i] - 1 == mutableK) return ch
} else if (ch == '#') {
val half = lens[i] / 2
if (half > 0 && mutableK >= half) mutableK %= half
} else if (ch == '%') {
if (lens[i] > 0) mutableK = lens[i] - 1 - mutableK
}
}
return '.'
}
}
Swift #
class Solution {
func processStr(_ s: String, _ k: Int) -> Character {
var mutableK = k
let chars = Array(s)
let n = chars.count
var lens = [Int](repeating: 0, count: n)
var currLen = 0
for i in 0..<n {
let ch = chars[i]
if ch >= "a" && ch <= "z" {
currLen += 1
} else if ch == "*" {
currLen = max(0, currLen - 1)
} else if ch == "#" {
currLen *= 2
}
lens[i] = currLen
}
if currLen <= mutableK {
return "."
}
for i in stride(from: n - 1, through: 0, by: -1) {
let ch = chars[i]
if ch >= "a" && ch <= "z" {
if lens[i] - 1 == mutableK {
return ch
}
} else if ch == "#" {
let half = lens[i] / 2
if half > 0 && mutableK >= half {
mutableK %= half
}
} else if ch == "%" {
if lens[i] > 0 {
mutableK = lens[i] - 1 - mutableK
}
}
}
return "."
}
}
Other languages
Welcome to contribute code to GitHub repo -> 3614. Process String with Special Operations II. Thanks!Level Up Your Developer Identity
- 📄 Resume, Portfolio & Blog
- 🌐 Custom Domain Binding Supported
- ✨ Premium Subdomain:
https://yourname.coder.id