Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# 3612. Process String with Special Operations I

[LeetCode Link](https://leetcode.com/problems/process-string-with-special-operations-i/)

Difficulty: Medium
Topics: String, Simulation

Acceptance Rate: 68.3%

## Hints

### Hint 1

This problem is asking you to *replay* a sequence of instructions one by one and watch how a string evolves. When a problem describes a list of rules applied "from left to right," it is usually a **simulation** problem — you don't need a clever formula, you need to faithfully model what each character does.

### Hint 2

Think about which data structure makes each of the four operations cheap and easy to express:

- appending a letter,
- removing the last character,
- duplicating the whole string,
- reversing the whole string.

A growable sequence of bytes (a slice/`[]byte` or string builder) handles all of these naturally. The only operation you must be careful with is removal, since the string might be empty.

### Hint 3

The whole solution is a single pass over `s` with a `switch` on the current character. The key insight is that the constraints are tiny (`s.length <= 20`), so you can afford to literally perform each operation, even the ones that look expensive like duplicate and reverse. There is no hidden trick — correctness and careful edge-case handling (empty `result` on `*`) are what matter.

## Approach

Maintain a mutable buffer (`result`) that starts empty. Walk through `s` left to right and apply each character:

1. **Lowercase letter** — append it to `result`.
2. **`'*'`** — remove the last character of `result`, *but only if `result` is non-empty*. Removing from an empty buffer is a no-op.
3. **`'#'`** — duplicate `result` by appending a copy of it to itself (`result = result + result`).
4. **`'%'`** — reverse `result` in place.

After the loop, `result` is the answer.

Walking through Example 1, `s = "a#b%*"`:

| Step | Char | Operation | `result` |
|------|------|-----------|----------|
| 0 | `a` | append | `"a"` |
| 1 | `#` | duplicate | `"aa"` |
| 2 | `b` | append | `"aab"` |
| 3 | `%` | reverse | `"baa"` |
| 4 | `*` | remove last | `"ba"` |

Final answer: `"ba"`.

Using a `[]byte` buffer keeps every operation simple: append is `append(...)`, remove is a length truncation `result = result[:len(result)-1]`, duplicate is `append(result, result...)`, and reverse is a standard two-pointer swap. Each maps directly to the rule it implements, which keeps the code readable and easy to verify.

## Complexity Analysis

Let `n = len(s)`. The buffer can grow when `#` duplicates it. In the worst case the length doubles on a `#`, so the final length is bounded by `O(2^n)`, but with `n <= 20` this is a small, fixed bound.

Time Complexity: O(n + L) where `L` is the total work done across duplicate/reverse operations on the buffer. Each `#` and `%` touches at most the current buffer length, so the total is proportional to the sum of buffer sizes seen — bounded by the final length. With the given constraints this is effectively constant-bounded and very fast.

Space Complexity: O(L) for the buffer holding the result, where `L` is the final string length.

## Edge Cases

- **`'*'` on an empty `result`**: Must be a no-op. Forgetting the empty check causes an index-out-of-range panic (slicing `result[:len(result)-1]` when `len == 0`). See Example 2 where `z*` empties the buffer before further ops.
- **`'#'` on an empty `result`**: Duplicating `""` yields `""` — naturally handled, but worth confirming your code doesn't special-case it incorrectly.
- **`'%'` on an empty or single-character `result`**: Reversing should leave it unchanged; a correct two-pointer loop handles this automatically.
- **A buffer that becomes empty mid-processing** then receives more letters: ensure appends still work after the buffer was emptied.
- **Strings consisting only of special characters** (e.g. `"*%#"`): the result stays empty throughout.
74 changes: 74 additions & 0 deletions problems/3612-process-string-with-special-operations-i/problem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
number: "3612"
frontend_id: "3612"
title: "Process String with Special Operations I"
slug: "process-string-with-special-operations-i"
difficulty: "Medium"
topics:
- "String"
- "Simulation"
acceptance_rate: 6833.9
is_premium: false
created_at: "2026-06-16T05:43:49.433883+00:00"
fetched_at: "2026-06-16T05:43:49.433883+00:00"
link: "https://leetcode.com/problems/process-string-with-special-operations-i/"
date: "2026-06-16"
---

# 3612. Process String with Special Operations I

You are given a string `s` consisting of lowercase English letters and the special characters: `*`, `#`, and `%`.

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 final string `result` after processing all characters in `s`.



**Example 1:**

**Input:** s = "a#b%*"

**Output:** "ba"

**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"`

Thus, the final `result` is `"ba"`.

**Example 2:**

**Input:** s = "z*#"

**Output:** ""

**Explanation:**

`i` | `s[i]` | Operation | Current `result`
---|---|---|---
0 | `'z'` | Append `'z'` | `"z"`
1 | `'*'` | Remove the last character | `""`
2 | `'#'` | Duplicate the string | `""`

Thus, the final `result` is `""`.



**Constraints:**

* `1 <= s.length <= 20`
* `s` consists of only lowercase English letters and special characters `*`, `#`, and `%`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package main

// 3612. Process String with Special Operations I
//
// Approach: straightforward left-to-right simulation. We maintain a []byte
// buffer `result` and apply each character of s:
// - lowercase letter: append it
// - '*': remove the last character (no-op if the buffer is empty)
// - '#': duplicate the buffer (append a copy of it to itself)
// - '%': reverse the buffer in place
// The constraints are tiny (len(s) <= 20), so performing each operation
// literally is both simple and fast.
func processStr(s string) string {
result := make([]byte, 0)

for i := 0; i < len(s); i++ {
switch c := s[i]; c {
case '*':
if len(result) > 0 {
result = result[:len(result)-1]
}
case '#':
result = append(result, result...)
case '%':
for l, r := 0, len(result)-1; l < r; l, r = l+1, r-1 {
result[l], result[r] = result[r], result[l]
}
default:
result = append(result, c)
}
}

return string(result)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package main

import "testing"

func TestProcessStr(t *testing.T) {
tests := []struct {
name string
s string
expected string
}{
{"example 1: a#b%* -> ba", "a#b%*", "ba"},
{"example 2: z*# empties then duplicates empty", "z*#", ""},
{"edge case: empty result on leading remove", "*abc", "abc"},
{"edge case: only special chars stays empty", "*%#", ""},
{"edge case: single letter", "q", "q"},
{"edge case: duplicate then reverse palindrome-ish", "ab#", "abab"},
{"edge case: reverse of empty is empty", "%", ""},
{"edge case: remove all then append", "ab**c", "c"},
{"edge case: reverse single char unchanged", "a%", "a"},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := processStr(tt.s); got != tt.expected {
t.Errorf("processStr(%q) = %q, want %q", tt.s, got, tt.expected)
}
})
}
}