Indexing and slicing
A slice s[start:stop] includes start and excludes stop. Its length is stop minus start when bounds are valid and step is one. This half-open convention lets adjacent pieces meet without overlap: s[:k] + s[k:] reconstructs the original string.
Use half-open intervals and slices without losing boundary cases.
s = "interview"
assert s[0:5] == "inter"
assert s[:0] == ""
assert s[5:] == "view"
assert s[::-1] == "weivretni"
assert s[:3] + s[3:] == s
Index versus slice
Indexing outside the sequence raises IndexError; slices clip to the available range. Avoid using clipping to hide incorrect bounds. Work out whether an empty interval is legitimate before entering a loop.
A left rotation
To rotate abcd left by one, split at index one and exchange the pieces: bcd + a. Reduce large rotation amounts modulo length, but return early for empty input because modulo zero is invalid.
def rotate(s, k):
if not s:
return ""
k %= len(s)
return s[k:] + s[:k]
assert rotate("abcd", 6) == "cdab"
assert rotate("", 3) == ""
Cost model
String slicing copies the selected characters. Repeatedly slicing large substrings inside a loop can turn a seemingly linear algorithm into quadratic work. Keep indices while searching and create the result slice once.
Guided practice
Find the common prefix of two strings. Advance one index while it is less than both lengths and the characters match; return the prefix ending at that index.
Independent practice
Complete Reverse a message, Shared prefix, and Rotate left. Include empty strings and a rotation larger than the input length.
Takeaway
Use half-open intervals consistently and count copied characters when analyzing complexity.
Deeper check: Boundary reasoning without guesswork
A slice is a half-open interval. Its end is the first position not included, so length is end−start when both bounds are ordinary in-range nonnegative indices. Python clamps many slice bounds, whereas direct indexing raises IndexError outside the string. Do not rely on clamping to hide a logically incorrect boundary. Negative zero is still zero, a common bug when taking or removing a suffix of length zero.
text = "abcd"
assert text[1:3] == "bc"
assert text[4:] == ""
assert text[-0:] == text
assert text[:-0] == ""
assert text[0:99] == text
Apply the idea
Test a prefix/suffix helper with sizes 0, 1, len(text), and larger than the input. Describe each expected interval before running the function. In a removal operation, explicitly handle a zero-length suffix rather than slicing to -0.