The Scramble Check
An easy Python interview practice problem on DataDriven. Write the Python and run it against test cases, with instant feedback.
- Domain
- Python
- Difficulty
- easy
- Seniority
- L3
Problem
Every character counts exactly as written, capitals, spaces and punctuation included, and each one has to appear the same number of times in both strings. An on-call engineer cleaning up log tokens wants to know whether s2 is just s1 with its characters shuffled.
Same letters, different order - are these two strings secret twins?
Example 1
Input:
s1 = "listen", s2 = "silent"
Output:
true
Example 2
Input:
s1 = "hello", s2 = "world"
Output:
false
Worked solution and explanation
What this really is
This is a multiset equality check dressed up as log hygiene. Everyone reaches for counting; what separates candidates is knowing what each shortcut cannot see. Compare sets and 'aabb' passes as 'abbb', because both hold just a and b. Lowercase first and 'Listen' matches 'silent', which the engineer never asked for. And spend one string's counts against the other without checking lengths, and 'error' passes as 'eror': a shorter string can never overdraw a longer one's budget, so the leftovers go unnoticed.
Building it
Step 1: Guard on length first
A shuffle neither adds nor drops a character, so unequal lengths end it right there. In this shape the guard is not a speed trick: the spend-down loop below only catches s2 asking for a character s1 ran out of, never s1 holding characters s2 didn't use.
Step 2: Count s1 into remaining
Walk s1 once and build remaining, how many of each character are available. Capitals, spaces and dashes are keys like any other; nothing is normalized.
Step 3: Spend the counts against s2
Walk s2 and decrement. If a character's count is already zero, or it never appeared, s2 has more of it than s1: return False on the spot. Reach the end and every count is back to zero, because the lengths matched and nothing went negative.
def check_string_permutation(s1, s2):
if len(s1) != len(s2):
return False
remaining = {}
for ch in s1:
if ch in remaining:
remaining[ch] += 1
else:
remaining[ch] = 1
for ch in s2:
if remaining.get(ch, 0) == 0:
return False
remaining[ch] -= 1
return True- Two maps compared
- Count both strings and compare the dicts with
==. Dict equality sees leftovers on either side, so the length check is only a shortcut here, and you hold two maps until the very end. - One map spent down
- Count
s1, decrement throughs2. Half the memory and it exits on the first overdraw, but it only looks one way, so the length guard is what makes it correct.
Common follow-up questions
- What if the check should be case-insensitive? (Normalizing both strings the same way before counting, and saying out loud that it changes the contract.)
- How would you check whether `s2` can be built from a subset of the characters in `s1`? (Dropping the length guard on purpose: the one-way spend-down is exactly the subset check.)
- What if you needed to find all groups of shuffled tokens in a list of strings? (Grouping by a canonical key such as the sorted string or a frequency tuple.)