Skip to content
String Manipulation: Complete Guide with Examples

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

O(N^2)
into single passes that cost
O(N)
, and interviewers expect you to reach for them without hesitation.

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.

Every technique on this page is available as a review deck in the app. Add String Manipulation to your review queue and the moves will stick well past interview day.

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

O(N)
time with
O(1)
space because no copy of the string is ever made.

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 costs
    O(N^2)
    in allocation. Plan the build step before you write it.

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.

TechniqueTimeSpaceExplanation
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

O(N^2)
because each of the N centers can expand across the whole string. Rabin-Karp is linear on average but degrades when hash collisions force repeated verification. KMP trades
O(M)
preprocessing space for the guarantee that no character of the text is compared more than once.

Memorizing complexity tradeoffs is half the battle. The other half is remembering them under pressure. Review this pattern with spaced repetition so the analysis becomes automatic by interview day.

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

O(N^2)
. To catch this during practice, count the allocations instead of trusting the loop looks simple. In Java use a 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.

These mistakes are expensive because they look correct until they fail on a hidden test case. Our review decks flag exactly these edge cases and drill them until they are reflex. Add this pattern to your queue .

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.

Reading about a pattern once is not enough to own it in an interview. Practice this pattern with spaced repetition and turn recognition into recall.