有对搜索引擎有点了解的,就应该知道倒排索引吧。不知道也不要紧,看看就行了,这种数据结构比较简单,但很实用,
可以参见百度百科http://baike.baidu.com/view/676861.htm。
这里举个例子来说明,有时候在非搜索引擎的运用中,倒排索引也能显著提高算法效率。
例. 在一串由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。
另外,在去年(2010)ACM成都区域赛现场赛时,有一道题目也是将顺看很棘手的问题转化成倒排,然后用KM解,倒排在里面也起了很大的作用。