DFA算法与敏感词识别
不得不说的字典树( Trie )
具有以下特征:
- 根节点不包含字符,除根节点外的每一个子节点都包含一个字符。
- 从根节点到某一节点,路径上经过的字符连接起来,就是该节点对应的字符串。
- 每个单词的公共前缀作为一个字符节点保存。
总结:相对Hash,Trie占用空间少、查询效率高
DFA原理
DFA(Deterministic Finite Automaton,即确定有穷自动机。其原理为:有一个有限状态集合和一些从一个状态通向另一个状态的边,每条边上标记有一个符号,其中一个状态是初态,某些状态是终态。但不同于不确定的有限自动机,DFA中不会有从同一状态出发的两条边标志有相同的符号。
举个例子:
root = {
isEnd = 0
王 = {
isEnd = 0
八 = {
isEnd = 0
蛋 = {
isEnd = 1
}
羔 = {
isEnd = 0
子 = {
isEnd = 1
}
}
}
}
}
运行逻辑:
- 检测词,切割成一个个字:王、八、羔、子
- 程序拿着
王到敏感词字典树找,有就返回王下面的子树,无则不是敏感词 - 接上面 子树 程序开始匹配第二个字
八,有就返回八下面的子树,无则不是敏感词。以此类推... - 若
isEnd = true,表明敏感词搜索已经结束,检测词不是敏感词。
程序实现
class DfaMap
{
public function get($key)
{
return isset($this->$key) ? $this->$key : null;
}
public function put($key, $value)
{
$this->$key = $value;
}
}
class DfaFilter
{
public $map = null;
public function addWordToMap($word)
{
$len = mb_strlen($word);
if (is_null($this->map)) {
$map = new DfaMap();
$map->put('isEnd', 0);
} else {
$map = $this->map;
}
$tmp = $map;
for ($i = 0; $i < $len; $i++) {
$nowWord = mb_substr($word, $i, 1);
$nowMap = $map->get($nowWord);
if (!is_null($nowMap)) {
$map = $nowMap;
} else {
$newMap = new DfaMap();
$newMap->put('isEnd', 0);
$map->put($nowWord, $newMap);
$map = $newMap;
}
if ($i == ($len - 1)) {
$map->put('isEnd', 1);
}
}
$this->map = $tmp;
}
//仅支持最大匹配
public function searchFromMap($string)
{
$len = mb_strlen($string);
$tmp = $this->map;
$map = $this->map;
$str = '';
$result = [];
for ($i = 0; $i < $len; $i++) {
$nowWord = mb_substr($string, $i, 1);
$nowMap = $map->get($nowWord);
if (!is_null($nowMap)) {
$str .= $nowWord;
if ($nowMap->get('isEnd')) {
array_push($result, $str);
$str = '';
$map = $tmp;
} else {
$map = $nowMap;
}
} else {
if (!empty($str)) {
$i--;
}
$str = '';
$map = $tmp;
}
}
return $result;
}
}
$DfaFilter = new DfaFilter();
$DfaFilter->addWordToMap( '王羔子八' );
$DfaFilter->addWordToMap( '狗' );
var_dump($DfaFilter->searchFromMap('狗'));
硬伤
- 敏感词量一上来,内存分分钟炸掉
- 无意义词
~!@#$%,拼音/字母干扰薇信/薇芯/∨信/∨芯/v送rmb
公司最近自己做敏感词识别。
我打算用ES了,@掘金 有无更好的玩法?
就比如这篇文章,就被掘金的“敏感词”屏蔽了😂😂😂
原创不易,如果觉得我的文章对你有帮助,请点赞鼓励