kmp算法用来求解"字符串p在字符串s中的首次出现位置"这样的问题。
暴力法就不谈了,这里介绍kmp算法。
考虑这样一种情况:
s = "a b a b a b a b c"
p = "a b a b c"
上面标红的地方是两个字符串首次不相等的地方,不相等就要将指针后移,进行重新匹配,那么将指针后移多少呢?
根据kmp算法,这里要后移成这样:
"a b a b a b a b c"
"a b a b c"
上面标红的地方就是下次开始比较的地方。
解释为什么这样移动:
上面的例子中,指针在index为4的地方停下来,说明index为4之前的字符串是相等的,在index为4的地方发生了不匹配。
我们可以得到:对于字符串s和p,他们的0-3个字符是相等的。设t = s.substring(0,4);即t为s或者p的0-3.
因为t的最长公共前后缀为"ab", 所以"ab"不用再次比较了,进而从"ab"的下一位开始比较即可。
对于最长公共前后缀,我们只求p的,而且用next数组来表示。
经过测试:
字符串长度为50000的时候,暴力法用时约为10ms,kmp用时约为6ms。
测试的时候,一个有趣的问题出现了:java: 常量字符串过长
原因:是用String xx = "";来创建的字符串。在IDEA中,字符串长度超过65535,IDEA会提示java: 常量字符串过长
。使用javac 进行编译也会有类似的提示
解决办法:用new关键字将字符串创建为一个对象。因为堆空间是足够的。
package test; public class kmp { public static void main(String[] args) { long before = System.currentTimeMillis(); System.out.println(new kmp().getSubstringIdx("abababc", "ababc")); long after = System.currentTimeMillis(); System.out.println((after-before)+"ms"); } //kmp算法, 返回字符串substring在字符串s中的首次出现位置,没找到就返回-1 public int getSubstringIdx(String s, String substring){ int[] next = getNext(substring); int i = 0, j = 0;//i指向s的开始, j指向substring的开始 int n1 = s.length(), n2 = substring.length(); while(i<n1 && j<n2){ int tempI = i;//记录i的位置 while(s.charAt(i) == substring.charAt(j)){//开始比较 i++; j++; if(j == n2)return i-n2;//找到了 if(i == n1)return -1; } //没找到,要回退,j向前移动 if(j>0)j = next[j-1]; } return -1;//没找到 } //得到next数组,即求substring的最长公共前后缀的长度。 public int[] getNext(String substring){ int[] next = new int[substring.length()]; next[0] = 0; int j = 0, i = 1;//i指向后缀的末尾,j指向前缀的末尾 while(i<substring.length()){ if (substring.charAt(i) == substring.charAt(j))next[i++] = ++j;//两个后缀末尾字符相等,则i,j都向前移动 else{ /* //末尾字符不相等,退而求其次,前缀后退,后缀不动。 不懂这一步,可以举例子: a b a b c 0 0 1 2 0 */ if (j > 0)j = next[j - 1]; else next[i++] = 0;//j已经退到0了,不能再退了,则next[i]=0,i++; } } return next; } //暴力法 public int getSubstringIdxBF(String s, String substring){ for(int i=0;i<s.length();i++){ int tempI = i; for(int j=0;j<substring.length();j++){ if (s.charAt(i) == substring.charAt(j)){ if(j == substring.length()-1)return tempI; i++; } else{ i = tempI; break; } } } return -1; } }