串的模式匹配算法

目录

算法目的

算法应用

算法种类

BF算法

代码实现

运行结果

KMP算法

next数组

代码实现

运行结果


算法目的

确定主串中所含子串(模式串)第一次出现的位置(定位)

算法应用

搜索引擎,拼写检查,语言翻译,数据压缩等。

算法种类

  • BF算法(Brute-Force,又称古典的、经典的、朴素的、穷举的)
  • KMP算法(特点:速度快)

BF算法

算法的思路是从S的每一个字符开始依次与T的字符进行匹配,采用穷举法的思路。

如图,i 为主串的指向 ,j为子串的指向,当匹配失败是,i 和j需要进行回溯,规则如下:

i = i - j + 2, j = 1

即 i 指向主串的下一个字符,j 指向子串的第一个字符,一直到匹配成功或主串搜索完毕。

代码实现

需要说明的是,上面分析的是基于第一个元素的位置是1而言的,而我们实际编程的时候第一个字符的下标是0,索引回溯时有一点不同,但是主要思路还是不变的。

#include <iostream>
#include <string>
using namespace std;

int bfsearch(string s, string t)
{
    int i,j = 0;
    while(i < s.size() && j < t.size())
    {
        if(s[i] == t[j])
        {
            i++;
            j++;
        }
        else
        {
            i = i - j + 1; // 这里并不是 i = i - j + 2, 原因是实际编程时起始下标为 0 
            j = 0;
        }
    }
    if(j >= t.size())
    {
        return i - j;
    }
    else
    {
        return -1;
    }
}

void test01()
{
    string s = "abcdefg";
    string t = "def";
    cout << "主串 s = " << s << endl;
    cout << "子串 t = " << t << endl;
    cout << "子串 t 在主串 s 的 " << bfsearch(s, t) << " 号位。(索引从 0 开始。)" << endl;
}


int main()
{
    test01();

    system("pause");
    return 0;
}

运行结果

对于主串 s 而言,第一个元素 a 的下标为 0,即 子串 def 在其的 3 号位,运行结果一致~

KMP算法

KMP算法是D.E.Kunth、J.H.Morris 和 V.R.Pratt共同提出的,简称KMP算法。该算法较BF算法有较大改进,从而使算法效率有了某种程度的提高。利用已经部分匹配的结果而加快模式串滑动的速度,且主串S的指针i不避回溯!这样可将时间复杂度提升至O(n+m)! 

主要思想就是若模式串中存在相等的前后缀,那么上一次匹配的后缀部分等同于下一次匹配的前缀部分,此时直接从相等的前缀部分的下一个字符开始匹配! 

next数组

  • 前缀:包含首位字符但不包含末尾字符的子串
  • 后缀:包含末位字符但不包含首位字符的子串
  • next数组定义:当主串与模式串的某一位不匹配时,模式串要退回的位置
  • next[j]: 其值 = 第 j 位字符前面 j - 1 位字符串组成的子串的前后缀重合的字符数 +1
  • 复杂度:构造next数组算法的时间复杂度和空间复杂度均为O(n).

代码实现

#include <iostream>
#include <string>
using namespace std;

// 获取next数组
void getNext(int* next, const string& s)
{
    int j = 0;
    next[0] = 0;
    for(int i = 1; i < s.size(); i++)
    {
        while (j > 0 && s[i] != s[j])
        {
            j = next[j - 1];
        }
        if (s[i] == s[j])
        {
            j++;
        }
        next[i] = j;
    }
}

int kmpsearch(string s, string t)
{
    if (t.size() == 0)
    {
        return 0;
    }
    int* next = new int[t.size()];
    getNext(next, t);
    int j = 0;
    int index = -1;
    for (int i = 0; i < s.size(); i++)
    {
        while(j > 0 && s[i] != t[j])
        {
            j = next[j - 1];
        }
        if (s[i] == t[j])
        {
            j++;
        }
        if (j == t.size())
        {
            index = i - t.size() + 1;
        }
    }
    delete[] next;
    next = nullptr;
    return index;
}

void test01()
{
    string s = "abcdefg";
    string t = "def";
    cout << "主串 s = " << s << endl;
    cout << "子串 t = " << t << endl;
    cout << "子串 t 在主串 s 中的位置是:" << kmpsearch(s, t) << endl;
}

int main()
{
    test01();

    system("pause");
    return 0;
}

运行结果

 与预想结果一致,但是现在好像还没有考虑到在主串中重复存在模式串的情况,或者是默认只匹配一次!所以算法仍需改进!

 

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值