问题:
实现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回-1。
注意:当 needle
是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle
是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。
思路及代码:
1. 我的做法:不多加分析地上来就暴力搜索,没有理清思路框架,多层循环容易出错,此法不可取!尤其注意里面的【逻辑Bug】
/* 我的思路:双指针
*
* 代码逻辑:1.定义两个指针,分别指向haystack和needle,判断*p =?= *q(表示“是否等于”)
* 2. 如果不相等,那么p++,q指向needle开头;如果相等,那么p++,q++,逐位判断。
*
* 逻辑Bug:例如"mississipi"和"issip",在s1中查找s2时,检查到s2的'p'时,s1到了第三个's',
* 这时已经错过了第二个'i',所以将返回-1,造成错误!
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int strStr(char* haystack, char* needle);
void main()
{
int result;
char s1[100]="mississippi";
char s2[50]="issip";
result=strStr(s1,s2);
printf("%d\n",result);
system("pause");
}
int strStr(char* haystack, char* needle)
{
if(strlen(needle)==0)
return 0; //needle为空时,返回值为0(这与C语言的strstr()以及Java的indexOf()定义相符!)
if(strlen(haystack)==0)
return -1; //haystack为空时,返回值为-1。
int loc=0,i=1,j=0; //表示needle首字母第一次出现;
int len2=strlen(needle); //C语言有求字符串长度的函数,头文件为<string.h>
char *p1=haystack,*p2=needle;
do
{
p2=needle; //如果不相等,那么p2指向开头
if(*p1!=*p2)
{
p1++;
loc++;
}
else
{
while(*p2!='\0'&&*p1==*p2)
{
p1++;
p2++;
loc++;
}
i++;
}
}while(*p1!='\0');
if(*p2=='\0')
{
loc=loc-len2; //最后一次*p1==*p2之后,loc又自加了一次,因此这里不用减一!
return loc;
}
return -1;
}
2. 参考网友的代码(一):思路清晰,尤其循环嵌套做得很好,这种想法值得参考!
/* 代码思路:数组
*
* 代码逻辑:1.最大前提(最外层循环):haystack剩下的长度 > needle的长度
* 2. 第二个前提(第二层循环):haystack中存在needle[0]
* 3.第三层循环,haystack中存在needle[0]时,才移动指向needle的指针(此时移动才有意义)
*
* 巧妙之处:haystack中找到needle[0]时,指向haystack的指针并没有移动,而是利用i+j的形式来调用haystack,这就避免了【错过】的现象。
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int strStr(char* haystack, char* needle);
void main()
{
int result;
char s1[100]="mississippi";
char s2[50]="issip";
result=strStr(s1,s2);
printf("%d\n",result);
system("pause");
}
int strStr(char* haystack, char* needle)
{
int i,j,lenHay,lenNee;
lenHay = strlen(haystack);
lenNee = strlen(needle);
if(lenNee == 0) //如果needle长度为0,那么返回0。
return 0;
for(i=0;i<lenHay - lenNee + 1;i++)
//i<lenHay-lenNee+1的原因在于,如果lenHay剩下还未检查的字符长度不如lenNee长,那么在lenHay中将不存在lenNee(避免了很多运算)。
{
if(haystack[i] == needle[0]) //这里的条件判断可以省略很多不必要第二轮循环
{
for(j=0;j<lenNee;j++)
{
if(haystack[i+j] != needle[j]) /*这里很巧妙,判断满足相等的条件时,没有去
改变i的值,而是用i+j的方式,这就避免了【我的代码】那个【错过】的问题!*/
break;
}
if(j == lenNee)
return i;
}
}
return -1;
}
3. 参考网友的代码(二):
class Solution
{
/**
* 思路1
* 暴力破解,这个没啥说的,挨个循环吧
* 耗时
* 执行用时 : 56 ms, 在Implement strStr()的PHP提交中击败了26.32% 的用户
* 内存消耗 : 16.5 MB, 在Implement strStr()的PHP提交中击败了100.00% 的用户
* 时间复杂度
* O(n)
*/
function strStr($haystack, $needle)
{
if (!$needle) {
return 0;
}
$length = strlen($needle);
$i = 0; // 短指针,指向匹配字符串头
while ($str = substr($haystack, $i, $length)) {
if ($str == $needle) {
return $i;
}
$i++;
}
return -1;
}
/**
* 思路2
* KMP 算法
* 利用已经部分匹配这个有效信息,保持i指针不回溯,通过修改j指针,让模式串尽量地移动到有效的位置。
* 我这个写的不太好,虽然时间上没什么大改变,不过接受了一种新思想。
* 具体算法示例
* 输入 haystack = "hello world", needle = "orld"
* 1:循环 haystack 字符串,既然要找匹配 'or' 的字符串,那么开头肯定是为 'o' 的。
* 根据这个,我们找到了下标为 5 的以 'o' 开头的。
* 2:从 5 开始做循环匹配,'o','w','o','r' 发现字符串不匹配,需要重新寻找。
* 这时我们就没必要在从 6 开始找,因为在处理匹配字符串时,找到了另一个 'o' 的位置.
* 这样我们就可以把下一次循环改变为 'o' 的位置,从而减少循环。
* 耗时
* 执行用时 : 28 ms, 在Implement strStr()的PHP提交中击败了42.11% 的用户
* 内存消耗 : 16.5 MB, 在Implement strStr()的PHP提交中击败了100.00% 的用户
* 时间复杂度
* O(n)
* 详解
* https://www.cnblogs.com/yjiyjige/p/3263858.html
*/
function strStr($haystack, $needle)
{
if (!$needle) {
return 0;
}
$target_length = strlen($needle);
$length = strlen($haystack);
$i = 0;
while ($i <= $length) {
if ($length - $i < $target_length) {
return -1;
}
$str = '';
if ($haystack[$i] == $needle[0]) {
$kmp = 0;
for ($j = 0; $j < $target_length; $j++) {
$str .= $haystack[$i + $j];
if (($haystack[$i + $j] == $needle[0]) && !$kmp) {
$kmp = $i + $j + 1;
}
}
if
4. 自己编写的最终版:
/* 参照网友二的思路,自己编写的代码 */
/* 解题思路:双指针法??(指针和数组索引有什么不一样???)
*
* 整体逻辑:1.定义两个指针i,j分别指向两个字符串
* 2.第一个大前提:haystack剩下的字符长度不能比needle长度小,不然返回-1(避免很多计算)
* 3.第二个大前提:haystack中存在needle首字符
* 4.在上面两个前提下,才对指向needle的指针进行自加操作
*
* 巧妙之处:haystack中找到needle[0]时,指向haystack的指针并没有移动,而是利用i+j的形式来调用haystack,这就避免了【错过】的现象。
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int strStr(char* haystack, char* needle);
void main()
{
int result;
char s1[100]="a";
char s2[50]="a";
result=strStr(s1,s2);
printf("%d\n",result);
system("pause");
}
int strStr(char* haystack, char* needle)
{
int len1,len2;
int i=0,j;
len1=strlen(haystack);
len2=strlen(needle);
if(len2==0)
return 0;
for(;i<=len1-len2;i++) //第一前提(巧妙之处:在haystack中找到needle的首字母之后,i不再自加,避免了我的思路中"mississip""issip"错过第二个'i'的现象)
{
j=0; //每开始进行一次for循环,实质上是对needle首字符进行搜索,因此每for一次,j要归零。
if(haystack[i]==needle[0]) //第二前提
{
while(haystack[i+j]==needle[j]) //在两个前提下,才进行判断
{
j++;
if(j>=len2) //约束退出循环的条件(若不加退出条件,可正确执行,但会造成“溢出”)
break;
}
}
if(j==len2)
return i;
}
return -1;
}