13. 字符串查找(普通算法和kmp算法)

描述
对于一个给定的 source 字符串和一个 target 字符串,你应该在 source 字符串中找出 target 字符串出现的第一个位置(从0开始)。如果不存在,则返回 -1

样例

如果 source = "source" 和 target = "target",返回 -1

如果 source = "abcdabcdefg" 和 target = "bcd",返回 1

挑战 

O(n2)的算法是可以接受的。如果你能用O(n)的算法做出来那更加好。(提示:KMP)

思路

普通思路是在source中从第一个字符开始比较,如果source的第一个和target中的第一个相同,就接着比较两者的第二个字符,直到target的全部比较完成,就说明找到了对应的子串,如果在target全部比较之前就已经有不同的,就说明从这一个开头的比较是没有对应的子串,需要从soirce的下一个字符从新比较,直到source的全部比完。

int strStr(const char *source, const char *target) {
        int i=0,j=0;
           if (source == NULL || target == NULL) {
                return -1;
        }
    	for(int i=0;source[i]!='\0';i++){      //从第一个开始比较,直到source没有了结束
    		for(int j=0,k=i;source[k]!='\0';j++){ 循环判断target的每一个字符是否和source的对应上
    			if(source[k]==target[j]){
    				k++;
    			}else{
    				break;
    			}
    			if(target[j+1]=='\0'){ //如果target判断结束,说明查找成功
    				return k-j-1;
    			}
    		}
    	}
    	if ((i == j && source[i] == target[j]) || target[0]=='\0') {
            return i - j;
        }
    	return -1;
    }

提高要求就是使用KMP算法,这个算法我开始不会,在网上看了很多解释才理解

KMP算法:可以实现复杂度为O(m+n)

为何简化了时间复杂度: 
充分利用了目标字符串ptr的性质(比如里面部分字符串的重复性,即使不存在重复字段,在比较时,实现最大的移动量)。 
上面理不理解无所谓,我说的其实也没有深刻剖析里面的内部原因。

考察目标字符串ptr: 
ababaca 
这里我们要计算一个长度为m的转移函数next。

next数组的含义就是一个固定字符串的最长前缀和最长后缀相同的长度。

比如:abcjkdabc,那么这个数组的最长前缀和最长后缀相同必然是abc。 
cbcbc,最长前缀和最长后缀相同是cbc。 
abcbc,最长前缀和最长后缀相同是不存在的。

**注意最长前缀:是说以第一个字符开始,但是不包含最后一个字符。 
比如aaaa相同的最长前缀和最长后缀是aaa。** 
对于目标字符串ptr,ababaca,长度是7,所以next[0],next[1],next[2],next[3],next[4],next[5],next[6]分别计算的是 
aababaababababaababacababaca的相同的最长前缀和最长后缀的长度。由于aababaababababaababacababaca的相同的最长前缀和最长后缀是“”,“”,“a”,“ab”,“aba”,“”,“a”,所以next数组的值是[-1,-1,0,1,2,-1,0],这里-1表示不存在,0表示存在长度为1,2表示存在长度为3。这是为了和代码相对应。

《13. 字符串查找(普通算法和kmp算法)》

void cal_next(char *str, int *next, int len)
{
    next[0] = -1;//next[0]初始化为-1,-1表示不存在相同的最大前缀和最大后缀
    int k = -1;//k初始化为-1
    for (int q = 1; q <= len-1; q++)
    {
        while (k > -1 && str[k + 1] != str[q])//如果下一个不同,那么k就变成next[k],注意next[k]是小于k的,无论k取任何值。
        {
            k = next[k];//往前回溯
        }
        if (str[k + 1] == str[q])//如果相同,k++
        {
            k = k + 1;
        }
        next[q] = k;//这个是把算的k的值(就是相同的最大前缀和最大后缀长)赋给next[q]
    }
}

kmp算法:

int KMP(char *str, int slen, char *ptr, int plen)
{
    int *next = new int[plen];
    cal_next(ptr, next, plen);//计算next数组
    int k = -1;
    for (int i = 0; i < slen; i++)
    {
        while (k >-1&& ptr[k + 1] != str[i])//ptr和str不匹配,且k>-1(表示ptr和str有部分匹配)
            k = next[k];//往前回溯
        if (ptr[k + 1] == str[i])
            k = k + 1;
        if (k == plen-1)//说明k移动到ptr的最末端
        {
            return i-plen+1;//返回相应的位置
        }
    }
    return -1;  
}

    原文作者:KMP算法
    原文地址: https://blog.csdn.net/qq_36633274/article/details/79155733
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞