Skip to content
Laviyx Learn

Try

DSA course menu

Strings in DSA: Immutability, Building Strings and Classic Problems

Strings for data structures and algorithms in Python: why strings are immutable, the O(n²) concatenation trap, using join, character counting, palindromes and anagrams.

Beginner6 min readUpdated
On this page

A string is an array of characters, so indexing s[i] is O(1) and scanning it is O(n). The big difference from a list: in Python, strings are immutable. You can never change a string; every "change" builds a new one.

Python
s = "code"
try:
    s[0] = "m"
except TypeError as e:
    print("Error:", e)

t = "m" + s[1:]       # build a new string instead
print(s, t)
Output
Error: 'str' object does not support item assignment
code mode

The concatenation trap#

Because each + creates a new string and copies both parts, building a long string with += in a loop copies the growing result again and again: O(n²) characters copied in total.

This counts the characters each approach copies:

Python
def copies_with_plus(parts):
    result, copied = "", 0
    for p in parts:
        copied += len(result) + len(p)   # a new string of this length is built
        result = result + p
    return copied

def copies_with_join(parts):
    return sum(len(p) for p in parts)    # join copies each part once

parts = ["ab"] * 1000
print(copies_with_plus(parts))
print(copies_with_join(parts))
Output
1001000
2000

Collect the pieces in a list and call "".join(pieces) once. That copies each character once: O(n).

Counting characters#

Many string problems become easy once you count characters with a dictionary or collections.Counter:

Python
from collections import Counter

word = "mississippi"
counts = Counter(word)
print(counts)
print(counts.most_common(2))
Output
Counter({'i': 4, 's': 4, 'p': 2, 'm': 1})
[('i', 4), ('s', 4)]

Anagrams#

Two words are anagrams if they use the same letters the same number of times ("listen" and "silent"). Comparing counts is O(n); sorting both is O(n log n):

Python
from collections import Counter

def is_anagram(a, b):
    return Counter(a) == Counter(b)

print(is_anagram("listen", "silent"))
print(is_anagram("rat", "car"))
print(sorted("listen") == sorted("silent"))
Output
True
False
True

Palindromes#

A palindrome reads the same forwards and backwards. Checking from both ends towards the middle needs no copy:

Python
def is_palindrome(s):
    left, right = 0, len(s) - 1
    while left < right:
        if s[left] != s[right]:
            return False
        left += 1
        right -= 1
    return True

for word in ["racecar", "level", "python"]:
    print(word, is_palindrome(word))
Output
racecar True
level True
python False

s == s[::-1] is shorter and also O(n), but it builds a reversed copy (O(n) extra space). The two-index version uses O(1) space; it is the two pointers pattern.

Common mistakes#

Practice#

Practice exercise

Write first_unique(s) that returns the index of the first character that appears exactly once in s, or -1 if there is none. It should be O(n).

Show hint

Two passes: first count every character, then walk the string again and return the first index whose character has a count of 1.

Show solution
Python
from collections import Counter

def first_unique(s):
    counts = Counter(s)
    for i, ch in enumerate(s):
        if counts[ch] == 1:
            return i
    return -1

print(first_unique("leetcode"))
print(first_unique("loveleetcode"))
print(first_unique("aabb"))
Output
0
2
-1

Both passes are O(n), and the counter holds at most one entry per distinct character.

Key takeaway#

Next: the structure behind fast lookups, Hash Tables, Sets and Maps.

Finished this lesson?