hdu1251-字典树

  背景: 最近培训讲到acm了,当年没有好好呆着所以就,特别菜.学习下吧,刷刷水题.

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=1251

  这题我傻傻的用String的startsWith()去做了,整挺好,tle了,大概是因为要遍历输入的字符,然后数据量比较大.这的确比较挫.

  去看了下思路,然后去了解了下Trie字典树.

字典树图例

  看图可以很明显看出来,从一个root节点展开,然后每个字符分别作为子节点继续展开,直到单词结束,将最后遍历到的字符标记成terminal,此处terminal对应我题解中的isEnd.用于告知程序此处是一个单词结束.

  也没什么好奖的啦,咱网友水平都是有的嘛,直接上一下题解,都有注释啦应该挺详细了.

package hdu.p1251;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

/**
 * @author Relic
 * @desc hdu1251 统计难题(字典树)
 * @date 2019-12-04 19:50
 */
public class Main {
    
    

    /*
      url: http://acm.hdu.edu.cn/showproblem.php?pid=1251

      解法字典树: 看别人的思路有两种解法
                1.直接map去解
                    - 遍历输入的数据中的字符,将字符作为字符串放入 map (<String,Integer>)中,如果有则直接+1
                    - 每次遍历将字符串更新为从第一个到当前遍历到的所有的字符.
                      形象点: 两个输入 abc acm 遍历完之后为 { a:2, ab:1, abc:1, ac:1, acm:1}
                    - 输入结束后,去取数量的时候,直接根据输入的字符去 map 中取出 value
                    - 当然 这就特别挫,挺 low 的,而且随着文本的增加 这个 map 也会非常的,恶心!
                2.使用字典树实现.

      这里就讲一下字典树的解法

      定义一个节点类,四个属性:
                        - value: 此节点中字符的值
                        - count: 包含父节点到此路径所代表的的字符串出现的次数
                        - sons: 子节点元素
                        - isEnd: 是否已经是节点尾部

      字典树主要两个方法: 插入与查询
      插入:  1.将输入的字符串转化成字符数组
            2.将当前调用方法的节点作为父节点
            3.遍历字符数组,根据当前字符定位所处子节点的位置 (从'a'开始,直接减去'a')
            4.如果确定的位置上的字典树节点不存在,则新建节点,将字符的值赋值给节点
            5.字典树节点出现的次数: count 累加一次
            6.将下一次循环要遍历的节点更新为子节点
            7.当字符遍历到最后一个时, 将当前节点的isEnd状态更新为true

      查询:  1.查找更简单了
            2.直接更新字符数组去依次遍历节点,如果其中一个节点为空直接返回0,输出最后节点的 count 值
      难点还在于打印字典树.... 但题目其实并不需要这个
     */

    /**
     * 每个节点最多为26个字母
     */
    private static final int NODE_SIZE = 26;

    public static void main(String[] args) {
    
    
        Scanner sc = new Scanner(System.in);
        TrieNode root = new TrieNode();
        String str;
        while (!"".equals(str = sc.nextLine())) {
    
    
            root.insert(str);
        }
        /// 打印测试下字典树的遍历
        //  System.out.println(root);
        while (sc.hasNextLine()) {
    
    
            System.out.println(root.search(sc.nextLine()));
        }
    }

    static class TrieNode {
    
    
        StringBuilder word = new StringBuilder();
        char value;
        int count;
        TrieNode[] sons;
        boolean isEnd;

        TrieNode() {
    
    
            sons = new TrieNode[NODE_SIZE];
        }

        /**
         * 在当前节点开始插入字符
         *
         * @param str 需要插入的字符
         */
        public void insert(String str) {
    
    
            count++;
            // String转为字符数组
            TrieNode node = this;
            char[] chars = str.toCharArray();
            for (int i = 0; i < chars.length; i++) {
    
    
                char ch = chars[i];
                //获取在子节点中的位置
                int pos = ch - 'a';
                TrieNode son = node.sons[pos];
                if (son == null) {
    
    
                    son = new TrieNode();
                    son.value = ch;
                    node.sons[pos] = son;
                }
                son.count++;
                node = son;
                if (i == chars.length - 1) {
    
    
                    son.isEnd = true;
                }
            }
        }

        /**
         * 从当前节点开始所搜以此关键词为前缀的单词有多少个
         *
         * @param keywords 关键词
         * @return 个数
         */
        public int search(String keywords) {
    
    
            char[] chars = keywords.toCharArray();
            TrieNode node = this;
            for (char ch : chars) {
    
    
                TrieNode son = node.sons[ch - 'a'];
                if (son == null) {
    
    
                    return 0;
                }
                node = son;
            }
            return node.count;
        }

        /**
         * 重写一下toString方法,查看字典树
         *
         * @return 字母顺序的字典树
         */
        @Override
        public String toString() {
    
    
            List<String> strList = new ArrayList<>();
            travel(this, strList);
            return strList.toString();
        }

        public void travel(TrieNode node, List<String> strList) {
    
    
            if (node.isEnd) {
    
    
                strList.add(word.toString());
            }
            for (TrieNode son : node.sons) {
    
    
                if (son != null) {
    
    
                    word.append(son.value);
                    travel(son, strList);
                    //回溯到原来的长度
                    word.deleteCharAt(word.length() - 1);
                }
            }
        }

    }
}

  后面两天抽一天写下spring batch,最近有应用场景,记录下,顺便帮助下有需求的人,这东西中文文档太少,磕磕绊绊问题也太多了.

猜你喜欢

转载自blog.csdn.net/hexiaodiao/article/details/103431395