Substring with Concatenation of All Words

HardSliding Window · Hash Map🧩 Sliding WindowLeetCode #30 ↗Updated

Problem

You are given a string s and an array words in which every word has the same length. A position in s is a match if the text starting there is some ordering of all the words glued together, each word used exactly as many times as it appears in words. Return every matching start index, in any order.
Inputs = "barfoothefoobarman", words = ["foo","bar"]Output[0,9]
Inputs = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]Output[]
Inputs = "barfoofoobarthefoobarman", words = ["bar","foo","the"]Output[6,9,12]

1 ≤ |s| ≤ 104  ·  1 ≤ words.length ≤ 5000  ·  1 ≤ words[i].length ≤ 30  ·  lowercase letters only

Try it yourself

Java
class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        
    }
}
Your code is saved in this browser.Submit on LeetCode ↗
Key insight
Because all words share length L, only L different “word grids” exist (start offsets 0..L-1). On each grid, treat whole words as the units of a sliding window with a frequency map — just like a fixed-alphabet anagram search.
PatternSliding Window

Instinct Longest, shortest or count of contiguous subarrays/substrings meeting a rule? Grow a window on the right, shrink it on the left, never step back.

Memory hack Move like a caterpillar - the head stretches forward to grab, the tail pulls up only when the body breaks the rule.

Open the Sliding Window guide →

Approaches

Brute ForceCut, Sort and Compare

Time O(n · m log m · L)Space O(m · L)

Sort a copy of words once. For every start index, cut the next m chunks of length L, sort them, and compare with the sorted words. Two multisets are equal exactly when their sorted forms are equal.

Java
public List<Integer> findSubstring(String s, String[] words) {
    List<Integer> result = new ArrayList<>();
    int m = words.length, len = words[0].length();
    int total = m * len;
    String[] target = words.clone();
    Arrays.sort(target);
    for (int i = 0; i + total <= s.length(); i++) {
        String[] chunks = new String[m];
        for (int j = 0; j < m; j++) {
            chunks[j] = s.substring(i + j * len, i + (j + 1) * len);
        }
        Arrays.sort(chunks);
        if (Arrays.equals(chunks, target)) result.add(i);
    }
    return result;
}

Easy to prove correct, but sorting at every index is wasteful.

At a glance

LevelApproachTimeSpace
Brute ForceCut, Sort and CompareO(n · m log m · L)O(m · L)
BetterCount Map with Early ExitO(n · m · L)O(m · L)
OptimalWord-Level Sliding Window per OffsetO(n · L + m · L)O(m · L)
Common pitfalls
  • Using a set instead of a count map — duplicate words like ["good","good"] break.
  • Sliding one character at a time inside a grid — chunks must be aligned to the offset.
  • Forgetting to drop a word after recording a match, so the window grows past m words.
Interview tips
  • Relate it to Find All Anagrams: same window, but the “letters” are whole words.
  • State the complexity in terms of n, m and L — substring hashing costs O(L).
  • Ask whether words can repeat; the answer drives the count-map design.
Pattern
Sliding window over tokens → find permutations of a multiset of fixed-length tokens inside a long string.
My notes saved in this browser