String questions dominate the easy-to-medium slot of many coding rounds, and Python makes them look deceptively short. You can check a palindrome with s == s[::-1] and compare anagrams with Counter, but the follow-up is often, "Now do it without slicing or built-ins." You need the quick Pythonic form and the manual logic underneath it. Palindromes, anagrams, and run-length compression are the three patterns that recur most, and each has a short Python form and a manual form worth carrying into the room.
The three problems and the two versions you must know
Palindrome checking, anagram testing, and run-length compression rest on a small set of reusable moves: scanning from both ends, counting occurrences, and grouping adjacent equal characters. The syntax is not the hard part. The interviewer wants to know whether you can state the assumptions, choose a suitable approach, and defend its time and space cost.
For each problem, prepare two versions:
A concise Pythonic solution that is fast to write in a timed round
A manual solution that exposes the algorithm when slicing,
Counter, or other helpers are banned
The second version proves that you understand the pattern rather than remembering a trick. It also prepares you for language-independent questions. The placement preparation category carries the courses that put these drills inside a full coding-round plan.
Palindrome: two-pointer and the normalise trap
A palindrome reads the same from left to right and right to left. The direct Python check is:
def is_palindrome_pythonic(s: str) -> bool:
return s == s[::-1]Slicing creates a reversed string, so this takes O(n) time and O(n) extra space. When extra space matters, use two pointers:
def is_palindrome(s: str) -> bool:
left = 0
right = len(s) - 1
while left < right:
if s[left] != s[right]:
return False
left += 1
right -= 1
return TrueFor s = "racecar", the scan is exact:
left = 0,right = 6:r == rleft = 1,right = 5:a == aleft = 2,right = 4:c == cBoth pointers reach index
3, the middlee, without a mismatch
Therefore, "racecar" is a palindrome. The loop compares at most n/2 pairs, which is still O(n) time, and it uses O(1) extra space.
The real trap is the input rule. If punctuation, spaces, and case do not matter, normalise first:
def normalised_palindrome(s: str) -> bool:
clean = "".join(ch.lower() for ch in s if ch.isalnum())
return is_palindrome(clean)"A man, a plan, a canal: Panama" becomes "amanaplanacanalpanama", which passes the two-pointer check. Without normalisation, the first uppercase letter, spaces, and punctuation would make a naive comparison fail. Ask whether case and punctuation count before you code.

Anagram: Counter equality and the O(n) vs O(n log n) choice
Two strings are anagrams when they contain the same characters with the same frequencies. Python's Counter expresses that rule directly:
from collections import Counter
def are_anagrams_pythonic(a: str, b: str) -> bool:
return Counter(a) == Counter(b)For "listen" and "silent", both frequency maps contain l:1, i:1, s:1, t:1, e:1, and n:1. The maps are equal, so the answer is True. For "rat" and "car", the character counts differ, so the answer is False.
For lowercase English letters, the manual version can use a fixed array:
def are_anagrams(a: str, b: str) -> bool:
if len(a) != len(b):
return False
counts = [0] * 26
base = ord("a")
for ch in a:
counts[ord(ch) - base] += 1
for ch in b:
counts[ord(ch) - base] -= 1
return all(count == 0 for count in counts)The length check rejects impossible pairs early. Every character in the first string adds one, every character in the second subtracts one, and all zeros mean perfect cancellation. This is O(n) time and O(1) auxiliary space because the array size stays 26. If the alphabet is not fixed, use a dictionary and call the space cost O(k), where k is the number of distinct characters.
You could also write sorted(a) == sorted(b), but sorting takes O(n log n) time and builds sorted results. Counter is normally the clearer O(n) choice. Decide whether to lower-case or remove spaces before counting because that changes the problem itself.
String compression: run-length with the length guard
Run-length compression replaces a consecutive run with the character and its count. Both versions below must return the original string unless the compressed result is shorter. The Pythonic form leans on itertools.groupby, which hands you each run already grouped:
from itertools import groupby
def compress_pythonic(s: str) -> str:
compressed = "".join(ch + str(len(list(group))) for ch, group in groupby(s))
return compressed if len(compressed) < len(s) else sgroupby is exactly the helper an interviewer removes next, so keep the manual scan ready:
def compress(s: str) -> str:
if not s:
return s
parts = []
run_length = 1
for i in range(1, len(s)):
if s[i] == s[i - 1]:
run_length += 1
else:
parts.append(s[i - 1])
parts.append(str(run_length))
run_length = 1
parts.append(s[-1])
parts.append(str(run_length))
compressed = "".join(parts)
return compressed if len(compressed) < len(s) else sWork through the manual scan on s = "aaabbbccccd":
The
arun has length 3, so emita3.The
brun has length 3, so emitb3.The
crun has length 4, so emitc4.The final
drun has length 1, so emitd1.The result is
"a3b3c4d1". Its length is2 + 2 + 2 + 2 = 8.The original length is
3 + 3 + 4 + 1 = 11.Since
8 < 11, return"a3b3c4d1".
Now try s = "abcdef". Every run has length 1, so the candidate is "a1b1c1d1e1f1". Six character-count pairs take 6 × 2 = 12 positions, while the original has length 6. Since 12 is not less than 6, return the original "abcdef".

The scan is O(n). The output and joined compressed string use O(n) space in the worst case. A two-pass variant can calculate the compressed length first, but Python strings are immutable, so returning a new string still requires output storage.
The traps coding rounds set
The most common compression bug is forgetting the final flush. Runs are normally emitted when the next character differs, but the last run has no next character. That is why the two parts.append calls appear after the loop.
Other traps are equally predictable:
s[::-1]creates a new string, so it is not anO(1)-space answer.Countercreates a frequency object, so it is not an in-place solution.Repeated
out += piececan lead to quadratic work because strings are immutable. Collect pieces in a list, then call"".join(parts)once.An empty string and a single-character string are palindromes.
Compression should handle the empty string, a single character, and one long run.
A 26-slot anagram array is valid only when the character set has been restricted to lowercase English letters.
If slicing, aliasing, or mutability details still cause surprises, work through Python output-based questions before your next round.
How interviewers push past the one-liner
After accepting the short solution, an interviewer may ban slicing, ban Counter, ask for in-place work, or demand the complexity. They may also probe inputs such as "", "a", "aaaa", mixed case, spaces, or Unicode. State what you assume, then adapt only what the clarified problem requires.
KnowledgeGate's question bank carries more than 450 Python questions, including a Collections and Strings set that drills these patterns. Combine that volume with the timing and attempt-order advice in coding round strategy for placements, rather than solving isolated questions without a round plan.
Short version and next step
For a palindrome, normalise as required and use two pointers. For an anagram, compare frequency counts in O(n) time. For compression, count adjacent runs, flush the last run, and return the compressed result only when it is shorter. Know the Pythonic version and the manual version of each.
Run timed sets in DSA Using Python, then connect them to the wider Mera Placement Hoga track. The goal is not to memorise three answers. It is to recognise three reusable string patterns quickly.




