import java.util.*;

/**
 * NC97 字符串出现次数的TopK问题
 * @author d3y1
 */
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * return topK string
     * @param strings string字符串一维数组 strings
     * @param k int整型 the k
     * @return string字符串二维数组
     */
    public String[][] topKstrings (String[] strings, int k) {
        // return solution1(strings, k);
        // return solution2(strings, k);
        return solution3(strings, k);
    }

    /**
     * 哈希 + 排序
     * @param strings
     * @param k
     * @return
     */
    private String[][] solution1(String[] strings, int k){
        if(k == 0){
            return new String[][]{};
        }

        HashMap<String, Integer> map = new HashMap<>();
        for(String str: strings){
            map.put(str, map.getOrDefault(str,0)+1);
        }

        ArrayList<Map.Entry<String,Integer>> list = new ArrayList<>(map.entrySet());
        Collections.sort(list, (o1, o2) -> {
            if(!o1.getValue().equals(o2.getValue())){
                // 降序
                return o2.getValue()-o1.getValue();
            }else{
                // 升序
                return (o1.getKey()).compareTo(o2.getKey());
            }
        });

        int size = Math.min(list.size(), k);
        String[][] results = new String[size][2];
        Map.Entry<String,Integer> entry;
        for(int i=0; i<size; i++){
            entry = list.get(i);
            results[i] = new String[]{entry.getKey(), String.valueOf(entry.getValue())};
        }

        return results;
    }

    //////////////////////////////////////////////////////////////////////////////////////

    /**
     * 哈希 + 小根堆
     * @param strings
     * @param k
     * @return
     */
    private String[][] solution2(String[] strings, int k){
        if(k == 0){
            return new String[][]{};
        }

        HashMap<String, Integer> map = new HashMap<>();
        for(String str: strings){
            map.put(str, map.getOrDefault(str,0)+1);
        }

        // PriorityQueue<Node> minHeap = new PriorityQueue<>(new Comparator<Node>(){
        //     @Override
        //     public int compare(Node o1, Node o2){
        //         if(o1.cnt != o2.cnt){
        //             return o1.cnt-o2.cnt;
        //         }else{
        //             // 输入: ["abab","b","b","ab"],2  ->  输出: [["b","2"],["abab","1"]]  -> 错误
        //             // return (o2.str+o1.str).compareTo(o1.str+o2.str);
        //             // 输入: ["abab","b","b","ab"],2  ->  输出: [["b","2"],["ab","1"]]  -> 正确
        //             return (o2.str).compareTo(o1.str);
        //         }
        //     }
        // });
        PriorityQueue<Node> minHeap = new PriorityQueue<>((o1, o2) -> {
            if(o1.cnt != o2.cnt){
                // 升序
                return o1.cnt-o2.cnt;
            }else{
                // 降序
                // 输入: ["abab","b","b","ab"],2  ->  输出: [["b","2"],["abab","1"]]  -> 错误
                // return (o2.str+o1.str).compareTo(o1.str+o2.str);
                // 输入: ["abab","b","b","ab"],2  ->  输出: [["b","2"],["ab","1"]]  -> 正确
                return (o2.str).compareTo(o1.str);
            }
        });
        for(Map.Entry<String, Integer> entry: map.entrySet()){
            minHeap.offer(new Node(entry.getKey(), entry.getValue()));
            if(minHeap.size() > k){
                minHeap.poll();
            }
        }

        // maxHeap.size() <= k
        // int size = Math.min(maxHeap.size(), k);
        int size = minHeap.size();
        String[][] results = new String[size][2];
        int i = size-1;
        Node top;
        while(!minHeap.isEmpty()){
            top = minHeap.poll();
            results[i--] = new String[]{top.str, String.valueOf(top.cnt)};
        }

        return results;
    }

    private class Node {
        String str;
        int cnt;
        Node(String str, int cnt){
            this.str = str;
            this.cnt = cnt;
        }
    }

    //////////////////////////////////////////////////////////////////////////////////////

    /**
     * 自定义比较器
     */
    private class EntryComparator implements Comparator<Map.Entry<String,Integer>>{
        @Override
        public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
            if(!o1.getValue().equals(o2.getValue())){
                // 升序
                return o1.getValue()-o2.getValue();
            }else{
                // 降序
                return (o2.getKey()).compareTo(o1.getKey());
            }
        }
    }

    /**
     * 哈希 + 小根堆
     * 优化
     * @param strings
     * @param k
     * @return
     */
    private String[][] solution3(String[] strings, int k){
        if(k == 0){
            return new String[][]{};
        }

        HashMap<String, Integer> map = new HashMap<>();
        for(String str: strings){
            map.put(str, map.getOrDefault(str,0)+1);
        }

        Comparator<Map.Entry<String, Integer>> entryComparator = new EntryComparator();
        PriorityQueue<Map.Entry<String,Integer>> minHeap = new PriorityQueue<>(k, entryComparator);
        for(Map.Entry<String, Integer> entry: map.entrySet()){
            if(minHeap.size() < k){
                minHeap.offer(entry);
            }else{
                // minHeap.peek() < entry
                if(entryComparator.compare(minHeap.peek(), entry) < 0){
                    minHeap.poll();
                    minHeap.offer(entry);
                }
            }
        }

        // maxHeap.size() <= k
        // int size = Math.min(maxHeap.size(), k);
        int size = minHeap.size();
        String[][] results = new String[size][2];
        int i = size-1;
        Map.Entry<String,Integer> top;
        while(!minHeap.isEmpty()){
            top = minHeap.poll();
            results[i--] = new String[]{top.getKey(), String.valueOf(top.getValue())};
        }

        return results;
    }
}