字符串匹配算法
一串文本字符串String,一个模式字符串pattern,字符串匹配算法的工作就是检测String中是否含有对应模式字符串。然后返回模式字符串pattern在文本字符串String所在的位置,如果不存在返回-1。或者返回一个布尔值代表这个String是否含有对应模式字符串。
暴力求解
暴力求解即从第0个字符开始互相匹配,直到存在不同的字符
前移一个字符继续进行匹配
直到存在完全一致的字符串,返回字符串所在序号或者true
或者直到文本字符串末尾也不存在所求字符串返回false或者-1
暴力求解代码
public class ExhaustiveAlgorithm {
public int isMatch(String s, String p) {
char[] chars = s.toCharArray();
char[] charp = p.toCharArray();
int lengths = chars.length;
int lengthp = charp.length;
for(int i = 0; i < lengths - lengthp + 1; i++){
boolean flag = true;
for(int j = 0; j < lengthp; j++){
if(chars[i+j] == charp[j])
continue;
else{
flag = false;
break;
}
}
if(flag){
return i;
}
}
return -1;
}
}
设文本字符串的长度是m,模式字符串长度是n,暴力求解的时间复杂度粗略估计是O(mn)的样子,效率相当低。主要是逐个后移太消耗时间。
KMP算法
KMP算法创造了一种最长前后缀数的概念。
即最长的相等前后缀长度
KMP算法原理如下:
当匹配到不同字符时,如图:
并不是如同暴力求解,仅向前挪动一格。而是已匹配过的字符串中的最长前后缀数
来跳动较多格,使后缀和前缀对齐,避免重复计算相同的字符串
因此只需要次数为文本字符串长度的比较,即时间复杂度为O(n)
KMP算法代码
public class KMPAlgorithm {
public int isMatch(String s, String p){
char[] schars = s.toCharArray();
char[] pchars = p.toCharArray();
int slength = s.length();
int plength = p.length();
int[] fixCount = new int[plength];
fixCount[0] = -1;
if(plength == 2)
fixCount[1] = 0;
for (int i = 2; i < plength; i++){
fixCount[i] = computeFixCount(pchars, plength, i);
}
int position = 0;
for(int i = 0; i < slength; i++){
if(position==-1){
position = 0;
continue;
}
if(schars[i] == pchars[position]){
if(position == plength-1)
return i-position;
else{
position++;
continue;
}
}else{
i--;
position = fixCount[position];
}
}
return -1;
}
public static int computeFixCount(char[] pchars, int plength, int fixLength){
for(int i = fixLength ; i > 0; i--){
boolean flag = true;
for(int j = 0; j < i; j++){
if(pchars[j] == pchars[plength-fixLength+j])
continue;
else{
flag = false;
break;
}
}
if(flag){
return i;
}
}
return 0;
}
public static void main(String[] args){
KMPAlgorithm kmpAlgorithm = new KMPAlgorithm();
String sString = "paTTmSHoYEIsEmpaHyASXTlPaVnkGpJLDPGfcbGHASPiQfjniuKWqtIbUARDLtW";
String pString = "nkGpJLDPGfcbGHASPiQfjniu";
int index = 26;
//boolean bool = scanner.nextBoolean();
System.out.println(kmpAlgorithm.isMatch(sString, pString) + " " + index);
}
}
java中的indexOf方法
java中的默认实现是java.lang.String.indexOf(String),看看这个方法是怎么做的:
//source是匹配的字符串
//sourceOffset是偏移量
//sourceCount是需要计算的总数
//target是模式字符串
//targetOffset是模式字符串的便宜了
//targetCount是模式字符串的总数
//fromIndex是从哪个序号开始搜索
static int indexOf(char[] source, int sourceOffset, int sourceCount,
char[] target, int targetOffset, int targetCount,
int fromIndex) {
//如果启动序号比文本字符串总数还大
if (fromIndex >= sourceCount) {
//如果模式字符串长度为0,就返回起始匹配的字符序号,因为为0就任意满足嘛
//同时为了确保数组不越界,最大只能返回sourceCount。如果模式长度不为0,那就返回-1
return (targetCount == 0 ? sourceCount : -1);
}
//确保启动的序号非负
if (fromIndex < 0) {
fromIndex = 0;
}
//如果模式字符串长度是0,就返回起始匹配的字符序号
if (targetCount == 0) {
return fromIndex;
}
//第一个模式字符
char first = target[targetOffset];
//文本字符串的最大序号
int max = sourceOffset + (sourceCount - targetCount);
//起始字符是文本字符的偏移量加上启动序号,i要小于最大的文本字符串序号
for (int i = sourceOffset + fromIndex; i <= max; i++) {
/* Look for first character. */
//找到和第一个模式字符串相等的文本字符串中的字符
if (source[i] != first) {
while (++i <= max && source[i] != first);
}
/* Found first character, now look at the rest of v2 */
//进行后续的比较
if (i <= max) {
int j = i + 1;
int end = j + targetCount - 1;
for (int k = targetOffset + 1; j < end && source[j]
== target[k]; j++, k++);
if (j == end) {
/* Found whole string. */
return i - sourceOffset;
}
}
}
//没有找到返回-1
return -1;
}
看得出java中的indexOf基本上等同于暴力搜索