Strings in Python

Strings show up constantly in DSA: palindromes, anagrams, parsing, and pattern matching. In Python a string behaves like an immutable sequence of characters, so much of what you learned about lists applies here too.

Strings are immutable#

You can read a character by index, but you cannot change one in place:

1word = "algorithm"
2print(word[0])    # 'a'
3print(word[-1])   # 'm'
4# word[0] = "A"   # TypeError: strings cannot be modified

To “change” a string, build a new one. This immutability matters for performance: repeatedly adding to a string in a loop creates many copies.

Slicing works just like lists#

1s = "abcdef"
2print(s[1:4])    # 'bcd'
3print(s[:3])     # 'abc'
4print(s[::-1])   # 'fedcba'  (reversed, a one-line palindrome helper)

Reversing with s[::-1] gives you an instant palindrome check:

1def is_palindrome(s):
2    return s == s[::-1]
3
4print(is_palindrome("racecar"))  # True
5print(is_palindrome("hello"))    # False

Useful string methods#

1text = "  Hello World  "
2print(text.strip())        # 'Hello World'  (trim whitespace)
3print(text.lower())        # '  hello world  '
4print("a,b,c".split(","))  # ['a', 'b', 'c']
5print("-".join(["a", "b"])) # 'a-b'
6print("hello".find("ll"))  # 2  (index, or -1 if absent)
7print("hello".replace("l", "L"))  # 'heLLo'

split and join are a common pair: split a sentence into words, process them, then join them back.

Building strings efficiently#

Because strings are immutable, concatenating inside a loop with + is slow (it copies each time). Collect pieces in a list and join once:

 1# Slow for large loops:
 2result = ""
 3for ch in "abc":
 4    result += ch
 5
 6# Preferred:
 7pieces = []
 8for ch in "abc":
 9    pieces.append(ch)
10result = "".join(pieces)
11print(result)   # 'abc'

The join approach is O(n) overall instead of O(n squared). See the Big-O cheat sheet for why repeated copying adds up.

Characters and codes#

Sometimes you need the numeric code of a character, for example to map letters to array positions:

1print(ord("a"))   # 97
2print(chr(97))    # 'a'
3print(ord("c") - ord("a"))  # 2  (offset of 'c' from 'a')

That offset trick is common when counting letters with a fixed-size list of 26 slots.

An anagram example#

1from collections import Counter
2
3def is_anagram(a, b):
4    return Counter(a) == Counter(b)
5
6print(is_anagram("listen", "silent"))  # True

Next: functions and recursion, where you start structuring your own algorithms.