Word Frequency Counter
Problem
Read the text (one or more lines). Words are sequences of letters; ignore case and all other characters. Print each different word and its count in the form word count. Sort by count (largest first), and sort words with equal count alphabetically.
Constraints
- 1 <= total length <= 10^5
Examples
Input
the cat and the hat
Output
the 2 and 1 cat 1 hat 1
Solutions
A map from word to count is the standard tool for frequency problems. The final sort uses two keys: the count (descending) and the word (ascending).
/*
* Problem : Process commands line by line.
* Input : Several lines of commands.
* Output : One line for each word.
* Approach: Count in a map, then sort.
* Time : O(n + k log k)
* Space : O(k)
* Java : 8 or newer.
*/
import java.util.Scanner;
public class Main {
// This method solves the task. It receives the input and returns the answer.
static String[] solve(String[] lines) {
// Step 1: A HashMap counts the words. Key = word, value = how often it appeared.
java.util.Map<String, Integer> counts = new java.util.HashMap<>();
for (String line : lines) {
// Step 2: Make everything lower case and cut the text at every character that is not a letter.
for (String word : line.toLowerCase().split("[^a-z]+")) {
if (!word.isEmpty()) {
counts.put(word, counts.getOrDefault(word, 0) + 1);
}
}
}
// Step 3: Sort the words. A higher count goes first. For equal counts the words are in alphabetical order.
java.util.List<String> words = new java.util.ArrayList<>(counts.keySet());
words.sort((first, second) -> {
int byCount = counts.get(second) - counts.get(first);
return byCount != 0 ? byCount : first.compareTo(second);
});
// Step 4: Make the output lines.
String[] output = new String[words.size()];
for (int i = 0; i < output.length; i++) {
output[i] = words.get(i) + " " + counts.get(words.get(i));
}
return output;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
java.util.List<String> all = new java.util.ArrayList<>();
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
if (!line.isEmpty()) {
all.add(line);
}
}
String[] lines = all.toArray(new String[0]);
System.out.println(String.join("\n", solve(lines)));
}
}
Hints (2)
- Which collection maps a word to a number?
- How do you sort by two keys?