Interview question
Count normalized words in text
Implements a word-frequency counter with normalization, punctuation handling, and stable ordering.
TL;DR
Implements a word-frequency counter with normalization, punctuation handling, and stable ordering.
String processing, dictionaries, normalization, ordering, edge cases, and clear spoken reasoning.
Practice the problem like a real interview: restate, reason, implement, and test.
Implement CountWords(string text) returning a list of (word, count) pairs. Words are case-insensitive, punctuation is ignored, and results are ordered by count descending, then word ascending. Treat letters and digits as word characters.
Input: "API, api! SQL api?"
Output: [ ("api", 3), ("sql", 1) ]
I would scan the string once and build the current word with a StringBuilder. When I hit a non-letter-or-digit character, I flush the current word into a dictionary. At the end I flush once more for a trailing word. Then I order by count descending and word ascending. I would say this out loud because the important parts are normalization, delimiter handling, and deterministic output.
public static IReadOnlyList<(string Word, int Count)> CountWords(string? text)
{
if (string.IsNullOrWhiteSpace(text))
{
return Array.Empty<(string Word, int Count)>();
}
var counts = new Dictionary<string, int>(StringComparer.Ordinal);
var current = new StringBuilder();
void Flush()
{
if (current.Length == 0)
{
return;
}
var word = current.ToString().ToLowerInvariant();
counts[word] = counts.TryGetValue(word, out var count) ? count + 1 : 1;
current.Clear();
}
foreach (var ch in text)
{
if (char.IsLetterOrDigit(ch))
{
current.Append(ch);
}
else
{
Flush();
}
}
Flush();
return counts
.OrderByDescending(pair => pair.Value)
.ThenBy(pair => pair.Key, StringComparer.Ordinal)
.Select(pair => (pair.Key, pair.Value))
.ToList();
}
The scan is O(n), where n is the text length. Sorting unique words costs O(k log k), where k is the number of unique words. Space is O(k) for the dictionary plus the current word buffer.
api---sql.API api Api.http2.Next in Strings & Collections: First Unique Character