hihoCoder:Trie图


详细的文章题目链接如下 :http://hihocoder.com/problemset/problem/1036

这周的题目没有做,现在补上,但是还是要学习一下思路。

题目要求:给出一篇长篇的文章,和一本字典(里面包含了很多英文单词),问那些文章中

那些词语在词典中出现,朴素的做法就是,直接枚举话,假设词语数量为N,每个词语长度为L,

文章的长度为M,时间上要O(N*L*M),

(1)使用Trie树,把所有字典建立起一颗trie树,那么对于文章中的匹配就是从第一个字符串开始,

在trie树中进行路径选择,当找到一个标记节点的时候表示匹配到一个词语;当文章的当前字符

串找不到下一个路径时候,表示第一个字符串开头的不是字典中的词,继续从第二个字符串开始

匹配,依次类推;

(2)显然,在我们匹配的过程中,我们既然已经从str的当前起点i开始匹配了l个长度,那么在枚举str的下一个起点i+1的时候,就意味着最开始的l-1个字符都已经在之前的计算中匹配过了,如果我们能够利用好这个信息的话,就能够大大的减少时间复杂度。”“换句话说,如果我们从str的当前起点开始,匹配了l个长度走到了A结点,如果我们把A结点对应的字符串(即从tree的0号走到A结点的路径)去掉第一个字符,形成一个新的字符串,那么这个字符串肯定是和从str的下一个起点开始,长度为l-1的子串是一样的,而如果我们能够预先找到这个字符串在tree中对应的结点B',我们就不用像之前所说的那样从0号节点走到A结点然后回到0号结点再走到B结点,而是可以直接从0号结点走到A结点然后直接跳转到B’结点然后再根据从str[i+l..k1]这一段走到B结点!

所以我们的问题规约成了:如何对于一棵给定的Trie树,找到其中每一个结点对应的后缀结点——所谓的后缀

结点就是这个结点在Trie中对应路径去掉第一个字符之后在Trie中对应的结点。

(3)对于一个trie树,可以知道,

根节点(也就是空串),去掉第一个字符之后也是空串;

与根结点相连的,也就是一级结点,去掉第一个字符,也就是他自己本身,所以一级结点的后缀结点是根结点

接着,我们继续看二级结点,显然二级结点的父节点是与他相连的一级结点,他们通过字符串‘x',假设;

那么二级结点的后缀结点就是((一级结点的后缀结点)通过字符串’x‘相连的子节点了)

依次递推,就可以求出所有结点的后缀结点
下面是我的AC代码,脑残了,有些细节写快,纠结两天,睡觉

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author yuan
 */
public class Main {

    
    public static void main(String[] args) throws FileNotFoundException {
        Scanner cin ;
        cin = new Scanner(System.in);
   //     FileInputStream ins = new FileInputStream(new File("in"));
  //      cin = new Scanner(ins);
        int n;
        n = cin.nextInt();
//        System.out.println(n);
        String context = null;
        TrieMap trieMap = new TrieMap();
        for(int i = 0; i < n; ++i){
            trieMap.insert(cin.next());
        }
        trieMap.adjustEndNode();
//        System.out.println(trieMap.foreach());
        context = cin.next();
        System.out.println(trieMap.find(context) ? "YES" : "NO");
    }

    public static class TrieMap {

        TrieNode head;
        public static final int SIZE = 30;
        public static final int WORDLENGTH = 1001;

        public TrieMap() {
            head = new TrieNode();
            head.nextNode = head;
        }
        
        public List<String> foreach(){
            ArrayList<String> list = new ArrayList<>();
            char[] chs = new char[WORDLENGTH];
            backTrace(list, this.head, chs, 0);
            return list;
        }
        
        private void backTrace(List<String> list, TrieNode curNode, char[] chs, int cur){
            Collection<TrieNode> children = curNode.children.values();
            for(TrieNode node : children){
                chs[cur] = node.ch;
                backTrace(list, node, chs, cur + 1);
                if(node.isEndNode){
                    list.add(new String(chs, 0, cur + 1));
                }
            }
        }

        public void insert(String str) {
            char chs[] = str.toCharArray();
            TrieNode curNode = this.head;
            for (int i = 0; i < chs.length; ++i) {
                TrieNode node = curNode.children.get(chs[i]);
                if (node == null) {
                    node = new TrieNode(chs[i]);
                    curNode.children.put(chs[i], node);
                }
                curNode = node;
            }
            curNode.isEndNode = true;
        }

        public void adjustEndNode() {
            LinkedList<TrieNode> queue = new LinkedList<>();
            this.head.nextNode = this.head;
            Collection<TrieNode> children = this.head.children.values();
            for(TrieNode node : children){
                node.nextNode = this.head;
                queue.add(node);
            }
//            System.out.println(queue);
            TrieNode curNode = null;
            while(!queue.isEmpty()){   
                curNode = queue.removeFirst();
                children = curNode.children.values();
                for(TrieNode node : children){
                    node.nextNode = curNode.nextNode.children.get(node.ch);
                    if(node.nextNode == null){
                        node.nextNode = this.head;
                    }
                    queue.add(node);
                }
            }
        }

        public boolean find(String str) {
            boolean flag = false;
            char[] chs = str.toCharArray();
            TrieNode curNode = this.head;
            TrieNode node = null;
            for (int i = 0; i < chs.length;++i) {
                node = curNode.children.get(chs[i]);
                if (node == null) {
                    node = curNode.nextNode;
                } else if (node.isEndNode) {
                    flag = true;
                    break;
                }
                curNode = node;
            }
            return flag;
        }
    }

    public static class TrieNode {

        boolean isEndNode = false;
        char ch = ' ';
        HashMap<Character, TrieNode> children;
        TrieNode nextNode = null;

        public TrieNode(Character ch) {
            this.isEndNode = false;
            this.ch = ch;
            this.children = new HashMap<>(TrieMap.SIZE);
            this.nextNode = null;
        }

        public TrieNode() {
            this(' ');
        }
        
        @Override
        public String toString(){
            return super.toString();
        }
    }
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值