对于串匹配问题,最基础的做法是分别遍历源字符串和目标字符串,当失配时,源字符串前进一位,目标字符串归0位。
int searchString(const char *src, const char *sub) {
int srcLen = strlen(src);
int subLen = strlen(sub);
int srcIndex = 0;
int subIndex = 0;
for (; srcIndex < srcLen - subLen + 1; srcIndex++) {
for (subIndex = 0; sub[subIndex]; subIndex++) {
if (src[srcIndex + subIndex] != sub[subIndex]) {
break;
}
}
if (0 == sub[subIndex]) {
return srcIndex;
}
}
return NOT_FOUND;
}
但这种方式需要每次回溯目标字符串,使得算法过于累赘。
现在的目标就是避免每次回溯到头。
对于一个目标字符串,如上图,若字符串在第三个a失配,则说明目标b前的两个a在源串存在,因为遍历源串使得字符串偏移,则偏移前2个a重复,偏移后一个a重复。
数组开头两位必定是0,从第三位(即下标为2)开始计算,j用于计算目标串的位,源串需要从i - 1开始查找,若两者相同,目标串应查找下一位,i需要随时加
int *getNext(const char *sub, int subLen) {
int index = 2;
int j = 0;
int *next = NULL;
next = (int *) calloc(sizeof(int), subLen);
while (sub[index]) {
if (sub[index-1] == sub[j]) {
next[index++] = ++j;
} else {
if (j == 0) {
next[index++] = j;
} else {
j = next[j];
}
}
}
return next;
}
Next数组完成后,对字符串查找就简单多了:
int kmpMatch(const char *src, const char *sub) {
int srcLen = strlen(src);
int subLen = strlen(sub);
int i = 0;
int j = 0;
int *next;
next = getNext(sub, subLen);
while (sub[j] && srcLen - i >= subLen - j) {
if (src[i] == sub[j]) {
i++;
j++;
continue;
} else if (j != 0) {
j = next[j];
} else {
i++;
}
}
free(next);
return sub[j] ? NOT_FOUND : i - j;
}
首先截止条件是 整体字符串未查找的数量>目标字符串剩余数量
若字符串和目标字符串相等,下标共加,若不相等,分两种情况:
①j不为0说明前面有部分匹配,用next数组查对应下标
②j为0则字符串下标加一即可。