IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 使用前缀树实现敏感词过滤(字符串的匹配) -> 正文阅读

[数据结构与算法]使用前缀树实现敏感词过滤(字符串的匹配)

概述

记录一下,学习使用前缀树,来实现从一传字符串中找出敏感词,并替换掉。起始就是一个字符串匹配算法,但是要清楚,铭感词可以有很多个,我们从数据库或者文件中加载我们的敏感词,并且加入到树中,组成一个铭感词树,下面就是当我们传递过来一个字符串时,我们使用这个树过滤,得出我们想要的结果

树的结构

假设我们的敏感词为 abc,abb,aac,ad,那我们期望得到一颗如下的树,按照我们字符前缀组成的一颗树,此外我们还需要对该串的结束字符做一个标识,即叶子节点就是结束字符。
在这里插入图片描述
代码设计如下

	/**
     * 前缀树
     */
    private class PrefixTreeNode {
        /**
         * val 
         */
        private char val;
        /**
         * 标识当前字符是否为结束字符
         */
        private boolean isEndKeyWord = false;
        /**
         * 当前节点的下一个节点
         */
        private Map<Character, PrefixTreeNode> subRoot = new HashMap<>();
}

整体代码如下

public class SensitiveWordFilter {
    private final PrefixTreeNode prefixTreeRoot = new PrefixTreeNode();
    private final String REPLACE_SYMBOL = "***";


    // 初始化前缀树
    {
        // 加载文件
        InputStream inputStream = SensitiveWordFilter.class.getClassLoader().getResourceAsStream("sensitive_word.txt");
        // 字节流转换为字符流
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        String data = null;
        while (true) {
            try {
                if ((data = reader.readLine()) == null) break;
                this.addKeyWord(data);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 添加前缀树关键词
     *
     * @param data
     */
    public void addKeyWord(String data) {
        // node 为当前节点
        PrefixTreeNode node = prefixTreeRoot;
        for (int i = 0; i < data.length(); i++) {
            char val = data.charAt(i);
            // 获取一下node节点的子节点val
            PrefixTreeNode subNode = node.getSubNodeByKey(val);
            if (subNode == null) {
                //  当前节点不存在该子节点,需要初始化,添加一下
                subNode = new PrefixTreeNode(val);
                // 该节点为最终节点的话,设置一下
                if (data.length() == (i + 1)) subNode.setEndKeyWord(true);
                // 添加到我们的当前节点的子节点下
                node.addSubNode(val, subNode);
            }
            // 当前节点存在该字节点,修改node节点为该字节点,进入下一次循环
            node = subNode;
        }
    }

    /**
     * 对我们的字符串进行敏感词检查
     *
     * @param data
     * @return
     */
    public String doFilter(String data) {
        if (data == null || data.length() == 0) return null;
        StringBuilder result = new StringBuilder();
        PrefixTreeNode curNode;
        for (int i = 0; i < data.length(); i++) {
            curNode = prefixTreeRoot;
            char val = data.charAt(i);
            // 检查当前节点是否有该字符节点
            PrefixTreeNode subNode = curNode.getSubNodeByKey(val);
            if (subNode == null) {
                result.append(val);
            } else {
                // 设置当前节点为该子节点
                curNode = subNode;
                // 存在的话
                int startIndex = i;
                for (int j = startIndex + 1; j < data.length(); j++) {
                    char target = data.charAt(j);
                    PrefixTreeNode curSubNode = curNode.getSubNodeByKey(target);
                    // 该字符不是当前节点的子节点
                    if (curSubNode == null) {
                        // 回到原来的地方
                        startIndex = j;
                        break;
                    } else if (curSubNode.isEndKeyWord()) {
                        // 当前节点不存该子几点时或者当前节点对应的该字符节点为结束字符时
                        result.append(REPLACE_SYMBOL);
                        startIndex = j + 1;
                        break;
                    } else {
                        curNode = curSubNode;
                    }
                }
                i = startIndex - 1;
            }
        }
        return result.toString();
    }

    /**
     * 前缀树
     */
    private class PrefixTreeNode {
        /**
         * val
         */
        private char val;
        /**
         * 标识当前字符是否为结束字符
         */
        private boolean isEndKeyWord = false;

        public boolean isEndKeyWord() {
            return isEndKeyWord;
        }

        public void setEndKeyWord(boolean endKeyWord) {
            isEndKeyWord = endKeyWord;
        }

        /**
         * 当前节点的下一个节点
         */
        private Map<Character, PrefixTreeNode> subRoot = new HashMap<>();

        public PrefixTreeNode(char val, Map<Character, PrefixTreeNode> subRoot) {
            this.val = val;
            this.subRoot = subRoot;
        }

        public PrefixTreeNode(char val) {
            this.val = val;
        }

        /**
         * 根据key获取当前节点的字节点
         *
         * @param val
         * @return
         */
        public PrefixTreeNode getSubNodeByKey(char val) {
            return subRoot.get(val);
        }

        /**
         * 增加一个子节点
         *
         * @param val
         * @param node
         */
        public void addSubNode(char val, PrefixTreeNode node) {
            subRoot.put(val, node);
        }

        public PrefixTreeNode() {
        }

        public Character getVal() {
            return val;
        }

        public void setVal(Character val) {
            this.val = val;
        }

        public Map<Character, PrefixTreeNode> getSubRoot() {
            return subRoot;
        }

        public void setSubRoot(Map<Character, PrefixTreeNode> subRoot) {
            this.subRoot = subRoot;
        }
    }
}

代码解读,设计思路

首先这个树是一个内部类,因为只有敏感词过滤类使用该类,是一个特有的数据结构,设计思想可以参考JDK,好多类的设计,都是高内聚,对类的操作,高内聚,外部类,仅仅只是要调用而已。
每次创建对象时,都会从类路径下加载一个文件,存储我们铭感词的文件,然后,加入到我们的敏感词树中。
一开始我们就创建一个根节点,不存储数据,他的子节点才开始存储数据,为了和后面的设计保持一致。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-04-28 12:04:59  更:2022-04-28 12:08:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 6:28:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码