文章目录
- 1. 化串为数
- 2. 凡物皆数
- 3. 亦是数
1. 化串为数
接下来的这节,我们再来讨论一种十分另类的串匹配算法,也就是所谓的 Karp-Rabin 算法。回顾此前所介绍的几种串匹配算法,我们所面临的难题是一样的。也就是说在这里,我们每次比较的对象不再是单个字符,而是由一系列的字符所构成的串或者子串。因此,如果局部子串的长度为 m,那么对于所有的 n 个潜在的对齐位置,我们在最坏情况下都可能需要执行 m 次比对,因此在 最坏情况下,我们往往必须付出如此之高的代价。
那么,能否从中自然地剔除掉这个因子 m 呢?我们马上就会看到一种别致的方法,也就是将每一个串视作为一个数。正确切地讲,应该是整数。不难理解,果真若能实现这种转换,那么任何两个串之间的比较,都相当于整数之间的比较,从而我们只需花费常数的时间,也就是说与字符串的长度无关。那么这些美好的设想能够实现吗?答案是能够。
实际上,不少前贤先哲都已经指出,包括串在内的万事万物,其实本质上讲都是数,或者确切地讲是整数。没错,整数。它们才是自然的本源。
2. 凡物皆数
是的,凡物皆数。关于自然的本源,这是很多人所秉持的一种信念,在我看来,其中最坚定的信奉者,同时也是最高明的实践者,莫过于哥德尔。比如,为了证明伟大的不完备定理,他发明了一种简洁而强大的编号方式,来对逻辑系统中几乎所有的组成部分统一以自然数来做标识,这种编号是基于素数序列来完成的。
我们知道,素数虽然是无限的,但同时也是可数的。因此,我们可以用 p(k) 来指代第 k 个素数,比如前几个都众所周知,首先是2,第二个是3,第三个是5,第四个为7以及第五个是11,诸如此类。
那么哥德尔敏锐地发现,任何一个有限维度的自然数向量,按照某种法则都会唯一的对应于某个自然数。
比如,我们考察这样一个有整数构成的 8 维向量,它的各个分量依次为3 1 4 1 5 9 2 6。我们现在来找出它所对应的那个自然数。既然是 8 维,所以我们首先要搬出前 8 个素数,也就是 2 3 5 7 11 13 17 19,这 8 个素数将分别与向量的 8 个分量一 一配对。
~
第 1 个分量为3,所以我们相应地将它转换为第一个素数,2 的 3+1 也就是4次方;第 2 个分量为 1,所以我们也将它转化为第二个素数 3的 1+1,也就是2次方;第 3 个分量是4,所以我们也将它转化为第三个素数 5 的 4 + 1,也就是5次方;以下依次类推。我可以得到第 4 个素数的 1+1 次方,第 5 个素数的 5+1 次方,第 6 个素数的9+1次方,第 7 个素数的 2+1 次方,以及最后第 8 个素数的 6+1 次方。
显然,这 8 个因子的乘积依然应该是一个自然数。也就是说,如此我们的确可以将任何一个向量转化为一个自然数。而这种转换方法还具有一个更为精妙、更为神奇的特征,根据如此得出的一个自然数,我们还可以反过来忠实地还原此前的向量。
我想,点破了这层窗户纸之后,你应该能够独立地看出这种还原的方法,对吧?
3. 亦是数
既然万事万物的本源都对应于自然数,那么串也自然应该对应于数。接下来我们就来看看这句话如何兑现。
首先来考虑一种我们最为熟悉的串,也就是由十进制数字所构成的串。比如,由阿拉伯数字所构成的这样一个串。如果我要说这个串是一个自然数,我想你不会有任何异议的,没错,这就是我们最常用的技术方法,那么一般的串呢?
应该还记得我们的约定,组成字符串的每一个字符都来自于事先约定的某个字母表,而在这里,字母表的规模又是至关重要的。如果将它记作 d,那么字母表中的所有字符也就可以按照任何一种次序,与0到 d -1 之间的整数一 一对应了。于是,基于这个字母表所建立起来的任何一个字符串都可以视作为一个 d 进制的自然数。
不妨考察,以26个大写英文字母所构成的字符集,于是,由大写字母所构成的任何一个英文单词也必然对应于某个 26 进制的自然数。
~
比如在单词 cat 中,c 对应的编号为2,a 对应于0,而 t 对应于19。如果你关心这个数字的具体数值,不妨借用一下我们在第4章所给出的进制转换算法。
不过这个方法还存在一点小小的瑕疵,好在修补这个瑕疵并不困难,这个任务不妨由你独立地来完成(上图已经给出方法)。
既然每一个串都可以对应于一个自然数,那么接下来很自然的一个模式串在某个主串中能够出现,仅当这个子串在数值上与模式串相当。
请注意,经过这样的视角转换,我们已经在无形中将串与串的比对,转化为了整数与整数的比对, 也就是说,串与串之间的比对将有望在常数的时间内完成。
果真如此,以上也就自然给出了一个串匹配的算法。的确如此,我们已经朝着这个方向迈出了最重要的一步,尽管我们还需要做更多细致的工作。