违禁词过滤完整设计与优化(前缀匹配、二分查找)
2019独角兽企业重金招聘Python工程师标准>>>
可能不止在天朝,绝大多数网站都会需要违禁词过滤模块,用于对不雅言论进行屏蔽;所以这个应该算是网站的基础功能。大概在去年的时候我开发过这个功能,当时用6600+(词数)的违禁词库,过滤2000+(字数)的文章,耗时大概20ms左右,当时自我感觉还挺良好。过了这一段时间,回想一下,其实有不少地方可以做优化、可以总结;于是从头到尾捋了一遍。
原始需求:
逻辑整理:
/*** 过滤违禁词* @param sentence:待过滤字符串* @return*/private BadInfo findBadWord(String sentence) {CharType[] charTypeArray = getCharTypes(sentence);//获取出每个字符的类型BadInfo result = new BadInfo(sentence);BadWordToken token;int i = 0, j;int length = sentence.length();int foundIndex;char[] charArray;StringBuffer wordBuf = new StringBuffer();while (i < length) {// 只处理汉字和字母if (CharType.HANZI == charTypeArray[i]|| CharType.LETTER == charTypeArray[i]) {j = i + 1;wordBuf.delete(0, wordBuf.length());//新的一轮匹配,清除掉原来的wordBuf.append(sentence.charAt(i));charArray = new char[] { sentence.charAt(i) };foundIndex = wordDict.getPrefixMatch(charArray);//前缀匹配违禁词//foundIndex表示记录了前缀匹配的位置while (j <= length && foundIndex != -1) {// 表示找到了if (wordDict.isEqual(charArray, foundIndex)&& charArray.length > 1) {token = new BadWordToken(new String(charArray), i, j);result.addToken(token);//记录下来i = j - 1; // j在匹配成功时已经自加了,这里是验证确实是违禁词,所以需要将j前一个位置给i}// 去掉空格while (j < length&& charTypeArray[j] == CharType.SPACE_LIKE)j++;if (j < length&& (charTypeArray[j] == CharType.HANZI || CharType.LETTER == charTypeArray[j])) {//将下个字符和前面的组合起来, 继续前缀匹配wordBuf.append(sentence.charAt(j));charArray = new char[wordBuf.length()];wordBuf.getChars(0, charArray.length, charArray, 0);foundIndex = wordDict.getPrefixMatch(charArray,foundIndex);//前缀匹配违禁词j++;} else {break;}}}i++;}return result;}
上面的逻辑和代码实现只是过滤违禁词外层实现,具体如何在违禁词库中,查询指定字符串,是最为关键的,即:词典WordDict的数据结构,和它的算法getPrefixMatch() 方法,也是涉及到性能优化的地方。
数据结构:词典
private char[][][] wordItem_real;
第一维 wordItem_real[i] 其含义是:具有相同开头汉字X,的所有违禁词(一组)。其中下标 i 为 X 的 GB2312 码,这样只要对文档中的某一个汉字一转码,就能马上找到以此汉字开头的所有违禁词,算是一种散列吧;
算法:二分查找与前缀匹配
/*** * * @see{getPrefixMatch(char[] charArray)}* @param charArray* 前缀单词* @param knownStart* 已知的起始位置* @return 满足前缀条件的第一个单词的位置*/public int getPrefixMatch(char[] charArray, int knownStart) {int index = Utility.getGB2312Id(charArray[0]);if (index == -1)return -1;char[][] items = wordItem_real[index];if(items == null){return -1; //没有以此字开头的违禁词}int start = knownStart, end = items.length - 1;int mid = (start + end) / 2, cmpResult;// 二分查找法while (start <= end) {cmpResult = Utility.compareArrayByPrefix(charArray, 1, items[mid],0);if (cmpResult == 0) {// 获取第一个匹配到的(短的优先)while (mid >= 0&& Utility.compareArrayByPrefix(charArray, 1,items[mid], 0) == 0)mid--;mid++;return mid;// 找到第一个以charArray为前缀的单词} else if (cmpResult < 0)end = mid - 1;elsestart = mid + 1;mid = (start + end) / 2;}return -1;}
public static int compareArrayByPrefix(char[] shortArray, int shortIndex,char[] longArray, int longIndex) {// 空数组是所有数组的前缀,不考虑indexif (shortArray == null)return 0;else if (longArray == null)return (shortIndex < shortArray.length) ? 1 : 0;int si = shortIndex, li = longIndex;while (si < shortArray.length && li < longArray.length&& shortArray[si] == longArray[li]) {si++;li++;}if (si == shortArray.length) {// shortArray 是 longArray的prefixreturn 0;} else {// 此时不可能si>shortArray.length因此只有si <// shortArray.length,表示si没有到达shortArray末尾// shortArray没有结束,但是longArray已经结束,因此shortArray > longArrayif (li == longArray.length)return 1;else// 此时不可能li>longArray.length因此只有li < longArray.length// 表示shortArray和longArray都没有结束,因此按下一个数的大小判断return (shortArray[si] > longArray[li]) ? 1 : -1;}}
主要的思路和实现代码都已经讲明了,若大家有更好的过滤违禁词的算法,希望分享,周末愉快。
转载于:https://my.oschina.net/BreathL/blog/56265
违禁词过滤完整设计与优化(前缀匹配、二分查找)相关推荐
- 网站前端进行违禁词过滤js代码
var strChar=['最佳','最具','最爱','最赚','最优','最优秀','最好','最大','最大程度','最高','最高级','最高端','最奢侈','最低','最低级','最底', ...
- WordPress上好用的违禁词过滤插件分享
违禁词过滤插件可以删除或者替换文章.页面和评论内容中出现的违禁词,同时也可以将出现违禁词的内容设为待审核状态或者移至回收站,等待进一步的人工处理.本插件不仅仅可以过滤违禁词,只要内容中出现不想要的文字 ...
- LeetCode 528. 按权重随机选择(前缀和+二分查找)
文章目录 1. 题目 2. 解题 1. 题目 给定一个正整数数组 w ,其中 w[i] 代表下标 i 的权重(下标从 0 开始),请写一个函数 pickIndex ,它可以随机地获取下标 i,选取下标 ...
- LeetCode 497. 非重叠矩形中的随机点(前缀和+二分查找)
文章目录 1. 题目 2. 解题 1. 题目 给定一个非重叠轴对齐矩形的列表 rects,写一个函数 pick 随机均匀地选取矩形覆盖的空间中的整数点. 提示: 整数点是具有整数坐标的点. 矩形周边上 ...
- 209 长度最小的子数组(前缀和+二分查找、滑动窗口)
1. 问题描述: 给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组,并返回其长度.如果不存在符合条件的子数组,返回 0. 示例: 输入:s = ...
- 通过Trie实现违禁词过滤
敏感词过滤 生活在天朝的网站,必须要有保持和谐的工具.根据网站的规模不同选择不同的技术方案: 1.前期上一个敏感词过滤系统,发的文章只要命中敏感词就不让发. 2.后期可以通过机器学习来自动识别一篇简历 ...
- [Leedcode][JAVA][第209题][长度最小的子数组][滑动窗口][前缀和][二分查找][双指针]
[问题描述][中等] 给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组,并返回其长度.如果不存在符合条件的连续子数组,返回 0.示例: 输入: ...
- LeetCode 1712. 将数组分成三个子数组的方案数(前缀和 + 二分查找)
文章目录 1. 题目 2. 解题 221 / 3117,前7.1% 574 / 9692,前 5.9% 周赛前2题如下: LeetCode 5641. 卡车上的最大单元数(排序,模拟) LeetCod ...
- 敏感词过滤工具类(DFA算法匹配字典)
直接调用方法:wordFilter package com.util;import java.io.BufferedReader; import java.io.FileNotFoundExcepti ...
最新文章
- Csharp: 拼音转汉字字符搜索字符串
- python百度云资源-python学习资源--百度云
- 记阿里的一次壮烈牺牲
- JAVA截取字符串方法
- Java代理设计模式(Proxy)的四种具体实现:静态代理和动态代理
- 区块链跟银行有什么关系?
- html5 视口,html5 – 在媒体查询中更改视口
- 基于JAVA+SpringMVC+Mybatis+MYSQL的汽车维修管理平台
- Python+matplotlib绘图使用Latex引擎渲染坐标轴刻度文本上标
- Qt 方式问题_vortex_新浪博客
- 阿里云何勉:如何定义团队的研发效能?
- 【开发】MFC到Delphi的皮肤移植
- 微信语音识别开放平台
- Unity3d 周分享(16期 2019.5.1 )
- 汽车距离报警系统c语言编程,基于单片机的汽车防盗报警系统的设计本科生毕业论文.doc...
- 口碑、银盒子相关功能是否支持问题
- 计算机兴趣小组活动实施方式,信息技术兴趣小组活动总结范文(通用5篇)
- 国产手机均价下跌,苹果逆势增长,iPhone仍是消费者最爱
- lunix上silk转mp3 和 mp3转silk
- 开源魔兽世界私服搭建
热门文章
- linux如何查看进程及端口,Linux如何查看端口及进程
- 协同过滤算法分类-UserCF
- python批量打印复印_惠普集群打印 小规模灵活批量打印方案
- java中判断list是否为空
- uefi装完系统后无法引导_uefi u盘无法引导怎么办
- vue 过滤器的使用
- android 获取手机蓝牙是否与其他设备蓝牙配对连接成功,android开发获取手机已连接的蓝牙设备(包括已链接的设备和已经配对绑定的设备)...
- matlab解决迷宫问题,用matlab处理蚂蚁迷宫问题
- loadrunner controller无法创建vuser
- 获取设备型号、设备类型等信息