-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]分别计算的是
a,ab,aba,abab,ababa,ababac,ababaca的相同的最长前缀和最长后缀的长度。由于a,ab,aba,abab,ababa,ababac,ababaca的相同的最长前缀和最长后缀是“”,“”,“a”,“ab”,“aba”,“”,“a”,所以next数组的值是[-1,-1,0,1,2,-1,0],这里-1表示不存在,0表示存在长度为1,2表示存在长度为3。这是为了和代码相对应。
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;
}