strStr()函数的用途是在一个字符串S中寻找某个字串P第一次出现的位置。并返回其下标,找不到时返回-1。最简单的办法就是找出S全部的子串和P进行比較,然而这种方法比較低效。假设我们从S的下标0和P的下标0開始对每一个字符进行比較,假设相等则下标添加,比較后面的字符。假设两者一直相等直到P的下标达到最大值。则表示在S中找到了P。而且第一次出现的位置为0,返回0,但假设在中间某个位置两个字符不相等时。这时S的下标要退回到1,P的下标回到0。又一次開始比較。

后来,有三个牛认为这样不爽。于是他们搞了一个KMP算法,并以他们三人名字的最開始字符命名这个算法。

这个算法的优点是在比較中的某个位置,两个串的字符不相等时。不须要S回退,而P的下标回退到某个值。然后继续和S当前失配的字符进行比較。

关键就是在这里了。P的下标要回退,退到哪儿啊?我们举个样例,S为“abcabcabeg”,P为"abcabe",从下标0開始比較,一直到下标5才发现字符不相等,S相应字符为c。P相应字符为e。这时候。我们就考查P中下标5曾经的字符串,即“abcab”。对于这个字符串,S下标5之前也存在相同的串。我们还发现这个串的前缀和后缀都有“ab”,即下标0、1和3、4分别组成的字符串相等,同一时候等于S串中下标3、4组成的字符串。因此。我们仅仅须要把P的下标退到2的位置。然后跟S的下标5相应的字符继续比較就可以,由于P的0、1下标相应字符和S的3、4下标相应的字符相等。假设对于每一个失配的位置,我们都这样对P的下标进行调整,而S的下标继续往前而不后退,效率会提高非常多。

如今最大的问题就是怎么计算P失配时应该退回到的下标值。而从前面的分析中能够看出是对字符串的前后缀相等部分的长度来获取的。这样问题就变成了怎么编写代码来较为高效地计算在失配时P调整到的新下标值。

对于P中每一个位置,我们能够计算出在该位置失配时应该调整到的下标,并存放在一个next数组中,比方上面P在下标为5时失配。调整到的新下标为2。则有next[5]等2。

写到这里,发现好像讲得越来越乱了。还是贴我自己的代码吧。事实上KMP最关键也是不太好理解的地方就是对next数组的理解和计算方法。

void fillNext(char* p, int* next)

{

    if(!p) return;

     

    int len = strlen(p);

     

    int i = 1;

    int j = 0;

     

    for(int i = 0; i < len; ++i) next[i] = 0;

     

    while(i < len-1)

    {

        if(p[i] == p[j])

        {

            i++;

            j++;

            next[i] = j;

        }

        else

        {

            if(j == 0)

            {  

                i++;

                next[i] = 0;

            }

            else j = next[j];

        }

    }

}

 

int strStr(char* haystack, char* needle) {

 

    if(!haystack || !needle) return -1;

   

    int len_h = strlen(haystack);

    int len_n = strlen(needle);

 

    if(len_n > len_h) return -1;

     

    if(len_n == 0) return 0;

 

    int* next = malloc(sizeof(int)*len_n);

    fillNext(needle, next);

     

    int i = 0;

    int j = 0;

     

    while(i < len_h)

    {

        if(haystack[i] == needle[j])

        {

            j++;

            i++;

            if(j == len_n)break;

        }

        else

        {

            if(j == 0)i++;

            else j = next[j];

        }

    }

     

    free(next);

 

    if(j == len_n)

    {   

        return i - len_n;

    }

     

    return -1;

}

用KMP算法实现strStr()的更多相关文章

  1. 70. Implement strStr() 与 KMP算法

    Implement strStr() Implement strStr(). Returns a pointer to the first occurrence of needle in haysta ...

  2. Linux GCC下strstr的实现以及一个简单的Kmp算法的接口

    今天做了一道题,要用判断一个字符串是否是另一个字符串的子串,于是查了一下strstr的实现. 代码如下: char *strstr(const char*s1,const char*s2) { con ...

  3. 第3章:LeetCode--算法:strStr KMP算法

    https://leetcode.com/problems/implement-strstr/  28. Implement strStr() 暴力算法: int ViolentMatch(char* ...

  4. 自己对kmp算法的理解,借由 28. 实现 strStr() 为例

    做题思路 or 感想 : 就借由这道题来理解一下kmp算法吧 kmp算法的操作过程我觉得有句话很合适 :KMP 算法永不回退 目标字符串 的指针 i,不走回头路(不会重复扫描 目标字符串),而是借助 ...

  5. KMP算法的优化与详解

    文章开头,我首先抄录一些阮一峰先生关于KMP算法的一些讲解. 下面,我用自己的语言,试图写一篇比较好懂的 KMP 算法解释. 1. 首先,字符串"BBC ABCDAB ABCDABCDABD ...

  6. KMP算法 --- 在文本中寻找目标字符串

    KMP算法 --- 在文本中寻找目标字符串 很多时候,为了在大文本中寻找到自己需要的内容,往往需要搜索关键字.这其中就牵涉到字符串匹配的算法,通过接受文本和关键词参数来返回关键词在文本出现的位置.一般 ...

  7. KMP算法详细分解

    1. 引言 给定一个主串(以 S 代替)和模式串(以 P 代替),要求找出 P 在 S 中出现的位置,此即串的模式匹配问题. Knuth-Morris-Pratt 算法(简称 KMP)是解决这一问题的 ...

  8. 从头到尾测地理解KMP算法【转】

    本文转载自:http://blog.csdn.net/v_july_v/article/details/7041827 1. 引言 本KMP原文最初写于2年多前的2011年12月,因当时初次接触KMP ...

  9. 很详尽KMP算法(厉害)

    作者:July时间:最初写于2011年12月,2014年7月21日晚10点 全部删除重写成此文,随后的半个多月不断反复改进.后收录于新书<编程之法:面试和算法心得>第4.4节中. 1. 引 ...

随机推荐

  1. 可点击的icon按钮 无障碍 ARIA 可访问性

    最简单: <input type="image" src="email.png" width="14" height="14 ...

  2. JavaScript系列----作用域链和闭包

    1.作用域链 1.1.什么是作用域 谈起作用域链,我们就不得不从作用域开始谈起.因为所谓的作用域链就是由多个作用域组成的.那么, 什么是作用域呢? 1.1.1作用域是一个函数在执行时期的执行环境. 每 ...

  3. 多少牛逼的程序员毁在low逼的英文发音上(JAVA)

    最最常用的关键词及音标 数据类型:boolean.byte.short.int.long.double.char.float.double. 包引入和包声明:import.package. 用于类和接 ...

  4. [转载] Netty

    转载自http://lippeng.iteye.com/blog/1907279 Netty是什么? 本质:JBoss做的一个Jar包 目的:快速开发高性能.高可靠性的网络服务器和客户端程序 优点:提 ...

  5. Spring+SpringMVC+MyBatis+easyUI整合进阶篇(九)Linux下安装redis及redis的常用命令和操作

    redis简介 Redis是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库. Redis与其他key-value缓存产品有以下三个特点: Redis支持数据的持久化,可以将内存 ...

  6. Python 面向对象(三) 魔术方法

    __getitem__ 在对实例或对象使用索引访问时调用,self[key]__dir__ 收集当前模块的信息,包括继承自其它基类(包括object类)的属性和方法 __new 定义如何创建实例__i ...

  7. Spring学习笔记(二)之装配Bean

    一,介绍Bean的装配机制 在Spring中,容器负责对象的创建并通过DI来协调对象之间的关系.但是我们要告诉Spring创建哪些Bean并且如何将其装配在一起.,装配wiring就是DI依赖注入的本 ...

  8. Bitmap.Config 说明 ALPHA_8 ARGB_4444 ARGB_8888 RGB_565

    这篇文章的目的是了解Bitmap.Config 你可以在使用这个方法的时候会遇到 Bitmap android.graphics.Bitmap.createBitmap(int width, int ...

  9. idea+scala+spark遇到的一些问题

    1.windows中以本地模式运行spark遇到"Could not locate executable null\bin\winutils.exe in the Hadoop binari ...

  10. java八大基本数据类型

    java中八大数据类型的储存空间以及使用场景表示如下 )1.int:4字节,可以表示的数为-2^31 - 2^31-1.整数的默认类型.封装类也如此 .整数相除的时候,会舍弃小数部分.结果也是整数,例 ...