zoukankan      html  css  js  c++  java
  • Sunday算法(字符串查找、匹配)

      字符串查找算法中,最著名的两个是KMP算法(Knuth-Morris-Pratt)和BM算法(Boyer-Moore)。两个算法在最坏情况下均具有线性的查找时间。但是在实用上,KMP算法并不比最简单的c库函数strstr()快多少,而BM算法则往往比KMP算法快上3-5倍。但是BM算法还不是最快的算法,这里介绍一种比BM算法更快一些的查找算法。
      例如我们要在"substring searching algorithm"查找"search",刚开始时,把子串与文本左边对齐,

    substring searching algorithm
    search
    ^

      结果在第二个字符处发现不匹配,于是要把子串往后移动。但是该移动多少呢?这就是各种算法各显神通的地方了,最简单的做法是移动一个字符位置;KMP是利用已经匹配部分的信息来移动;BM算法是做反向比较,并根据已经匹配的部分来确定移动量。这里要介绍的方法是看紧跟在当前子串之后的那个字符(上图中的'i')

      显然,不管移动多少,这个字符是肯定要参加下一步的比较的,也就是说,如果下一步匹配到了,这个字符必须在子串内。所以,可以移动子串,使子串中的最右边的这个字符与它对齐。现在子串'search'中并不存在'i',则说明可以直接跳过一大片,从'i'之后的那个字符(即‘n’)开始作下一步的比较,如下图:

    substring searching algorithm
               search
             ^

      比较的结果,第一个字符就不匹配,再看子串后面的那个字符,是'r',它在子串中出现在倒数第三位,于是把子串向前移动三位,使两个'r'对齐,如下:

    substring searching algorithm
                  search

      哈!这次匹配成功了!回顾整个过程,我们只移动了两次子串就找到了匹配位置,是不是很神啊?!可以证明,用这个算法,每一步的移动量都比BM算法要大,所以肯定比BM算法更快。


    下面是这个算法的c代码。注意我假设了每个字符的值都介于0-127之间(即纯ascii码)。

     1 #ifndef SUNDAY_H
     2 #define SUNDAY_H
     3 
     4 /********************************************************
     5 Sunday算法:
     6     假设源字符串为src,匹配串为pattern。
     7     如果src的某字符不匹配:
     8     (1)该字符在pattern中,且在pattern中最右边的位置为n。那么
     9     下次匹配直接移动pattern使得n的位置和该字符的位置对应的地方。
    10     (2)该字符不在pattern中,显然没有比较的意义,则直接跳过去,
    11     将pattern的头部移到与该字符下一个字符对应的位置。
    12 *********************************************************/
    13 
    14 #include <string.h>
    15 #include <stdio.h>
    16 
    17 // 假设出现的字符都是0~127范围内的,即都是ascii字符
    18 char *sunday(const char *src, char *pattern) {
    19     if (NULL == src)
    20         return NULL;
    21     if (NULL == pattern)
    22         return (char *)src;
    23 
    24     int len1, len2, shift[128];
    25 
    26     len1 = strlen(src);
    27     len2 = strlen(pattern);
    28 
    29     // construct shitf table
    30     for (int i = 0; i < 128; i++)
    31         shift[i] = len2 + 1;  // 默认是移动len2+1,即直接跳过
    32             
    33     // adjust shift table
    34     const char *p;
    35     for (p = pattern; *p; p++)
    36         shift[*p] = len2 - (p - pattern); // p-pattern为字符相对于第一个元素的偏移量,len2-(p-pattern)则表示从右往左数的偏移量,即要移动的步数
    37 
    38     const char *s, *pSrc = src;
    39     // start search
    40     while (pSrc + len2 <= src + len1) { // 如果pSrc + len2不越界(若越界,则说明src剩余未匹配的字符数量小于pattern的长度,则肯定不会匹配成功)
    41         for (p = pattern, s = pSrc; *p; ++p, ++s) 
    42             if (*p != *s) // mistach
    43                 break;// break内循环            
    44         
    45         if ('' == *p)  // found it!
    46             return (char *)pSrc;
    47 
    48         pSrc += shift[pSrc[len2]]; // pSrc[len2]取出下一个需要匹配的字符,shift[pSrc[len2]]取出该字符需要让指针移动多少步
    49     }
    50 
    51     return NULL;
    52 }
    53 
    54 #endif

     下面是main:

     1 #include "Sunday.h"
     2 
     3 int main() {
     4     char *src = "123456789", *patt = "234";
     5     char *ret = sunday(src, patt);
     6 
     7     printf("%s
    ", ret);
     8 
     9     return 0;
    10 }

      代码最后:pSrc += shift[pSrc[len2]];这里。

    举个例子来理解下,比如:

    123456789

      237

    那么4和7不匹配,len2就表示'5'这个位置,所以pSrc[len2]表示取出'5'这个字符。

    所以pSrc下次就移动到'5'这个位置重新开始匹配了。

    ref:http://operatingfocus.bokee.com/3557609.html

  • 相关阅读:
    任务框架--Quartz 配置文件
    地址和值
    线性基学习笔记
    S07
    如何在实际项目中使用PageHelper分页插件
    设计模式:原型模式
    常用JS代码片段
    Thomson Plaza里面的三家店以及水果大会
    13.搜索过滤
    07-多线程
  • 原文地址:https://www.cnblogs.com/lingshaohu/p/3961013.html
Copyright © 2011-2022 走看看