之前老婆的购书福利买了一些书,买了一本《大话数据结构》,这个假期有时间看了一下。
说实话,书一般,不太适合计算机科班的人看,插科打诨的内容比较多,有些内容不太严谨。前面的内容还好,后面就一般了,比如KMP算法,到了图相关的内容就放飞了。
另外数据结构的一些内容,还是需要自己好好磨着性子看一下,转化成自己的理解的东西。
也整理下自己看的内容。
1,正本书的结构
2,几个内容整理
KMP算法
KMP算法前段时间也是终于能按自己的理解消化了一遍,比大一大二刚学的时候又理解了不少,其实断断续续也是看了好多遍。一直觉得KMP算法是比较精妙的,现在来看确实很经典。这本书并没有介绍的特别好,没有讲解的很透彻。
KMP算法使用在字符串查找里的一种算法,暴力的模板匹配是用子串去匹配待查找的串,一个字符一个字符的是比对,如果失败,则退回去重新从下一个字符开始匹配,但是很多时候我们不需要退回去,而是利用子串自身的信息,跳到下一个匹配点进行匹配。KMP的核心思想是如何利用好子串的已知信息进行匹配,如果能明白这一点,基本上KMP就理解了一半了。
因此KMP算法一共分为两个部分:
1,计算子串的已知信息
2,利用子串已知信息去模板匹配,搜索
我们可以先不考虑1,我们可以自己先假设我们已知子串的已知信息,先把2写出来。这里也介绍下子串的已知信息是什么。子串的已知信息是子串的每个位置都带有一个信息,这个信息引导2里面模板匹配,比如下面的字符串
aaaaaaaab-----> -1 0 1 2 3 4 5 6 7
ababababab-----> -1 0 0 1 2 3 4 5 6 7
举个例子
子串:ababababab
原始串:ababcababababababab
当子串从原始串第一个开始匹配,匹配到第5个字符c时,对应的子串是a,不匹配。这个时候如果是暴力方法,可能就是重新把子串退回到原始串的第2个b开始匹配,但是子串带有已知信息,根据对应的已知信息,第5个子串是2,则可以从原始串下下标为2也就是第3个a开始匹配。
上面就是利用子串已知信息去模板匹配,搜索的方案,其中kmp_next是求位置信息的函数。
def kmp_find(src, dst):
next = kmp_next(dst)
i, j = 0, 0
while i < len(src) and j < len(dst):
if j == -1 or src[i] == dst[j]:
i = i + 1
j = j + 1
else:
j = next[j]
if j == len(dst):
return i - j
else:
return -1
那第1个计算子串的已知信息如何计算呢?
可以用一个暴力方式,也就是计算第i个字符的已知信息时,将以该字符为尾的子串的子串与以子串起始位置字符为首字符的子串做对比,通过计算最大匹配字符串来确定位置信息。但是其实这个是模板搜索的一个类似问题,也可以通过上述的方法来进行求位置信息。
def kmp_next(dst):
next = [0] * len(dst)
next[0] = -1
i, j = 0, -1
while i < len(dst) - 1:
if j == -1 or dst[i] == dst[j]:
i = i + 1
j = j + 1
next[i] = j
else:
j = next[j]
return next
上述的方案其实是可以优化的,优化的点在于,很多时候在用位置信息去匹配的时候,也会出现很多浪费的情况。
比如上述的例子,这个地方理解其实有点绕,优化的方案是嵌套位置信息,即在计算位置信息时,发现第i个和第j个相同时,循环来去设置i的next值,即next[j]位置处的next值,改进的方案如下
def kmp_next(dst):
next = [0] * len(dst)
next[0] = -1
i, j = 0, -1
while i < len(dst) - 1:
if j == -1 or dst[i] == dst[j]:
i = i + 1
j = j + 1
# 只改动了这里
if dst[i] == dst[j]:
next[i] = next[j]
else:
next[i] = j
else:
j = next[j]
return next
其实只改动了之前的一行,在得到i和j位置的元素相等时,递归处理。