【Leetcode】30. Substring with Concatenation of All Words

题目地址:

https://leetcode.com/problems/substring-with-concatenation-of-all-words/

给定一个长 n n n的字符串 s s s和一个长 m m m的字符串数组 A A A A A A里每个单词的长度都相等,都是 l l l,要求寻找所有的位置 i i i使得 s [ i : i + m l − 1 ] s[i:i+ml-1] s[i:i+ml1]恰好是 A A A中所有字符串按某个顺序的拼接。

先将 A A A的所有字符串加入一个哈希表,接着开始遍历 s s s。我们将对于 s s s的遍历分为 l l l次,每次从 [ 0 , l ) [0,l) [0,l)其中一个下标出发,设 0 ≤ t < l 0\le t<l 0t<l,那么可以得出 m m m个字符串 s [ t , t + l ) , s [ t + l , t + 2 l ) , . . . , s [ t + ( m − 1 ) l , t + m l ) s[t,t+l), s[t+l,t+2l),...,s[t+(m-1)l,t+ml) s[t,t+l),s[t+l,t+2l),...,s[t+(m1)l,t+ml),只需要看一下这 m m m个字符串是不是恰好是 A A A中的那些字符串即可。但是这里,直接比较两个哈希表是否相等效率太低,我们可以另外开一个变量,这个变量存这 n n n个字符串里包含于多重集 A A A的那些字符串有多少个( A A A中可能有重复,所以要视 A A A为一个多重集),如果恰好是 m m m个,就说明两个哈希表相等了。为了加速,可以用字符串哈希,将对字符串的处理变为对整数的处理。代码如下:

class Solution {
 public:
  using ull = unsigned long long;
  vector<int> findSubstring(string s, vector<string>& ws) {
    int n = s.size(), m = ws.size(), len = ws[0].size();
    vector<ull> ha(n + 1), pow(n + 1);
    pow[0] = 1;
    constexpr ull P = 131;
    for (int i = 1; i <= n; i++)
      ha[i] = ha[i - 1] * P + s[i - 1], pow[i] = pow[i - 1] * P;
    auto hash_s = [&](int l, int r) {
      return ha[r + 1] - ha[l] * pow[r - l + 1];
    };
    // 将A的所有字符串哈希值存入一个哈希表
    unordered_map<ull, int> mp;
    for (int k = 0; k < ws.size(); k++) {
      auto& w = ws[k];
      ull ha = 0;
      for (int i = 0; i < w.size(); i++) ha = ha * P + w[i];
      ++mp[ha];
    }
    vector<int> res;
    for (int st = 0; st < len; st++) {
      unordered_map<ull, int> cnt_mp;
      int cnt = 0;
      for (int i = st; i + len - 1 < n; i += len) {
        // 有一个字符串出窗口了,将其删去
        if (i - m * len >= 0) {
          auto h = hash_s(i - m * len, i - (m - 1) * len - 1);
          if (cnt_mp[h]-- <= mp[h]) cnt--;
        }
        auto h = hash_s(i, i + len - 1);
        if (++cnt_mp[h] <= mp[h]) cnt++;
        if (cnt == m) res.push_back(i - (m - 1) * len);
      }
    }
    return res;
  }
};

时间复杂度 O ( l m + n ) O(lm+n) O(lm+n),空间 O ( n + m ) O(n+m) O(n+m)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值