IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++ 串联所有单词的子串 -> 正文阅读

[C++知识库]C++ 串联所有单词的子串

C++ 串联所有单词的子串

??给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。

?? s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。

  • 例如,如果 words = [“ab”,“cd”,“ef”], 那么 “abcdef”, “abefcd”,“cdabef”, “cdefab”,“efabcd”, 和 “efcdab” 都是串联子串。 “acdbef” 不是串联子串,因为他不是任何 words 排列的连接。

??返回所有串联字串在 s 中的开始索引。你可以以 任意顺序 返回答案。

示例1

输入:s = "barfoothefoobarman", words = ["foo","bar"]
输出:[0,9]
解释:因为 words.length == 2 同时 words[i].length == 3,连接的子字符串的长度必须为 6。
子串 "barfoo" 开始位置是 0。它是 words 中以 ["bar","foo"] 顺序排列的连接。
子串 "foobar" 开始位置是 9。它是 words 中以 ["foo","bar"] 顺序排列的连接。
输出顺序无关紧要。返回 [9,0] 也是可以的。

示例2

输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
输出:[]
解释:因为 words.length == 4 并且 words[i].length == 4,所以串联子串的长度必须为 16。
s 中没有子串长度为 16 并且等于 words 的任何顺序排列的连接。
所以我们返回一个空数组。

示例3

输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
输出:[6,9,12]
解释:因为 words.length == 3 并且 words[i].length == 3,所以串联子串的长度必须为 9。
子串 "foobarthe" 开始位置是 6。它是 words 中以 ["foo","bar","the"] 顺序排列的连接。
子串 "barthefoo" 开始位置是 9。它是 words 中以 ["bar","the","foo"] 顺序排列的连接。
子串 "thefoobar" 开始位置是 12。它是 words 中以 ["the","foo","bar"] 顺序排列的连接。

提示:

  • 1 <= s.length <= 104
  • 1 <= words.length <= 5000
  • 1 <= words[i].length <= 30
  • words[i] 和 s 由小写英文字母组成

思路/解法

方式一

回溯算法 + KMP字符串匹配算法即可求解,但是时间复杂度过高。

回溯算法可参考https://blog.csdn.net/qq135595696/article/details/124134925

KMP字符串匹配算法可参考https://blog.csdn.net/qq135595696/article/details/123439626

class Solution 
{
public:
    void PrefixTable(string pattern, vector<int>& prefix)
    {
        prefix[0] = 0;
        int len   = 0;
        int i     = 1;

        while(i < pattern.length())
        {
            if(pattern[i] == pattern[len])
            {
                len++;
                prefix[i] = len;
                i++;
            }
            else
            {
                if(len > 0)
                {
                    len = prefix[len - 1];
                }
                else
                {
                    prefix[i] = 0;
                    i++;
                }
            }
        }
    }


    void MovePrefixTable(vector<int>& prefix)
    {
        int i;
        for(i = prefix.size() - 1; i > 0;i --)
        {
            prefix[i] = prefix[i - 1];
        }
        prefix[i] = -1;
    }


    void KMPSearch(string haystack, string needle, vector<int>& res)
    {
        int i = 0;
        int j = 0;
        vector<int> prefix(needle.length(), 0);
        PrefixTable(needle, prefix);
        MovePrefixTable(prefix);

        while(i < haystack.length())
        {
            if(j == needle.length() - 1 && haystack[i] == needle[j])
            {
                res.push_back(i - j);
                j = prefix[j];
                if (-1 == j)
				{
					j++;
					i++;
				}
				continue;
            }

            if(haystack[i] == needle[j])
            {
                i++;
                j++;
            }
            else
            {
                j = prefix[j];
                if(-1 == j)
                {
                    j++;
                    i++;
                }
            }
        }
    }

	void TraceBack(vector<string>& words, string back, int len, vector<int>& res, vector<int>& visited, string haystack)
	{
		if (back.length() == len)
		{
            KMPSearch(haystack, back, res);
            return;
		}

		for (int i = 0; i < words.size(); i++)
		{
			if (visited[i] == 1 || (i > 0 && words[i] == words[i - 1] && visited[i - 1] == 0))
			{
				continue;
			}

			visited[i] = 1;
			back += words[i];

			TraceBack(words, back, len, res, visited, haystack);

			visited[i] = 0;
			back       = back.substr(0, back.length() - words[i].length());
		}
	}

	vector<int> findSubstring(string s, vector<string>& words)
	{
		vector<int> visited(words.size(), 0);
		string      back;
		vector<int> res;
		std::sort(words.begin(), words.end());
		TraceBack(words, back, words.size() * words[0].length(), res, visited, s);
		return res;
	}
};

方式二

滑动窗口算法,滑动窗口移动的步长为stride = words[0].size(),窗口的长度为limit = words.size() * stride。

以下用例需要注意:

"lingmindraboofooowingdingbarrwingmonkeypoundcake"
["fooo","barr","wing","ding","wing"]

答案是:[13],仔细观察会发现第四个单词为 ofoo 而不是 fooo,导致错过了答案。

即需要设定滑动窗口的起始位置并不一定是从零开始,而是在一定范围内,为[0,stride - 1]。

作者:dodo_1202
链接:https://leetcode.cn/problems/substring-with-concatenation-of-all-words/solution/by-dodo_1202-cqbe/

class Solution 
{
public:
	vector<int> findSubstring(string s, vector<string>& words)
	{
		int n      = s.length();
        int stride = words[0].length();
        int limit  = words.size() * stride;

        unordered_map<string, int> need;
        for(auto w : words)
        {
            need[w]++;
        }
        vector<int> res;
        
        //遍历起点
        for(int start = 0; start < stride; start++)
        {
            //left 和 right 指向的是当前滑动窗口的起始位置以及当前滑动窗口位置
            int left  = start;
            int right = start;
            //cnt 记录窗口内满足要求的单词数量
            int cnt   = 0;
            unordered_map<string, int> window; //记录当前合法窗口内容

            while(right < n)
            {
                //右边届入窗
                string curRight = s.substr(right, stride);
                if(need.count(curRight))
                {
                    window[curRight]++;
                    if(window[curRight] == need[curRight])
                    {
                        cnt++;
                    }
                }

                //左边届收缩
                if(right - left + stride > limit)
                {
                    string curLeft = s.substr(left, stride);
                    if(need.count(curLeft))
                    {
                        if(window[curLeft] == need[curLeft])
                        {
                            cnt--;
                        }
                        window[curLeft]--;
                    }
                    left += stride;
                }

                //采集结果
                if(right - left + stride == limit && cnt == need.size())
                {
                    res.push_back(left);
                }
                right += stride;
            }
        }
        return res;
    }
};
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-09-13 10:54:41  更:2022-09-13 10:57:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 14:51:58-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码