InterviewPrepKit

Home / Coding / Sliding Window

Substring with Concatenation of All Words

hard Original β†—
Solving tips
  • Key insight: since all words share length w, a valid window splits into fixed chunks, so run w sliding-window passes (one per offset 0..w-1) striding by w words at a time.
  • Match a Counter of words (duplicates matter, so use a multiset not a set); expand right by a chunk, shrink left while a word exceeds its quota, and emit when the window holds exactly k chunks.
  • On an unknown chunk, hard-reset the window (clear counter, count, move left past it), not just decrement.
  • Target O(n*w) time and O(k*w) space; pitfalls are striding by one character instead of one word and off-by-one in the chunk loop bound range(offset, n-w+1, w).

Problem

You are given a string s and a list words in which every word has the same length. A concatenated substring of s is a substring that is exactly the words of words glued together in some order β€” each word used exactly as many times as it appears in the list, with nothing in between. Return the starting indices (in any order) of all concatenated substrings in s.

Examples

  • s = "barfoothefoobarman", words = ["foo", "bar"] β†’ [0, 9] β€” "barfoo" starts at 0 and "foobar" starts at 9; both are the two words in some order.
  • s = "wordgoodgoodgoodbestword", words = ["word", "good", "best", "word"] β†’ [] β€” every candidate needs "word" twice plus "good" and "best", and no window of length 16 delivers that.
  • s = "barfoofoobarthefoobarman", words = ["bar", "foo", "the"] β†’ [6, 9, 12] β€” "foobarthe", "barthefoo", and "thefoobar" are all permutations of the three words.

Constraints

  • 1 <= len(s) <= 10^4
  • 1 <= len(words) <= 5000, 1 <= len(words[i]) <= 30 β€” all words the same length
  • s and all words consist of lowercase English letters.
  • Checking every start index against every word from scratch multiplies to ~10^8 character work β€” the intended solutions exploit the fixed word length to do better.

Think about it first

Hint 1 Duplicate words are allowed in `words`, so the thing to match is a *multiset* (a Counter of word β†’ count), not a set. A window matches when its chunk counts equal that Counter.
Hint 2 Every valid window has exactly `len(words) * word_len` characters and β€” because all words share one length β€” splits into chunks at fixed offsets. So a candidate window is determined entirely by its start index, and its chunks by start position modulo the word length.
Hint 3 Run one sliding-window pass per starting offset `0 … word_len βˆ’ 1`, moving in strides of `word_len`. Add the chunk entering on the right to a running Counter; if some word's count exceeds its quota, shrink from the left (whole chunks at a time) until the excess is gone; when the window holds exactly `len(words)` chunks, record its start.
Your workspace Not runnable by design β€” this is your interview scratchpad. Saved on this device.