【20190321】【每天一道算法题】实现strStr()(双指针)

问题:

实现 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;
} 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Satisfying

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值