首页 > 其他分享 >kmp模式匹配例题思考

kmp模式匹配例题思考

时间:2024-03-02 18:12:41浏览次数:22  
标签:string int pattern next start length kmp 例题 模式匹配

题目描述

读入一个字符串数组string[],再读入一个短字符串。要求查找string[]中和短字符串的所有匹配,输出行号和匹配的字符串以。匹配时不区分大小写,并且可以有一个中括号表示的模式匹配。例如,对aa[123]bb来说,aa1bb,aa2bb,aa3bb都算匹配。

输入格式:
第一行输入字符串数组的长度
接下来几行输入字符串,作为主串
最后输入模式串

输出格式:
若干行,每一行由行号和匹配的字符串组成

样例:
输入:
2
daa1bb
daa2bb
aa[123]bb

输出:
1 aa1bb
2 aa2bb

使用C++ string库下的函数解决

算法思想

首先找到中括号里面的内容,记为brackets,对pattern字符串进行如下处理:首先将[]里面的内容替换为空,接下来依次将中括号里面的字符插入到pattern串中被替换的位置。再依次进行匹配,在匹配的时候需要注意不区分大小写,实际上就是多加一个判断条件。

c++ 算法描述

#include <iostream>
#include <string>

using namespace std;

string pattern;
string *text;
int length;// 测试主串的数目

/**
	使用C++自带的函数实现
*/
void kmp()
{
    /*
        思路一:使用C++自带的一些函数
        将pattern分解成正常的匹配格式:
        a[123]b===>a1b,a2b,..
    */
    int start = pattern.find('[');
    int end = pattern.find(']');
    string brackets = pattern.substr(start, end - start + 1); // 括号里面的内容

    pattern.replace(start, end - start + 1, "");

    // 轮流替换
    for (int i = 0; i < length; i++)
    {
        for (int j = 1; j + 1 < brackets.length(); j++)
        {

            // 注意不区分大小写,因此对于模式串
            /*
                aa[a]bb需要查两次,分别是aaAbb和aaabb
            */
            if (brackets[j] >= 'a' && brackets[j] <= 'z')
            {
                pattern.insert(start, 1, brackets[j] - 32);
            }

            if (brackets[j] >= 'A' && brackets[j] <= 'Z')
            {
                pattern.insert(start, 1, brackets[j] + 32);
            }
            // 上面的两个if只有一个可以执行
            if (text[i].find(pattern) != string::npos)
            {
                cout << i + 1 << " " << pattern << endl;
            }
            pattern.replace(start, 1, "");

            pattern.insert(start, 1, brackets[j]);
            if (text[i].find(pattern) != string::npos)
            {
                cout << i + 1 << " " << pattern << endl;
            }
            pattern.replace(start, 1, "");
        }
    }
}

int main()
{

    cin >> length;
    text = new string[length];
    for (int i = 0; i < length; i++)
    {
        cin >> text[i];
    }
    cin >> pattern;

    // 函数处理
    kmp();
}

测试结果

image

时间复杂度分析

输入n组测试数据,模式串的长度为m,其中括号里面包含的元素的个数为
p,时间复杂度为O(p*(m+n)),其中c++里面find函数查找时间复杂度为O(m+n),如果是在考试中,我肯定就这样写了。时间复杂度也可以接收,因为一般括号里面的内容比较短。

使用改进版的kmp算法

算法思想

在原有kmp算法的基础上,实际上就是加入了两种特殊的情况:

  1. 不区分大小写
  2. 模式串存在多种匹配可能

因此只需要处理好上面的两种特殊情况,实际也可以解决这个问题。

其基本思路为,先对模式串处理,去掉[]里面的内容,包括中括号,使用通配符 * 代替。通配符之前的元素可以正常使用原来的kmp算法,关键就是通配符后面的元素。

生成next数组

  1. 通配符 * 前面的元素按照原来算法进行,通配符后面的元素,分两种情况处理:记通配符的位置为start。

匹配

算法描述

#include <iostream>
#include <string>

using namespace std;

string pattern;
string *text;
int length; // 测试主串的数目

// 处理两种特殊的相等

// 判断brackets里面上是否包含t,如果t是字母,判断是否包含其大小写
bool isEqual(string brackets, char t)
{
    if (brackets.find(t) != string::npos)
    {
        return true;
    }
    else
    {
        if (t >= 'a' && t <= 'z')
        {
            return brackets.find(char(t - 32)) != string::npos;
        }
        if (t >= 'A' && t <= 'Z')
        {
            return brackets.find(char(t + 32)) != string::npos;
        }
    }
    return false;
}

/**
 * @brief 不区分大小写情况判断两个字符是否相等
 * 
 * @param i 
 * @param j 
 * @return true 
 * @return false 
 */
bool isEqual(char i, char j){
	if (i == j)
        return true;

    if (i < 65 || j < 65)
    {
        return false;
    }
    if (i > 97 || j > 97)
        return false;

    return abs(i - j) == 32;
}

/**
 * @brief 改进版的kmp模式匹配
 * 
 */
void kmp2()
{
    // 将形如 aa[123]bb替换为aa*bb
    int start = pattern.find('[');
    int end = pattern.find(']');
    string brackets = pattern.substr(start, end - start + 1); // 括号里面的内容
    pattern.replace(start, end - start + 1, "*");

    // 初始化next数组
    int next_length = pattern.length();
    int *next = new int[next_length];
    int i = 0;
    int j = -1;
    next[0] = -1;
    while (i < next_length)
    {
        if (j == -1 ||
            (j == start && isEqual(brackets, pattern[i])) || // 特殊情况一,j为通配符,此时pattrn[j]的值属于中括号里面的内容,需要分别匹配
            (j != start && isEqual(pattern[i], pattern[j])) || 
            (i == start && isEqual(brackets, pattern[j]))) // 特殊情况2,求解next[start+1]
        {
            i++;
            j++;
            // 匹配的情况
            next[i] = j;
        }
        else
        {
            j = next[j];
        }
    }
    // 打印next数组
    // for (int i = 0; i < next_length; i++)
    // {
    //     printf("%d ", next[i]);
    // }
    // printf("\n");

    // 开始匹配
    for (int len = 0; len < length; len++)// len组测试数据
    {

        // 进行模式匹配
        i = 0;
        j = 0;
        while (j < next_length && i < text[len].length())
        {

            if (j == -1 ||
                (j != start && isEqual(text[len][i], pattern[j])) ||
                (j == start && isEqual(brackets, text[len][i])))
            {
                i++;
                j++;
            }
            else
            {
                j = next[j];
            }
        }
        if (j == next_length)
        {
            cout << len + 1 << " " << text[len].substr(i - j, j) << endl;
        }
    }
}


int main()
{

    cin >> length;
    text = new string[length];
    for (int i = 0; i < length; i++)
    {
        cin >> text[i];
    }
    cin >> pattern;

    // 函数处理
    // kmp();
    kmp2();
}

测试结果

image

标签:string,int,pattern,next,start,length,kmp,例题,模式匹配
From: https://www.cnblogs.com/pgzlhq/p/18048992

相关文章

  • 代码随想录 第九天 | 烤馍片(kmp)算法 ●28. 实现 strStr() ●459.重复的子字符串
    烤馍片算法(kmp):为了不让遍历的指针回退,每次不相等的时候找不相等之前的字符串的最长相等前后缀。i表示目标字符串,j表示需要在目标找到的字符串的指针。最长相等前后缀的长度就是之前有多少个与needle字符串相同,直接将j跳到上一元素位置记录的最长相等前后缀长度(next数组),这样i就可以......
  • 关于KMP模式匹配的一些思考
    算法简介模式匹配给定主串text和模式串pattern,在主串中查找,如果找到了模式串,返回模式串在主串中的起始位置,从1开始计数。暴力求解求解模式匹配算法的核心思想是:蛮力法。即使用两个指针i和j,其中i指针用来遍历text,j指针用来遍历pattern。当text[i]==text[j]的时候,继续比较;如果不......
  • 写给rust初学者的教程(一):枚举、特征、实现、模式匹配
    这系列RUST教程一共三篇。这是第一篇,介绍RUST语言的入门概念,主要有enum\trait\impl\match等语言层面的东西。安装好你的rust开发环境,用cargo创建一个空项目,咱们直接上代码。懵逼的同僚可以参考我8年前的rust文章:https://www.iteye.com/blog/somefuture-2275494,虽然8年了,然并不......
  • 费用流例题
    1.k取方格数一个矩阵格子,从最左上角出发到最右下角,走k次,每个格子上有一个数,走过一次就变为0,问能取到的所有数字之和最大值。每个点i拆成两个点i和i'(左半边与右半边),两点之间连两条边一条容量为1,费用为-a[i],另一条容量为无穷,费用为0每个点的右半边与相邻点的左半边连起来......
  • 【学习笔记】KMP算法(字符串匹配优化算法)
    KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的作用是,在一个长字符串内匹配一个短字符串(判断str1.contains(str2))时,减少匹配的次数,提高匹配效率。 必要概念:最长公共前后缀字符串......
  • KMP
    把以前写的KMP放上来。错别字有点多(KMP盲猜今天会有很多人听不懂郭老师讲的KMP,于是提前发一篇有关KMP的博客。建议搭配KMP模板题解食用。引入对于字符串匹配问题,我们定义需要查找字符串叫“模式串”(长度为\(M\)),被查找字符串叫“文本串”(长度为\(N\))。暴力查找或使用......
  • KMP 字符串搜索算法
    KMP字符串搜索算法是Knuth、Morris、Pratt三位在类似的时间段内一起发明的一种字符串搜索算法,该算法的主要原理是利用待查找子串中的某些信息,在匹配失败时能够减少回退的步数算法原理假设现在有一个待搜索的字符串ABABAC,如何利用现有的字符串实现在字符不匹配时尽可能向后调......
  • KMP字符串匹配算法
    什么是KMPKMP算法主要应用在字符串匹配问题。因为是由这三位学者发明的:Knuth,Morris和Pratt,所以取了三位学者名字的首字母。所以叫做KMP 核心思想KMP的主要思想是:「当出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,可以利用这些信息避免从头再去做匹配了。」(可......
  • POJ--1961 Period(KMP)
    记录18:262024-2-22http://poj.org/problem?id=1961利用KMP构造next数组,其实next数组就是方便于找到下一个应该比较的字符,或者说是不动目标字符,移动查找字符,这里面利用next数组就可以很快捷的移动这道题是利用next字符,给出证明的结果是当i-next[i]能整除i时,S[1~i-......
  • KMP 算法和 TIRE 树
    1.KMP算法KMP算法,是判断一个字符串是否在一个字符串中出现过,能够快速匹配字符串在文本串中的有无,位置,次数,我们在匹配字符串中可以找到失配点,就可以不用从\(1\)重新查找,从某个特定点进行查找,大大减小了时间复杂度。考虑一组样例:字符串:abcdf文本串:abcdabcdef我们来将他匹......