Understand
30-second explanation
Build the interview-ready mental model before comparing implementations.
Given a list of strings and a list of queries, return for each query how many times it appears in the strings. Don't re-scan all strings per query — that's O(n·q). Build a frequency map (string → count) in one pass over the strings, then answer each query with an O(1) lookup, defaulting to 0 for misses. Total O(n + q).
Interview contextWhat is testedWhere candidates slipHow to stand outExplore interview contextHide interview context
What's being tested
Whether you avoid the naive nested scan and instead precompute counts so repeated queries are cheap — the core 'amortize work into a hash map' instinct.
Where candidates slip
Looping over every string for every query (O(n·q)), or returning null/None for a query that never appears instead of 0.
How to stand out
Say 'I'll trade O(n) preprocessing for O(1) per query' before coding, and note the exact-match requirement so the interviewer knows you won't accidentally do substring matching.
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 previewstrings[]in map?YesNoExplore 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 — Re-scan Strings Per Query
1Hint 1Explore hintHide hint
The literal approach: for each query, walk the whole strings list and count exact matches.
2Hint 2Explore hintHide hint
How many total comparisons is that across q queries and n strings?
3Hint 3Explore hintHide hint
Notice you rescan the same unchanged strings list for every query. Can you summarize it once instead?
A · Core idea
For each query, loop over every input string and increment a counter when they match exactly.
Why it's the natural first attempt. It mirrors the question directly: 'how many times does this query appear?' becomes 'count the strings equal to this query'. No data structures, just two loops.
Where the waste is. The strings list never changes between queries, yet we walk all of it again for each one. With n strings and q queries that's n·q string comparisons. For the constraints (both up to 1000) that's a million comparisons — survivable here, but the moment either grows, it balloons. More importantly, it signals you didn't spot the reuse.
The fix in one sentence. Summarize the unchanging strings list once into a count table, then each query reads the table in constant time.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
strings = ['aba','baba','aba','xzxb'], queries = ['aba','xzxb','ab']Array state
query 'aba'
scan all 4 strings
[2, 1, 0]C · Code with teaching comments
Reference implementation previewpublic int[] matchingStrings(String[] strings, String[] queries) { int[] result = new int[queries.length]; for (int i = 0; i < queries.length; i++) { int count = 0; for (String s : strings) {Explore the full codeHide the full code
D · Why it works and complexityO(n·q) timeO(1) spaceOpen the invariant and proofExplore the proofHide the proof
The strings list is read-only across all queries — anything you recompute per query that doesn't depend on the query is wasted work. That observation is the whole reason to precompute.
ComplexityO(n·q) time, O(1) space — q queries × n strings (× up to 20 chars per compare) = O(n·q) comparisons; nothing is reused between queries.
E · Edge cases and pitfalls5query not present — Inner loop finds no match → 0.Explore edge casesHide edge cases
Edge cases handled
query not presentInner loop finds no match → 0.duplicate stringsEach occurrence increments the count, so duplicates are counted fully.
Pitfalls
- Using == instead of .equals in Java — compares references, not contents.
- Doing substring matching (contains) instead of exact equality.
- Rescanning the unchanged list for every query when one pass would suffice.
Approach 2 of 2 · Optimal
Frequency Map — Precompute Counts (Optimal)
1Hint 1Explore hintHide hint
What structure lets you ask 'how many of this exact string?' in O(1) after a single pass?
2Hint 2Explore hintHide hint
Walk strings once, incrementing a map keyed by the string. Then each query is a lookup.
3Hint 3Explore hintHide hint
For a query missing from the map, the answer is 0 — use a default-zero accessor.
A · Core idea
Make a single pass over the input strings, tallying each into a hash map keyed by the string. Then answer every query with one map lookup.
Why one pass beats many. Every query asks the same kind of question — 'how many copies of this exact string exist?' — about the same unchanging collection. So compute all those counts once: a map from string to its occurrence count. After that, the strings list is never touched again.
Why the default-zero lookup matters. A query like 'ab' may never appear in the strings. The map simply has no key for it. Reading a missing key naively returns null (Java) or raises KeyError (Python). Using getOrDefault(query, 0) / freq.get(query, 0) returns 0 cleanly — which is exactly the count of a string that never occurs.
Why this is exact-match by construction. The map key is the whole string. Two strings collide in the same bucket only if they're equal, so 'abc' and 'ab' are distinct keys and never cross-count. No extra logic needed to enforce exact matching — the data structure does it.
Why it scales. Preprocessing is O(n), each query is O(1) average, so q queries cost O(q). Compared to the brute force's O(n·q), the saving grows with the number of queries — which is the whole point when many queries hit one dataset.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
strings = ['aba','baba','aba','xzxb'], queries = ['aba','xzxb','ab']Hash-map state
after 'aba','baba','aba'
Continue scanning
counts accumulate
[2, 1, 0]C · Code with teaching comments
D · Why it works and complexityO(n + q) timeO(n) spaceOpen the invariant and proofExplore the proofHide the proof
Counting occurrences is the textbook hash-map-of-counts pattern. The mental move is 'separate the query-independent work (counting) from the per-query work (lookup)'. Do the expensive part once. The same shape powers anagram grouping, mode finding, and 'first unique character'.
ComplexityO(n + q) time, O(n) space — Building the map is one pass over n strings: O(n). Each of q queries is an O(1) average lookup: O(q). Total O(n + q); the map holds up to n distinct keys → O(n) space.
E · Edge cases and pitfalls5query equal to a substring of a string — No match — keys are whole strings, so 'ab' ≠ 'aba'.Explore edge casesHide edge cases
Edge cases handled
query equal to a substring of a stringNo match — keys are whole strings, so 'ab' ≠ 'aba'.all queries identicalEach does the same O(1) lookup; the map is built only once.
Pitfalls
- Returning null/None for an absent query instead of 0.
- Building the map inside the query loop, reintroducing O(n·q).
- Keying on something other than the whole string and accidentally matching substrings.
Practice
Try the solution yourself
Edit the optimal implementation, test another input, and observe the result.
Editable code previewpublic int[] matchingStrings(String[] strings, String[] queries) { Map<String, Integer> freq = new HashMap<>(); for (String s : strings) { freq.merge(s, 1, Integer::sum); }Explore the playgroundClose the playground
Avoid mistakes
Recognize the interview traps
Understand why common attempts fail before reviewing the corrected implementation.
1Reference equality in Java
`==` compares object references; two equal strings can be different objects and the count silently stays 0.
Explore the code comparisonHide the code comparison
Reference equality in Java
`==` compares object references; two equal strings can be different objects and the count silently stays 0.
Don't write this
if (s == queries[i]) {
count++;
}Write this instead
if (s.equals(queries[i])) {
count++;
}2Crashing on absent queries
A query not in the map has no entry; a plain get returns null (Java) or raises KeyError (Python).
Explore the code comparisonHide the code comparison
Crashing on absent queries
A query not in the map has no entry; a plain get returns null (Java) or raises KeyError (Python).
Don't write this
result.append(freq[query]) # KeyError when query absentWrite this instead
result.append(freq.get(query, 0))3Rebuilding the map per query
Constructing the frequency map inside the query loop throws away the whole benefit and is back to O(n·q).
Explore the code comparisonHide the code comparison
Rebuilding the map per query
Constructing the frequency map inside the query loop throws away the whole benefit and is back to O(n·q).
Don't write this
for query in queries:
freq = {}
for s in strings:
freq[s] = freq.get(s, 0) + 1
result.append(freq.get(query, 0))Write this instead
freq = {}
for s in strings:
freq[s] = freq.get(s, 0) + 1
result = [freq.get(q, 0) for q in queries]Revise
Carry the pattern into the interview
Pattern to remember
Precompute a frequency map, then O(1) lookups
Code pattern to memorize
freq = {}
for s in strings: freq[s] = freq.get(s, 0) + 1
result = [freq.get(query, 0) for query in queries]Rules to remember
- 1Count exact string matches —
'ab'and'abc'are different keys, no prefix or substring logic. - 2Build the map once over
strings, then each query is a singleO(1)lookup. - 3A query absent from the map must return 0, not null/None — use
getOrDefault/dict.get(q, 0). - 4Preserve query order in the output: the i-th answer corresponds to the i-th query.
- 5Strings are case-sensitive and matched in full; don't trim or lowercase unless the prompt says so.
Use this pattern when
- Repeated 'how many times does X appear?' questions over a fixed collection
- Many lookups against the same dataset — amortize the scan once
- Exact-match counting where a hash map's O(1) average lookup fits
Choose another pattern when
- Queries are substring or prefix matches → trie or suffix structure, not a flat frequency map
- The collection changes between queries → you'd rebuild or maintain incrementally
- You need ranking by frequency, not point lookups → heap or sorted counts
Interview takeaway
When the same dataset is queried many times for exact-match counts, pay O(n) once to build a frequency map, then every query is O(1). The whole skill is recognizing 'count occurrences' equals 'hash map of counts'.
Continue
Transfer the pattern to another problem
Use a direct variation or pattern sibling to prove the idea is reusable.
Transferable patternPrecompute a frequency map, then O(1) lookups. When many exact-match count queries hit one fixed dataset, summarize it once (O(n)) and answer each query in O(1). The hash key being the whole string enforces exact matching for free.
Practice queueDirect variations and pattern siblings
Direct follow-ups
- First Unique CharacterSame frequency-map pass, then scan for the first count of 1.
- Group AnagramsMap a canonical key (sorted letters) to a bucket of strings.
- Frequency QueriesComing soonMaintain a value→count map plus a count→how-many map for live add/remove/has-frequency queries.