目的:算法
爲了解決字符串模式匹配數組
歷程:性能
樸素模式匹配:逐次進行比較spa
KMP算法:利用匹配失敗獲得的信息,來最大限度的移動模式串,以此來減小比較次數提升性能code
概念:blog
m:是目標串長度索引
n:是模式串長度字符串
j:某次匹配時,第一次出現的不一樣的索引位置(有的稱爲:失配位)get
k:最長首尾串長度(有的稱爲:最長公共先後綴)string
核心思想:
S S0 S1 ...... Si-j-1 Si-j Si-j+1 Si-j+2 ...... Si-2 Si-1 Si ...... Sn-1
|| || || || || ×
P P0 P1 P2 Pj-2 Pj-1 Pj
有Si-j-1Si-jSi-j+1 Si-j+2 ...... Si-2 Si-1=P0P1 P2 ...... Pj-2 Pj-1
若是 P0P1 P2 ...... Pj-2 ≠ P1 P2 ...... Pj-2Pj-1
則能夠當即判定 P0P1 P2 ...... Pj-2 ≠ Si-j+1 Si-j+2 ...... Si-2 Si-1,即:樸素模式匹配的下一次移動必定不匹配,則能夠跳過這一次
若是 P0P1 P2 ...... Pj-3 ≠ P2 ...... Pj-2Pj-1
則能夠當即判定 P0P1 P2 ...... Pj-2 ≠ Si-j+1 Si-j+2 ...... Si-2 Si-1,即:樸素模式匹配的下一次移動必定不匹配,則能夠跳過這一次
直到第一次出現相等的狀況終止:P0P1 P2 ...... Pk-1 = Pj-k ...... Pj-2Pj-1
獲得的k就是最長的首尾串長度,而後經過 j-k 獲得了咱們須要移動的位數,這樣咱們就利用了匹配失敗的結果,獲得了咱們能夠移動的步數,提高了性能
關於k:
其實肉眼就直接能看出來,k是最長首尾串長度,好比:
11111 k=4(前綴:1111,後綴:1111)
12112 k=2(前綴:12,後綴:12)
12345 k=0(無相同前綴後綴)
例子:
S=ababababababb
P=abababb
重申一下原理:樸素模式匹配效率低的緣由是一位一位的比較,丟棄了以前失敗的信息。而KMP算法從匹配失敗的信息中獲得能夠最大移動的步數,以此來減小比較的次數,來提高性能。
這裏並無說起,next數組及newnext數組,模式串的特徵向量N,其實不用管它,思想理解了,只是別人起了個叫法而已。
Java代碼:
/** * 樸素模式匹配 * * @param source 目標串 * @param pattern 模式串 */ private static void plain(String source, String pattern) { int res=0; int sourceLength=source.length(); int patternLength=pattern.length(); for(int i=0;i<=(sourceLength-patternLength);i++){ res++; String str=source.substring(i, i+patternLength); if(str.equals(pattern)){ p("樸素模式:匹配成功"); break; } } p("樸素模式:一共匹配"+res+"次數"); }
//KMP算法實現
private static void KMP(String source, String pattern) { int[] N=getN(pattern); int res=0; int sourceLength=source.length(); int patternLength=pattern.length(); for(int i=0;i<=(sourceLength-patternLength);){ res++; String str=source.substring(i, i+patternLength);//要比較的字符串 p(str); int count=getNext(pattern, str,N); p("移動"+count+"步"); if(count==0){ p("KMP:匹配成功"); break; } i=i+count; } p("KMP:一共匹配"+res+"次數"); } /** * 獲得下一次要移動的次數 * * @param pattern * @param str * @param N * @return 0,字符串匹配; */ private static int getNext(String pattern,String str,int[] N) { int n = pattern.length(); char v1[] = str.toCharArray(); char v2[] = pattern.toCharArray(); int x = 0; while (n-- != 0) { if (v1[x] != v2[x]){ if(x==0){ return 1;//若是第一個不相同,移動1步 } return x-N[x-1];//x:第一次出現不一樣的索引的位置,即j } x++; } return 0; } private static int[] getN(String pattern) { char[] pat=pattern.toCharArray(); int j=pattern.length()-1; int[] N=new int[j+1]; for(int i=j;i>=2;i--){ N[i-1]=getK(i,pat); } for(int a:N) p(a); return N; } private static int getK(int j, char[] pat) { int x=j-2; int y=1; while (x>=0 && compare(pat, 0, x, y, j-1)) { x--; y++; } return x+1; } private static boolean compare(char[] pat,int b1,int e1,int b2,int e2){ int n = e1-b1+1; while (n-- != 0) { if (pat[b1] != pat[b2]){ return true; } b1++; b2++; } return false; } public static void p(Object obj) { System.out.println(obj); }
next數組:
KMP能提升性能緣由是減小了比較次數,也就是知道k
而k從只和j有關,這就意味着移動的次數只和模式串有關,和目標串無關
簡單來講,就是咱們獲得模式串後就能立馬知道移動的次數,這就是next數組。裏面儲存的就是k值。