28. 找出字符串中第一个匹配项的下标
题目
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1 。
示例 1:
输入:haystack = “sadbutsad”, needle = “sad”
输出:0
解释:“sad” 在下标 0 和 6 处匹配。
第一个匹配项的下标是 0 ,所以返回 0 。
示例 2:
输入:haystack = “leetcode”, needle = “leeto”
输出:-1
解释:“leeto” 没有在 “leetcode” 中出现,所以返回 -1 。
提示:
- 1 <= haystack.length, needle.length <= 104
- haystack 和 needle 仅由小写英文字符组成
解题思路
暴力解法:
- 处理边界条件:len_h 和 len_n比较
- 遍历数组haystack,一旦其中有字符和needle首字符相匹配,进入匹配判断
- 判断匹配直接返回下标,若不匹配继续遍历数组haystack,直到结束
暴力解法时间复杂度 O(m * n)
代码
int strStr(char* haystack, char* needle) {int len_h = strlen(haystack);int len_n = strlen(needle);if (len_n == 0) return 0; // 如果needle是空字符串,按照题意应返回0if (len_h < len_n) return -1;// 注意这里循环条件改为 <= for (int head = 0; head <= (len_h - len_n); head++) {if (haystack[head] == needle[0]) {int i;// 尝试匹配整个needlefor (i = 0; i < len_n; i++) {if (haystack[head + i] != needle[i]) {break; // 不匹配,跳出内层循环}}// 如果i等于len_n,说明完全匹配if (i == len_n) {return head;}}}return -1; // 未找到匹配
}
改进代码
KMP算法
KMP算法核心思想
部分匹配表(Next数组):预处理模式串,构建一个数组记录当匹配失败时,模式串应该回退到的位置。
避免回溯:利用已知信息,当匹配失败时,不必将模式串回退到起点,而是根据部分匹配表回退到合适位置。
详细工作原理
构建Next数组:
- Next[i]表示当第i个字符匹配失败时,模式串应该回退到的位置
- 它实际上记录了模式串中前缀和后缀的最长公共元素长度
匹配过程:
- 主串索引i不回退,只前进
- 当匹配失败时,根据Next数组调整模式串索引j的位置
- 这样避免了重复比较已知不会匹配的部分
// 构建Next数组
void buildNext(char* pattern, int* next, int len) {// 初始化int j = 0;next[j] = 0;/* j:指向前缀末尾位置,同时j也表示i之前的子串的最长相等前后缀的长度i:指向后缀末尾位置*/for(int i = 1; i < len; i++){// 处理不相等的情况 -> 让j进行回退while(j > 0 && pattern[j] != pattern[i]){j = next[j - 1];}/*回退结束有两种情况:1.找到最长相等前后缀的位置了。2.没有相等的前后缀,此时j=0*/if(pattern[j] == pattern[i]) {j++;}next[i] = j;}
}// KMP匹配函数
int strStr(char* haystack, char* needle) {if (!needle[0]) return 0; // 空模式串匹配任何位置int len_h = strlen(haystack);int len_n = strlen(needle);if (len_h < len_n) return -1;// 创建并初始化Next数组int* next = (int*)malloc(sizeof(int) * len_n);buildNext(needle, next, len_n);// 进行匹配int i = 0; // 主串索引int j = 0; // 模式串索引while (i < len_h && j < len_n) {if (haystack[i] == needle[j]) {i++;j++;} else if (j > 0) {j = next[j-1]; // 根据Next数组调整模式串索引} else {i++;}}free(next); // 释放内存// 如果j等于len_n,说明找到完全匹配if (j == len_n) {return i - j;}return -1; // 未找到匹配
}