String Building, Palindromes and Counting
26 min · Intermediate
Need help? Ask a tutor on WhatsApp (opens in a new tab)Strings are sequences of characters that you cannot change. Most string problems come down to three tools: build the answer in a list and join, walk from both ends, or count with a table.
Build strings with join
str.join builds one string from many strings, with a separator placed between them. It is the usual way to assemble text, because a string cannot be changed in place.
A report line is made of many small pieces. Strings cannot be changed, so += in a loop may copy the growing text each time. Collect the pieces in a list and join them once.
parts = []for n in range(1, 6): parts.append(str(n * n))print(", ".join(parts))Example 1 · Mask an order id
ticket = "TK20481357"masked = "*" * (len(ticket) - 4) + ticket[-4:]print(masked)******1357
What this code does
"*" * 6repeats the star six times.ticket[-4:]keeps the last four characters.- Joining the two parts builds the final text once.
Example 2 · Join with a format for each piece
marks = {"Asha": 91, "Ravi": 78, "Meena": 85}print(", ".join(f"{name}:{mark}" for name, mark in marks.items()))Asha:91, Ravi:78, Meena:85
What this code does
- The generator makes one small string per student.
joinputs ", " between them, and no separator is left at the end.- Doing the same with += needs special care for the last comma.
join is called on the separator and takes a list of strings. The pieces must already be strings.
Palindromes with two pointers
Palindrome means a text that reads the same forwards and backwards, such as level. Checks often ignore case, spaces and punctuation.
A palindrome reads the same forwards and backwards. Compare the first and last characters, then move inward. Skip anything that is not a letter or a digit, and ignore capital letters.
text = "A man, a plan, a canal: Panama"left, right = 0, len(text) - 1ok = Truewhile left < right: if not text[left].isalnum(): left += 1 elif not text[right].isalnum(): right -= 1 elif text[left].lower() != text[right].lower(): ok = False break else: left += 1 right -= 1print(ok)Example 3 · A palindrome the short way
def is_palindrome(text): letters = [c.lower() for c in text if c.isalnum()] return letters == letters[::-1]
print(is_palindrome("Was it a car or a cat I saw?"))print(is_palindrome("Order 66"))True False
What this code does
- The comprehension keeps only letters and digits, in lower case.
- A list equals its reverse exactly when the text is a palindrome.
- O(n) time and O(n) space. The pointer version in the section uses O(1) space.
Example 4 · Longest palindrome by expanding
def longest_palindrome(s): best = "" for centre in range(len(s)): for lo, hi in ((centre, centre), (centre, centre + 1)): while lo >= 0 and hi < len(s) and s[lo] == s[hi]: lo -= 1 hi += 1 if hi - lo - 1 > len(best): best = s[lo + 1:hi] return best
print(longest_palindrome("babad"))print(longest_palindrome("forgeeksskeegfor"))bab geeksskeeg
What this code does
- Every palindrome grows outward from a centre. The centre is a letter (odd length) or a gap between two letters (even length).
- Each centre expands while both ends match, and the longest result is kept.
- O(n squared) time and O(1) extra space.
No cleaned copy is made, so the extra space is O(1). Time is O(n).
Count with a dictionary or Counter
Frequency count means a table that maps each distinct item to how many times it appears. It is built in one pass, in O(n) time.
Frequency counting answers: how many times does each item appear? Walk once and add one to a slot in a dictionary. Counter from the standard library does the same in one line.
from collections import Counter
word = "mississippi"counts = {}for ch in word: counts[ch] = counts.get(ch, 0) + 1print(counts)print(Counter(word))Example 5 · Most common characters
from collections import Counter
print(Counter("mississippi").most_common(2))[('i', 4), ('s', 4)]What this code does
most_common(2)returns the two biggest counts as (item, count) pairs.- Equal counts keep the order in which the items first appeared.
- Counting is O(n); picking the top few is cheap.
Both take O(n) time. A missing key in a Counter gives 0, while a plain dictionary needs .get.
Anagrams and the first unique character
Two words are anagrams if they use the same letters the same number of times, so their counts are equal. The same count table also finds the first character that appears only once.
from collections import Counter
print(Counter("listen") == Counter("silent"))word = "swiss"counts = Counter(word)print(next((c for c in word if counts[c] == 1), None))Example 6 · Group words that are anagrams
words = ["listen", "silent", "enlist", "google", "gogole", "cat"]groups = {}for word in words: groups.setdefault("".join(sorted(word)), []).append(word)print(list(groups.values()))[['listen', 'silent', 'enlist'], ['google', 'gogole'], ['cat']]
What this code does
- Words that are anagrams have the same letters when sorted, so the sorted text is a key.
setdefaultcreates an empty list the first time a key is seen.- Time is O(n * k log k) for n words of length k.
The second pass goes through the word, not the table, so the original order decides which character is first.
Next step
Check your understanding
11 quick questions