倒排索引求子串

    有对搜索引擎有点了解的,就应该知道倒排索引吧。不知道也不要紧,看看就行了,这种数据结构比较简单,但很实用,

    这里举个例子来说明,有时候在非搜索引擎的运用中,倒排索引也能显著提高算法效率。

 

    例. 在一串由0,1,2,3,4,5,6,7,8,9这十个字符组成的长度为n的序列中,找出各字符至少出现一次且长度最小的子串长度。

算法一:暴力

    枚举一个起点一个终点,检查区间的字符是否满足条件,求出最小值。时间复杂度O(n^3),慢得要死。


算法二:优化的暴力

    算法思想同上,但在检查敬意字符满足条件与否的时候,并不扫描一遍,而是直接计算得到,降低开销。具体实现如下:

    用一个二维数组s[n][10]记录从第一个字符到当前字符0,1,2,3,4,5,6,7,8,9各自出现的次数,扫描一遍序列就可以得到。这样在检查区间[i,j]是否满足条件的时候,只用检查是否满足s[j][k]-s[i][k]>=1,其中k=0-9。这样时间复杂度降为O(n^2),快一个数量级了,哈哈,不过当n>=10000时,还是太慢。


算法三:倒排索引

    为原序列建立倒排索引,将原问题转化成倒排索引求交集操作。比如,有序列如下:

seq: 1338523690784612

pos: 0123456789012345

倒排索引如下(记录位置):

0 => 9

1 => 0 -> 14

2 => 5 -> 15

3 => 1 -> 2 -> 6 -> 13

4 => 12

5 => 4

6 => 7 -> 13

7 => 10

8 => 3 -> 11

9 => 8

    要统计出现0-9均出现的最短区间长度,实际上可以这样转化,倒排记录上记着每个字符出现的位置。设置10个指针,各自初始指向各自倒排的第一个元素。可以看出,指向的这十个值已经构成一组满足条件的解,它的区间长度为10个数中的最大值减去最小值加1。例子中长度为12-0+1=13。

    选择哪个指针先移动,这比较重要。此时p4指向的值为12,为当前最大值。p1指向的值为0,最小,那么选择移动p1可能会得到更优的结果,因为它的值在增大,而max-min的差值就会减小。移动后,更新最大值和最小值,得到一个新的区间长度,最坏的情况是要再统计当前10个指针指向的最大值和最小值。每个倒排索引都会走一遍,这样算下来,总共最多会花10n的时间。

    其实在指针移动过程中,可以维护3个变量并记录下它们的位置,max,min,mmin,其中mmin表示倒数第二小的数。这样指向min的指针移向下一个位置时有以下几种情况:

    1) value <= mmin,max-value为区间长度,min = value,当前指针继续移向下一个位置

    2) mmin < value <= max,max-mmin为区间长度,min=mmin,mmin的指针移向下一个位置,扫描10个指针,找出倒数第二小的,作为新的mmin

    3) value > max,value-mmin为区间长度,min=mmin,max=value,mmin的指针指向下一个位置,扫描10个指针,找出倒数第二小的,作为新的mmin

    这样处理后,扫描的次数又会降很多,次数接近n,时间复杂度为O(n)。

    最后,你如果还想提供速度,比如要处理上亿条或10亿条数据,还是嫌太慢,可以将它改为基于跳表的倒排,可以直接跳过一些指针,理想的比较次数可以达到n/k

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值