字符串小结

 

前些日子学习了字符串及其相关内容,学的懵懵懂懂的,于是决定在这里总结一下,算是为自己的学习历程打个标记。

String的第一个内容是hash。这个强有力的优化方式会给解题带来巨大便利,很可惜的是我并没有能参透。据我理解,hash实际上就是将待散列的数据根据某种规则重新映射的一个过程,而这个过程往往会大大的降低数据的空间复杂度和访问所需的时间复杂度。比如以下数列:12,23,34,45,50,我设置一个散列函数f(v)=v/10,那么最坏情况下需要查找5此才能找到的50只需要一次[f(50)=50/10=5]即可找到,好的hash确实可以带来很大的便利,但若是没有规定出一个好的散列函数,反而会使空间时间增加,这也是我目前无法流畅使用的原因之一。

下一个内容是TRIE树,这个内容还是比较“打脑壳”的,至今我依旧是云里雾里,也许这后几天学的东西我都存在很多泡沫,需要时间去压实它们。TRIE是实现后缀数组和AC自动机的先决条件之一,

从任何一个角度来讲我都没有不学好它的理由。其实,TRIE就是一个牺牲空间换取时间的算法,利用字符串的公共前缀来建树储存,使查找所需的时间复杂度大大降低,低至O(k),此处k为查找的key串长度。虽然hash理论上是O(1)的时间复杂,但是前文也有提到不是每个哈希都是好哈希,而哈希还会有至少O(k)的计算复杂度,所以TRIE综合上来说还是比较优越的。它有一些基本性质譬如根节点不储存字符、除根节点外任意节点只有一个字符、从根节点往下遍历到一叶子节点便是一个单词(字符串)而且每个节点的子节点包含的字符串必须不同,大概就和下图一样↓

再下个内容就是KMP算法了,这也算是string里为数不多我彻底掌握了的算法吧,至少是摸清运作原理了,这也归功于一位大神的BLOG(因为没有保存地址,现在找不到博客源地址了,文末无法放出连接,非常抱歉)。首先是一个例题,一个文本串S和一个模式串P,如何在S中找到P第一次出现的位置,这就是KMP解决的主要问题。首先可以从暴力入手 :假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置,则:如果当前字符匹配成功(即S[i] == P[j]),则i++,j++,继续匹配下一个字符;如果失配(即S[i]! = P[j]),令i = i - (j - 1),j = 0。相当于每次匹配失败时,i 回溯,j 被置为0。

思路很清晰,但明显效率太低,很有可能存在着一种情况:模式串已经有len-1项匹配成功,而且此时位于接近文本串末端的位置,但因为失配,所以会重新返回到起点再来配,若S长到一种程度,这种前功尽弃的情况会使得算法效率无比底下,而KMP就是实现 “记忆化”匹配:假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++,继续匹配下一个字符;如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]。此举意味着失配时,模式串P相对于文本串S向右移动了j - next [j] 位。换言之,当匹配失败时,模式串向右移动的位数为:失配字符所在位置 - 失配字符对应的next 值,即移动的实际位数为:j - next[j],且此值大于等于1。而next数组储存的是之前字符串中有多长的相同前缀和后缀,此也意味着在某个字符失配时,该字符对应的next 值会告诉你下一步匹配中,模式串应该跳到哪个位置(跳到next [j] 的位置)。如果next [j] 等于0或-1,则跳到模式串的开头字符,若next [j] = k 且 k > 0,代表下次匹配跳到j 之前的某个字符,而不是跳到开头那样去傻傻的疯狂循环。

下一个字符串的知识点是AC自动机,同样,它我也没有掌握透彻,依旧只能概写。上文有提到KMP是为了解决单个字符串如何快速匹配的问题,而AC自动机解决的便是多字符串的匹配问题,也就是说模式串P不再单一。以我之见,KMP处理单个字符串的匹配使用的是线性结构的next数组去解决记忆化的问题,而对于多个模式串而言线性结构似乎显得太过乏力,于是便只能使用TRIE字典树来储存数据,树上会存在一个fail指针来指向跳转的节点,目前我也就掌握到这,甚至代码实现都没有过,着实是万分惭愧。

以上内容便是对于字符串的总结以及,因为上述内容纯手打而且本人学识尚浅,很多算法理解肤浅,若发现什么bug,请务必告知我,谢谢。

posted @ 2017-02-22 17:15  GuanHuaEdison  阅读(110)  评论(0编辑  收藏  举报