1. Naive Window Substring Slicing
Intuition
Let M = words.length and L = words[0].length. The total concatenation length is totalLen = M * L. Loop i from 0 to N - totalLen. For each i, slice M chunks of size L. Check if chunk frequency matches the expected word frequency map. If all M chunks match, record i.
When to apply: Basic baseline comparison.
Algorithm
- Compute
wordCountmap. - Loop
ifrom 0 ton - m * L: - Create
seenmap. - For
jfrom 0 tom - 1: -
w = s.substr(i + j * L, L). - If
wnot inwordCount: break. -
seen[w]++. - If
seen[w] > wordCount[w]: break. - If all
mwords matched:res.push(i). - Return
res.
#include <iostream>#include <string>#include <vector>#include <unordered_map> class Solution {public: std::vector<int> findSubstring(std::string s, std::vector<std::string>& words) { std::vector<int> res; if (s.empty() || words.empty()) return res; int n = s.size(), m = words.size(), l = words[0].size(); if (n < m * l) return res; std::unordered_map<std::string, int> counts; for (const auto& w : words) counts[w]++; for (int i = 0; i <= n - m * l; ++i) { std::unordered_map<std::string, int> seen; int j = 0; while (j < m) { std::string word = s.substr(i + j * l, l); if (counts.find(word) == counts.end()) break; seen[word]++; if (seen[word] > counts[word]) break; j++; } if (j == m) res.push_back(i); } return res; }}; int main() { Solution sol; std::vector<std::string> words = {"foo", "bar"}; auto ans = sol.findSubstring("barfoothefoobarman", words); for (int x : ans) std::cout << x << " "; // 0 9 std::cout << std::endl; return 0;}Time & Space Complexity
- Time complexity:
O((N - M * L) * M * L)— Tests (N - M * L) positions, each performing M hash map lookups and substring operations of length L. - Space complexity:
O(M * L)
Why Brute Force Doesn't Scale:
Sliding window advances in O(1) amortized chunk steps rather than rescanning M words.
Only two small frequency hash maps are maintained during sliding.