出自:http://blog.csdn.net/fengchaokobe/article/details/8919074

字符串匹配问题这是个老话题了,而我们也热衷于学习和探讨这个问题,并且我们也经常会用到它。比如说,我们用vim打开一个文本文件,要在这个文件中查找某一个字符串时,我们只需在底行模式下输入/String即可;再比如,在linux终端中,我们要把当前目录下所有的c文件打印出来,那么这时候我们就会利用正则表达式来进行匹配操作(所有的c文件可表示为*.c),而不是挨个去找。

好了,书接正文。写本篇文章的目的有二:

其一:先前也写过字符串匹配的文章,不过那篇文章只说了字符串固定匹配以及KMP算法,没有对字符串动态匹配进行讲述。所以一直想写关于动态匹配的文章;

其二:对于一个算法爱好者,如一位仁兄所说:Say what you think and show your code!如若这样,岂不快哉!

第一章 固定串匹配

我为什么叫它固定串呢?我还是举个例子说明一下吧(没办法,语言表达能力不好啊)!假设有源字符串…superfcing…,我们想知道子串superfc是否在源串中出现过?此时不难看出子串是一个确定的字符串(不含有不确定的字符),那么我就将它成为固定字符串匹配法。

解决这种情况的算法不用我多说,自然是KMP算法(最坏情况为O(strlen(source_string) + k))了。对于这个算法先前也说过,在这就不细说了。这个算法的关键步骤就是源串前缀数组的生成。下面我用图例给出前缀数组的生成过程:

对于KMP算法,归总起来就是:扫描源字符串,更新并标记子串在源串中出现的位置。

附注:关于KMP算法的前缀函数,当数组的起始下标为0时,务必不要让前缀函数的初值设为0,如果这样的话,可能会死循环。你如果对此有兴趣,自己可以试试。

好了,固定串匹配的情况就是这样,解决方法也有了,接下来我们看看代码的实现:

代码中使用到的变量代表的含义:

[html] view plaincopyprint?
  1. /*  Source:源串
  2. **  SourceLength:源串长度
  3. **  Prefix:前缀数组
  4. **  Pattern:匹配串,即子串
  5. **  P_Length:子串长度
  6. **  Buffer:当源串中有子串匹配时,用Buffer保存这些子串
  7. */<span style="font-size:18px;"></span>
[cpp] view plaincopyprint?
  1. /**为KMP算法得到前缀数组**/
  2. void GetPrefixArray(char *Source, int *Prefix, int SourceLength)
  3. {
  4. int k = 0, i;
  5. Prefix[k] = -1;//当下标起始为0时,起始值务必设为1(原因见如下解释)
  6. for(i = 1; i < SourceLength; i++)
  7. {
  8. while(Source[k] != Source[i]  &&  k > 0)
  9. {
  10. k = Prefix[k];
  11. }
  12. if(Source[k] == Source[i])
  13. {
  14. ++k;
  15. }
  16. Prefix[i] = k;
  17. }
  18. }
[cpp] view plaincopyprint?
  1. void StringMatchOfKMP(char *Source, int SourceLength, int *Prefix,
  2. char *Pattern, int P_Length, char *Buffer)
  3. {
  4. int i = 0, j = 0, index = 0;
  5. GetPrefixArray(Source, Prefix, SourceLength);
  6. while(i < SourceLength)
  7. {
  8. while(j > 0 && Source[i] != Pattern[j])
  9. {
  10. j = Prefix[j];
  11. }
  12. if(Source[i] == Pattern[j])
  13. {
  14. j++;
  15. }
  16. if(j == P_Length)
  17. {
  18. memcpy((Buffer + index * P_Length), Pattern, strlen(Pattern));
  19. index++;//当有多个匹配时,index * P_Length为每个子串在Buffer中的起始位置
  20. }
  21. i++;
  22. }
  23. }

第二章 单字符动态匹配

所谓单字符动态匹配,就是:当字串中出现?字符时,该字符表示匹配任意的一个字符(因为它是任意的一个字符,所以说它是单字符动态匹配)。那么这种情况怎么解决呢?很简单,我们只需要对源串做一次遍历,并不需要像固定串匹配那样利用KMP算法。即子串在匹配源串的过程中,当遇见?字符时,我们直接认为子串中的该字符和源串中对应位置的字符匹配成功。举个例子,假设源串为helloSuperfc,子串为S*per,那么当源串和子串中的S匹配时,分别做++,这个时候源串中的u就会和子串中的*相比较,此时我们就认为*就是字符u,匹配成功,然后进行下一个字符的匹配。

好,给个图示:

单字符动态匹配就是这样,我们来看看它的实现代码:

[cpp] view plaincopyprint?
  1. void StringMatchOfAsk(char *Source, int SourceLength, char *Pattern,
  2. int P_Length, char *Buffer)
  3. {
  4. int i = 0, j = 0;
  5. int index = 0;
  6. while(i < SourceLength)
  7. {
  8. if((Pattern[j] == Source[i]) || (Pattern[j] == '?'))
  9. {
  10. /*匹配完成*/
  11. if((j + 1) == P_Length)
  12. {
  13. memcpy((Buffer + index * P_Length), (Source + i - P_Length + 1), P_Length);
  14. index++;
  15. j = 0;
  16. }
  17. else
  18. {
  19. j++;
  20. i++;
  21. }
  22. }
  23. else
  24. {
  25. i = i - j + 1;//相当于i++
  26. j = 0;
  27. }
  28. }
  29. }

第三章 多字符动态匹配

所谓多字符动态匹配,就是:当子串中出现*字符时,该字符表示可以匹配至少0个以上的字符(因为它可以匹配多个字符,所以说它是多字符动态匹配)。那么这种情况怎么解决呢?我认为它比单字符动态匹配还要简单,即当子串中的*与源串中的对应字符比较时,我就认为*字符包含从源串中的当前位置开始,直到当子串中的下一个字符与源串中的字符相等时,匹配结束。这么说太拗口了,我用一个图例来说明一下:

我们来看看代码的实现:

[cpp] view plaincopyprint?
  1. void StringMatchOfStar(char *Source, int SourceLength, char *Pattern,
  2. int P_Length, char *Buffer)
  3. {
  4. int i = 0, j = 0;
  5. int index = 0;
  6. while(i < SourceLength)
  7. {
  8. if(Source[i] == Pattern[j])
  9. {
  10. /*匹配完成*/
  11. if(P_Length == ++j)
  12. {
  13. memcpy(Buffer, (Source + index), i - index + 1);
  14. break;
  15. }
  16. /*保存起始匹配的下标*/
  17. if(0 == j - 1)
  18. {
  19. index = i;
  20. }
  21. }
  22. else if(Pattern[j] == '*')
  23. {
  24. j++;
  25. }
  26. i++;
  27. }
  28. }

第四章 结束语

转载于:https://www.cnblogs.com/mfryf/archive/2013/05/21/3090247.html

零零散散学算法之再叙字符串匹配相关推荐

  1. 数据结构与算法之美 32 字符串匹配基础(中):如何实现文本编辑器中的查找功能

    如何实现文本编辑器中的查找功能 背景 BM 算法的核心思想 BM 算法原理分析 1. 坏字符规则 2. 好后缀规则 BM 算法代码实现 背景 文本编辑器中的查找替换功能,我想你应该不陌生吧?比如,我们 ...

  2. 【算法无用系列】字符串匹配那些事——BM算法

    文章目录 前言 一.BM算法 1.1.坏字符规则 1.2.好后缀规则 前言 BF算法和RK算法思路比较简单,但是效率却不尽人意,适合较短的字符串匹配时使用,如果需要在较长的字符串匹配时,则需在算法上进 ...

  3. 算法设计:精确字符串匹配

    首先来学学精确字符串匹配: 那我们要怎么找呢?: 好麻烦,有没有别的?: 指纹就是一个标识符,但这个标识符怎么找呢? 就是把每个字母当成一位十进制数的数字,我开头记录下目标的字符串是什么数字,然后再一 ...

  4. 尤雨溪:先学算法,再学源码!

    算法可以说是前端进阶必须要掌握的能力,不仅在工作中可以提高我们的代码运行效率,而且在大厂面试的时候也是必考内容. Object和Map如何选择?Map又是如何实现的? 说说VDOM DIFF的底层原理 ...

  5. 1097: 零起点学算法04——再模仿一个算术题

    Description 上题会模仿了吧.再来模仿一个.  现在要求你模仿一个乘法的算术题 Input 没有输入 Output 输出9乘以10的值 Sample Output 90 Source 零起点 ...

  6. 字符串算法之KMP(字符串匹配)

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

  7. (算法)通俗易懂的字符串匹配KMP算法及求next值算法

    大多数据结构课本中,串涉及的内容即串的模式匹配,需要掌握的是朴素算法.KMP算法及next值的求法.在考研备考中,参考严奶奶的教材,我也是在关于求next值的算法中卡了一下午时间,感觉挺有意思的,把一 ...

  8. 零零散散学算法之详解几种数据存储结构

    影响空间规模的几种数据存储结构 正文 所谓数据存储结构,就是数据的元素与元素之间在计算机中的一种表示,它的目的是为了解决空间规模问题,或者是通过空间规模问题从而间接地解决时间规模问题.我们知道,随着输 ...

  9. 字符串匹配KMP算法的讲解C++

    转自http://blog.csdn.net/starstar1992/article/details/54913261 也可以参考http://blog.csdn.net/liu940204/art ...

最新文章

  1. 这两年:我的数据竞赛之路
  2. Great Power, Great Responsibility: The 2018 Big Data AI Landscape
  3. 初识ABP vNext(2):ABP启动模板
  4. 【转】谈谈Unicode编码,简要解释UCS、UTF、BMP、BOM等名词
  5. 小明历险记:规则引擎drools教程一
  6. redis删除不存在的key会报错吗_Redis哈希类型
  7. java用多线程实现爬虫_JAVA 多线程爬虫实例详解
  8. C语言——函数的调用
  9. 菲涅尔单缝衍射matlab,单缝菲涅尔衍射的光强分布.pdf
  10. 神州数码云平台基础环境搭建
  11. 循环冗余校验码CRC原理与LFSR循环码编码器原理
  12. 神州数码配置命令总结-(已更新)
  13. 过拟合与欠拟合及解决方法
  14. 设计师:室内设计师的简介、工作内容、工作要求、设计常识(硬装/软装/榻榻米/马卡龙/地台/公共空间/玄关/闭水实验、家具知识(欧式雕花家具-欧式雕花家具)、室内设计常用尺寸之详细攻略
  15. 教程:从零开始 使用Python进行深度学习!
  16. 几倍根号用学生计算机,几倍根号几怎么算不要网上抄的 祥细的 例如2√2 3√3 4√4 怎么算...
  17. 奇虎360校园招聘2015笔试题目
  18. 美德乐吸奶器怎么样?
  19. 第十一届蓝桥杯 ——成绩统计
  20. 这是毕业生们唯一的信念;我所看到最好的毕业纪念文

热门文章

  1. Firefox 不响应 event.keyCode 问题的解决方案
  2. python读取大文件内存不够_大型CSV文件(numpy)上的Python内存不足
  3. (72)FPGA面试题-使用不同的代码实现2-4译码器?使用if语句
  4. FPGA实现低高速接口更新说明
  5. cesium 局部加载_cesium自定义气泡窗口infoWindow后续优化篇 - GIS之家
  6. 韦东山和正点原子IMX6ULL开发版的区别
  7. 4x root 红米_好激动,红米NOTE4X开发版成功获得完全ROOT教程,非假ROOT!!
  8. ajax html页面传值乱码,jQuery Ajax传值到Servlet出现乱码问题的解决方法
  9. 用Openswan组建Linux IPSec ---第二部分
  10. PPP协议体系的实现