1. 描述
给你两个字符串 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 。
来源:力扣(LeetCode)
2. 思路
这道题有两种解法:
1. 朴素解法:也就是每个字逐步匹配,遇到不能匹配的,从头开始匹配;这样时间复杂度为O(m*n) m是被匹配的字符串(haystack)长度;n是需匹配的字符串(needle)长度。
2. KMP法: 优先对需匹配的字符(needle)换建立一个prefix的相同前驱长度, 如下(列:abababc):
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
字符串 | a | b | a | b | a | b | c |
相同前驱长度 | 0 | 0 | 1 | 2 | 3 | 4 | 0 |
我们可以先列出所有的前驱,计算从左到右 和从右到左的相同字符串的最大长度
相同前驱长度 | 所有前驱串 | 说明 |
0 | a | 本身为0 |
0 | ab | ab 没有相同的为0 |
1 | aba | aba 一个相同 |
2 | abab | abab 两个相同 |
3 | ababa | ababa ababa 三个相同 |
4 | ababab | ababab ababab 四个相同 |
0 | abababc | abababc 没有相同为0 |
private int[] Build_PrefixTable(string pattern,int n)
{
if (n == 0) return new int[0];
int[] prefix = new int[n];
int i = 1, j = 0;
while(i < n)
{
while (j > 0 && pattern[i] != pattern[j])
{
j = prefix[j - 1];
}
if (pattern[i] == pattern[j])
{
j++;
}
prefix[i] = j;
i++;
}
return prefix;
}
然后对比被匹配(haystack)和需匹配(needle):
public int StrStr(string haystack, string needle)
{
if(needle.Length>haystack.Length) return -1;
int n = needle.Length, m = haystack.Length;
var prefix = Build_PrefixTable(needle,n);
for(int i=0,j=0;i<m;i++)
{
while(j>0 && haystack[i]!= needle[j])
{
j=prefix[j-1];
}
if(haystack[i]==needle[j])
{
j++;
}
if(j==n)
{
return i-n +1;
}
}
return -1;
}