LeetCode30. 串联所有单词的子串

题目链接:https://leetcode.cn/problems/substring-with-concatenation-of-all-words/description/?envType=study-plan-v2&envId=top-interview-150

被这道题卡了很久,现在过了,把思路整理一下。

因为给定单词长度都是相等的,一开始就想把s按单词长度分割,用哈希表比对。然而s中可能有其他单词,这种方法不合适。

一个字母一个字母扫肯定会超时,那还是要借助哈希表,利用每个单词长度相等这个信息来进行比较。这时候,感恩一位老哥写的题解,让我茅塞顿开:

s								barfoofoobarthefoobarman																							
words								"bar", "foo", "the"																							
																															
								0	1	2	3	4	5	6	7	8	9	10	11	12	13	14	15	16	17	18	19	20	21	22	23
								b	a	r	f	o	o	f	o	o	b	a	r	t	h	e	f	o	o	b	a	r	m	a	n
i=0																															
滑动窗口start = 0								b	a	r	f	o	o	f	o	o															
																															
滑动窗口start = 3											f	o	o	f	o	o	b	a	r												
																															
滑动窗口start = 6														f	o	o	b	a	r	t	h	e									
																															
滑动窗口start = 9																	b	a	r	t	h	e	f	o	o						
																															
滑动窗口start = 15																				t	h	e	f	o	o	b	a	r			
																															
滑动窗口start = 15																							f	o	o	b	a	r	m	a	n
i=1																															
滑动窗口start =1									a	r	f	o	o	f	o	o	b														
																															
滑动窗口start = 4												o	o	f	o	o	b	a	r	t											
																															
滑动窗口start = 7															o	o	b	a	r	t	h	e	f								
																															
滑动窗口start = 10																		a	r	t	h	e	f	o	o	b					
																															
滑动窗口start = 13																					h	e	f	o	o	b	a	r	m		
																															
																															

 这种处理的方法是将s分成一个个符合words长度的单词(就简称为基本单位吧),先从下标为0的位置开始分割,那还有很多种分割方式没有考虑到啊,不急,再从下标为1的位置开始分割,如此重复,一直到完成从下标为words的长度 - 1的分割过程,当我们想继续分割的时候,欸~,已经完成一个周期了,在分下去就是从下标为1的位置开始分割除去第一个基本单位的模样了,那自然就不用考虑了呀。

既然所有种类都分割出来了,我们就要一一比对了。这里我们借助哈希表就可以轻松完成。比对的详解我就写在代码注释里面啦

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> ans;
        int m = words.size();
        int w = words[0].size();
        int n = s.size();
        unordered_map<string, int> count;
        for(auto word : words){
            count[word]++;//先遍历words,确认窗口中各个单词需要出现的次数。
        }
        
        for(int i = 0; i < w; i++){//进行分割处理,找到所有分割类别
            int cnt = 0;//记录窗口中有几个合规(出现在words中,且不多于出现次数)的单词,在cnt == words总数时自然就找到啦
            unordered_map<string, int> window;
            for(int j = i; j + w <= n; j += w){//每次加一个基本单位长度,可以理解成一格一格
                if(j - i >= w * m){//窗口装不下啦,窗口左边就要向右移动
                    string word = s.substr(j - m * w, w);//将最左边的基本单位记录下来
                    window[word]--;//出去了,所以窗口中这个单位出现的次数 - 1
                    if(window[word] < count[word]){//满足这个条件,说明刚刚移出去的基本单位是合规的,那cnt就要 - 1 啦
                        cnt--;
                    }
                }
                string word = s.substr(j, w);//判断窗口右边新加进来的单位是否合规
                window[word]++;
                if(window[word] <= count[word]){
                    cnt++;
                }
                if(cnt == m){//因为窗口只装的下 m * w个长度,cnt == m的话,自然就满足题目要求啦
                    ans.push_back(j - w * (m - 1));//j此时是最右边,答案要的是字符串最左边的位置
                }
            }
        }
        return ans;
    }
};

希望对你有所帮助:D

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值