C++ 关键词过滤,屏蔽应用
关键词过滤–延伸多关键词过滤(业务需求)
首先最笨的方法是每个关键词完整的存放在一个容器中,文本内容解析完传过来的时候进行过滤,那么这种方法要对每一个关键词做一次完整比对,不相同则比较下一组,这种最坏的情况为每匹配到一个关键词都要匹配关键词个数的次数。在文本中关键词出现频率较高时,需要检查的文件个数较多时,效率很差。
所以我们把多关键词构建成多叉树结构(即字典树),对于每一个关键词只需要遍历查找一次
(从根节点到终端节点为一个关键词,终端节点中成员key(用来判断是否找到关键词)保存完整的关键词)

打个比方:关键词为{计算机,软件管家,软件包,软件名称,软件},构建成上图的结构。根节点的map容器中key(map的key)为{软,计} ,每个key对应的value指向下一节点,下面两个节点的map容器中key为别为{件},{算},依次往后。
如果要过滤的的文件中第一个字为’软’或者’计’则继续匹配,看文本中第二个字是否为’件’或者’算’,是继续匹配,每次匹配判断一下当前节点中key(成员变量key)是否为空, key不为空则说明匹配到关键词 ,下一节点不为空则继续匹配。如果不是这’软’或者’计’,则往后移动取文件的第二个字看是不是’软’或者’计’继续匹配
业务应用:点击检查设置,选则关键词点击确定按钮后,会将关键词更新到文件中,然后将文件中的关键词加载到basic_trie类中,使用search检查返回结果(包括了关键词及其位置(相对于文本首部))

百度了一下这种应该是叫做DFA算法,基于dfa构建多叉树(字典树)
#ifndef OTHERTRIE_HPP
#define OTHERTRIE_HPP
#include <typeinfo>
#include <memory>
#include <string>
#include <map>
#include <queue>
namespace mytrie {
using uchar = unsigned char;
//存储查询到的关键词结果
template<typename CharType>
class result{
public:
std::basic_string<CharType> sentence;
unsigned int start;
unsigned int fin;
public:
explicit result(){
start = 0;
fin = 0;
}
explicit result(const std::basic_string<CharType>& sentence, unsigned int start, unsigned int fin):
sentence(sentence), start(start), fin(fin){}
};
//构建多叉树结构的节点
template<typename CharType>
struct Node{
//只在一个关键词的最后一个节点保存完整的关键词 用于判断是否找到关键词
std::basic_string<CharType> key;
//map中的key保存字节流的一个字节,value指向下一节点
std::map<uchar, std::unique_ptr<Node<CharType>>> child;
};
template<typename CharType>
class basic_trie{
private:
//根节点
std::unique_ptr<Node<CharType>> _root;
bool module = typeid(uchar).hash_code() == typeid(CharType).hash_code() || typeid(char).hash_code() == typeid(CharType).hash_code();
public:
//初始化根节点
basic_trie():_root(new Node<CharType>()){}
//插入关键字
void insert(const std::basic_string<CharType>& key)
{
Node<CharType>* root = _root.get();
if(nullptr == root || key.empty())
{
return ;
}
int len;
if(module)
{
len = key.size();
}
else
{
len = key.size() * sizeof(CharType);
}
//关键词转为字节流
uchar* p = reinterpret_cast<uchar*>(const_cast<CharType*>(key.c_str()));
bool flag = false;
//一次插入形成一个分支 再次插入(调用insert)关键词形成多叉树结构
for(auto i = 0; i < len; ++i, ++p)
{
if(!root->child[*p])
{
root->child[*p] = std::unique_ptr<Node<CharType>>(new Node<CharType>());
flag = true;
}
root = root->child[*p].get();
}
//一个关键词链的终端节点保存完整的关键词
if(flag)
{
root->key = key;
}
return ;
}
//下一节点不为空则继续匹配 key不为空则说明匹配到关键词
std::vector<result<CharType>> search(const std::basic_string<CharType>& content)
{
Node<CharType>* node = _root.get();
if(nullptr == node || content.empty())
{
return std::vector<result<CharType>>();
}
int len;
if(module){
len = content.size();
} else {
len = content.size() * sizeof(CharType);
}
uchar* p = reinterpret_cast<uchar*>(const_cast<CharType*>(content.c_str()));
std::vector<result<CharType>> res;
int i = 0;
int skipNum = 0;
while(i < len)
{
//过滤关键词中间有空格特殊字符等情况 如:"秘 密" 而不是"秘密"
while ((i < len) && (*p <= 0x20))
{
++i;
++p;
++skipNum;
}
if(node->child[*p])
{
//before the root match, skipNum reset 0
if(node == _root.get())
skipNum = 0;
Node<CharType>* temp = node->child[*p].get();
if(!temp->key.empty())
{
res.emplace_back(result<CharType>(temp->key, i + 1 - skipNum - temp->key.size() * sizeof(CharType), i));
}
node = node->child[*p].get();
}
else
{
skipNum = 0;
node = _root.get();
}
++p;
++i;
}
return res;
}
};
}
#endif // OTHERTRIE_HPP
浙公网安备 33010602011771号