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++详细讲解图论的基础与图的储存

    C++详细讲解图论的基础与图的储存

    图论〔Graph Theory〕是数学的一个分支。它以图为研究对象。图论中的图是由若干给定的点及连接两点的线所构成的图形,这种图形通常用来描述某些事物之间的某种特定关系,用点代表事物,用连接两点的线表示相应两个事物间具有这种关系
    2022-05-05
  • 简单谈谈C++ 中指针与引用

    简单谈谈C++ 中指针与引用

    下面用通俗易懂的话来概述一下,指针-对于一个类型T,T*就是指向T的指针类型,也即一个T*类型的变量能够保存一个T对象的地址,而类型T是可以加一些限定词的,引用-引用是一个对象的别名,主要用于函数参数和返回值类型,符号X&表示X类型的引用。
    2015-09-09
  • C语言 深入探究动态规划之区间DP

    C语言 深入探究动态规划之区间DP

    这几天在做有关dp的题,看到一个石子合并的问题,本来以为是个贪心,后来仔细一想压根不是贪心。贪心算法的思路是每次都取最大的,然而石子合并问题有个限制条件就是每次只能取相邻的,这就决定了它不是个贪心
    2022-04-04
  • C++20中的协程(Coroutine)的实现

    C++20中的协程(Coroutine)的实现

    这篇文章主要介绍了C++20中的协程(Coroutine)的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • 深入理解strcpy与memcpy的区别

    深入理解strcpy与memcpy的区别

    本篇文章是对strcpy与memcpy的区别进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • Android App仿微信界面切换时Tab图标变色效果的制作方法

    Android App仿微信界面切换时Tab图标变色效果的制作方法

    这篇文章主要介绍了Android App仿微信界面切换时Tab图标变色效果的制作方法,重点讲解了图标的绘制技巧,需要的朋友可以参考下
    2016-04-04
  • C++中友元的实例详解

    C++中友元的实例详解

    这篇文章主要介绍了C++中友元的实例详解的相关资料,希望通过本文大家能掌握友元的使用方法,需要的朋友可以参考下
    2017-09-09
  • Arduino控制舵机详解 附代码

    Arduino控制舵机详解 附代码

    rduino是一款便捷灵活、方便上手的开源电子原型平台,它构建于开放原始码simple I/O介面版,并且具有使用类似Java、C语言的Processing/Wiring开发环境,这篇文章主要介绍了Arduino控制舵机详解(含代码),需要的朋友可以参考下
    2023-05-05
  • C++构造函数+复制构造函数+重载等号运算符调用

    C++构造函数+复制构造函数+重载等号运算符调用

    这篇文章主要介绍了C++构造函数+复制构造函数+重载等号运算符调用,文章叙述详细,具有一定的的参考价值,需要的小伙伴可以参考一下
    2022-03-03
  • Qt之简单的异步操作实现方法

    Qt之简单的异步操作实现方法

    这篇文章主要介绍了Qt之简单的异步操作实现方法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11

最新评论