在做字符串的匹配时,我们有字符串S和T,现在想要从S中找到等于T的连续子串,一个朴素的想法是从S的每个字符开始,逐个和T的字符匹配,如果匹配不成功,再从S的下一个字符开始逐个匹配,这样的算法复杂度是O(NM)。
为了优化算法,我们注意到一个事实,即当我们匹配失败时,前面一定有一部分字符是匹配成功了的。我们寄希望于前面已经匹配成功的字符可以为我们带来信息,使我们尽量减少重复适配的次数。
例如T为abdcabc时,我们注意到如果匹配最后一位,即第二个c时,失败,那么我们至少知道前面的字符应该是abdcabx,x是和c匹配失败的字符,按朴素的算法思路,我们倒退回第一个a,然后前进到b,再开始新一轮的匹配,然后失败,前进到c……但仔细观察可以发现,倒退到b,d,c都是无意义的,我们必须倒退到某个地方,使得它是T的某个前缀才行,即倒退到第二个a,然后再将x与d比较。
这样,我们就有了KMP算法,KMP算法的精髓是next数组,其含义是如果S的当前位置i和T的位置j匹配失败了,那么我们直接把j退回到nextj,而非重新匹配。
求next数组的方法也很有意思,我们让T自身和自身匹配,如果匹配成功,后移,如果失败,那么其中一个退回到当前位置的next位置上继续匹配。
POJ1961_Period
描述
如果一个字符串S是由字符串T循环若干次组成的,那么T称为S的一个循环元,如果S没有比T更短的循环元,那么称T为S的最小循环元。
现在给出一个字符串S,对于S的每个前缀,若存在循环次数大于1的最小循环元,则输出最小循环元的长度及其循环次数。
解题思路
让字符串自匹配得next数组,此时字符串某个位置的前缀长度和相应next位置长度的差值一定是循环元长度的倍数。
解决
1 |
|