针对串的操作,在主串s里面查找子串sub,从pos位置开始的第一个符合的子串,返回第一个字符的下标。
BF算法如下:
时间复杂度:O(mn)
当字符相等时,j++,i++,当不等时,j需要回退到0号下标,i需要回退到之前的位置+1
int BF(const char *s,const char *sub, int pos)
//在s里面查找子串sub,从pos位置开始的第一个符合的子串,返回第一个字符的下标
{
assert(sub != NULL);
assert(s != NULL);
int lens = strlen(s);
int lensub = strlen(sub);
if (pos<0 || pos>lens)
{
return -1;
}
int i = pos;//i从pos开始走主串
int j = 0;//j从0开始走子串
while (i < lens && j < lensub)
{
if (s[i] == sub[j])//当字符相同时,继续走
{
i++;
j++;
}
else//当字符不同时,i退回到次字符,j退回0
{
i = i - j + 1;
j = 0;
}
}
if (j >= lensub)
{
return i - j;//当j遍历完子串时,返回下标[i-j]
}
else
{
return -1;
}
}
KMP算法:与BF算法不同的是,相对于 BF 算法来说KMP 算法更为高效,原因在于 BF 算法的时间复杂度是:O(mn)M 代表主串的长度,n 代表子串的长度。而KMP 的话,时间复杂度就变为 O(m+n);KMP 和 BF 唯一不一样的地方在,主串的i 并不会回退,并且 j 也不会移动到 0 号位置。但在这里j需要回退到k位置,于是引出了存放串的各个位置对应的k值的一个next[]数组,也就是用 next[j] = k来表示,不同的 j 来对应一个K 值, 这个K 就是你将来要移动的j 要移动的位置。而K 的值是这样求的:
1、规则:找到匹配成功部分的两个相等的真子串(不包含本身),一个以下标 0 开始,另一个以 j-1 下标结尾。
2、不管什么数据 next[0] = -1;next[1] = 0;在这里,我们以下标来开始,而说到的第几个第几个是从 1 开始;
-1 的理由:当主串为–”defrdes” 子串为:”abc” 一开始就匹配失败。
0 的理由:当子串在 1 号下标匹配,此时为 0;
next 数组的优化,即如何得到 nextval 数组:
有如下串:aaaaaaaab,他的 next 数组是-1,0,1,2,3,4,5,6,7.而修正后的数组 nextval 是:
-1,-1,-1,-1,-1,-1,-1,-1,7。为什么出现修正后的数组,假设在 5 号处失败了,那退一步还是 a,还是相等, 接着退还是 a。
例:
串 : a b c a a b b c a b c a a b d a b
next: -1 0 0 0 1 1 2 0 0 1 2 3 4 5 6 0 1
nextval:-1 0 0 -1 1 0 2 0 -1 0 0 -1 1 0 6 -1 0
黑体a的nextval 求法:a的next为4,而4号下标的值也为a,所以nextval就不为黑体a的next值,而为4号下标a的next值,即为1。
//next数组:保存匹配失败后。需要回退的位置
//找到相同的两个真子串
//一个以0号下标开始,一个是以j-1号下标结尾
void GetNext(int *next,const char *sub)//求子串next数组的算法
{
assert(sub != NULL);
int lensub = strlen(sub);
next[0] = -1;
next[1] = 0;
int i = 2;//第二项
int k = 0;//第二项下标对应的k值
while (i < lensub)//例:abcababcabc-1 0 0 0 1 2 1 2 3 4 5 a的next数组值即第一个a的k值为-1,第一个b的为0,继续走。。。
{
if ((k == -1) || sub[k] == sub[i - 1])//当第一个b的k值作字符下标所对应的字符 a不等于b时,else
{
next[i] = k + 1;//**要求的字符的k值,给k+1,则第三个字符c的k值为-1+1=0
i++;
k++;
}
else
{
k = next[k];//**next[0]=-1付给k,k=-1进入if()中
}
}
}
int KMP(const char *s,const char *sub, int pos)
{
int lens = strlen(s);
int lensub = strlen(sub);
int *next = (int *)malloc(sizeof(int)*(lensub+1));//开辟next[]数组并不是子串的长度,在子串长度上加1
assert(next != NULL);
GetNext(next,sub);//调用next函数,得到子串的next数组
if (pos<0 || pos>lens)
{
return -1;
}
int i = pos;//i从pos开始走主串
int j = 0;//j从0开始走子串
while (i < lens && j < lensub)
{
if ((j == -1) || s[i] == sub[j])
{
i++;
j++;
}
else
{
j = next[j];//i不往回走,j往回走到next对应下标位置
}
}
free(next);
if (j >= lensub)
{
return i - j;//当j遍历完子串时,返回下标[i-j]
}
else
{
return -1;
}
}