其在标准库的函数原型为
const char * strstr ( const char * str1, const char * str2 );
char * strstr ( char * str1, const char * str2 );
这是典型的字符串查找匹配问题。常用的方法有普通方法、KMP算法、BM算法、利用字典树等。
普通法:
普通法很直观,双重循环进行匹配,失配的时候就回溯回去重新匹配,时间复杂度为O(N^2)。下面给出了普通方法从右向左的匹配形式。
char *strStr(char *s, char *p) {
int sn = strlen(s);
int pn = strlen(p);
int j = 0; //j指向母串
int i; //j指向子串
while(j <= sn - pn)
{
for(i = pn - 1; i>=0 && p[i] == s[j+i]; --i);
if(i < 0)
return s + j;
else
++j;
}
return NULL;
}
KMP方法:
众所周知,KMP算法的时间复杂度是O(N),因为其匹配过程中母串指针完全不需要回溯,一路向右。对于该算法,求解next数组是最关键的地方,用有限状态自动机的思想去理解KMP算法是一种非常好的思路。对于有限状态机,一般来讲状态转换函数是一个二维数组: A[当前状态值][输入值] = 下一状态值。
对于字符串匹配问题来说,可以将状态转换函数省略为一维数组 : next[当前状态] = 下一状态值, (其默认的输入值为匹配失败的输入,“状态值”就是已经匹配的字符个数。)
next数组只记录匹配失败时的状态转换。匹配成功的输入值是对应位置的字符,匹配成功的话状态值都是加1。
比如,子串“abaabcac”。
状态 0 1 2 3 4 5 6 7 8
字符 ∅ a b a a b c a c
初始值
next[0] = 0
next[1] = 0
递推关系
假设next[i] = j ,即i状态时的失配后的下一状态值是j。
这表明第1~第j的字符子串,等于,第i-j+1~第i的字符子串。
则,
若p[j] == p[i],(第j+1字符等于第i+1字符),那么next[i+1] = next[i] + 1
若p[j] != p[i],那么分析1~j子串内部,令j=next[j],重新判断p[j] == p[i]是否相等,直至判定到相等或者j为0。
class Solution {
public:
char *strStr(char *haystack, char *needle) {
return _strStr(haystack, needle);
}
int _next[100000] = {0};
void getnext(char str[], int size)
{
_next[0] = 0;
_next[1] = 0;
int i = 1, j = 0;
for(;i<size-1;i++)
{
j = _next[i];
if(str[i] == str[j])
{
_next[i+1] = j + 1;
}
else
{
j = _next[j];
while((str[i] != str[j]) && (j != 0) )
{
j = _next[j];
}
_next[i+1] = j;
}
}
}
char *_strStr(char *s, char *p)
{
int sn = strlen(s);
int pn = strlen(p);
if(pn == 0)
return s;
getnext(p, pn);
int i=0,j=0;
while(j < sn)
{
if(p[i] == s[j])
{
i++;
j++;
}
else if( i == 0)
j++;
else
i = _next[i];
if(i == pn)
return s + j - i;
}
return NULL;
}
};