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.
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.
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)Error: 'str' object does not support item assignment
code modeThe 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:
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))1001000
2000Collect 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:
from collections import Counter
word = "mississippi"
counts = Counter(word)
print(counts)
print(counts.most_common(2))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):
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"))True
False
TruePalindromes#
A palindrome reads the same forwards and backwards. Checking from both ends towards the middle needs no copy:
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))racecar True
level True
python Falses == 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
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"))0
2
-1Both 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?
Related lessons
Recommended DSA resources
- Cracking the Coding Interview by Gayle Laakmann McDowell
189 practice questions with solutions, for coding interview preparation.
Some links are affiliate links. If you buy through them, Laviyx Learn may earn a commission at no extra cost to you. As an Amazon Associate, we earn from qualifying purchases.