LeeCode 30

LeeCode 30

题目:

image.png

思路:

朴素的想法,是创建一个 m p mp mp哈希表记录 w o r d s words words中所有字符出现的次数,令 n n n s . s i z e ( ) s.size() s.size(), m m m w o r d s . s i z e ( ) words.size() words.size() l e n len len w o r d s [ 0 ] . s i z e ( ) words[0].size() words[0].size()。然后枚举 s s s中的每一个字符为一个子串的起点,创建第二个 c n t H A S H cntHASH cntHASH,再遍历子串中的单词,最后检查 c n t cnt cnt是否等于 m p mp mp,显然该算法的时间复杂度为 O ( n × m × l e n ) O(n\times m\times len) O(n×m×len)

image.png

根据题目给的数据范围,最坏约为 1 0 9 10^{9} 109,而LeetCode时间限制一秒,一般一秒处理 1 0 7 10^{7} 107左右,所以超时了

Code

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        int n = s.length(), m = words.size(), w = words[0].length();
        unordered_map<string, int> map;
        for (const string& word : words) {
            map[word]++;
        }
        vector<int> ans;
        for (int i = 0; i + m * w <= n; i++) {
            unordered_map<string, int> cur;
            string sub = s.substr(i, m * w);
            bool valid = true;
            for (int j = 0; j < sub.length(); j += w) {
                string item = sub.substr(j, w);
                if (map.find(item) == map.end()) {
                    valid = false;
                    break;
                }
                cur[item]++;
            }
            if (valid && cur == map) {
                ans.push_back(i);
            }
        }
        return ans;
    }
};

优化:

每个子串的起点是不可避免的需要枚举的,但是我们检查子串是否合法的的过程中,窗口每向前移动一次,我们就要遍历一次子串,与此同时我们发现,当前检查完的 c n t cnt cnt哈希表可以通过删除左侧的单词,添加右侧的的单词,得到窗口向前直接滑一个单词的长度后的 c n t cnt cnt的状态,所以我们把 n   m o d   l e n n\bmod len nmodlen将起点分为len类(只有   m o d   l e n \bmod len modlen才能遍历所有的合法的起点,因为每次要向前滑 l e n len len i + k × l e n ( 0 ≤ i ≤ l e n − 1 ) i+k\times len (0\leq i\leq len-1) i+k×len(0ilen1)遍历每个与它相同余数的起点。

AcCode

class Solution
{
public:
    vector<int> findSubstring(string s, vector<string> &words)
    {
        unordered_map<string, int> mp;
        for (auto x : words)
        {
            mp[x]++;
        }
        vector<int> ans;
        int n = s.size();
        int m = words.size();
        int len = words[0].size();
        for (int i = 0; i < len; i++)
        {
            unordered_map<string, int> cnt;
            for (int j = i; j + len <= n; j += len)
            {
                // j,i为起始位置,len为单词长度
                string tmp = s.substr(j, len);
                cnt[tmp]++;
                // 当有一个完整的窗口时,滑动窗口,每次移动一个单词的长度
                if (j >= i + m * len)
                {
                    string tmp = s.substr(j - m * len, len);
                    // 删除最左边的单词,cnt[tmp]>=1,不会出现负数
                    cnt[tmp]--;
                    if (cnt[tmp] == 0)
                    {
                        cnt.erase(tmp);
                    }
                }
                //j是最后一个单词的第一个字符
                if (j >= i + (m - 1) * len)
                {
                    if (cnt == mp)
                    {
                        ans.push_back(j - (m - 1) * len);
                    }
                }
            }
        }
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值