LintCode : 字符串查找
题目
对于一个给定的 source 字符串和一个 target 字符串,你应该在 source 字符串中找出 target 字符串出现的第一个位置(从0开始)。如果不存在,则返回 -1。
样例
如果 source = “source” 和 target = “target”,返回 -1。
如果 source = “abcdabcdefg” 和 target = “bcd”,返回 1。
思路
O(n^2):
朴素的字符串匹配方法,从模式串第一个字符开始匹配,中途发现不匹配的字符,则放弃此次匹配,接着从第二个字符开始匹配。知道发现匹配的字符串,返回结果。O(n^2)的算法貌似是可以AC的,但是我们发现可以有复杂度更低的方法去完成这道题,这就是我们经典的KMP算法了。
O(n):
KMP算法运行过程中每趟匹配过程中出现字符比较不等时,不回溯主指针i,利用已得到的“部分匹配”结果将模式向右滑动尽可能远的一段距离,继续进行比较。
s1 s2 s3…si-j+1 si-j+2…si-2 si-1 si si+1
‖ ‖ ‖ ‖ ≠
p1 p2 … pj-2 pj-1 pj pj+1
‖ ‖
p1 … pk-1 pk pk+1
① “p1p2…pk-1” = “si-k+1si-k+2…si-1”
②“pj-k+1pj-k+2…pj-1” = “si-k+1si-k+2…si-1”(部分匹配)
③ “p1p2…pk-1” = “pj-k+1pj-k+2…pj-1” (真子串)
为此,定义next[j]函数,表明当模式中第j个字符与主串中相应字符“失配”时,在模式中需重新和主串中该字符进行比较的字符串的位置。
next[j] = max { k | 1 < k < j , 且”p1…pk-1” = “pj-k+1…pj-1”
( 这里我设置 next[0] = -1, next[1] = 0 )
求解next
p1 p2…pj-k+1…pj-1 pj pj+1 next[j]=k
‖ ‖ ≠
p1 … pk-1 pk pk+1 next[k]=k’
p1 …… pk’ pk’+1 next[k’]=k”
p1… pk’’ pk’’+1 next[k’’]=k’’’
也就是说比较当前所求的值得字符与前一个字符是否相同,如果相同则前值+1,如果不相同则递归的比较next[next[j - 1]]的字符与前一个字符是否相同。
代码
int get_len(const char *str) {
if(str == NULL)
return -1;
int i = 0;
while(str[i] != '\0') {
i++;
}
return i;
}
void get_next(int *next, const char *source) {
next[0] = -1;
next[1] = 0;
int i = 1;
int j = 1;
while(i < get_len(source)) {
if(source[i] == source[next[j]]) {
i++;
next[i] = next[j] + 1;
j = i;
}
else if(next[j] == -1) {
i++;
next[i] = 0;
j = i;
}
else {
j = next[j];
}
}
}
int strStr(const char *source, const char *target) {
int sl = get_len(source);
int tl = get_len(target);
if(sl < tl)
return -1;
if(tl == 0 && sl != -1) {
return 0;
}
if(sl == -1 || tl == -1)
return -1;
int *next = new int[tl + 1];
get_next(next, target);
int i = 0, j = 0;
while(i < sl && j < tl) {
if(j == -1 || source[i] == target[j]) {
i++;
j++;
}
else {
j = next[j];
}
}
if(j == tl)
return i - j;
else
return -1;
}