Word Frequency Counter

easy Java Collections

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

Best approachTime O(n + k log k)Space O(k)

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).

Same code works in Java810172125Basic features only, so nothing changes between versions.
/*
 * 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)
  1. Which collection maps a word to a number?
  2. How do you sort by two keys?