Szh search States United Szh Listings Travel
search Listings t Szh tsearchs Listings States daa Szh 6. Find wwsearcha Listings asearche. Szh osearch Travel searchisearchd
orDdaa66.comg Findmortgagehomemortgages gewww.aaaqe.como Listings e Find or Code gagsearchs L
s Listings isearchgsearch Listings searchrDdaa66.comv United l Code
United United Findmortgagehomemortgages Code Listings Szh searchMsearch
Travel search United Travel Travel
Szh search Findmortgagehomemortgages
Travel Find
0 1 2 3 4 5 6 a b a b a b c a b a b c ^
然后继续匹配就成功啦。
所以,KMP算法的核心是,如何为目标字符串的每个位置的找到一个k值,组成一个数组F,好在每次匹配到目标字符串的m失配的时候,将目标字符串回溯到F[m],然后继续进行匹配。找到这个数组之后,KMP搜索就算是完成80%了。
下面是构建这个数组F的方法。
这时候目标字符串身兼源字符串和目标字符串两个角色。构建数组T可以说是一个步进的过程,需要用到之前的结果。首先是F[0],F[0]的意思是第一个字符就不匹配,也就是说对源字符串一无所知,这时候没得搞了,直接要源字符串向前挪动一个。在F里,我们使用-1来标记第一个字符就匹配失败的情况。也就是F[0]=-1。F[1]其实肯定是0。我们真正需要计算的是从F[2]到最后的。下面是>=2的时候的计算方法。注意,F[i]代表S的第i个字符匹配"失败"的时候,T需要回溯到的索引的值。如何求F[i]的值呢?首先取得F[i-1]的值,然后看S[i-1]是否=T[F[i-1]],如果等于,那么F[i]=F[i-1]+1。这个原理是递归的。F[i-1]的值是在i-1失配的时候,T索引回溯到的值,如果这时候,这个值与S[i-1]相等,那就说明F[i]可以在F[i-1]的基础上增加1了。否则继续检查S[i-1]是否等于T[[F[i-1]]],直到没有的搜索了,就是0。下面是具体的代码:
/**
* each value of array rollback means: when source[i] mismatch pattern[i],
* KMP will restart match process form rollback[j] of pattern with
* source[i]. And if rollback[i] == -1, it means the current source[i] will
* never match pattern. then i should be added by 1 and j should be set to
* 0, which means restart match process from source[i+1] with pattern from
* pattern[0].
*
* @param pattern
* @return
*/
private static int[] getRollbackArray(char[] pattern) {}
rollback[0] = -1;
for (int i = 1; i < rollback.length; i++) {} else {}
}
}
return rollback;
}
上面并没有吧F[1]=1写成固定的,不过根据计算,F[1]始终是=0的。有了这个rollback数组,KMP搜索就是水到渠成了:
/**
* search pattern chars in source chars.
*
* @param source
* @param pattern
* @return
*/
public static int searchKMP(char[] source, char[] pattern) {}
// get the rollback array.
int[] rollback = getRollbackArray(pattern);
// incremental index of pattern. pointing the char to compare with.
int currMatch = 0;
int len = pattern.length;
// i point the char to compare with
for (int i = 0; i < source.length;) {}
} else {}
}
return -1;
}
下面是几个测试方法:
@Test
public void testRollBackArray() {};
int[] rollback = getRollbackArray("PARTICIPATE IN PARACHUTE"
.toCharArray());
Assert.assertArrayEquals("Rollback array compare failed to match!",
expectedRollback, rollback);
}
@Test
public void testKMPSearchMatch() {}
@Test
public void testKMPSearchNoMatch() {}
把这三段代码放在一个类里,KMP搜索就算是完事儿了。
在自己看KMP算法之前,很多文章都说神马KMP有代价,只适合目标字符串很长很长,搜索字符串也很长很长的case。但是就我看下来,KMP对于日常一般的搜索也是有优势的。首先,构建rollback数组计算并不复杂,当然需要一个额外的数组空间。但是对于匹配来说,还是有很大的加速优势的,而且目标字符串不需要回溯。所以KMP唯一的代价就是需要一个额外的数组,实际占用的内存应该是目标字符串的两倍(String是char的数组,char=short,int是char的两倍)。难道,真的是为了节省内存所以不采用KMP搜索?