String Manipulation: Complete Guide with Examples
String manipulation shows up in some form in most coding interviews. The problems look different each time, but the underlying moves repeat. You compare characters with pointers. You count character frequencies. You match a pattern against text without rescanning what you already checked. Those moves turn nested-loop solutions that cost
Definition: string manipulation covers the techniques for processing text efficiently. The common thread is avoiding brute-force character comparisons wherever the structure of the problem allows a shortcut.
Real-World Analogy
Imagine proofreading a long document for a specific phrase. You do not reread the whole document for every candidate phrase. You scan, you skip ahead, and you only slow down when a few characters line up.
The same habits apply to strings. To check whether a word reads the same backward, you compare the first and last letters and move inward, instead of writing the word out backward and comparing two full copies. To check whether two sentences use the same words, you count how many times each word appears rather than trying every reordering. To find a phrase, you compare only the positions that can still match, not every position against every position. Each of those habits is a technique in this pattern.
Visual Explanation
The two-pointer palindrome check is the simplest place to see the core idea. Two pointers start at opposite ends of the string and compare characters as they close in.
graph TD
I["left = 0, right = n - 1"] --> C{"s[left] == s[right]?"}
C -->|"Yes"| M["left++, right--"]
M --> D{"left >= right?"}
D -->|"No"| C
D -->|"Yes"| P["Palindrome"]
C -->|"No"| N["Not a palindrome"]
Each comparison settles one pair of characters. The pointers move inward because the edges are settled after each match. When they cross, every pair has matched, and the string is a palindrome. The whole check costs
Pattern matching uses a different shortcut. Instead of comparing every position, you compare hashes of fixed-length windows against the hash of the pattern.
graph LR
W1["Window: abc"] --> H1["Hash H1"]
H1 --> SL["Slide window one character"]
SL --> W2["Window: bcd"]
W2 --> H2["Hash H2"]
H2 --> CM{"H2 equals pattern hash?"}
CM -->|"No"| SL
CM -->|"Yes"| V["Compare characters directly"]
The point of hashing is that the next window hash comes from the previous one in constant time instead of a full recomputation. A hash match does not guarantee a real match, so the last step always verifies the actual characters. This is the rolling hash idea behind Rabin-Karp.
When to Use This Pattern
These techniques are the right tool when the problem has a few specific characteristics.
- The problem asks whether a string reads the same forward and backward, or whether one string is a rearrangement of another. Pointer comparisons and frequency counts handle both without sorting or copying.
- You need to find a substring inside a larger string and the brute-force scan is too slow. KMP and Rabin-Karp both avoid rescanning characters that already matched.
- The problem compares strings up to a transformation, like case, punctuation, or character order. Normalize once, then apply a single comparison or count pass.
- The solution must inspect every character once and keep constant extra space. Two-pointer scans fit that requirement exactly.
- You are building a new string from an existing one in a loop. The algorithmic part is simple, but repeated concatenation on immutable strings quietly costsin allocation. Plan the build step before you write it.O(N^2)
Complexity Analysis
The techniques in this pattern do not share one cost profile, but each one beats the brute-force version of its problem by a known margin.
| Technique | Time | Space | Explanation |
|---|---|---|---|
| Two-pointer comparison | O(N) | O(1) | Each character visited at most once |
| Expand around center | O(N^2) | O(1) | N centers, each expands up to N |
| Frequency counting | O(N) | O(K) | K is the alphabet size |
| Rolling hash (Rabin-Karp) | O(N + M) | O(1) | Average case; worst case O(N * M) on heavy collisions |
| KMP prefix function | O(N + M) | O(M) | Pattern preprocessed once into an M-length table |
The time is linear for pointer and counting techniques because every character is touched a constant number of times. Expand around center is the exception at
Common Mistakes
These errors all share one root cause. The cause is assuming strings behave like simple arrays without checking the details of the language or the problem statement.
Building strings with repeated concatenation. The most common performance bug in string problems is result += s[i] inside a loop. Strings are immutable in Python, Java, and JavaScript, so every append allocates a new string and copies everything written so far, which makes the loop cost
StringBuilder, and in other languages collect into a list and join once at the end.Getting substring boundaries wrong. The second argument to a substring call is end-exclusive in Python and Java, but the slice conventions differ in other languages, and mixing them up fails on strings of length two. To catch this during practice, trace a two-character string on paper and confirm which indices the call actually keeps.
Skipping normalization in comparisons. Palindrome and anagram problems usually say to ignore case and non-alphanumeric characters. Solutions that compare raw characters fail hidden tests with uppercase letters or punctuation. To catch this during practice, read the problem statement for the exact normalization rule before writing the comparison, and test with a mixed-case input.
Trusting hash equality without verification. In Rabin-Karp, two different substrings can produce the same hash. The collision is rare, but the algorithm must verify a hash match by comparing the actual characters. To catch this during practice, remove the verification step deliberately and run a long text with many similar substrings to see the wrong answer appear.
Related Patterns
- Two Pointers . Palindrome checking is a direct application of the two-pointer technique. The two-pointers page covers the general case for sorted arrays and pair-finding problems.
- Sliding Window . Substring problems with constraints use window pointers and frequency maps. The sliding-window page owns the window mechanics that string problems frequently combine with.
- Hash Table . Frequency counting is a hash table application. Anagram detection and grouping problems appear on the hash-table page.
Next Steps
Once the concept is clear, the next step is making the code automatic. Check out the code templates for memorizable implementations in 6 languages, then work through the practice problems to apply the pattern to real interview questions.