7.1 String Basics
A string is an ordered sequence of characters. The characters can be letters, digits, symbols, or spaces — whatever you can type on a keyboard. Python lets you write a string between single or double quotes. Both produce exactly the same string; choose whichever quote does not appear inside your text.
The string "Cambridge" stored in the variable word has 9 characters. Each character has a fixed position (called an index), starting at 0. So C is at index 0, a at index 1, all the way to e at index 8.
# Source: moshikur.com | Cambridge A Level CS 9618
greeting = "Hello"
language = 'Python'
fullName = "Md. Moshikur Rahman"
emptyStr = "" # a valid string with zero characters
mixed = "Room 204, Lab-3"- Once a string exists, you cannot change a character inside it.
word[0] = "B"always raises aTypeError.- To "change" a string, you must build a new string and assign it back to the variable.
- This is not a quirk of Python — most exam languages enforce it.
- Every Paper 4 task that says "modify the string" really means "build a new one and replace the old one."
Once you internalise that, the code structure becomes the same every time: declare an empty newString = "", loop, append, output.
- Read the assignment: word stores "Cat"
- The middle line begins with # so Python ignores it
- print(word) outputs the string unchanged
- If uncommented, word[0] = "B" raises TypeError (strings are immutable)
- First work out what name stores
- print(name) outputs the string unchanged
- Think about whether Python lets you change one character in place
- The + operator joins strings end-to-end with no extra spaces
- The literal "-" is inserted between a and b
- print(a, b) separates arguments with a single space by default
- + inserts nothing between the two strings
- print(room, num) inserts a space by default
- The only character to change is at index 0
- Keep characters from index 1 onwards using word[1:]
- Concatenate "B" in front of the slice
- Index 0 holds "D" — that is the character to change
- word[1:] gives "og"
- Concatenate "L" with that slice
- In Paper 4 mark schemes, "modify a string" never means item assignment.
- The examiner expects to see an empty string built up character by character, or a slice-and-concatenate expression.
- Writing
s[i] = cearns zero marks even if the rest of the logic is correct.
String Basics
7.2 Indexing & Slicing
Each character in a Python string has a position called an index. The first character is at index 0, the second at 1, and so on. Python also accepts negative indexes that count backwards from the end: -1 is the last character, -2 the second-last.
# Source: moshikur.com | Cambridge A Level CS 9618
word = "Cambridge"
print(word[0]) # C — first character
print(word[4]) # r
print(word[-1]) # e — last character
print(word[-3]) # d — third from the ends[start:end]returns the characters from positionstartup to but not including positionend.- Either may be omitted:
s[:3]means "from the beginning to index 3",s[5:]means "from index 5 to the end". - The slice length is always
end - start.
The fact that the end index is excluded looks strange at first, but it has a useful side-effect: the number of characters in the slice is always end - start. So word[2:5] always gives 3 characters, no matter what word is.
Pseudocode MID vs Python slicing — the off-by-one rule
The pseudocode function MID(string, x, y) returns y characters starting at position x, where x is counted from 1. Python slicing counts from 0. The conversion is straightforward but easy to get wrong under exam pressure.
MID(s, x, y) → s[x − 1 : x − 1 + y] — subtract 1 from the start, then add the length to find the end. So MID("ABCDEFGH", 2, 3) picks positions 2, 3, 4 in pseudocode (B, C, D) — in Python this is the slice s[1:4], which returns "BCD".
| Pseudocode | Python equivalent | Result for "ABCDEFGH" |
|---|---|---|
MID(s, 1, 3) | s[0:3] | "ABC" |
MID(s, 2, 3) | s[1:4] | "BCD" |
MID(s, 4, 2) | s[3:5] | "DE" |
LEFT(s, 3) | s[0:3] or s[:3] | "ABC" |
RIGHT(s, 3) | s[-3:] | "FGH" |
- Identify the pseudocode position: 5 characters starting at position 4
- Position 4 in pseudocode is the 4th character (O)
- Convert to Python: position 4 becomes index 3
- Slice end = 3 + 5 = 8
- Convert position 3 to a Python index (3 - 1 = 2)
- 4 characters, so slice end = 2 + 4 = 6
- Indexes 2, 3, 4, 5 of "COMPUTING" are M, P, U, T
- word[0] is the first character
- word[-1] is the last character
- word[2]: P=0, Y=1, T=2
- word[-3]: H=-3, O=-2, N=-1
- Draw the index row twice: positive numbers above, negative below
- S=0, C=1, H=2, O=3, O=4, L=5
- S=-6, C=-5, H=-4, O=-3, O=-2, L=-1
- s[:4] — from start, up to (not including) index 4
- s[4:] — from index 4 to the end
- s[2:6] — indexes 2, 3, 4, 5
- s[-3:] — last three characters
- Count the included characters: slice length is always end - start
- B=0, A=1, N=2, G=3, L=4, A=5, D=6, E=7, S=8, H=9
- When converting
MID(s, x, y)to Python, always write the slice ass[x-1 : x-1+y]. - Memorise that pattern.
- The most common Paper 4 mistake is writing
s[x:x+y], which is off by one and silently returns the wrong substring — no error, just wrong marks.
Indexing & Slicing
7.3 Length Without len()
Examiners sometimes design Paper 4 questions that expect you to demonstrate understanding, not just knowledge of built-ins. When a question says "write a function that returns the number of characters in a string", you score full marks by showing the loop-and-count logic. Quietly using len() can lose method marks because the mark scheme rewards each line of the manual algorithm.
You count characters in a string the same way you count anything else in programming: set a counter to zero, walk through every item, and add one each time. With a string, walking through means a for loop over each character.
# Source: moshikur.com | Cambridge A Level CS 9618
word = "Cambridge"
count = 0
for ch in word:
count = count + 1
print("Length:", count) # Length: 9- Set a counter variable to 0.
- Loop through every character.
- Inside the loop, increase the counter when the condition is met (here, the condition is "always true").
You can also write the loop using an explicit index, which is closer to the Cambridge pseudocode style. Both are correct, but the index version is often easier to extend later when you need positions, not just counts.
# Source: moshikur.com | Cambridge A Level CS 9618
word = "Cambridge"
count = 0
i = 0
while i < len(word): # if len() is allowed
count = count + 1
i = i + 1
print("Length:", count)If the question forbids len() entirely, the simpler approach is to use the for ch in word loop above, which knows when to stop without needing the length in advance.
- Define the function with one parameter s
- Initialise a counter: count = 0 before the loop
- Iterate using for ch in s — works without knowing the length
- Return the result and test it
- Extend the counter pattern by adding an if inside the loop
- Convert the character to lower case before checking
- Test if the lower-cased character is in the string "aeiou"
- Return count at the end
- Counter pattern: start at 0
- Loop and compare each character to " "
- Return the counter
- Same counter pattern as countSpaces
- Change the comparison character to ","
- Counter pattern
- Compare each character with the range "A" to "Z" using ASCII ordering
- Use ch >= "A" and ch <= "Z"
- Same loop as countUpper
- Compare each character against "a" and "z"
- When a Paper 4 task says "write code to find the length / count / total" it nearly always wants the algorithm shown, not the built-in.
- Show the counter, the loop, and the increment — those are the marks.
- Reserve
len()for cases where the question explicitly allows it or uses it incidentally inside another task.
Length Without len()
7.4 Pseudocode Functions in Python
Cambridge mark schemes and pseudocode inserts use a fixed set of string functions — LEFT, RIGHT, MID, LENGTH, TO_UPPER, TO_LOWER. The exam will give you the pseudocode and tell you to write the program in Python. Knowing the one-to-one Python equivalent for each call is the difference between answering in two minutes and panicking through a translation.
| Pseudocode call | Python equivalent | Example |
|---|---|---|
LENGTH(s) | len(s) | len("Happy Days") → 10 |
LEFT(s, n) | s[0:n] or s[:n] | "ABCDEFGH"[:3] → "ABC" |
RIGHT(s, n) | s[-n:] | "ABCDEFGH"[-3:] → "FGH" |
MID(s, x, y) | s[x-1 : x-1+y] | "ABCDEFGH"[1:4] → "BCD" |
TO_UPPER(s) | s.upper() | "Error 803".upper() → "ERROR 803" |
TO_LOWER(s) | s.lower() | "JIM 803".lower() → "jim 803" |
UCASE(ch) | ch.upper() | "h".upper() → "H" |
LCASE(ch) | ch.lower() | "W".lower() → "w" |
str1 & str2 | str1 + str2 | "Summer" + " " + "Pudding" |
NUM_TO_STR(n) | str(n) | str(87.5) → "87.5" |
STR_TO_NUM(s) | int(s) or float(s) | float("23.45") → 23.45 |
- When converting
MID(s, x, y)to Python, subtract 1 from the start, then add the length to find the end:MID(s, x, y) → s[x-1 : x-1+y]. - This is the single most-tested translation in Paper 4 string questions.
- Translate LEFT: LEFT(word, 3) → word[:3] → "INF"
- Translate MID: MID(word, 5, 4) → word[5-1 : 5-1+4] → word[4:8] → "RMAT"
- Translate RIGHT: RIGHT(word, 2) → word[-2:] → "ON"
- Replace & with +; replace TO_UPPER with .upper()
- Apply the off-by-one rule for MID
- b is 4 characters starting at position 12
- LEFT(name, 4) → name[:4] → "Prog"
- RIGHT(name, 4) → name[-4:] → "uage"
- LEFT("BANGLADESH", 4) → "BANG"
- RIGHT("BANGLADESH", 4) → "DESH"
- Concatenate with "-" between, then .upper()
- .upper() has no effect here because the input is already upper-case
- Translate each pseudocode call to its Python equivalent on a separate line
- LEFT("Programming", 4) = "Prog"
- RIGHT("Programming", 4) = "ming"
- LEFT(id, 5) → "S0204"
- MID(id, 7, 1) → 1 character at pseudocode position 7 → Python index 6 → "A"
- LENGTH(id) = 16, so RIGHT(id, 16-8) = RIGHT(id, 8) → "MOSHIKUR"
- Apply the same off-by-one rule
- LENGTH(id) for "S1099-B-NAYEEM" is 14
- RIGHT(id, 14-8) = RIGHT(id, 6) = "NAYEEM"
- When the question gives a pseudocode algorithm and asks for Python, copy each pseudocode line and replace it with its Python equivalent — do not rewrite the logic from scratch.
- Stay line-for-line with the pseudocode.
- The mark scheme is also line-by-line, so this strategy hits each mark point in turn.
Pseudocode Functions in Python
7.5 Searching Strings
Validation, file processing, OOP, and exception handling questions all eventually ask "does this string contain X?" or "where does X start in this string?" — usernames, file extensions, command parsers, postcode validators. Paper 4 expects you to write the search loop yourself, not rely on in or .find(). Once you understand the search pattern, every variation (count, replace, split) is built from the same structure.
Searching a string by hand has two shapes. The first is searching for a single character — one loop, one comparison. The second is searching for a substring — an outer loop over starting positions and an inner loop checking the match.
Finding the first position of a character
# Source: moshikur.com | Cambridge A Level CS 9618
text = "banana"
target = "n"
position = -1 # -1 means "not found yet"
for i in range(len(text)):
if text[i] == target:
position = i
break # stop at the first match
print("First position of", target, ":", position) # 2- Set
position = -1as the "not found" sentinel. - Loop through indexes 0 to
len(s) - 1usingrange(len(s)). - On a match, record the index and
break. - After the loop,
positionis either the first index or still -1.
Finding a substring (manual sliding window)
# Source: moshikur.com | Cambridge A Level CS 9618
text = "CATALOGUE"
sub = "LOG"
n = len(text)
m = len(sub)
found = False
pos = -1
for i in range(n - m + 1):
match = True
for j in range(m):
if text[i + j] != sub[j]:
match = False
break
if match:
found = True
pos = i
break
if found:
print("Found at position", pos) # 4
else:
print("Not found")Each iteration of the outer loop slides a window of size m one place to the right. The inner loop checks the m characters against the substring. The last valid starting position is n - m, so the outer loop is range(n - m + 1).
- Define the function with one parameter code, returns a Boolean
- Reject codes that are too short (fewer than 3 characters)
- Build the first three characters into a string by looping
- Compare the prefix to "BK-"
- Use the parallel of the worked example
- Take the last three characters with a loop or a slice
- Compare to "-CS"
- Sentinel value: start with position = -1
- Loop with an index using range(len(s))
- If the lower-case character is in "aeiou", record the index and break
- Use the same shape as firstVowel
- The condition becomes "0" <= s[i] <= "9"
- Set found = False
- Outer loop slides the window from index 0 to len(text) - len(sub)
- Inner loop checks the window character by character
- If all characters match, set found = True and break
- Same outer/inner loop as the worked example
- Instead of a Boolean, return the index i where the match started
- Initialise pos = -1 as the sentinel
- When the question forbids a method, write the long form even if the slice would be one line.
- Examiners want to see the loop and the comparison because they are the marks.
- A one-line slice like
sid[-3:] == "-CS"may earn only the answer mark, not the algorithm marks.
Searching Strings
7.6 Counting & Replacing
Counting is the most common Paper 4 mini-task ("how many vowels", "how many spaces"). Replacing builds directly on it — you walk the same loop but, instead of incrementing a counter, you build a new string. Both are also the foundation for caesar cipher questions, text cleaning, and CSV parsing.
The build-up pattern for replacing characters: start with newText = "", walk the original with a for loop, append either the original character or a substitute, then output newText. Never try to modify the original string in place — strings are immutable.
Counting occurrences of a character
# Source: moshikur.com | Cambridge A Level CS 9618
text = "banana"
target = "a"
count = 0
for ch in text:
if ch == target:
count = count + 1
print("Count of", target, ":", count) # 3Replacing characters by building a new string
# Source: moshikur.com | Cambridge A Level CS 9618
text = "HELLO"
oldChar = "L"
newChar = "X"
newText = ""
for ch in text:
if ch == oldChar:
newText = newText + newChar
else:
newText = newText + ch
print(newText) # HEXXO- Any string transformation (replace, encode, clean) follows the same shape: start with
newString = "", walk the original, append either the original or a substitute, then outputnewString. - Never try to modify the original string in place.
- Define the function with one parameter s, returns an integer
- Initialise the counter: count = 0
- For each character, test if it is between "0" and "9"
- Return the count
- Same loop as countDigits, but instead of counting, append
- Initialise newText = ""
- If the character is between "0" and "9", append "*"
- Otherwise append the original character
- Counter pattern
- Lower-case the character once
- Check it is a letter AND it is not a vowel
- Same shape as countConsonants
- Invert the letter check
- Letter = ("a" <= low <= "z"); non-letter is the opposite
- Build-up pattern: newText = ""
- Loop and append either "_" or the original character
- Same build-up pattern
- If the character is a vowel, append nothing (skip)
- Otherwise append it
- Comparing characters with
>=and<=works in Python because each character has an underlying ASCII value. - "0" is 48, "9" is 57, so
"0" <= ch <= "9"tests whetherchis a digit. - Mark schemes accept either this style or a list of digits inside an
if ch in "0123456789"check.
Counting & Replacing
7.7 Reversing & Palindromes
Reversal appears in past Paper 4 questions both directly ("reverse this string") and as a sub-routine inside larger tasks (palindrome checks, number-to-binary output, queue printouts). The palindrome check is a small but rich combination of indexing, looping, and Boolean reasoning — exactly what the mark scheme rewards.
Reversing by reading backwards
# Source: moshikur.com | Cambridge A Level CS 9618
word = "PYTHON"
reversedWord = ""
for i in range(len(word) - 1, -1, -1):
reversedWord = reversedWord + word[i]
print(reversedWord) # NOHTYPThe range(len(word) - 1, -1, -1) generates the indexes 5, 4, 3, 2, 1, 0 for "PYTHON". The stop value is -1 because the loop must include index 0 — and range excludes its stop value.
Detecting a palindrome (pair-comparison)
# Source: moshikur.com | Cambridge A Level CS 9618
def isPalindrome(s):
n = len(s)
isPal = True
i = 0
while i < n // 2 and isPal:
if s[i] != s[n - 1 - i]:
isPal = False
i = i + 1
return isPal
print(isPalindrome("MADAM")) # True
print(isPalindrome("PYTHON")) # False
print(isPalindrome("RACECAR")) # True- Loop only to the middle of the string (
n // 2), comparings[i]withs[n - 1 - i]. - If any pair differs, the string is not a palindrome.
- This halves the work compared with reversing and comparing.
- Define the function with one parameter s, returns a string
- Initialise an empty result string: result = ""
- Loop backwards using range(len(s) - 1, -1, -1)
- Append s[i] and return
- Get n = len(s) and set a flag isPal = True
- Loop i from 0 while i < n // 2
- If s[i] differs from s[n - 1 - i], set isPal = False and break
- Return isPal
- Initialise result = "" and i = len(s) - 1
- Loop while i >= 0, appending s[i]
- Decrement i on each pass
- range(len(s) - 1, -1, -1) generates the indexes backwards
- Loop and append s[i] to result
- Convert the whole string to lower case first
- Apply the pair-comparison palindrome check on the lower-cased version
- First build a new string with the spaces stripped out
- Then run the standard pair-comparison check
- When the question asks for a palindrome check, do not first reverse the string and then compare.
- The mark scheme typically lists the pair-comparison approach as the model solution.
- Following the same approach earns the algorithm marks; the reverse-and-compare version often earns only the final correctness mark.
Reversing & Palindromes
7.8 random.sample() — picking unique items
This function is on the page because of a real, recent Paper 4 question. In October/November 2025 Paper 42, Question 2(a), candidates were asked to generate 20 unique random integers between 0 and 100. The official Python mark scheme answered with random.sample(range(0, 101), 20). The same function works on strings — and that is what makes it useful here: when you need to pick unique characters from a string (a one-time password, a random initial, a sampled list with no repeats), this is the cleanest answer.
The function random.sample(population, k) returns a list of k unique items chosen from the population. The population can be any sequence — a list, a range, or a string. Because Python strings are sequences of characters, you can pass a string in directly and get back a list of distinct characters.
# Source: moshikur.com | Cambridge A Level CS 9618
import random
# 1. The exact mark-scheme line from 9618/42 O/N 2025 Q2(a)
TheArray = random.sample(range(0, 101), 20)
print(TheArray)
# example: [37, 4, 88, 12, 99, ...] — 20 unique integers, 0–100
# 2. The same idea, applied to a string of characters
letters = "ABCDEFGHIJKLMNOP"
picks = random.sample(letters, 5)
print(picks)
# example: ['F', 'A', 'L', 'B', 'J'] — 5 unique characters
# 3. Join the list back into a string (common follow-up step)
password = "".join(random.sample("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", 8))
print(password)
# example: "K7F3LBQ9"random.sample(seq, k)— returns k unique items (no repeats). RaisesValueErrorif k > len(seq).random.choice(seq)— returns one item, with repeats possible across calls.random.choices(seq, k=N)— returns N items, repeats allowed.
sample when uniqueness matters (lottery picks, unique IDs, password characters with no repeats).- Import the module: import random at the top
- Reject impossible lengths: if n > 26, return "ERROR"
- Pick unique characters: random.sample("ABC...Z", n)
- Join back into a string with "".join(...)
- Exactly the same shape as the worked example, but with the digit pool
- import random
- Reject n > 10 (only 10 digits available)
- random.sample("0123456789", n)
- Join with "".join(...) and return
- import random
- Reject input that is too short (len(s) < 2)
- random.sample(s, 2) returns a list of 2 distinct characters
- Join into a string with "".join(...)
- Exactly the same shape as the worked example, but with k = 3
- Different error string: "TOO SHORT"
- The key insight: random.sample(s, len(s)) picks every character once, in random order
- Join the list back into a string
- This is the direct mark-scheme pattern from O/N 2025 Q2(a)
- Watch the exclusive upper bound: range(1, 31) gives 1 to 30 inclusive
- Cambridge accepts
random.sample(range(0, 101), 20)as the model Python answer for "20 unique random integers between 0 and 100 (inclusive)" — see 9618/42 O/N 2025 mark scheme. - Remember the upper bound is exclusive in
range(), so you writerange(0, 101), notrange(0, 100). - Test the boundary case in your head before you write the call.
random.sample()
7.9 Exam-Style Task
Individual techniques are useful, but Paper 4 marks reward you for assembling them into one complete, working program. This task combines indexing, searching, counting, and output formatting. Treat it like a real exam — write your code first, then check against the model solution.
Question (Paper 4 style) — 12 marks
A school stores a single line of registration data for each student as a string of the format:
SXXXX-G-NAMEwhere:
SXXXXis a 5-character student ID starting with the letterSfollowed by 4 digitsGis a single grade character ('A','B'or'C')NAMEis the student's name (at least 1 character)- The two hyphens
-always appear in fixed positions
Examples of valid records: S0204-A-MOSHIKUR, S1099-B-NAYEEM.
Write a Python program that:
- Asks the user to enter a registration record.
- Validates that the record begins with "S" followed by 4 digits, that the 7th character is one of "A", "B", "C", and that hyphens appear at Python indexes 5 and 7. [6]
- If the record is valid, outputs the student ID, grade, and name on separate lines. [3]
- If the record is invalid, outputs a clear error message identifying which part failed. [3]
Total marks: 12 (AO3)
- Read the input with input()
- Validate the ID: first character must be "S"; characters at indexes 1 to 4 must be digits
- Validate the structure: index 5 must be "-"; index 6 must be a grade letter; index 7 must be "-"
- The name (from index 8 onwards) must contain at least one character
- If valid, extract studentID = record[0:5], grade = record[6], name = record[8:]
- Output the three parts on separate lines with labels
- For invalid records, print a clear error message
Mark scheme (model)
| Marking point | AO | Marks |
|---|---|---|
| Input read using input() and stored in a variable | AO3 | 1 |
| Check that first character is "S" | AO3 | 1 |
| Loop or four explicit checks for digits at indexes 1–4 | AO3 | 2 |
| Check hyphens at indexes 5 and 7 | AO3 | 1 |
| Check grade character is one of A, B, C | AO3 | 1 |
| Extract student ID, grade, name using slicing or loops | AO3 | 3 |
| Output on three separate lines with labels | AO3 | 1 |
| Sensible error message for each invalid case | AO3 | 2 |
Total: 12. Accept equivalent algorithms — e.g. building a digit-check using "0123456789" membership instead of comparison, or using .startswith if not explicitly forbidden.
- In the real exam you will not have a live playground — but you will have scrap paper.
- Before writing your code, draw the index row underneath the string in the question (positive numbers above, negative below).
- Two minutes of drawing saves five minutes of debugging.
✓ Key Points Summary
7.10 Practice Tasks
Fifteen exam-style string tasks. Each shows only the question — click Hint for the thought process, or Help for the worked Python solution. Try each one yourself before revealing.
- For every string task, the marker checks: (1) correct use of slicing with the off-by-one rule for MID, (2) the build-up pattern (never item assignment), (3) the counter pattern when counting, (4) the pair-comparison palindrome check, (5)
random.samplefor unique picks. - The most common mark loss is using
len(),.replace(), ors[::-1]when the question forbids them.
Question Bank
Answer all questions, then press Submit Quiz to see your score.
Question 1Multiple Choice
What happens when you try word[0] = "B" in Python where word = "Cat"?
Question 2True / False
In Python, the expression "Lab" + "204" produces "Lab 204" (with a space).
Question 3Multiple Choice
Given word = "PYTHON", what does word[-3] return?
Question 4Multiple Choice
Which Python slice is equivalent to pseudocode MID("ALGORITHM", 4, 5)?
Question 5True / False
The slice s[2:5] always returns exactly 3 characters (regardless of what s is, as long as s is long enough).
Question 6Multiple Choice
Without using len(), which function correctly counts vowels in a string?
Question 7Multiple Choice
What is the Python equivalent of pseudocode RIGHT(s, 3)?
Question 8True / False
When searching for the first position of a character, you should initialise position = -1 as the "not found" sentinel.
Question 9Multiple Choice
What is the output of maskDigits("PIN: 9X87")?
Question 10True / False
isPalindrome("RACECAR") returns True using the pair-comparison check.
Question 11Multiple Choice
Which call generates 20 unique random integers from 0 to 100 inclusive (per the O/N 2025 mark scheme)?
Question 12True / False
In the build-up replacement pattern, you should always modify the original string in place to save memory.
Answer all 12 questions to enable submission.