From 7ec0fe5c1a70d3fe2c753ecaf5e14d2d9c29d876 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 17 Jun 2026 05:25:21 +0000 Subject: [PATCH] feat: add solution for 3614. Process String with Special Operations II --- .../analysis.md | 113 ++++++++++++++++++ .../problem.md | 97 +++++++++++++++ .../solution_daily_20260617.go | 68 +++++++++++ .../solution_daily_20260617_test.go | 36 ++++++ 4 files changed, 314 insertions(+) create mode 100644 problems/3614-process-string-with-special-operations-ii/analysis.md create mode 100644 problems/3614-process-string-with-special-operations-ii/problem.md create mode 100644 problems/3614-process-string-with-special-operations-ii/solution_daily_20260617.go create mode 100644 problems/3614-process-string-with-special-operations-ii/solution_daily_20260617_test.go diff --git a/problems/3614-process-string-with-special-operations-ii/analysis.md b/problems/3614-process-string-with-special-operations-ii/analysis.md new file mode 100644 index 0000000..0d675b0 --- /dev/null +++ b/problems/3614-process-string-with-special-operations-ii/analysis.md @@ -0,0 +1,113 @@ +# 3614. Process String with Special Operations II + +[LeetCode Link](https://leetcode.com/problems/process-string-with-special-operations-ii/) + +Difficulty: Hard +Topics: String, Simulation + +Acceptance Rate: 26.9% + +## Hints + +### Hint 1 + +Read the constraints before reaching for the obvious approach. The string `s` is short +(at most `10^5`), but the final `result` can grow to `10^15` characters because of the +duplication operator `'#'`. Actually building `result` is impossible — you would run out +of memory long before you finished. Ask yourself: do you really need the *whole* string, +or just *one* character of it? + +### Hint 2 + +Since you only need the `k`-th character, think about working **backwards**. If you knew +the length of `result` after every operation, could you trace where index `k` "came from" +as you undo each operation one at a time? This is a classic trick for problems where the +structure explodes in size but only a single position is queried. + +### Hint 3 + +Do two passes. **Pass 1 (forward):** compute the length of `result` after each operation +— this is cheap and never builds the string. **Pass 2 (backward):** start with the target +index `pos = k` and walk the operations from last to first, rewriting `pos` to be the index +it occupied in the *previous* `result`. Each operator has a simple inverse mapping on the +index: + +- A letter appended at the end occupies index `prevLen`; if `pos == prevLen`, that letter + *is* your answer. +- `'#'` (duplicate) doubled the string, so if `pos >= prevLen`, subtract `prevLen` to fold + it back into the first half. +- `'%'` (reverse) maps `pos` to `prevLen - 1 - pos`. +- `'*'` (remove) only chopped the tail, so a valid `pos` is unchanged. + +When `pos` lands exactly on an appended letter, you are done. + +## Approach + +The whole difficulty here is the size of `result`. Letters add one character, `'*'` removes +one, `'%'` is length-preserving, and `'#'` *doubles* the length. After enough `'#'` +operations the string is astronomically large, so simulation is out. But the question only +asks for a single index `k`, which lets us avoid materializing the string entirely. + +**Pass 1 — record lengths.** Walk `s` left to right and maintain a running length `cur`: + +- lowercase letter: `cur++` +- `'*'`: `cur--` if `cur > 0` (otherwise it stays 0) +- `'#'`: `cur *= 2` +- `'%'`: unchanged + +Store `lengths[i]` = length of `result` immediately after processing `s[i]`. The final +length is `cur`. If `k >= cur`, index `k` is out of bounds and the answer is `'.'`. + +**Pass 2 — trace the index backwards.** Set `pos = k` (a valid index into the final +`result`). Iterate `i` from `n-1` down to `0`. Let `prev` be the length *before* operation +`i`, which is `lengths[i-1]` (or `0` when `i == 0`). Rewrite `pos` according to the inverse +of `s[i]`: + +- **letter:** appending put that letter at index `prev`. If `pos == prev`, the letter + `s[i]` is exactly the character we are tracking — return it. Otherwise `pos < prev`, and + `pos` refers to a character already present before the append, so leave it unchanged. +- **`'*'`:** the operation removed the last character, so every surviving index is identical + to its index in the previous string. Leave `pos` unchanged. +- **`'#'`:** the previous string was concatenated with itself. Indices `[0, prev)` come from + the first copy; indices `[prev, 2*prev)` come from the second copy. If `pos >= prev`, + subtract `prev` to map it back onto the original copy. +- **`'%'`:** the string was reversed, so index `pos` came from index `prev - 1 - pos`. + +Because every valid `pos` either resolves to an appended letter or is carried back to the +start, by the time we finish the loop we will have returned a letter. (If we somehow fall +through, returning `'.'` is a safe guard.) + +**Why it works:** at every step `pos` is maintained as "the index in the current `result` +of the character we ultimately want." The inverse maps above are exact, so following them +to the beginning lands us on the precise letter that produced that position. We never store +more than the `lengths` array. + +**Walkthrough (Example 2):** `s = "cd%#*#"`, `k = 3`. Forward lengths are +`[1, 2, 2, 4, 3, 6]`, final length `6`, so `k = 3` is in bounds. Tracing backward with +`pos = 3`: + +- `i=5 '#'`, `prev=3`: `pos >= 3` → `pos = 0` +- `i=4 '*'`, `prev=4`: unchanged → `pos = 0` +- `i=3 '#'`, `prev=2`: `pos < 2` → `pos = 0` +- `i=2 '%'`, `prev=2`: `pos = 2 - 1 - 0 = 1` +- `i=1 'd'`, `prev=1`: `pos == 1` → return `'d'`. ✓ + +## Complexity Analysis + +Time Complexity: O(n), where `n = len(s)` — one forward pass to build lengths and one +backward pass to trace the index. +Space Complexity: O(n) for the `lengths` array. + +## Edge Cases + +- **`k` out of bounds:** if `k >= finalLength` (including the case where `result` ends up + empty), return `'.'`. Example 3 (`"z*#"`, `k = 0`) ends with an empty string. +- **`'*'` on an empty string:** removing from an empty `result` is a no-op; guard with + `if cur > 0` so the length never goes negative. +- **`'#'` on an empty string:** duplicating an empty string stays empty (`0 * 2 = 0`), which + the length recurrence handles naturally. +- **Large indices:** `k` can be up to `10^15` and the length up to `10^15`, so use 64-bit + integers (`int64`) for both the running length and `pos` to avoid overflow. +- **`'%'` mapping near the boundary:** the reverse map `prev - 1 - pos` must use `prev`, the + length *before* the reverse (which equals the length after, since reverse preserves + length), to stay within bounds. diff --git a/problems/3614-process-string-with-special-operations-ii/problem.md b/problems/3614-process-string-with-special-operations-ii/problem.md new file mode 100644 index 0000000..49081da --- /dev/null +++ b/problems/3614-process-string-with-special-operations-ii/problem.md @@ -0,0 +1,97 @@ +--- +number: "3614" +frontend_id: "3614" +title: "Process String with Special Operations II" +slug: "process-string-with-special-operations-ii" +difficulty: "Hard" +topics: + - "String" + - "Simulation" +acceptance_rate: 2686.3 +is_premium: false +created_at: "2026-06-17T05:23:09.270497+00:00" +fetched_at: "2026-06-17T05:23:09.270497+00:00" +link: "https://leetcode.com/problems/process-string-with-special-operations-ii/" +date: "2026-06-17" +--- + +# 3614. Process String with Special Operations II + +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 from `result`, if it exists. + * A `'#'` **duplicates** the current `result` and **appends** it to itself. + * A `'%'` **reverses** the current `result`. + + + +Return the `kth` 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 <= 105` + * `s` consists of only lowercase English letters and special characters `'*'`, `'#'`, and `'%'`. + * `0 <= k <= 1015` + * The length of `result` after processing `s` will not exceed `1015`. diff --git a/problems/3614-process-string-with-special-operations-ii/solution_daily_20260617.go b/problems/3614-process-string-with-special-operations-ii/solution_daily_20260617.go new file mode 100644 index 0000000..fbe7e1f --- /dev/null +++ b/problems/3614-process-string-with-special-operations-ii/solution_daily_20260617.go @@ -0,0 +1,68 @@ +package main + +// Process String with Special Operations II (LeetCode 3614) +// +// The final `result` can grow to 10^15 characters because of the '#' +// duplication operator, so we never build it. Instead we: +// +// 1. Forward pass: record the length of result after each operation. +// 2. Backward pass: starting from index k, undo each operation, rewriting +// the index to the position it occupied in the previous string. When the +// index lands on an appended letter, that letter is the answer. +// +// Runs in O(n) time and O(n) space, where n = len(s). +func processStr(s string, k int64) byte { + n := len(s) + + // Pass 1: length of result after processing each character. + lengths := make([]int64, n) + var cur int64 + for i := 0; i < n; i++ { + switch s[i] { + case '*': + if cur > 0 { + cur-- + } + case '#': + cur *= 2 + case '%': + // reverse: length unchanged + default: + cur++ // lowercase letter + } + lengths[i] = cur + } + + // Out of bounds (covers the empty-result case as well). + if k >= cur { + return '.' + } + + // Pass 2: trace index k back to the letter that produced it. + pos := k + for i := n - 1; i >= 0; i-- { + var prev int64 + if i > 0 { + prev = lengths[i-1] + } + switch s[i] { + case '*': + // Removed the tail; surviving indices are unchanged. + case '#': + // Duplicated; fold the second copy back onto the first. + if pos >= prev { + pos -= prev + } + case '%': + // Reversed; mirror the index. + pos = prev - 1 - pos + default: + // Letter appended at index prev. + if pos == prev { + return s[i] + } + } + } + + return '.' +} diff --git a/problems/3614-process-string-with-special-operations-ii/solution_daily_20260617_test.go b/problems/3614-process-string-with-special-operations-ii/solution_daily_20260617_test.go new file mode 100644 index 0000000..4554404 --- /dev/null +++ b/problems/3614-process-string-with-special-operations-ii/solution_daily_20260617_test.go @@ -0,0 +1,36 @@ +package main + +import "testing" + +func TestSolution(t *testing.T) { + tests := []struct { + name string + s string + k int64 + expected byte + }{ + // Examples from the problem statement. + {"example 1: remove after reverse, result \"ba\"", "a#b%*", 1, 'a'}, + {"example 2: duplicate/remove/duplicate, result \"dcddcd\"", "cd%#*#", 3, 'd'}, + {"example 3: result empty, k out of bounds", "z*#", 0, '.'}, + + // Additional edge cases. + {"edge case: single letter, k in bounds", "x", 0, 'x'}, + {"edge case: single letter, k out of bounds", "x", 1, '.'}, + {"edge case: remove on empty result stays empty", "*", 0, '.'}, + {"edge case: duplicate then index into second copy", "ab#", 2, 'a'}, + {"edge case: reverse a simple string", "abc%", 0, 'c'}, + {"edge case: large k via duplication", "a##", 3, 'a'}, + {"edge case: every char removed", "abc***", 0, '.'}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := processStr(tt.s, tt.k) + if result != tt.expected { + t.Errorf("processStr(%q, %d) = %q, want %q", + tt.s, tt.k, result, tt.expected) + } + }) + } +}