C C++算法题解LeetCode1408数组中的字符串匹配

 更新时间:2022年10月14日 09:21:10   作者:Junkman丶  
这篇文章主要为大家介绍了C C++算法题解LeetCode1408数组中的字符串匹配示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

题目描述

题目链接:1408. 数组中的字符串匹配

给你一个字符串数组 words ,数组中的每个字符串都可以看作是一个单词。请你按 任意 顺序返回 words 中是其他单词的子字符串的所有单词。

如果你可以删除 words[j] 最左侧和/或最右侧的若干字符得到 word[i] ,那么字符串 words[i] 就是 words[j] 的一个子字符串。

提示:

示例 1:

输入:words = ["mass","as","hero","superhero"]
输出:["as","hero"]
解释:"as" 是 "mass" 的子字符串,"hero" 是 "superhero" 的子字符串。
["hero","as"] 也是有效的答案。

示例 2:

输入:words = ["leetcode","et","code"]
输出:["et","code"]
解释:"et" 和 "code" 都是 "leetcode" 的子字符串。

示例 3:

输入: words = ["blue","green","bu"]
输出: []

整理题意

题目给定一个字符串数组 words,对于数组中的每个字符串来说,如果该字符串为数组中其他某个字符串的子串,那么就将该字符串加入答案字符串数组。可以按照任意顺序返回该答案数组。

解题思路分析

注意题目的数据提示:题目数据 保证 每个 words[i] 都是独一无二的。所以不存在两个相同的字符串,也避免了互为子字符串的情况。

根据题目数据范围来看,完全可以采用较为暴力的方法来进行解题,枚举每个字符串作为子串,检查是否为其他某个字符串的子串即可。

优化

在字符串匹配的时候可以采用 KMP 字符串匹配算法来进行优化时间复杂度。

具体实现

对于字符串匹配部分可以调用 string 中的 find() 函数进行匹配 t.find(p)(在字符串 t 中匹配字符串 p,也就是查找字符串 t 中是否包含字符串 p):

  • 此处需要用到 string 库中的 find() 函数与 string::npos 参数;

string::npos 参数是一个常数,用来表示不存在的位置。

  • stringfind() 返回值是子串的第一个字符在母串中的位置(下标记录),如果没有找到,那么会返回一个特别的标记 string::npos

可以对字符串数组 words 进行排序处理,这样就可以从最短的字符串开始匹配,且每次往后遍历匹配,因为前面的字符串一定短于当前字符串。

在使用 KMP 字符串匹配算法时需要注意:

  • KMP 字符串匹配算法的核心思想是 递归回溯思想,当匹配失败时根据 nxt 数组来进行回溯跳转;
  • nxt 数组表示模式串的子串的前缀和后缀相同的最长长度,这样就可以在匹配的过程中如果遇到不匹配的字符,模式串用 nxt 数组进行递归跳转到最长符合的位置进行继续匹配,从而不需要目标串进行重复的往返匹配。
  • 其中需要要注意的一个技巧是 nxt[0] = -1,在把 nxt 数组进行向右偏移时,第 0 位的值,我们将其设成了 -1,这只是为了编程的方便,并没有其他的意义。
  • 还需要注意 nxt 数组的优化,优化后在回溯跳转的时候会回溯跳转到首次与当前字符不一样字符的位置,避免了跳转到和当前字符一样的位置进行重复判断。
  • 在实现 getNext() 函数的时候需要注意 nxt 数组溢出问题,可以通过增加 nxt 数组大小,或减少 getNext() 函数中循环遍历的次数来防止越界出现的运行错误。
  • 需要注意在 getNext() 函数中 j 的初始化为 -1,但在 KMP() 函数中 j 的初始化为 0

复杂度分析

代码实现

暴力

class Solution {
public:
    vector<string> stringMatching(vector<string>& words) {
        // 新知识:string::npos
        vector<string> ans;
        ans.clear();
        // 双重循环暴力寻找
        for(auto &word1 : words){
            int l1 = word1.length();
            for(auto &word2 : words){
                int l2 = word2.length();
                // 当 l2 大于 l1 时 并且可以在 w2 中找到 w1 时
                if(l1 < l2 && word2.find(word1) != string::npos){
                    ans.emplace_back(word1);
                    break;
                }
            }
        }
        return ans;
    }
};

暴力 + 优化

class Solution {
public:
    vector<string> stringMatching(vector<string>& words) {
        sort(words.begin(), words.end(), [](string &a, string &b){
            return a.length() < b.length();
        });
        // 新知识:string::npos
        vector<string> ans;
        ans.clear();
        int n = words.size();
        // 双重循环暴力寻找
        for(int i = 0; i < n; i++){
            int l1 = words[i].length();
            for(int j = i + 1; j < n; j++){
                int l2 = words[j].length();
                // 当 l2 大于 l1 时 并且可以在 w2 中找到 w1 时
                if(l1 < l2 && words[j].find(words[i]) != string::npos){
                    ans.emplace_back(words[i]);
                    break;
                }
            }
        }
        return ans;
    }
};

KMP

class Solution {
    void getNext(string &p, vector<int> &nxt){
        // 把PMT进行向右偏移时,第0位的值,我们将其设成了-1,
        // 这只是为了编程的方便,并没有其他的意义。
        nxt[0] = -1;
        int i = 0, j = -1;
        int len = p.length();
        // ★注意 nxt 数组越界
        while(i < len){
            // j = -1 或者 匹配成功
            if(j == -1 || p[i] == p[j]){
                // nxt[++i] = ++j; 未优化前
                i++;
                j++;
                if(p[i] == p[j]) nxt[i] = nxt[j];
                else nxt[i] = j;
            }
            // 匹配失败,回溯
            else{
                j = nxt[j];
            }
        }
    }
    bool kmp(string &t, string &p, vector<int> &nxt){
        // ★注意这里的 j = 0 不是 j = -1
        int i = 0, j = 0;
        int lent = t.length();
        int lenp = p.length();
        while(i < lent && j < lenp){
            if(j == -1 || t[i] == p[j]){
                ++i;
                ++j;
            }
            else j = nxt[j];
        }
        if(j == lenp) return true;
        return false;
    }
public:
    vector<string> stringMatching(vector<string>& words) {
        sort(words.begin(), words.end(), [](string a, string b){
            return a.length() < b.length();
        });
        vector<string> ans;
        ans.clear();
        vector<int> nxt;
        int n = words.size();
        for(int i = 0; i < n; i++){
            int len_p = words[i].length();
            // ★注意 nxt 数组溢出
            // 可以这里 len_p + 1 也可以 getNext 中 -1
            nxt.resize(len_p + 1);
            getNext(words[i], nxt);
            for(int j = i + 1; j < n; j++){
                if(kmp(words[j], words[i], nxt)){
                    ans.emplace_back(words[i]);
                    break;
                }
            }
        }
        return ans;
    }
};

总结

  • 通过该题了解到了一个新的知识点:string::npos 参数用来表示不存在的位置。当 stringfind() 函数没有匹配成功时,那么就会返回这个参数 string::npos
  • 同时通过该题复习了 KMP 字符串匹配算法 的实现,在实现过程中需要注意 nxt 数组的大小,防止下标越界的运行错误;同时还需要注意在 getNext() 函数中 j 的初始化为 -1,但在 KMP() 函数中 j 的初始化为 0

测试结果:

以上就是C C++算法题解LeetCode1408数组中的字符串匹配的详细内容,更多关于C C++算法数组字符串匹配的资料请关注脚本之家其它相关文章!

相关文章

  • C++ Qt开发之使用QHostInfo查询主机地址

    C++ Qt开发之使用QHostInfo查询主机地址

    Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,本文将重点介绍如何运用QHostInfo组件实现对主机地址查询功能,希望对大家有所帮助
    2024-03-03
  • C++程序中main(int argc, char *argv[])函数的参数意义

    C++程序中main(int argc, char *argv[])函数的参数意义

    这篇文章主要介绍了C++程序中main(int argc, char *argv[])函数的参数意义,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-09-09
  • C语言实现BMP转换JPG的方法

    C语言实现BMP转换JPG的方法

    这篇文章主要介绍了C语言实现BMP转换JPG的方法,涉及C#图片格式转换的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-07-07
  • 详解C++11原子类型与原子操作

    详解C++11原子类型与原子操作

    这篇文章主要介绍了C++11原子类型与原子操作的相关资料,帮助大家更好的理解和学习c++,感兴趣的朋友可以了解下
    2020-08-08
  • C语言编程计算信噪比SNR理解学习

    C语言编程计算信噪比SNR理解学习

    这篇文章主要介绍了C语言编程信噪比SNR计算的理解学习,信噪比,英文名称叫做SNR或S/N(SIGNAL-NOISE RATIO)。是指一个电子设备或者电子系统中信号与噪声的比例
    2021-10-10
  • C++深入讲解哈夫曼树

    C++深入讲解哈夫曼树

    给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近
    2022-05-05
  • C语言菜鸟基础教程之常量和变量

    C语言菜鸟基础教程之常量和变量

    在C语言中,常量和变量都是可以用来存储和表示数据的,常量值在程序执行的过程中是不可变的,而变量是可变的
    2017-10-10
  • QT窗口/控件置顶方法举例详解

    QT窗口/控件置顶方法举例详解

    我们使用QT进行界面开发时,可能会遇到需要将窗口置顶的情况,下面这篇文章主要给大家介绍了关于QT窗口/控件置顶方法的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-01-01
  • opencv利用视频的前n帧求平均图像

    opencv利用视频的前n帧求平均图像

    这篇文章主要为大家详细介绍了opencv利用视频的前n帧求平均图像,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-03-03
  • C++基本用法实践之移动语义详解

    C++基本用法实践之移动语义详解

    移动(move)语义是C++引入了一种新的内存优化,以避免不必要的拷贝,下面小编就来和大家简单聊聊C++中移动语义的相关使用吧,希望对大家有所帮助
    2023-07-07

最新评论