滑动窗口+哈希计数匹配字符串+map判断匹配是否成功 30. 串联所有单词的子串 76. 最小覆盖子串

30. 串联所有单词的子串

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

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

示例 1:

输入:
  s = "barfoothefoobarman",
  words = ["foo","bar"]
输出:[0,9]

解释:
从索引 0 和 9 开始的子串分别是 “barfoo” 和 “foobar” 。
输出的顺序不重要, [9,0] 也是有效答案。
示例 2:

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

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

解题
哈希储存word中每一个string;
滑动窗口对s中每一个words总长度的字符串进行遍历,计数后与存储words的哈希表进行比较,若完全相同,即可;

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        int len=words.size();
        vector<int> res;
        if(!len) return res;
        int n=words[0].size();
        int wide= n*len;   //总长度
        if(s.size()<wide) return res;
        //字典存放每个词使用的次数
        unordered_map<string,int> dic;
        for(string t:words) dic[t]++;


        string tmp;  //扫描每一个子串
        unordered_map<string,int> T;
        bool flag;

        for(int i=0;i<s.size()-wide+1;i++)
        {   
            flag=false;
            tmp=s.substr(i,wide); //截取这段字符串
            //对tmp里每一个n比较
            int f=0;
            while(f<wide){
            string tt=tmp.substr(f,n);
            //看tt是否在dic中,若在则新建map里+1;
            //若不在,则continue
            if(dic.count(tt)) T[tt]++;
            else {
                flag=true;
                break;
            }
            f+=n;
            }
            for(auto i=T.begin();i!=T.end();i++)
            {
                if(i->second!=dic[i->first]) {
                    flag=true;
                    break;
                }
            }
            if(!flag) res.push_back(i); 
            T.clear();  //清空map
        }

        return res;
    }
};

注意点
哈希表间可以==比较,if(T==dic) res.push_back(i);
清空哈希表:T.clear()

76. 最小覆盖子串

解法1:暴力二重循环

class Solution {
public:
    string minWindow(string s, string t) {
        int len=t.size();
        if(!len) return "";
        //哈希表储存t里每个字母的次数
        unordered_map<char,int> dic;
        for(char a:t) dic[a]++;

        int n=s.size();
        if(n<len) return "";

        string tmp;
        int MINLEN=INT_MAX;
        int start=-1;
        int end=0;
        unordered_map<char,int> T;
        
        for(int i=0;i<n-len+1;i++)
            for(int j=i+len;j<=n;j++)
            {   
                tmp=s.substr(i,j-i);
                for(char t:tmp)
                    if(dic.count(t)) T[t]++;

                bool flag=1;
                for(auto t=dic.begin();t!=dic.end();t++)
                    if((t->second)>T[t->first]){
                        flag=0;
                        break;
                    }
                
                if(flag&&(j-i)<MINLEN)
                {
                    MINLEN=j-i;
                    start=i;
                    end=j-1;
                }
                T.clear();
            }
        string res="";
        if(start!=-1) res=s.substr(start,end-start+1);
        return res;

    }
};

解法2:滑动窗口+map判断改进
map存储t里每一个字符个数,遍历s,当s[i]在map中时(–map[s[i]]>=0)且cnt++,当cnt==t时说明整个数组匹配成功,记录此时的start与end,并与max比较;
然后右移left,若map[left++]>0说明该left在匹配的范围内,cnt–;

class Solution {
public:
    string minWindow(string s, string t) {
        unordered_map<char, int> map;
        for (auto c : t) map[c]++;
        int left = 0, cnt = 0, maxlen = s.size() + 1, start = left;
        for (int i = 0; i < s.size(); ++i) {
            if (--map[s[i]] >= 0) ++cnt;
            while(cnt == t.size()) {
                if (maxlen > i - left + 1) {
                    maxlen = i - left + 1;
                    start = left;
                } 
                if (++map[s[left]] > 0) cnt--;
                left++;
            }
        }
        return maxlen == s.size() + 1 ? "" : s.substr(start, maxlen);
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值