leetcode题:30. 串联所有单词的子串(困难)

一、题目描述

给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。

注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。

 

示例 1:

输入:
  s = "barfoothefoobarman",
  words = ["foo","bar"]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 "barfoor" 和 "foobar" 。
输出的顺序不重要, [9,0] 也是有效答案。
示例 2:

输入:
  s = "wordgoodgoodgoodbestword",
  words = ["word","good","best","word"]
输出:[]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

二、解题思路

滑动窗口法,所有words的单词总长度为窗口大小len,单个单词长度为单位len_w。先通过一个map,count_words统计所有单词的个数

1、遍历窗口的起始位置index = 0~len_w。left = index,right = index + len,进入循环,从right往前移动,left_inner为当前偏移位置,每次往前移len_w长度,通过map count_now统计当前单词的总数。

1)当前单词不在count_words里或者当前单词在count_now里的统计个数大于count_words里的个数的时候,说明这个窗口的子串肯定不满足匹配words所有单词,此时left偏移到left_inner+len_w的位置,right = left+len。

2)当inner_left == left并且不满足条件一的时候说明,子串满足匹配words所有单词。此时将left记录。同时往前偏移len的长度将left = right,right = left + len。

3)当不满足条件1和条件2的时候,说明从left_inner到right的区间内的单词是在words里的并且个数没有超过count_words里的个数,此时left_inner继续往前移动,left_inner  = left_inner - len_w。最后肯定会遇到条件1或者条件2进入下一个循环

当起始位置index = 0~len_w(因为起始位置限定在0~len_w之间,是因为如果其实位置为n*len_w+i的时候,相当于其实位置i的滑动窗口往前移动了n步(len_w为1步)的结果,已经遍历过了没有必要重复遍历)都遍历过了之后将所有可能匹配的索引都找到了。

三、代码

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> save;
        if(words.size() == 0)
            return save;
        map<string,int> word_counts;
        
        int lenw = words[0].size();
        for(int i = 0; i < words.size(); i ++)
        {
            word_counts[words[i]] ++;
        }
        int words_size = words[0].size()*words.size();
        for(int i = 0; i < words[0].size(); i++)
        {
            int left = i;
            int right = i + words_size;
            while(right <= s.size())
            {
                int inner_left = right - lenw;
                map<string,int> word_now;
                while(inner_left >= left)
                {
                    //cout<<"left="<<left<<endl;
                    //cout<<"left_inner="<<inner_left<<endl;
                    string stmp = s.substr(inner_left,lenw);
                    if(word_counts[stmp] == 0 || word_now[stmp] + 1 > word_counts[stmp])
                    {
                        left = inner_left + lenw;
                        right = left + words_size;
                        break;
                    }
                    else
                    {
                        if(inner_left <= left)
                        {
                            save.push_back(inner_left);
                            left = left + lenw;
                            right = left + words_size;
                            break;
                        }
                        word_now[stmp]++;
                        inner_left -= lenw;         
                    }
                }

            }
        }
        return save;
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值