美文网首页
41_KMP子串查找算法

41_KMP子串查找算法

作者: 编程半岛 | 来源:发表于2018-07-16 10:14 被阅读23次

关键词:

题目:如何在目标字符串S中查找是否存在子串P?

0. 朴素解法

思路:子串中的每一个字符与匹配字符串中的每一个字符一个个比较,如果相等,同时移动一位再比;如果不相等,则后移一位,从子串的第一个字符开始比较,直到找完全匹配的字符串为止。

int sub_str_index(const char* s, const char* p)
{
    int ret = -1;
    int sl = strlen(s);
    int pl = strlen(p);
    int len = sl - pl;
    
    for(int i=0; (ret<0) && (i<=len) ; ++i)
    {
        bool equal = true;
        
        for(int j=0; equal && (j<pl); ++j)
        {
            equal = equal && (s[i + j] == p[j]);
        }
        
        ret = (equal ? i : -1);
    }
    
    return ret;
}

1. 朴素解法的优化


上图中因为pa != pbpb == sb,所以:pa!=sb。因此子串p右移1位去比较是否相等是没有意义的
  • 匹配失败时的右移位数与子串本身相关,与目标串无关;
  • 移动位数 = 已匹配的字符数 - 对应的部分匹配值
  • 任意子串都存在一个唯一的部分匹配表(pmt)


    部分匹配表示例

2. 获取部分匹配表

  • 前缀:除了最后一个字符以外,一个字符串的全部头部组合
  • 后缀:处理第一个字符以外,一个字符串的全部尾部组合
  • 部分匹配值:前缀和后缀的最长共有元素的长度(前缀与后缀的交集的最大值)


    字符串"ABCDABD"部分匹配表示例

编程实现部分匹配表的关键步骤:

  • PMT[1] = 0 (下标为0的元素匹配值为0)
  • 从第2个字符开始递推(从下标为1的字符开始递推)
  • 假设PMT[n] = PMT[n-1] + 1(最长共有元素的长度)
  • 当假设不成立时,PMT[n]在PMT[n-1]的基础上减小

make_pmt()

int* make_pmt(const char* p)        // O(n)
{
    int len = strlen(p);
    int* ret = static_cast<int*>(malloc(sizeof(int) * len));

    if( ret != NULL )
    {
        int ll = 0;     // 前缀、后缀的交集中的最大长度(longest length)

        ret[0] = 0;     // 下标为一的ll值为0

        for(int i=1; i<len; ++i)    // 确定剩下元素的ll值
        {
            while( (ll > 0) && (p[i] != p[ll]) )
            {
                ll = ret[ll - 1];
            }

            if( p[i] == p[ll] )
            {
                ll++;
            }

            ret[i] = ll;
        }
    }
    else
    {
        THROW_EXCEPTION(NoEnoughMemoryExcetion, "No memory to create a pmt...");
    }

    return ret;
}

3. KMP子串查找

kmp算法
int kmp(const char* s, const char* p)       // O(n)
{
    int ret = -1;
    int sl = strlen(s);
    int pl = strlen(p);
    int* pmt = make_pmt(p);

    if( (pmt != NULL) && (pl > 0) && (pl <= sl) )
    {
        for(int i=0, j=0; i<sl; ++i)
        {
            while( (j > 0) && (s[i] != p[j]) )  //如果匹配不成功,移动子串,查pmt表改变j的值
            {
                j = pmt[j - 1];
            }

            if(s[i] == p[j])    // 单个字符匹配相等, j加一
            {
                ++j;
            }

            if( j == pl )   // 相等的长度等于子串的长度, 则匹配完成
            {
                ret = i + 1 - pl;
                break;
            }
        }
    }

    free(pmt);

    return ret;
}

4. 小结

  • 部分匹配表是提高子串查找效率的关键
  • 部分匹配表定义为前缀和后缀最长共有元素的长度
  • 可以用递推的方法产生部分匹配表
  • KMP利用部分匹配值子串移动位数的关系提高查找效率

声明:此文章仅是本人在学习狄泰学院《数据结构实战开发教程》所做的笔记,文章中包含狄泰软件资料内容,一切版权归狄泰软件所有!
实验环境:ubuntu10 + Qt Creator2.4.1 + Qt SDK 4.7.4

相关文章

  • 41_KMP子串查找算法

    关键词: 题目:如何在目标字符串S中查找是否存在子串P? 0. 朴素解法 思路:子串中的每一个字符与匹配字符串中...

  • 数据结构与算法--Boyer-Moore和Rabin-Karp子

    数据结构与算法--Boyer-Moore和Rabin-Karp子字符串查找 Boyer-Moore字符串查找算法 ...

  • 子字符串查找(1)

    一、定义 本文主要介绍子字符串查找的各类常用算法,如朴素匹配算法(暴力查找)、KMP算法、BM算法等。各类匹配算法...

  • KMP算法文章合集

    字符串的查找:朴素查找算法和KMP算法 暴力匹配算法与KMP算法(串的匹配) 字符串查找算法BF和KMP 字符串匹...

  • 数据结构与算法学习 (08)字符串匹配--BF算法/RK算法

    BF算法也就是串的模式匹配算法,在主串中查找与模式T(副串)相匹配的子串,如果匹配成功,找到该子串在主串出现的第一...

  • 数据结构与算法-- 暴力法查找子字符串

    数据结构与算法--子字符串查找 字符串的一种基本操作就是子字符串查找了,给定一段长度为N的文本字符串(主串)和长度...

  • Go 关于串的三个经典案例

    子串查找 介绍 子串查找,也可以成为字符串查找。其中有两个字符串,分为主串和子串(模式串)。在主串中查找是否含有子...

  • 算法(2)KMP算法

    1.0 问题描述 实现KMP算法查找字符串。 2.0 问题分析 “KMP算法”是对字符串查找“简单算法”的优化。 ...

  • 算法之美-KMP快速串匹配

    串匹配算法也称作模式匹配算法,就是在目标字符串中查找子字符串,常用于文本搜索、入侵检测等领域,将目标字符串定义为T...

  • 使用kmp算法查找子串

    问题:在字符串S中查找Sub 边界条件:S长度或者Sub长度为0,或者Sub长度大于S长度,返回-1; KMP算法...

网友评论

      本文标题:41_KMP子串查找算法

      本文链接:https://www.haomeiwen.com/subject/qkfypftx.html