给定一个text字符串,和一个pattern字符串,找出pattern字符串在text字符串中开始的位置,如果不能匹配到,则返回-1;
在使用普通的双指针进行匹配时,如果匹配失败,则text指针要回到匹配开始位置的下一位置再进行下一轮匹配,如果两个字符串包含大量重复的字符,则这种回退大部分是不必要的。时间复杂度:O((n-m)*m),n是text的长度,m是pattern的长度
KMP通过引入一个next数组来记录pattern指针可以回退的位置,使text指针不需要回退。时间复杂度:O(m+n)
例如:
text: [a, b, e, a, b, a, b, e, a, b, f]
pattern: [a, b, e, a, b, f]
next: [0, 0, 0, 1, 2, 0]
next数组的含义和如何构造是关键
class Solution { public int strStr(String haystack, String needle) { // 暴力解法,边界条件太麻烦,O((n-m)*m)的时间复杂度 int len1 = haystack.length(); int len2 = needle.length(); if(len2==0) return 0; int p=0; for(int q=0; q+len2<=len1; q++){ // 这里要进行优化处理,否则时间复杂度为O(nm) if(haystack.charAt(q)==needle.charAt(0)){ p = q; boolean flag = true; for(int i=0; i<len2; i++){ if(needle.charAt(i)!=haystack.charAt(q+i)){ flag = false; break; } } if(flag) return p; } } return -1; // KMP算法: 如果已经匹配的字符串包含相同的前缀和后缀,遇到下一个不匹配的位置时,指向needle的指针跳转到前缀的后一个位置,
// 不匹配的话,再回退后继续比较;先构造一个next数组来记录needle指针跳转的位置 int n=haystack.length(), m=needle.length(); if(m==0) return 0; // 先构造next数组,next数组中的元素表示当前两个元素不匹配时,needle指针要跳转的位置 // needle: [a, b, e, a, b, f] // next: [0, 0, 0, 1, 2, 0] int[] next = new int[m]; for(int i=1,j=0; i<m; i++){ while(j>0 && needle.charAt(i)!=needle.charAt(j)) j = next[j-1]; // 一直和前一位置的值比较,直到遇到相等的字符或者j=0;j通过next[j-1]来回退 if(needle.charAt(i)==needle.charAt(j)) j++; next[i] = j; } // 利用next数组进行跳转匹配,不再需要回退haystack的指针 for(int i=0,j=0; i<n; i++){ // 匹配不成功,needle指针回退并继续比较 while(j>0 && haystack.charAt(i)!=needle.charAt(j)) j = next[j-1]; if(haystack.charAt(i)==needle.charAt(j)) j++; if(j==m) return i - m + 1; } return -1; } }