题目描述

读入一个字符串数组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

posted on 2024-03-02 17:59  劈瓜者刘华强  阅读(3)  评论(0编辑  收藏  举报