Lesson 03 · 10 min read · Python for placements

Strings, and why they never change

Immutability and what it costs, slicing, f-strings, the join idiom, and the string operations coding rounds keep asking for, with complexity for each.

Strings are the most-used type in any coding round, and the one where a wrong instinct costs you the time limit rather than the answer.

A string cannot be modified

s = "hello"
s[0] = "H"      # TypeError: 'str' object does not support item assignment

Strings are immutable. Every operation that looks like it changes a string actually builds a new one:

s = "hello"
t = s.upper()
print(s, t)     # hello HELLO — s is untouched

s.upper() did not modify s; it returned a new string and s still points at the old one. This catches people out constantly:

name = "  raja  "
name.strip()            # returns "raja" and throws it away
print(repr(name))       # '  raja  '

name = name.strip()     # reassign — this is the fix
print(repr(name))       # 'raja'

Every string method returns a new string. If you are not assigning the result, you are doing nothing.

What immutability costs you

Building a string in a loop with += is quadratic:

# O(n²) — each += copies the whole string built so far
out = ""
for word in words:
    out += word

# O(n) — build a list, join once
parts = []
for word in words:
    parts.append(word)
out = "".join(parts)

For ten words the difference is invisible. For a hundred thousand it is the difference between passing and timing out. join is the idiom; use it.

join also reads better than it looks at first. The separator is the string you call it on:

print(", ".join(["a", "b", "c"]))     # a, b, c
print("".join(["a", "b", "c"]))       # abc
print("\n".join(lines))               # one per line

One rule: join needs strings. A list of integers raises TypeError, so convert first:

print(",".join(str(n) for n in [1, 2, 3]))   # 1,2,3

Slicing

s[start:stop:step]start included, stop excluded, and every index optional.

s = "placement"
print(s[0])        # p
print(s[-1])       # t     negative counts from the end
print(s[0:5])      # place
print(s[:5])       # place
print(s[5:])       # ment
print(s[::2])      # pae et
print(s[::-1])     # tnemecalp   — reverse
print(s[:])        # placement   — a full copy

s[::-1] is how you reverse a string in Python, and it is worth memorising because reversal appears in palindrome questions constantly:

def is_palindrome(s):
    clean = "".join(c.lower() for c in s if c.isalnum())
    return clean == clean[::-1]

print(is_palindrome("A man, a plan, a canal: Panama"))   # True

Slicing never raises IndexError, even when the range is nonsense — unlike single indexing:

s = "abc"
print(s[10:20])    # '' — empty, no error
print(s[10])       # IndexError

That asymmetry is deliberate and occasionally useful, but it also means a slicing bug fails silently with an empty result rather than loudly with an exception.

f-strings

Since Python 3.6, formatting is done with an f prefix and expressions in braces:

name, score = "Raja", 87.456

print(f"{name} scored {score}")            # Raja scored 87.456
print(f"{name} scored {score:.1f}")        # Raja scored 87.5
print(f"{score:>10.2f}")                   #      87.46   right-aligned in 10 chars
print(f"{1234567:,}")                      # 1,234,567
print(f"{0.8734:.1%}")                     # 87.3%
print(f"{name=}, {score=}")                # name='Raja', score=87.456

That last one — f"{x=}" — prints the expression and its value, and is the fastest debugging tool in the language.

You can put any expression inside the braces:

items = [1, 2, 3]
print(f"{len(items)} items, total {sum(items)}")   # 3 items, total 6

The older styles still work and you will meet them in existing code:

"%s scored %d" % ("Raja", 87)         # C-style, oldest
"{} scored {}".format("Raja", 87)     # .format(), Python 2.6+
f"{name} scored {score}"              # f-string, prefer this

The methods worth knowing cold

s = "Placement Season 2027"

s.lower(), s.upper()            # case
s.strip(), s.lstrip(), s.rstrip()   # whitespace at ends (or given characters)
s.split()                       # ['Placement', 'Season', '2027'] — splits on any whitespace
s.split("e")                    # split on a specific separator
s.replace("2027", "2028")       # every occurrence
s.startswith("Place")           # True
s.endswith("2027")              # True
s.find("Season")                # 10  — index, or -1 if absent
s.index("Season")               # 10  — index, or ValueError if absent
s.count("e")                    # 4
s.zfill(25)                     # pad with leading zeros
s.title()                       # 'Placement Season 2027'

The find / index pair is a real distinction: find returns -1 for absent, index raises. Use find when absence is normal, index when absence is a bug.

Character tests, useful for cleaning input:

"a".isalpha()      # True
"1".isdigit()      # True
"a1".isalnum()     # True
" ".isspace()      # True
"Abc".islower()    # False

And in is the readable membership test — no method needed:

print("Season" in s)      # True

Complexity, so you can reason about the time limit

Operation Cost Note
s[i] O(1) index
len(s) O(1) stored, not counted
s + t O(n + m) builds a new string
s += t in a loop O(n²) use join
"".join(parts) O(total) one pass
s[a:b] O(b − a) copies
x in s O(n · m) substring search
s.replace(...) O(n) new string
s[::-1] O(n) new string

The two lines to remember: += in a loop is quadratic, and every slice is a copy. A sliding-window solution that slices the window on each step is secretly O(n²).

What to take into an interview

  • Strings are immutable; every method returns a new string, so assign the result.
  • Building a string with += in a loop is O(n²). "".join(list) is O(n).
  • s[::-1] reverses. Slicing out of range gives ''; indexing out of range raises.
  • f-strings are the current formatting style. f"{x=}" prints name and value.
  • find returns −1 when absent; index raises ValueError.
  • in is the membership test, and on a string it is a substring search.
Lesson 3 of 12 · all 12 are free to read, no account. Course contents