前几天写好了字典,又刚好重温了KMP算法,恰逢遇到朋友吐槽最近被和谐的词越来越多了,于是突发奇想,想要自己实现一下敏感词屏蔽。

  基本敏感词的屏蔽说起来很简单,只要把字符串中的敏感词替换成“***”就可以了。对于子串的查找,就KMP算法就可以了。但是敏感词这么多,总不能一个一个地遍历看看里面有没有相应的词吧!

  于是我想到了前几天写的字典树。如果把它改造一下,并KMP算法结合,似乎可以节约不少时间。

  首先说明一下思路:

  对于KMP算法,这里不过多阐述。对于敏感词库,如果把它存进字典树,并在每个节点存上它的next值。在进行匹配的时候,遍历主串,提取出单个字(对于UTF-8编码,可以是任何国家的字),然后去字典树的根结点的unordered_map中进行查找是否存在。如果不存在,则对下一个字进行相同处理;如果存在,则进入该子节点,然后继续查找。字典树结构如下图:

用KMP算法与Trie字典树实现屏蔽敏感词(UTF-8编码)

  1~6是编号,后面说明一些东西的时候用到。

  Root节点里不存任何数据,只是提供一个词典的起始位置。那个表格用的是unordered_map。

  对于一个树型结构,如果直接用KMP算法中的next值来确定下一个应该在哪个节点进行查找似乎会有点问题。比如,对于5号节点,next值为1,但是要怎么用这个"1"进入要查找的节点呢?

  由于每个节点只需要知道自己如果匹配失败应该跳到哪个节点,我想了以下两种方案:

  1、把next改成存着节点的地址,类似线索二叉树,这样可以很方便地进行节点转换。

  2、用栈,每次进入子节点,就对原节点的地址进行压栈,next中存的值是要从栈中弹出几个元素。

  由于之前的字典树在遍历的时候采用list实现的栈来确定下一个词是哪个,于是我选择用第二种方案。

 

  方案有了,就是如何实现的事了。

  我先对字典树的数据结构进行修改:

DictionaryData.h

 1 #ifndef __DICTIONARYDATA_H__
 2 #define __DICTIONARYDATA_H__
 3 
 4 #include <string>
 5 #include <unordered_map>
 6 #include <memory>
 7 
 8 namespace ccx{
 9 
10 using std::string;
11 using std::unordered_map;
12 using std::shared_ptr;
13 
14 struct DictElem
15 {
16     string _word;
17     bool _isend;//是否到词尾
18     int _next;//KMP next值 此处有修改,存的是弹栈数量
19     unordered_map<string, shared_ptr<DictElem> > _words;
20 };
21 
22 typedef shared_ptr<DictElem> pDictElem;
23 
24 }
25 
26 #endif

  相应地,字典树的成员函数也要进行修改。

Dictionary.h

 1 #ifndef __DICTIONARY_H__
 2 #define __DICTIONARY_H__
 3 
 4 #include "DictionaryData.h"
 5 #include "DictionaryConf.h"
 6 
 7 #include <memory>
 8 #include <vector>
 9 #include <list>
10 
11 namespace ccx{
12 
13 using std::shared_ptr;
14 using std::vector;
15 using std::list;
16 using std::pair;
17 
18 class Dictionary
19 {
20     typedef pair<int, int> Loc;
21     typedef unordered_map<string, pDictElem>::iterator WordIt;
22     public:
23         Dictionary();
24         void push(const string & word);//插入
25         void push(vector<string> & words);//插入
26         bool search(const string & word);//查找
27         bool associate(const string & word, vector<string> & data);//联想
28         string Kmp(const string & word);
29 
30     private:
31         bool Kmp(vector<string> & word, vector<Loc> & loc);
32         void getKmpNext(const vector<string> & characters, vector<int> & next);
33         void AddWord(const string & word);
34         void splitWord(const string & word, vector<string> & characters);//把词拆成字
35         int search(vector<string> & data, pDictElem & pcur);
36         pDictElem _dictionary;
37         DictionaryConf _conf;    
38 
39 //遍历
40     public:
41         string getCurChar();
42         string getCurWord();
43         bool isEnd();
44         void resetIt();
45         void next();
46     private:
47         void resetPoint(pDictElem pcur);
48         void next(pDictElem & pcur, list<WordIt> & stackWord, list<pDictElem> & stackDict);
49         void nextWord(pDictElem & pcur, list<WordIt> & stackWord, list<pDictElem> & stackDict);
50         string getCurWord(list<WordIt> & stackWord);
51         
52         pDictElem _pcur;
53         WordIt _itcur;
54         
55 //用list实现栈,遍历时方便
56         list<WordIt> _stackWord;
57         list<pDictElem> _stackDict;
58 
59 //导入导出
60     public:
61         void leading_in();
62         void leading_out();
63 };
64 
65 }
66 
67 #endif
View Code

相关文章:

  • 2022-12-23
  • 2021-12-02
  • 2022-12-23
  • 2022-01-15
  • 2022-12-23
  • 2021-11-11
  • 2021-08-15
  • 2021-10-31
猜你喜欢
  • 2021-09-02
  • 2021-12-14
  • 2022-12-23
  • 2021-10-21
  • 2021-11-18
  • 2021-08-11
相关资源
相似解决方案