当前位置: 首页 > news >正文

郴州新网招聘手机版杭州百度首页优化

郴州新网招聘手机版,杭州百度首页优化,wordpress模板上传不,工信部网站实名认证怎么做目录 前置知识 概述 核心概念:fail指针 作用 构建 图示 Code 成员变量 创建销毁 添加词库 文本扫描 复杂度 Code 前置知识 在此前,你应该首先了解trie树(字典树)的概念: 「字符串」详解Trie&#xff0…

目录

前置知识

概述

核心概念:fail指针

作用

构建

图示

Code

成员变量

创建销毁

添加词库

文本扫描

复杂度 

Code


前置知识

在此前,你应该首先了解trie树(字典树)的概念:

「字符串」详解Trie(字典树|前缀树)并实现对应的功能 / 手撕数据结构(C++)

我们建议你先阅读这篇文章,以了解我们实现字典树所用的一些结构,比如trie_node,以及词尾节点存储了整个字符串这些概念。


概述

AC自动机是能以线性时间复杂度对整个文本进行黑名单词汇统计的数据结构。

我们先将黑名单语句词库逐条插入这棵字典树。当我们扫描文本时,就能以一次遍历实现对文本中所有出现的黑名单库中的语句进行统计。

你应该这样认识AC自动机:它首先是一颗字典树,其次是它还有一个成员:fail指针数组

就如同他的名字一样,这个自动机结构会根据输入的内容自动地调整内部的行为,这就是它能以单次扫描就匹配全部文本的奥妙之处。


核心概念:fail指针

作为一颗独特的字典树,AC自动机还有fail指针数组。

作用

它的定义是:trie_node* fail[i];

fail指针,顾名思议,它会在匹配失败时指向另一个可行的节点。

这个数组是干什么的?你可以认为如果我们的匹配失败,那么fail指针就会启动,并跳转到一个具有和当前节点相同字符的节点上,例如fail[5]存储了与5号节点具有相同字符的节点地址。

这样讲似乎很让人不能理解,我们来看这张图:

预先插入了以上敏感词字符串。如果我们要匹配的是“sher”,如果进行朴素匹配那么我们只能得到“she”,但我们知道有三个字符串“she”“he”“her”都在其中,我们怎么才能只扫描一次就得到这些字符串呢?fail指针做了这件事。

当我们匹配到字符'r'时,发现无法继续,此时fail指针发力了,由于位于5号节点,而fail[5]->8号节点的地址,所以我们可以跳转到8号节点继续匹配。

这样一来“she”“he”“her”就全部匹配上了。

下面我们来说说fail指针是怎么构建的。

构建

有三条原则:

1.root节点的fail指针指向自己,此后按层遍历,进行各个层次的fail指针的构建。

2.如果某节点A的fail指针指向另一非root节点B,那么A的字符与B的字符是相同的。如fail[5]->8号节点。

3.如果当前节点child1的父节点father1的fail指针指向另一节点father2,而father2名下恰好有与child1同字符的child2,那么fail[child1]->child2,如果没有同字符的child2,那么fail[child1]->root。

我们重点解释第三条:这是为了保证我们的匹配是有效的:

如果fail[father1]->father2,那么father1与father2是相同字符节点,那么可以保证从child1通过fail指针跳转到child2时,他们前面匹配过得一部分字符串(即father1和father2)是相同的,这样一来,可以保证在我们进行文本匹配时,不会出现3号节点跳转到6号节点(看上图)的现象。

*注意*:也有child1跳转到child2时不存在前面匹配过的一部分字符串,即child1的字符可能是某个敏感词的首字符,这时候跳转到root进行匹配(即第三条规则的最后一句话)。

图示

我们希望你结合以上三条规则在图上画出全部fail指针,在此我们给出答案:

Code

node->idx代表节点编号,它同时担任了fail指针数组下标的功能。

std::vector <trie_node*> fail;
void bulid_fail() {fail.resize(val_size,root);std::queue<trie_node*>que;que.push(root);while (!que.empty()) {int len = que.size();while (len--) {trie_node* node = que.front(); que.pop();for (int i = 0; i < branches; i++) {trie_node*& father= node;trie_node*& child = node->next[i];if (child != nullptr) {que.push(child);if (fail[father->idx]->next[i] != nullptr&&fail[father->idx]->next[i]!=child)fail[child->idx] = fail[father->idx]->next[i];}}}}//int i = 0;//for (const trie_node* node : fail)std::cout << i++ << "->" << node->idx << std::endl;
}

接下来,我们封装AC_automaton类,实现AC自动机的基本功能。(Code和测试案例附后)


成员变量

ac自动机以私有方式继承trie树,并封装std::vector <trie_node*> fail指针。

class AC_automaton:private trie{
private:std::vector <trie_node*> fail;...
public:...}
};

创建销毁

提供三种构造,一种析构函数,。

这些内容均于「字符串」详解Trie(字典树|前缀树)并实现对应的功能 / 手撕数据结构(C++)中提及。

我们删除了复制构造和复制赋值等于号,以防止两份AC自动机的fail指针指向同一组地址。

AC_automaton(int branch = 128) :trie(branch), fail(1, root) {};
AC_automaton(const trie& data) :trie(data), fail(1,root) {bulid_fail();
};
AC_automaton(const AC_automaton& another) = delete;
AC_automaton(const std::initializer_list<std::string> ini_list) :trie(ini_list), fail(1, root) {bulid_fail();
}
~AC_automaton() {};
AC_automaton& operator=(const AC_automaton& another) = delete;

添加词库

提供insert_blackedlist与它的重载,支持单条语句和多语句插入。

随后调用build_fail重建fail指针。

void insert_blacklist(const std::string str) {trie::insert(str);bulid_fail();
}
void insert_blacklist(const std::vector<std::string> strs) {trie::insert(strs);bulid_fail();
}

文本扫描

文本扫描是AC自动机的另一个核心部分,它是fail指针的具体应用。

初始化指针p指向根节点。text为待扫描文本。

对于这个流程,我们总结为以下几点:

1.当p指向根节点并且黑名单字符串库不存在当前text[i]开头的字符,那么跳过当前字符。

2.当p指针指向的节点储存了str黑名单字符串,将其加入统计结果。

3.当p指针无法继续向下匹配,启动fail指针,前往fail[p->idx](p->idx表示当前节点的序号,fail[p->idx]存储当前节点的跳转位置)

*注意*:因为跳转后的节点字符与跳转前的节点字符相同,此时请不要向后移动text文本字符,即不要进行i++操作,这会导致匹配错位。

4.脱离循环后将p位置可能存在的字符串加入统计结果,以及p位置的fail指针指向的节点也可能有字符串,他们都需要加入统计结果。这是由于p抵达最后一个字符时循环已经结束,并且fail指针有着p的相同前缀以及相同字符,他们都有可能成为统计结果。

std::vector<std::string> query(std::string text) {std::vector<std::string> ans;trie_node* p = root;const int len = text.size();for (int i = 0; i < len;) {if (p == root && p->next[text[i]] == nullptr)i++;if (p->str.empty()==false)ans.push_back(p->str);if (p->next[text[i]] != nullptr)p = p->next[text[i++]];else p = fail[p->idx];}if (p->str.empty() == false)ans.push_back(p->str);if(fail[p->idx]->str.empty()==false); ans.push_back(fail[p->idx]->str);return ans;
}

复杂度 

时间复杂度:插入:O(n*m) 扫描:O(m)

空间复杂度:插入:O(n*m) 扫描:O(1)

n:插入字符串数目

m:插入/待扫描字符串长度


Code

#include <queue>
#include "trie.h"
#ifndef AC_AUTOMATON
#define AC_AUTOMATON
class AC_automaton:private trie{
private:std::vector <trie_node*> fail;void bulid_fail() {fail.resize(val_size,root);std::queue<trie_node*>que;que.push(root);while (!que.empty()) {int len = que.size();while (len--) {trie_node* node = que.front(); que.pop();for (int i = 0; i < branches; i++) {trie_node*& father= node;trie_node*& child = node->next[i];if (child != nullptr) {que.push(child);if (fail[father->idx]->next[i] != nullptr&&fail[father->idx]->next[i]!=child)fail[child->idx] = fail[father->idx]->next[i];}}}}//int i = 0;//for (const trie_node* node : fail)std::cout << i++ << "->" << node->idx << std::endl;}
public:AC_automaton(int branch = 128) :trie(branch), fail(1, root) {};AC_automaton(const trie& data) :trie(data), fail(1,root) {bulid_fail();};AC_automaton(const AC_automaton& another) = delete;AC_automaton(const std::initializer_list<std::string> ini_list) :trie(ini_list), fail(1, root) {bulid_fail();}~AC_automaton() {};AC_automaton& operator=(const AC_automaton& another) = delete;void insert_blacklist(const std::string str) {trie::insert(str);bulid_fail();}void insert_blacklist(const std::vector<std::string> strs) {trie::insert(strs);bulid_fail();}std::vector<std::string> query(std::string text) {std::vector<std::string> ans;trie_node* p = root;const int len = text.size();for (int i = 0; i < len;) {if (p == root && p->next[text[i]] == nullptr)i++;if (p->str.empty()==false)ans.push_back(p->str);if (p->next[text[i]] != nullptr)p = p->next[text[i++]];else p = fail[p->idx];}if (p->str.empty() == false)ans.push_back(p->str);if(fail[p->idx]->str.empty()==false); ans.push_back(fail[p->idx]->str);return ans;}
};
#endif

测试 

#include <iostream>
#include "AC_automaton.h"
using namespace std;
int AC_automaton_test() {AC_automaton ACA = { "say","she","shy","he","her","hee","ee"};vector<string>&& ans = ACA.query("sherhee");cout << "-----------------test-----------------" << endl;for (const string& str : ans)cout << str << endl;;return 0;
}

http://www.hkea.cn/news/663078/

相关文章:

  • 市场营销产品推广策划方案seo合作代理
  • 东莞专业网站建设推广搜索引擎网络排名
  • 服务器做网站用什么环境好销售营销方案100例
  • 如何做DJ网站英文seo外链
  • 网站统计源码下载百度推广的步骤
  • 本地网站建设seo推广的方法
  • 东莞好的网站建设效果seo和sem分别是什么
  • 最新版wordpress背景手机网络优化软件
  • 丛台企业做网站推广免费建一级域名网站
  • 集宁网站建设免费网站推广网站破解版
  • 网站建设域名的购买有域名和服务器怎么建网站
  • 深圳有什么网站长沙百度seo
  • 台州企业网站模板建站怎么在百度上做公司网页
  • 烟台网站建设联系企汇互联专业网站维护收费标准
  • 网络客户服务平台搜索优化推广公司
  • 建设网站技术方案线上教育培训机构十大排名
  • 沈阳人流seo优化师就业前景
  • 开发区网站制作公司seo关键词有话要多少钱
  • 网站被篡改处理app拉新平台
  • 在线房屋设计网站seo推广平台服务
  • 电子政务门户网站建设代码短链接生成网址
  • 崔各庄地区网站建设百度非企渠道开户
  • 怎么用自己的电脑做网站服务器产品推广平台排行榜
  • 中国做的比较好的电商网站有哪些哈市今日头条最新
  • 微信怎么做网站推广百度网站优化培训
  • 网站开发支持多个币种电子技术培训机构
  • 移动网站设计与制作怎么找关键词
  • 国内移动端网站做的最好的厦门人才网597人才网
  • 建网站收费吗aso关键词覆盖优化
  • 西安的网站设计与制作首页微信视频号怎么推广引流