Understand
30-second explanation
Build the interview-ready mental model before comparing implementations.
Reduce the string to two layers of counts. Layer one: how often each letter appears (the frequency map). Layer two: how many letters share each frequency (the frequency-of-frequencies). The string is valid if there is a single distinct frequency, or two distinct frequencies that one deletion can merge — that is, the lower frequency is 1 held by a single letter (delete it whole), or the higher frequency is exactly one above the rest and held by a single letter (delete one of its copies). Everything else is invalid. O(n) time, O(1) space.
Interview contextWhat is testedWhere candidates slipHow to stand outExplore interview contextHide interview context
What's being tested
Whether you find the two-level count and then enumerate precisely the cases a single deletion can fix, instead of brute-forcing deletions or hand-waving the edge cases.
Where candidates slip
Applying the one-deletion allowance to several letters at once, missing that three distinct frequencies are unfixable, or requiring only the frequency gap without checking it occurs exactly once.
How to stand out
Lead with the invariant — 'a deletion shifts one letter's count down by one' — then derive that valid strings have ≤ 2 distinct frequencies with a single-step, single-occurrence gap, and name the two repair shapes explicitly.
Choose
Compare the available approaches
Use constraints and trade-offs to select the route you can defend in an interview.
Interactive decision treeInteractiveChoose the right approachFirst decision previewCount letters → freqDistinct frequencies?13+Explore the full treeCollapse the tree
Learn & trace
Build each solution step by step
Move from the core idea to a visual trace, implementation, proof, and edge cases.
Approach 1 of 2
Brute Force — Delete Every Index and Re-check
1Hint 1Explore hintHide hint
The definition is an operation: 'remove one character and check uniformity'. What's the most literal way to test it?
2Hint 2Explore hintHide hint
Check the original string first; if not uniform, delete each index in turn and re-test uniformity.
3Hint 3Explore hintHide hint
Re-counting for all n deletions is O(n) apiece → O(n²). But each deletion is just decrementing one letter's count, so you can decide it from the original counts.
A · Core idea
Start by testing s as-is: if all non-zero letter counts are equal, it's valid. Otherwise, for each index i, conceptually delete s[i], rebuild the frequency map for the shortened string, and test uniformity. Return "YES" at the first deletion that yields uniform counts.
Why it's the obvious first cut. The statement is operational — 'remove one character and check' — so the direct simulation removes each character and checks. It's correct beyond doubt.
Why it's slow. Recounting the full string after each of n deletions repeats nearly identical work n times. For n = 10⁵, that's ten billion operations.
Bridge to optimal. A deletion is fully captured by 'decrement one letter's frequency by one'. So all n deletions are knowable from the original frequency map — no string rebuilding required, which leads straight to the frequency-of-frequencies test.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
s = "abcc"Array state
base
a:1, b:1, c:2
YESC · Code with teaching comments
Reference implementation previewpublic String isValid(String s) { if (uniform(freqOf(s))) return "YES"; for (int i = 0; i < s.length(); i++) { StringBuilder sb = new StringBuilder(s); sb.deleteCharAt(i);Explore the full codeHide the full code
D · Why it works and complexityO(n²) timeO(1) spaceOpen the invariant and proofExplore the proofHide the proof
Because each candidate deletion only nudges one frequency down by one, you can evaluate every possible deletion from the original counts. That observation is what lets the optimal solution skip simulation entirely.
ComplexityO(n²) time, O(1) space — A uniformity check over the 26-letter map is O(n). Trying all n single-character deletions and re-checking is O(n²). The fixed alphabet keeps space O(1).
E · Edge cases and pitfalls3Rebuilding the frequency map for every deletion — O(n²).Explore edge casesHide edge cases
Pitfalls
- Rebuilding the frequency map for every deletion — O(n²).
- Counting zero-frequency letters as breaking uniformity.
- Skipping the zero-deletion check, so already-valid strings fall through.
Approach 2 of 2 · Optimal
Frequency-of-Frequencies Test (Optimal)
1Hint 1Explore hintHide hint
A single deletion shifts exactly one letter's count by one. How many distinct frequency values can survive that?
2Hint 2Explore hintHide hint
Build freq, then ffreq = how many letters have each frequency. One distinct frequency ⇒ valid.
3Hint 3Explore hintHide hint
With two distinct frequencies lo < hi: valid only if lo == 1 with a single occurrence (delete that letter whole), or hi == lo + 1 with a single occurrence (delete one of its copies). Otherwise NO.
A · Core idea
Count letters into freq, then count those frequencies into ffreq, a map from 'a frequency value' to 'how many letters have it'. The size of ffreq drives the decision.
One distinct frequency → valid. All letters already appear equally often; no deletion required.
Three or more distinct frequencies → invalid. A single deletion lowers exactly one letter's count by exactly one. It can reconcile at most two frequency groups, never three. Return "NO".
Exactly two distinct frequencies `lo < hi` — two repair shapes:
- 1
`lo == 1` and a single letter has it. That letter occurs once; deleting it removes the letter entirely (count 1 → 0) and every survivor shares
hi. Example:"aaabbbc"→ counts{3:2, 1:1}→ deletec. - 2
`hi == lo + 1` and a single letter has `hi`. One letter overshoots by exactly one occurrence; delete one copy (count
hi → lo) and all letters sharelo. Example:"abcc"→ counts{1:2, 2:1}→ delete onec.
Every other two-frequency configuration — two letters tied at count 1 ("aabbcd"), or a gap greater than one — requires more than one deletion, so it's "NO".
Why O(1) space. Both maps hold at most 26 entries no matter how long s is.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
s = "abcc"Array state
freq
a:1, b:1, c:2
YESC · Code with teaching comments
D · Why it works and complexityO(n) timeO(1) spaceOpen the invariant and proofExplore the proofHide the proof
The decisive move is the second count. The first layer exposes each letter's frequency; counting those frequencies reveals whether one deletion can pull everything into agreement. The single-occurrence requirement (ffreq[lo] == 1 or ffreq[hi] == 1) is what stops you from claiming a fix that would actually need several deletions.
ComplexityO(n) time, O(1) space — One O(n) pass builds the frequency map; the rest is constant work over at most 26 frequency values. Both maps are alphabet-bounded → O(1) space.
E · Edge cases and pitfalls8s = "aaa" — freq {a:3} → ffreq {3:1} → one distinct frequency → "YES".Explore edge casesHide edge cases
Edge cases handled
s = "aaa"freq {a:3} → ffreq {3:1} → one distinct frequency → "YES".s = "aaabbbc"ffreq {3:2, 1:1} → lo==1 and ffreq[lo]==1 → delete lone 'c' → "YES".s = "aabbcd"ffreq {2:2, 1:2} → two lone letters, neither shape holds → "NO".s = "aaaaabbbb"ffreq {5:1, 4:1} → hi-lo==1 and ffreq[hi]==1 → delete one 'a' → "YES".
Pitfalls
- Spending the single deletion across multiple letters.
- Forgetting the three-or-more distinct frequencies → always invalid rule.
- Checking only the frequency gap without requiring it to occur exactly once.
- Confusing shape 1 (lone count-1 letter) with shape 2 (one-too-high letter).
Practice
Try the solution yourself
Edit the optimal implementation, test another input, and observe the result.
Editable code previewpublic String isValid(String s) { int[] freq = new int[26]; for (int i = 0; i < s.length(); i++) freq[s.charAt(i) - 'a']++; Map<Integer, Integer> ffreq = new HashMap<>(); for (int f : freq) {Explore the playgroundClose the playground
Avoid mistakes
Recognize the interview traps
Understand why common attempts fail before reviewing the corrected implementation.
1Spreading the deletion across letters
"aabbcd" has two count-1 letters; equalising needs two deletions, but only one is allowed, so it is NO.
Explore the code comparisonHide the code comparison
Spreading the deletion across letters
"aabbcd" has two count-1 letters; equalising needs two deletions, but only one is allowed, so it is NO.
Don't write this
# wrong: drops all count-1 letters as if each were free
return "YES" if len({v for v in freq.values() if v > 1}) <= 1 else "NO"Write this instead
if len(ffreq) == 2:
lo, hi = sorted(ffreq)
if lo == 1 and ffreq[lo] == 1: return "YES"
if hi - lo == 1 and ffreq[hi] == 1: return "YES"2Ignoring the three-frequency case
A single deletion moves one letter by one step and cannot reconcile three distinct frequencies, so ffreq.size() >= 3 is always NO.
Explore the code comparisonHide the code comparison
Ignoring the three-frequency case
A single deletion moves one letter by one step and cannot reconcile three distinct frequencies, so ffreq.size() >= 3 is always NO.
Don't write this
if (ffreq.size() >= 2) {
// attempt repair without bounding the number of distinct counts
}Write this instead
if (ffreq.size() == 1) return "YES";
if (ffreq.size() == 2) { /* test the two shapes */ }
return "NO";3Dropping the single-occurrence guard
hi == lo + 1 isn't sufficient; if two letters share hi, one deletion fixes only one. The offending frequency must occur exactly once.
Explore the code comparisonHide the code comparison
Dropping the single-occurrence guard
hi == lo + 1 isn't sufficient; if two letters share hi, one deletion fixes only one. The offending frequency must occur exactly once.
Don't write this
if hi - lo == 1:
return "YES"Write this instead
if hi - lo == 1 and ffreq[hi] == 1:
return "YES"Revise
Carry the pattern into the interview
Pattern to remember
Two-level counting + single-deletion repair test
Code pattern to memorize
freq = Counter(s)
ffreq = Counter(freq.values())
valid = (
len(ffreq) == 1
or (len(ffreq) == 2 and (
(lo == 1 and ffreq[lo] == 1)
or (hi - lo == 1 and ffreq[hi] == 1)))
)Rules to remember
- 1Count the letters, then count those counts — the answer lives in the second layer.
- 2Already uniform (one distinct frequency) ⇒ valid with no deletion.
- 3Two distinct frequencies are repairable only in two precise shapes; everything else is not.
- 4The deletion budget is one character total, not one per letter.
- 5Three or more distinct frequencies can never be made equal with a single deletion.
Use this pattern when
- Decide validity when frequencies must match after removing at most one character
- The single edit is a deletion (lowers one letter's count by one)
- Fixed lowercase alphabet keeps both count maps O(1)
- Goal phrased as 'all characters appear the same number of times'
Choose another pattern when
- More than one deletion allowed → generalised k-deletion feasibility
- Edit is a replacement → frequency-difference (Anagram) reasoning
- Need minimum deletions, not yes/no → sum the surpluses instead
- Need to report which character to remove → track the offending letter
Interview takeaway
Collapse the frequency map into a frequency-of-frequencies. Because one deletion moves exactly one letter's count by exactly one, a valid string is locked to at most two frequency values separated by a single, single-occurrence step.
Continue
Transfer the pattern to another problem
Use a direct variation or pattern sibling to prove the idea is reusable.
Transferable patternFrequency of frequencies (two-level counting). When validity hinges on how character counts may differ under a limited number of edits, count the letters and then count those counts. The frequency-of-frequencies map makes the handful of single-edit-repairable shapes explicit.
Practice queueDirect variations and pattern siblings
Direct follow-ups
- Sherlock and the Valid String (count-map framing)Identical frequency-of-frequencies logic stated as a pure validity check.
- Unique Number of OccurrencesComing soonTests whether all frequency values are distinct — the same second-level count.
- Sort Characters By FrequencyComing soonBuilds a frequency map then orders by count.
- First Unique Character in a StringFrequency map then a scan for the first count-1 letter.