前言
最近项目需要用到敏感词过滤功能,最开始想的是使用正则匹配和mysql存储敏感词来对敏感词来进行过滤操作,但是这两种方法都感觉不好。大家都知道正则性能问题一直都是一个很大的问题,而使用mysql的话虽然可以实现,但是给数据库增加了额外的压力。后面经过Google了解到DFA(有穷自动机)算法可以解决我的问题。
什么是DFA算法?
DFA全称为:Deterministic Finite Automaton,即确定有穷自动机。其特征为:有一个有限状态集合和一些从一个状态通向另一个状态的边,每条边上标记有一个符号,其中一个状态是初态,某些状态是终态。但不同于不确定的有限自动机,DFA中不会有从同一状态出发的两条边标志有相同的符号。简单点说就是,它是是通过event和当前的state得到下一个state,即event+state=nextstate。理解为系统中有多个节点,通过传递进入的event,来确定走哪个路由至另一个节点,而节点是有限的。
说了这么多是不是还是一脸懵逼?没关系,下面我们直接来撸代码,里面都有进行注释。如果还是不懂的话,可以去google搜索dfa算法的详细介绍。
<?php
$obj = new DFA();
$obj->addKeyWord('王八蛋');
$obj->addKeyWord('王八羔子');
$obj->addKeyWord('香烟');
$obj->addKeyWord('狗儿子');
$obj->getHashMap();
var_dump($obj->searchKey('王八蛋'));
var_dump($obj->searchKey('王八'));
class DFA
{
private $arrHashMap = [];
public function getHashMap() {
print_r($this->arrHashMap);
}
public function addKeyWord($strWord) {
$len = mb_strlen($strWord, 'UTF-8');
// 传址
$arrHashMap = &$this->arrHashMap;
for ($i=0; $i < $len; $i++) {
$word = mb_substr($strWord, $i, 1, 'UTF-8');
// 已存在
if (isset($arrHashMap[$word])) {
if ($i == ($len - 1)) {
$arrHashMap[$word]['end'] = 1;
}
} else {
// 不存在
if ($i == ($len - 1)) {
$arrHashMap[$word] = [];
$arrHashMap[$word]['end'] = 1;
} else {
$arrHashMap[$word] = [];
$arrHashMap[$word]['end'] = 0;
}
}
// 传址
$arrHashMap = &$arrHashMap[$word];
}
}
public function searchKey($strWord) {
$len = mb_strlen($strWord, 'UTF-8');
$arrHashMap = $this->arrHashMap;
for ($i=0; $i < $len; $i++) {
$word = mb_substr($strWord, $i, 1, 'UTF-8');
if (!isset($arrHashMap[$word])) {
// reset hashmap
$arrHashMap = $this->arrHashMap;
continue;
}
if ($arrHashMap[$word]['end']) {
return true;
}
$arrHashMap = $arrHashMap[$word];
}
return false;
}
}