java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot敏感词过滤

SpringBoot使用前缀树实现敏感词过滤示例

作者:I'm Jie

最近项目用到了敏感词过滤,本文主要就来介绍一下SpringBoot使用前缀树实现敏感词过滤示例,具有一定的参考价值,感兴趣的可以了解一下

前缀树介绍

前缀树(Trie),也称为字典树或前缀字典树,是一种特殊的多叉树数据结构。它用于高效地存储和检索字符串集合。以下是前缀树的常见数据结构和相关术语:

基于这些术语,前缀树的基本操作包括插入、搜索、删除和前缀匹配。通过构建一个前缀树,可以实现高效地存储和检索大量字符串,快速判断一个字符串是否是集合中的成员,并找到具有给定前缀的所有字符串。

节点

前缀树(Trie)的节点结构通常由两部分组成:节点值和子节点集合。子节点集合通常使用散列表、数组或其他数据结构。

我们还需要使用 endOfWord 标识该节点是否为一个单词的结尾。如果某个节点的 isEndOfWord 为 true,则表示从根节点到当前节点的路径构成了一个完整的单词,即过滤词。

下面是一个示例的前缀树节点结构:

class TrieNode {
    private Map<Character, TrieNode> children; // 子节点集合
    private boolean endOfWord; // 标识是否为单词的结尾

    public TrieNode() {
        children = new HashMap<>();
        endOfWord = false;
    }

    public Map<Character, TrieNode> getChildren() {
        return children;
    }

    public boolean isEndOfWord() {
        return endOfWord;
    }

    public void setEndOfWord(boolean endOfWord) {
        this.endOfWord = endOfWord;
    }
}

通过这种节点结构,我们可以链接节点以形成一个树形结构,每个节点代表一个字符。通过不断地添加子节点,我们可以构建出完整的前缀树,用于高效地存储和搜索字符串集合。

初始化前缀树

前缀树有一个根节点(Root Node)作为起始节点。前缀树的初始化过程如下:

通过上述初始化过程,我们可以构建一个包含所有字符串集合中字符串的前缀树。这样,在后续的搜索或过滤操作中,我们可以利用前缀树的特性来提高效率,快速地查找和处理字符串。

添加敏感词

我们可以将一个敏感词插入到前缀树中。每个字符都对应着一个节点,通过连接节点的方式,形成了一个表示敏感词的路径。最后一个字符对应的节点被标记为敏感词的结尾,以便在后续的搜索操作中判断是否存在完整的敏感词。前缀树中添加一个敏感词的过程如下:

删除敏感词

要删除前缀树中的敏感词,可以采用递归的方式遍历前缀树来查找待删除的敏感词。默认从根节点开始,并通过字符索引递归地将路径沿着前缀树向下移动。

敏感词过滤

假设我们已经初始化完成一个前缀树,其中包含以下敏感词:「bad」、「bar」、「byd」、「cao」,我们对「This is a bad example. The bar is closed.」进行过滤:

通过前缀树,我们可以高效地找到和替换敏感词,将其过滤或标记为合适的内容。这样能够保护用户免受敏感词的影响。

代码实现

代码实现如下:

import java.util.HashMap;
import java.util.Map;

public class TrieFilter {
    private TrieNode root;

    public TrieFilter() {
        root = new TrieNode();
    }

    // 添加敏感词
    public void addWord(String word) {
        TrieNode current = root;
        for (char c : word.toCharArray()) {
            current = current.getChildren().computeIfAbsent(c, k -> new TrieNode());
        }
        current.setEndOfWord(true);
    }

    // 删除敏感词
    public void deleteWord(String word) {
        deleteWord(root, word, 0);
    }

    private boolean deleteWord(TrieNode current, String word, int index) {
        if (index == word.length()) {
            if (!current.isEndOfWord()) {
                return false; // 单词不存在于前缀树中,无需删除
            }
            current.setEndOfWord(false); // 将当前节点标记为非单词结尾
            return current.getChildren().isEmpty(); // 判断当前节点是否有其他子节点
        }

        char c = word.charAt(index);
        TrieNode child = current.getChildren().get(c);
        if (child == null) {
            return false; // 单词不存在于前缀树中,无需删除
        }

        boolean shouldDeleteChild = deleteWord(child, word, index + 1);

        if (shouldDeleteChild) {
            current.getChildren().remove(c); // 删除当前字符对应的子节点
            return current.getChildren().isEmpty(); // 判断当前节点是否有其他子节点
        }

        return false;
    }

    // 敏感词过滤
    public String filter(String text) {
        StringBuilder filteredText = new StringBuilder();
        StringBuilder currentWord = new StringBuilder();
        TrieNode current = root;

        for (int i = 0; i < text.length(); i++) {
            char c = text.charAt(i);

            if (Character.isLetterOrDigit(c)) {  // 字母或数字,继续匹配
                current = current.getChildren().get(Character.toLowerCase(c));
                if (current != null) {
                    currentWord.append(c);
                    if (current.isEndOfWord()) {
                        currentWord.replace(0, currentWord.length(), "***");
                    }
                } else {
                    filteredText.append(currentWord);
                    filteredText.append(c);
                    currentWord.setLength(0);
                    current = root;
                }
            } else {  // 非字母或数字,结束当前单词匹配
                filteredText.append(currentWord);
                filteredText.append(c);
                currentWord.setLength(0);
                current = root;
            }
        }

        filteredText.append(currentWord);
        return filteredText.toString();
    }

    // 节点结构
    class TrieNode {
        private Map<Character, TrieNode> children;
        private boolean endOfWord;

        public TrieNode() {
            children = new HashMap<>();
            endOfWord = false;
        }

        public Map<Character, TrieNode> getChildren() {
            return children;
        }

        public boolean isEndOfWord() {
            return endOfWord;
        }

        public void setEndOfWord(boolean endOfWord) {
            this.endOfWord = endOfWord;
        }
    }
}

使用示例:

public static void main(String[] args) {
  TrieFilter filter = new TrieFilter();
  filter.addWord("敏感词1");
  filter.addWord("敏感词2");
  filter.deleteWord("敏感词2");

  String text = "这是一段包含敏感词1和敏感词2的文本";
  String filteredText = filter.filter(text);
  System.out.println(filteredText);
}

输出结果:

这是一段包含***和敏感词2的文本

在上述示例中,我们创建了一个 TrieFilter 类来实现敏感词过滤功能。使用 addWord 方法将敏感词添加到前缀树中,然后使用 filter 方法对文本进行过滤,将匹配到的敏感词替换为 ***,使用 deleteWord 方法从前缀树中删除敏感词。

到此这篇关于SpringBoot使用前缀树实现敏感词过滤示例的文章就介绍到这了,更多相关SpringBoot敏感词过滤内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文