这个赛事我是写了一个,python单线程60秒左右,虽然不知道别人3秒是怎么做的,但是论复杂度,这的方法已经是线性时间复杂度了,所以这里写个分享,供大家指正。
线性复杂度:这里所说的线性复杂度指的是,只与需要匹配的文本线性相关,而与敏感词的数量没关系。如果你已经做到了这一点可以了解一下别人的方法,如果没做到,也可以参考一下本文的方法。这一方法的主要特点是把所有敏感词同时进行匹配,但是一开始的数据结构可能会有点麻烦。
数据结构部分:
散列索引Index:index是一65536的数组,全部初始化为-1,对于所有敏感词中的每一个通配符以外的字符我们把它存在数组char_array中,每个字符只存一次。对char_array中的每个字符取其Unicode编码值(python中可用ord函数获得),并在index中对应位置存储其char_array中的位置,即index[ord(char_array[i])]=i;这样敏感词中每个字符A就有char_array[index[ord(A)]]=A。实现了散列功能,提升查找效率。
每个字符需要记录的信息struct_C:记录以下6个标记
a:这个字符是否为某个敏感词的第一个字,是为1,否为0;
b:这个字符是否为某个敏感词的最后一个字,是则记录作为结尾它是第几个字的信息。(2,3,5,7,9,分别代表1、2、3、4、5;多种可能时相乘)
c:这个字符作为第一个字时后面接的字符集合;
d:这个字符作为第二个字时后面接的字符集合;
e:这个字符作为第三个字时后面接的字符集合;
f:这个字符作为第四个字时后面接的字符集合;(这里把问题简化一下不考虑更多情况了。)
为了方便后面查看一个字符是否接在某个字符后面,给每个字符一个专属的质数(素数)作为标记,如只有“上学”“上课”“上班”三个词时,可给出 (上:2,学:3,课:5,班:7)的对应表,这时“上”作为第一个字时后面接的字符集合可表示为 3X5X7 即 105;如果某个字符对应的素数a满足 105%a=0则这个字符必定属于后接字符集合中的一个。
以上部分都是对敏感词的处理,其目的是把所有敏感词合成一个整体,然后只进行一次匹配。
匹配过程:
对于一个字符串,从第一个字符开始查看其Unicode编码所对应index位置中值是否为-1;如果不为-1;则检查标记a是否为1;然后继续观察后面的4个字符是否为该字符的后接字符。就这样每次只看两个相邻的有效字符能否连接起来。下面举个例子说明:
假设只有“上.{0,3}学”(.{0,3}表示上课中间可以有0-3个任意字符),“上.{0,3}课”,“上.{0,3}班”,“训.{0,3}练”四个正则所匹配的内容为敏感内容;同上有(上:2,学:3,课:5,班:7,训:11,练:13)的对应表。现在要对“下班后去上培训课”字符串进行匹配。读到“下”字时,index中对应的内容为-1,不可能命中敏感内容;读到“班”字时,index中记录为3(因为班是第4个字);但是标记a为0,不可能作为敏感内容的第一个字;index对应内容为-1,继续跳过“后去”两个字符;遇到“上”字时,发现其可以作为敏感内容的开始,于是读取上作为第一个字的后接字符集105;”培“不是有效字符;”训“字是有效字符,但是其专属质数11不能被105整除(105=3*5*7,只有一种分解方式),继续过;”课“字是有效字符,并且5能被105整除,检查发现”课“刚好是一个敏感内容的结尾,并且刚好是第二个有效字,匹配通过”上培训课“。
其他:
其实通过这种方法进行匹配,只能做到和原有的正则项”近似“,虽然不等价,但也是一个较好的近似。其中有很多细节处理都没提及,不过本文的重点是线性时间复杂度:与需要匹配的敏感词数量多少无关,只与原文本长度有关。
原文链接:https://www.f2er.com/regex/360806.html