leetcode#30 串联所有单词的字串

leetcode#30 串联所有单词的字串

题目:

给定一个字符串 s 和一些 长度相同 的单词 words 。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
注意子串要与 words 中的单词完全匹配,中间不能有其他字符 ,但不需要考虑 words 中单词串联的顺序。

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

思路:

1、基本思路

设所有单词长度之和为len,设定一个滑动窗口长度为len,每次对比窗口里的字符串和这些单词是否相等即可,每次滑动窗口向后滑动一位。
==如何判断窗口里的字符串和这些单词相等:==要有抽象能力,把单词变成A,B,C,要看一些单词和一个字符串是否相等(顺序可变),不必挨个比对,只要每个单词出现一遍即可,所以每个单词原始次数为1,每出现1次减一即可,全部为0就是相等。不相等情况同理。也可以每出现一次出现次数加一,最后出现次数和原始次数相等就可以了。
字符串的话用map存就可以了unordered_map<string, int>mp;
c++中不存在,判断两个容器是否相等的函数,所以我们用cnt进行计数,计数的是单词出现次数(保证合理,不会出现总数相等,分别不等的情况),当cnt等于要匹配的单词个数时,插入答案即可。

2、优化

窗口是否可以后移动的更多

考虑下面三种情况:

在主串中substr(pos,len)剪切出单词进行比较

1、当前这个没有单词时,我们只能窗口后移一位,因为不能确定字符串后缀是否能继续匹配。
2、当这个单词出现次数超过限制时,窗口左端不断右移word.length位,直到,这个单词的次数不超过限制为止,其过程中,要把左端窗口排除的单词减去。
3、当这个单词合理,直接计数并放入map容器即可,因为下一个单词如果合理就一定会重复,这样窗口在第二步会左移。
4、当得到一个答案后,直接把窗口左端右移word.length位,即可。

其中要优化的位第一步:

考虑,如果整个窗口右移一位,窗口中的单词都会改变,这样就需要重新判定整个窗口的单词,而且这样并不会包含所有情况,比如

"ababaab"
["ab","ba","ba"]

abab重复,l进行了不可逆的右移,这样ab的后缀b就不能在进行匹配了,所以,要包含所有情况的就要遍历窗口左边界的初始值从0到word.length-1并且把右移一位变成右移word.length即可。
为什么包含所有情况?

{abc}|[][][]
[]{ab|c}[][]
[][]{a|bc}[]

[]是其他元素
{}内为目标串
| 为每三个元素一个分隔符

显而易见答案的位移只包含这三种,所以只要便利这三种位移就可以了。

AC代码

class Solution
{
public:
    vector<int> findSubstring(string s, vector<string> &words)
    {
        vector<int> ans;
        unordered_map<string, int> mp;
        unordered_map<string, int> tmp;
        int nums = words.size();
        int len = words[0].length();
        int size = nums * len;
        for (int i = 0; i < words.size(); ++i)
            mp[words[i]]++;
        for (int i = 0; i < len; ++i)
        {
            int l = i, r = i, cnt = 0;
            unordered_map<string, int> tmp;
            while (r + len - 1 < s.length())
            {
                string now = s.substr(r, len);
                r += len;
                if (mp.find(now) == mp.end())
                    l = r, tmp.clear(), cnt = 0;
                else
                {
                    ++tmp[now];
                    ++cnt;
                    while (tmp[now] > mp[now])
                    {
                        tmp[s.substr(l, len)]--;
                        l += len;
                        --cnt;
                    }
                }
                if (cnt == nums)
                    ans.push_back(l);
            }
        }
        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值