• 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] 也是可以的。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    示例2

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

    示例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
    • 2
    • 3
    • 4
    • 5
    • 6

    提示:

    • 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;
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118

    方式二

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

    以下用例需要注意:

    "lingmindraboofooowingdingbarrwingmonkeypoundcake"
    ["fooo","barr","wing","ding","wing"]
    
    • 1
    • 2

    答案是:[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;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
  • 相关阅读:
    【高危安全通告】微软8月多个漏洞修复
    关于OxyPlot.Wpf包没有Plot控件问题
    JSD-2204-使用Idea启动Nacos-创建csmall项目-Dubbo-Day03
    【设计模式】结构型-组合模式
    前端研习录(37)——ES6 数组扩展及新增方法讲解及示例分析
    《HelloGitHub》第 85 期
    (01)ORB-SLAM2源码无死角解析-(53) 闭环检测→了解闭环检测、
    kubectl使用及源码阅读
    Js逆向——捅了【马蜂窝】的ob混淆与加速乐
    七夕了,给你的那个TA画上一箭倾心吧~
  • 原文地址:https://blog.csdn.net/qq135595696/article/details/126809392