String Manipulation Tricks for Interviews
Essential string manipulation tricks — reverse words, reverse vowels, string rotation check, repeated substring pattern, multiply strings, and add binary.
What you'll learn
- ✓Reverse words in a string without extra libraries
- ✓Reverse only the vowels in a string using two pointers
- ✓Check if one string is a rotation of another in O(n)
- ✓Detect repeated substring patterns with the double-string trick
- ✓Multiply two large numbers represented as strings
- ✓Add two binary strings and handle carries correctly
Prerequisites
- •Python string basics — see Strings Intro
- •Two-pointer technique — see Two Pointers
- •Big O notation — see Big-O Explained
String manipulation problems are interview staples. They test whether you can think carefully about indexing, edge cases, and in-place operations. The problems in this post are not algorithmically deep — they rely on clever observations and clean implementation. That is exactly what makes them dangerous: small bugs hide in string code, and interviewers know it.
Problem 1: Reverse Words in a String
LeetCode 151. Given a string s, reverse the order of words. A word is a sequence of non-space characters. The result should have single spaces between words and no leading or trailing spaces.
The easy way
Python makes this trivial with split() and join():
def reverse_words(s: str) -> str:
words = s.split() # splits on any whitespace, removes empties
return " ".join(words[::-1])
Time: O(n). Space: O(n) for the word list.
The interview way — in-place simulation
Interviewers often want you to do it without split. The classic approach for mutable strings (like char[] in Java or C):
- Reverse the entire string.
- Reverse each word individually.
- Clean up extra spaces.
In Python, we simulate with a list:
def reverse_words_manual(s: str) -> str:
# Step 1: Convert to list and strip/collapse spaces
chars = []
i = 0
n = len(s)
while i {'<'} n:
if s[i] != ' ':
if chars and chars[-1] != ' ':
pass
elif chars:
chars.append(' ')
while i {'<'} n and s[i] != ' ':
chars.append(s[i])
i += 1
else:
i += 1
# Step 2: Reverse entire list
chars.reverse()
# Step 3: Reverse each word
start = 0
for end in range(len(chars) + 1):
if end == len(chars) or chars[end] == ' ':
# Reverse chars[start:end]
lo, hi = start, end - 1
while lo {'<'} hi:
chars[lo], chars[hi] = chars[hi], chars[lo]
lo += 1
hi -= 1
start = end + 1
return "".join(chars)
Walkthrough
For s = " the sky is blue ":
- After space cleanup:
['t','h','e',' ','s','k','y',' ','i','s',' ','b','l','u','e'] - After full reverse:
['e','u','l','b',' ','s','i',' ','y','k','s',' ','e','h','t'] - After reversing each word:
"blue is sky the".
Time: O(n). Space: O(n) because Python strings are immutable so we need a list.
Problem 2: Reverse Vowels of a String
LeetCode 345. Given a string s, reverse only the vowels.
Approach
Use two pointers, one from each end. Skip consonants. When both point to vowels, swap.
def reverse_vowels(s: str) -> str:
vowels = set('aeiouAEIOU')
chars = list(s)
left, right = 0, len(chars) - 1
while left {'<'} right:
while left {'<'} right and chars[left] not in vowels:
left += 1
while left {'<'} right and chars[right] not in vowels:
right -= 1
if left {'<'} right:
chars[left], chars[right] = chars[right], chars[left]
left += 1
right -= 1
return "".join(chars)
Example
For s = "leetcode":
- Vowels in order:
e, e, o, eat indices 1, 2, 5, 7. - After reversing vowels:
"leotcede".
Wait — let us trace carefully:
- left=1 (
e), right=7 (e) — swap (no change). left=2, right=6. - left=2 (
e), right=5 (o) — but right=6 isd, skip to right=5 (o). Swap: positions 2 and 5."leotcede". - left=3, right=4:
tandcare consonants. Pointers cross. Done. - Answer:
"leotcede".
Time: O(n). Space: O(n) for the character list (O(1) if mutable).
Problem 3: String Rotation Check
LeetCode 796. Given strings s1 and s2, return True if s2 is a rotation of s1.
The elegant trick
If s2 is a rotation of s1, then s2 must be a substring of s1 + s1. For example, "waterbottle" rotated to "erbottlewat" — and "erbottlewat" appears in "waterbottlewaterbottle".
def rotate_string(s1: str, s2: str) -> bool:
if len(s1) != len(s2):
return False
return s2 in (s1 + s1)
Why it works
A rotation splits s1 at some index i: s1 = A + B, and the rotation is B + A. In s1 + s1 = A + B + A + B, the substring B + A always appears.
Time: O(n) — Python’s in uses an efficient substring search.
Space: O(n) for the concatenated string.
Manual approach without concatenation
If the interviewer forbids the trick, you can check each rotation:
def rotate_string_manual(s1: str, s2: str) -> bool:
if len(s1) != len(s2):
return False
n = len(s1)
for i in range(n):
# Check if rotating s1 by i positions gives s2
match = True
for j in range(n):
if s1[(i + j) % n] != s2[j]:
match = False
break
if match:
return True
return False
Time: O(n^2) worst case. Space: O(1).
Problem 4: Repeated Substring Pattern
LeetCode 459. Given a string s, check if it can be constructed by taking a substring of it and appending multiple copies of the substring.
Approach 1: The double-string trick
Similar to rotation: if s has a repeating pattern, then removing the first and last character of s + s and checking if s still appears will confirm it.
def repeated_substring_pattern(s: str) -> bool:
doubled = (s + s)[1:-1] # remove first and last char
return s in doubled
Why it works
If s = "abcabc", then s + s = "abcabcabcabc". After removing first and last chars: "bcabcabcabc". The original s still appears at index 2. If s has no repeating unit, removing the boundary characters breaks all occurrences.
Approach 2: Divisor check
A repeating substring must have length d where d divides len(s) and d {'<'} len(s):
def repeated_substring_pattern_divisor(s: str) -> bool:
n = len(s)
for d in range(1, n // 2 + 1):
if n % d == 0:
pattern = s[:d]
if pattern * (n // d) == s:
return True
return False
Example
For s = "abab":
d = 1:"a" * 4 = "aaaa"— no.d = 2:"ab" * 2 = "abab"— yes.
Time: O(n * sqrt(n)) due to divisor checking. The double-string approach is O(n). Space: O(n).
Problem 5: Multiply Strings
LeetCode 43. Given two non-negative integers represented as strings, return their product as a string. You cannot convert to integers directly.
Approach
Simulate grade-school multiplication. For two numbers of length m and n, the product has at most m + n digits.
def multiply(num1: str, num2: str) -> str:
if num1 == "0" or num2 == "0":
return "0"
m, n = len(num1), len(num2)
result = [0] * (m + n)
# Multiply digit by digit, right to left
for i in range(m - 1, -1, -1):
for j in range(n - 1, -1, -1):
d1 = ord(num1[i]) - ord('0')
d2 = ord(num2[j]) - ord('0')
product = d1 * d2
# Position in result array
p1, p2 = i + j, i + j + 1
total = product + result[p2]
result[p2] = total % 10
result[p1] += total // 10
# Convert to string, skip leading zeros
result_str = ""
for digit in result:
if not (result_str == "" and digit == 0):
result_str += str(digit)
return result_str if result_str else "0"
Walkthrough
For num1 = "123", num2 = "45":
3 * 5 = 15— put 5 at position 4, carry 1 to position 3.2 * 5 = 10 + 1 = 11— put 1 at position 3, carry 1 to position 2.1 * 5 = 5 + 1 = 6— put 6 at position 2.3 * 4 = 12— add to positions 3 and 2.2 * 4 = 8— add to positions 2 and 1.1 * 4 = 4— add to positions 1 and 0.- Result array:
[0, 5, 5, 3, 5]→"5535".
Time: O(m * n). Space: O(m + n).
Problem 6: Add Binary
LeetCode 67. Given two binary strings a and b, return their sum as a binary string.
Approach
Process from right to left, adding digits and tracking the carry. This is the same algorithm as adding decimal numbers by hand.
def add_binary(a: str, b: str) -> str:
result = []
carry = 0
i, j = len(a) - 1, len(b) - 1
while i >= 0 or j >= 0 or carry:
total = carry
if i >= 0:
total += int(a[i])
i -= 1
if j >= 0:
total += int(b[j])
j -= 1
result.append(str(total % 2))
carry = total // 2
return "".join(reversed(result))
Example
For a = "1010", b = "1011":
1 0 1 0
+ 1 0 1 1
---------
1 0 1 0 1
- Position 0:
0 + 1 = 1, carry 0. - Position 1:
1 + 1 = 2, write 0, carry 1. - Position 2:
0 + 0 + 1 = 1, carry 0. - Position 3:
1 + 1 = 2, write 0, carry 1. - Carry remains: write 1.
- Answer:
"10101".
Time: O(max(m, n)). Space: O(max(m, n)) for the result.
Generalized: Add strings of any base
The same pattern works for decimal addition (LeetCode 415) — just change % 2 to % 10 and // 2 to // 10:
def add_strings(num1: str, num2: str) -> str:
result = []
carry = 0
i, j = len(num1) - 1, len(num2) - 1
while i >= 0 or j >= 0 or carry:
total = carry
if i >= 0:
total += ord(num1[i]) - ord('0')
i -= 1
if j >= 0:
total += ord(num2[j]) - ord('0')
j -= 1
result.append(str(total % 10))
carry = total // 10
return "".join(reversed(result))
Complexity summary
| Problem | Time | Space | Key Trick |
|---|---|---|---|
| Reverse words | O(n) | O(n) | Split + reverse, or reverse-all then reverse-each |
| Reverse vowels | O(n) | O(n) | Two pointers with vowel set |
| String rotation | O(n) | O(n) | Check if s2 in s1+s1 |
| Repeated pattern | O(n) | O(n) | Double-string with boundary removal |
| Multiply strings | O(m*n) | O(m+n) | Grade-school multiplication in array |
| Add binary | O(max(m,n)) | O(max(m,n)) | Right-to-left with carry |
Common interview tips
1. Always clarify input constraints
- Can the string be empty?
- Does it contain only ASCII or Unicode?
- Are there leading/trailing spaces?
- Is the input always valid (e.g., valid binary for add binary)?
2. Use ord() instead of int() for character-to-digit
When you need digit values from string characters, ord(ch) - ord('0') is clearer about what you are doing and avoids implicit type conversions.
3. Build results in a list, join at the end
Never concatenate strings in a loop with +=. It creates a new string every time, making the total cost O(n^2). Always use a list and "".join() at the end.
# Bad: O(n^2)
result = ""
for ch in chars:
result += ch
# Good: O(n)
parts = []
for ch in chars:
parts.append(ch)
result = "".join(parts)
4. Edge cases that catch people
- Empty strings.
- Single-character strings.
- Strings with all spaces (for reverse words).
- Leading zeros (for multiply and add).
- Strings of different lengths (for add binary).
Practice problems
- LeetCode 151 — Reverse Words in a String
- LeetCode 345 — Reverse Vowels of a String
- LeetCode 796 — Rotate String
- LeetCode 459 — Repeated Substring Pattern
- LeetCode 43 — Multiply Strings
- LeetCode 67 — Add Binary
- LeetCode 415 — Add Strings
- LeetCode 6 — Zigzag Conversion
- LeetCode 8 — String to Integer (atoi)
- LeetCode 28 — Find the Index of the First Occurrence in a String
Wrapping up
String manipulation problems reward careful implementation more than algorithmic brilliance. The key patterns — two pointers, right-to-left processing with carry, the double-string trick — are simple but powerful. Practice each one until the code flows without hesitation. In an interview, a clean solution to a “simple” string problem impresses more than a buggy attempt at something complex.
Related articles
- DSA Minimum Remove to Make Valid Parentheses — Stack Solution
Solve LeetCode 1249 Minimum Remove to Make Valid Parentheses using a stack. Two-pass and one-pass approaches with Python code and traces.
- DSA Simplify Unix Path Using a Stack — LeetCode 71 Solution
Solve Simplify Path LeetCode 71 with a stack. Handle ., .., multiple slashes, and edge cases. Python solution with step-by-step trace.
- DSA Anagram Problems: Patterns and Solutions
Master anagram problems — valid anagram checks, grouping anagrams by sorted and frequency keys, finding all anagrams in a string with sliding windows, and the minimum window substring problem.
- DSA String Encoding and Decoding Patterns
Master string encoding and decoding — delimiter-based encode/decode, run-length encoding, decoding nested bracket strings with stacks, string compression, and serialization patterns.