DSA Guide
Strings

Strings

Character counting, two pointers and normalisation — the recurring shapes of string problems

Strings are arrays of characters, so most array techniques carry over directly. What makes string problems distinct is that the content often has structure worth exploiting — letters have counts, words have boundaries, and equivalent strings can be reduced to a shared canonical form.

What a String Actually Is

A string is an array whose slots hold characters. Everything you know about arrays applies — index access is O(1), insertion in the middle is O(n).

word = "hello"
0
h
1
e
2
l
word[2]
3
l
4
o
Same layout as an array of numbers. The characters are stored as numbers too — 'h' is 104.

But one property changes how you write the code, in both languages used here.

Strings are immutable

You cannot change a character in place. word[0] = "H" fails in Python and silently does nothing in JavaScript.

Every "modification" builds a whole new string, copying every character:

s = "hello"
s = s + "!"        ← does NOT append.
                     Allocates a new 6-character string,
                     copies all of "hello" into it, adds "!"

The performance trap this creates

Building a string inside a loop with += copies everything built so far, every single time.

Building an n-character string with += :
  step 1 copies 1 char, step 2 copies 2, step 3 copies 3 …
  total ≈ n²/2 character copies      →  O(n²)

At 100,000 characters that is five billion copies. The fix is the same idea in both languages: collect the pieces in a list, then join once at the end.

Slow — O(n²)Fast — O(n)
Pythons += c in a loopparts.append(c)"".join(parts)
TypeScripts += c in a loopparts.push(c)parts.join("")

A list is mutable, so appending to it is genuinely cheap. Only the final join allocates.

Characters are numbers

Each character has a numeric code. That is what makes counting tricks work:

'a' = 97,  'b' = 98,  …  'z' = 122

ord(c) - ord('a')   →  0 for 'a', 1 for 'b', … 25 for 'z'

So a 26-slot array can count letters, using the letter itself to pick the slot — no hash map needed, and no hashing cost. This is the basis of most anagram and frequency problems below.

Two habits worth forming early

Say which alphabet you assume. "Lowercase English letters only" makes a 26-slot array correct. Without that guarantee, use a hash map — real text contains accents, emoji and capitals.

Case and whitespace are decisions, not details. Is "Hello" equal to "hello"? Does a trailing space matter? Problems that hinge on this rarely say so directly, and getting it wrong looks like a logic bug.

The Patterns

1. Counting — reduce a string to a multiset

Once you only care about which characters and how many, order stops mattering and the problem usually becomes easy.

Sets lose multiplicity

The most common bug in this family is using a set where counts are needed. "step" has an s; it does not have two. If the problem cares how many times something appears, count it.

2. Two pointers — walk from both ends or at different rates

3. Normalisation — make equivalent strings identical

Rather than asking "are these equivalent?", transform both into a canonical form and ask "are these equal?".

4. Simulation — follow the specification exactly

Some problems have no clever insight; the challenge is holding a fiddly rule set straight.

Language Gotchas Worth Memorising

SituationPythonTypeScript
Split on whitespace, dropping emptiess.split()s.trim().split(/\s+/)
Split on a single space (keeps empties)s.split(" ")s.split(" ")
Reverse a strings[::-1][...s].reverse().join("")
Integer divisiona // bMath.floor(a / b)
Truncate toward zeroint(a / b)Math.trunc(a / b)
Count charactersCounter(s)a Map built by hand
Missing key returns 0Counter[k]map.get(k) ?? 0

Build strings with join, not +=

Repeated result += char inside a loop allocates a new string each iteration — O(n²) overall. Collect pieces in a list and join once.

All Problems

ProblemDifficultyPattern
Valid PalindromeEasyTwo Pointers
Valid AnagramEasyCounting
Longest Common PrefixEasyVertical Scan
Length of Last WordEasyReverse Scan
Roman to IntegerEasyLocal Comparison
Is SubsequenceEasyTwo Pointers
Isomorphic StringsEasyTwo Hash Maps
Longest PalindromeEasyCounting
First Occurrence in a StringEasyString Matching
Keyboard RowEasyHash Map
Shortest Completing WordEasyCounting
Unique Email AddressesEasyNormalisation
Palindrome NumberEasyDigit Math
Integer to RomanMediumGreedy
Reverse Words in a StringMediumTwo Pointers
Zigzag ConversionMediumSimulation
Text JustificationHardSimulation

On this page