LeetCode 1065 字符串的索引对(非原版,是修改版)

题目

给出 字符串 text 和 字符串列表 words, 返回所有的索引对 [i, j] 使得在索引对范围内的子字符串 text[i]...text[j](包括 i 和 j)属于字符串列表 words,(要求words里面的单词必须连续且不重复,并且要包含words的所有单词),返回其中的一组集合即可,此外对于返回的集合顺序没有要求

示例 1:

输入: text = "thestoryooooofleetcodeandme", words = ["story","fleet","leetcode"]
输出: []
ps: 没找到,单词匹配必须连续

示例 2:

输入: text = "thestoryfleetcodeandme", words = ["story","fleet","code"]
输出: [[3,7],[8,12],[13,16]] 或者 [[3,7],[13,16],[8,12]] 均可,输出无顺序要求
ps: 找到了一组恰好包含words里面的单词(单词不能重复使用,且要全使用words里面的单词)。此外 words 里的单词的顺序无所谓,只要不重复使用就行,并且对于输出的顺序无要求,上面两个答案均可

示例 3:

输入: text = "thestoryfleetcodeandme", words = ["story","fleet","leetcode"]
输出: []
ps: words里的单词不能重复使用,视为无效,words里面的单词必须连续且不重复

解题思路

方法一

暴力解法,生成words的全排列,然后和text取子串,这个解法没有什么意义,只是让大家了解题目

方法二

动态规划?

1、先找到从 text 里的第 0~text长度减一 的所有匹配 words 里面的单词,做个标记。比如 text[0] 能匹配words里面的某个单词,就记录下来,没有就不记录;text[3] 恰好能匹配 words 的一个单词,就记录下来,如果 text[3] 能匹配上很多 words 单词就都记录下来(极端情况下,如果words 里面的单词都比较像,比如 a、aa、aaa,是有可能匹配多个的),代码里我是用二维数组记录的,叫做 v_map,v_map 的x坐标长度由text的长度决定;v_map 的y坐标长度由实际匹配的words的单词决定,最长为 words 的长度

2、然后我们遍历第一步中的 v_map,从 v_map[0] 开始,尝试找出从text[0]开始,能否连续匹配words中的单词(对应下面代码的solve和外层for循环),我们用map存储命中的words的下标,map还能去重,如果map的size和words相等,那就找到结果了

代码

#include <iostream>
#include <map>
#include <vector>
using namespace std;

void solve(int i, string& text, vector<vector<int>>& v_map, map<int, int>& m, vector<string>& words, vector<vector<int>>& res) {

    if (m.size() == words.size()) {
        // 当 m 长度为 words 的大小时,说明全部匹配
        res.clear();// 清理一下,以防万一
        for (auto i : m) {
            res.push_back({ i.second, i.second + (int)words[i.first].length() - 1 }); // 返回索引对集合
        }
        return;
    }
    if (i >= v_map.size()) {
        // 不能越界
        return;
    }

    for (auto j : v_map[i]) {
        if (m.count(j) > 0) {
            // 之前命中过,跳过
            continue;
        }
        else {
            // 命中,记录
            m[j] = i;
            solve(i + words[j].length(), text, v_map, m, words, res);
            // 移除这次的 j (key值),防止影响下一次匹配(因为m是引用不是拷贝)
            m.erase(j);
        }
    }
}

int main()
{
    //string text = "thestoryfleetcodeandme";
    //vector<string> words = { "story", "fleet", "code", "and" };
    string text = "thestoryfoorfooandme";
    vector<string> words = { "story", "foo", "foor", "and" };

    int len = text.length();
    vector<vector<int>> v_map(len); // 当 v_map 下标为 key 时, value 为命中(从v_map[key]开始的子串)对应在 words 的 下标集合数组,因为可能命中不止一个

    // 构建 v_map
    for (int i = 0; i < words.size(); i++) {
        int pos = 0;
        while (true) {
            pos = text.find(words[i], pos);
            if (pos != string::npos) {
                v_map[pos].push_back(i);
            }
            else {
                break;
            }
            pos += 1;
        }
    }

    map<int, int> m; // key 为在 words 的下标,value 为该 words 的下标命中后,对应 text 的下标起始位置
    vector<vector<int>> res; // res 为最终结果集
    for (int i = 0; i < len; i++) {
        // 每次遍历的目标是,从 text[i] 开始,接下来的连续的子串刚好命中 words 的所有单词,并且没有重复
        solve(i, text, v_map, m, words, res);
        // 匹配成功条件判定
        if (res.size() == words.size()) {
            // 匹配成功退出
            break;
        }
        else {
            // 从text[i]开始的连续索引对没有匹配成功,清空 m,准备下一次 for 循环,重新匹配
            m.clear();
        }
    }

    // 打印结果集
    for (int i = 0; i < res.size(); i++) {
        cout << '[';
        for (int j = 0; j < res[i].size(); j++) {
            cout << res[i][j];
            if (j == 0) {
                cout << ',';
            }
        }
        cout << ']' << endl;;
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值