1. Hash Map Frequency Counter with Linear Prefix Scan
Intuition
Maintain unordered_map<string, int>. insert increments map[word]. erase decrements map[word]. countWordsEqualTo reads map[word]. countWordsStartingWith scans all entries checking entry.startsWith(prefix).
When to apply: Basic baseline check.
Algorithm
- Step 1: Map stores string -> frequency count.
- Step 2:
insert:map[word]++. - Step 3:
countWordsEqualTo: returnmap[word]. - Step 4:
countWordsStartingWith: sum frequencies of all keys starting withprefix. - Step 5:
erase: decrementmap[word], remove key if 0.
#include <iostream>#include <string>#include <unordered_map>using namespace std; class Trie { unordered_map<string, int> freq;public: Trie() {} void insert(string &word) { freq[word]++; } int countWordsEqualTo(string &word) { return freq.count(word) ? freq[word] : 0; } int countWordsStartingWith(string &word) { int count = 0; int len = word.length(); for (const auto& [key, val] : freq) { if (key.length() >= len && key.rfind(word, 0) == 0) { count += val; } } return count; } void erase(string &word) { if (freq.count(word)) { freq[word]--; if (freq[word] == 0) freq.erase(word); } }}; int main() { Trie trie; string w1 = "apple", w2 = "apps", pref = "app"; trie.insert(w1); trie.insert(w1); trie.insert(w2); cout << "countWordsEqualTo('apple'): " << trie.countWordsEqualTo(w1) << endl; cout << "countWordsStartingWith('app'): " << trie.countWordsStartingWith(pref) << endl; trie.erase(w1); cout << "after erase, countWordsEqualTo('apple'): " << trie.countWordsEqualTo(w1) << endl; return 0;}Time & Space Complexity
- Time complexity:
insert: O(L), countEqual: O(L), countPrefix: O(N * L), erase: O(L)— Prefix search requires iterating through all distinct words in the hash map. - Space complexity:
O(N * L)— Stores words and their frequency counts.
Why Brute Force Doesn't Scale:
With 10,000 words and 10,000 operations, naive scan requires 108 operations.
Augmenting nodes with integer counters turns prefix queries into an instant O(L) pointer read.