链接28. 找出字符串中第一个匹配项的下标 - 力扣(Leetcode)
题目
思路
- 初步的思路是使用一个双指针,i遍历hay字符串,j遍历nee字符串,若在hay中找到了包含nee的部分,则输出(i-nlen+1);若有一个字母不匹配,则j从0开始、i继续往后遍历,直到找到首个匹配项的下标,或者i遍历到hlen末端
弊端:时间复杂度O(n^2)
好处就是容易想到,好实现
- 而后学习到可以用KMP算法做,即寻找next数组(最长相同前后缀)
- next/前缀表的用处:前缀表是用来回退的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配
- 什么是最长公共前后缀,如何通过最长公共前后缀得到next数组?
对KMP算法的详细解释 代码随想录 (programmercarl.com)
实现:
简单的思路:
代码(不减一):
// 找到needle不减一的前缀表
int j=0;
int *next=(int*)malloc(sizeof(int)*nlen);
next[0]=j;
for(int i=1;i<nlen;i++)
{
// 当被判断的字符串长度不为1.且前后缀不相同时
while(j>0&&needle[j]!=needle[i])j=next[j-1];//直至找到相同前后缀,或者字符串 遍历完成
// 当前后缀相同时
if(needle[j]==needle[i])j++;//j指针右移一位
// 把j的值(前缀长度)赋给前缀表中对应的i
next[i]=j;
}
- 用next数组来做匹配(用图来理解)
不匹配时,找到原来匹配的位置
代码:
// 用next表做haystack和needle的匹配
j=0;
for(int i=0;i<hlen;i++)//注意i此时时用于遍历haystack字符串的指针,从0开始
{
//若两个字母不匹配时
while(j>0&&needle[j]!=haystack[i])j=next[j-1];//j 寻找之前匹配的位置
//若两个字母匹配时
if(needle[j]==haystack[i])j++;
//若hay字符串中出现了nee完整的字符串(j遍历到最后一位,注意是到nlen而不是nlen-1)
if(j==nlen)return (i-nlen+1);
}
画图辅助理解整个过程
最终代码:
int strStr(char * haystack, char * needle){
int hlen=strlen(haystack);
int nlen=strlen(needle);
if(nlen==0)return 0;
// 作必要的判断
// 找到needle不减一的前缀表
int j=0;
int *next=(int*)malloc(sizeof(int)*nlen);
next[0]=j;
for(int i=1;i<nlen;i++)
{
// 当被判断的字符串长度不为1.且前后缀不相同时
while(j>0&&needle[j]!=needle[i])j=next[j-1];//直至找到相同前后缀,或者字符串 遍历完成
// 当前后缀相同时
if(needle[j]==needle[i])j++;//j指针右移一位
// 把j的值(前缀长度)赋给前缀表中对应的i
next[i]=j;
}
// 用next表做haystack和needle的匹配
j=0;
for(int i=0;i<hlen;i++)//注意i此时时用于遍历haystack字符串的指针,从0开始
{
//若两个字母不匹配时
while(j>0&&needle[j]!=haystack[i])j=next[j-1];//j 寻找之前匹配的位置
//若两个字母匹配时
if(needle[j]==haystack[i])j++;
//若hay字符串中出现了nee完整的字符串(j遍历到最后一位,注意是到nlen而不是nlen-1)
if(j==nlen)return (i-nlen+1);
}
// 若找不到
return -1;
}
注意细节,区分减一不减一,对k的操作差异!KMP算法有点难理解,之后再看看!
时间复杂度O(nlen+hlen)