题目:给定一个字符串s = “aabaabaaf”,请在字符s中找到字符串c = “aabaaf”, 并且返回c在s中第一个字符的下标。
这道题是十分经典的字符串匹配问题。
首先我们的直觉解法就是暴力破解,步骤如下:
Round 1: aabaabaaf // 从上面字符串第一个字符a开始比对,发现f与b不相等
aabaaf
Round 2: aabaabaaf // 从第二个字符a开始比对
aabaaf
Round 3: aabaabaaf // 从第二个字符b开始比对
aabaaf
.........
Round 4: aabaabaaf //匹配成功
aabaaf
我们尝试从S第一个字符串开头,一一的比较每个字符。如果失败了,我们就从s的下一个字符串开始,重复该步骤。
假设s的长度是n,c的长度是m,那么时间复杂度就是O(n*m)。咋一看好像复杂度不高,但是实际生活中,s和c一般都很长,导致时间复杂度很大。所以我们来对暴力破解做一下优化,就是KMP算法
其实KMP算法就是暴力破解的思路上加一点巧思,但是这巧思真的很妙。
我们重新来看暴力破解算法:
Round 1: aabaa baaf
aabaa f
首先,字符串到b与f的时匹配不成功。这意味着,s的前五位和c的前五位是相同的。
然后,按照暴力破解的思想,s中的第一个a已经不可能是匹配成功的结果了。所以我们把第一个a剔除掉。
Round 2: a abaa baaf
aabaa f
其次,如果这时候匹配成功(以第二个a开头),就要求在剔除a后,剩下的字符可以相同。因为剔除了一个变成4个了,所以我们只能比对前四个。
Round 3: a abaa baaf
aaba a f
可以看到,如果以第二个a开头的话,就要求abaa与aaba要相等。但是显然这是不成立的。所以,我们继续从下一个字符b匹配,那么这时候,就要求
Round 4: aa baa baaf
aab a a f
我们看到,前面两个a是被我们剔除的,我们只需要比对b开头的三个字符。如上所示,第三个b也不可能是匹配结果的一部分。依次类推,我们可以得到
Round 5: aab aa baaf
aa b a a f
这时候,我们发现,s的aa与c的aa终于相同了。这意味着从第四个字符a可能是字符串的匹配的开头。请注意,仅仅是可能,但是我们通过这个方法,就可以直接排除掉第二个字符a和第三个字符b的开头的情况,从而减少了两次遍历。
KMP算法就是通过这个思想来减少遍历次数的!
这个思想是KMP算法的核心,来看看我们怎么提炼。
我们发现,其实我们要做的就是找出字符串aabaa去掉前面的字符,aabaa后面的字符后相等的情况。
字符串:aabaa
a abaa
aaba a
-------
aa baa
aba aa
------
aab aa
aa baa
------
所以我们只需要找到符合这个情况并把它记录起来,以后每一个匹配到aabaa后,如果f匹配不成功,我们就可以直接从aa后的位置匹配啦。
我们把这个记录到一个数组里,这