Skip to content

Substring with Concatenation of All Words

You are given a string s and an array of strings words. All the strings of words are of the same length.

A concatenated string is a string that exactly contains all the strings of words concatenated in any order and without overlapping.

Return an array of the starting indices of all the concatenated substrings in s. You can return the answer in any order.

Input StringWordsOutputExplanation
"barfoothefoobarman"["foo","bar"][0, 9]Index 0: “barfoo” is “bar” + “foo”. Index 9: “foobarman”[0:6] = “foobar” is “foo” + “bar”.
"wordgoodgoodgoodbestword"["word","good","best","word"][]No substring matches the concatenation.
"barfoofoobarthefoobarman"["bar","foo","the"][6, 9, 12]Index 6: “barfoo” is “bar”+“foo”. Index 9: “foobar” is “foo”+“bar”. Index 12: “thefo…”
  • 1 <= s.length <= 10^4
  • 1 <= words.length <= 5000
  • 1 <= words[i].length <= 30
  • s and words[i] consist of lowercase English letters
  • All the strings of words are of the same length
ApproachTimeSpaceBest When
Sliding WindowO(n * m)O(m * k)General case — trades some constant-factor overhead for simplicity
Brute ForceO(n² * m)O(m * k)Input is tiny, academic purposes

where n = len(s), m = len(words), k = average word length

  • Pick Sliding Window if you want the optimal practical solution — it efficiently checks every valid window.
  • Pick Brute Force if you are learning the problem from scratch and want to understand the concept before optimization.
Best For Speed
Sliding Window
O(nm) time · O(mk) space
Learning
Brute Force
O(n²m) time · O(mk) space
Section titled “Approach 1: Sliding Window with Hash Map (Recommended)”
★ Recommended

For each possible starting position in the string, check if the next len(word) * len(words) characters form a valid concatenation of all words. Maintain a hash map to count word frequencies in the current window and compare against the expected frequency map.

The key insight is that because all words have the same length, we can divide any substring of that total length into fixed chunks and check if they match our word set.

⏱ Time O(n*m) Each position checked once, m words extracted per check 💾 Space O(m*k) Hash map stores unique words

Input: s = "barfoothefoobarman", words = ["foo", "bar"]

  • Word length: 3
  • Total concatenation length: 3 * 2 = 6
  • Expected frequencies: {foo: 1, bar: 1}
IndexWindowWordsFrequenciesMatch?
0”barfoo”[“bar”,“foo”]bar:1, foo:1
3”foothef”[“foo”,“the”,“f”]No valid split
9”foobar”[“foo”,“bar”]foo:1, bar:1

Animated walkthrough

How the sliding window checks each position

Watch the window slide over the string, extract words of fixed length, count frequencies, and compare against the expected pattern.

Step 1 of 3 Target: 6
Waiting to begin...
Array state
Memory / lookup state

function findSubstringWindow(s, words):
if words.empty() or s.empty():
return []
wordLen = len(words[0])
wordCount = len(words)
totalLen = wordLen * wordCount
// Build frequency map of words
wordFreq = {}
for word in words:
wordFreq[word] += 1
result = []
// Check every possible starting position
for i from 0 to len(s) - totalLen:
// Extract words from this window
windowFreq = {}
for j from 0 to wordCount - 1:
word = s[i + j*wordLen : i + (j+1)*wordLen]
windowFreq[word] += 1
// Compare frequencies
if windowFreq == wordFreq:
result.append(i)
return result
substring_with_concatenation_of_all_words_sliding_window.py
from typing import List
from collections import defaultdict
def find_substring_sliding_window(s: str, words: List[str]) -> List[int]:
"""
Find all starting indices of substrings that are concatenations of all words.
Uses sliding window with word frequency tracking.
Time: O(n * m), where n = len(s), m = len(words)
Space: O(m * k), where k = average word length
"""
if not words or not s:
return []
word_len = len(words[0])
word_count = len(words)
total_len = word_len * word_count
# Count frequency of each word
word_freq = defaultdict(int)
for word in words:
word_freq[word] += 1
result = []
# For each possible starting position
for i in range(len(s) - total_len + 1):
# Check if substring starting at i is a concatenation
window_freq = defaultdict(int)
# Extract and count words in this window
for j in range(word_count):
word = s[i + j * word_len:i + (j + 1) * word_len]
window_freq[word] += 1
# Check if frequencies match
if window_freq == word_freq:
result.append(i)
return result
# Test cases
if __name__ == "__main__":
# Example 1
s1 = "barfoothefoobarman"
words1 = ["foo", "bar"]
print(find_substring_sliding_window(s1, words1)) # [0, 9]
# Example 2
s2 = "wordgoodgoodgoodbestword"
words2 = ["word", "good", "best", "word"]
print(find_substring_sliding_window(s2, words2)) # []
# Example 3
s3 = "barfoofoobarthefoobarman"
words3 = ["bar", "foo", "the"]
print(find_substring_sliding_window(s3, words3)) # [6, 9, 12]
✓ Simple

For each starting position, extract the window substring and manually check if it can be decomposed into the word list. Iterate through the substring in word-sized chunks, remove matching words from a copy of the word list, and verify all words are consumed.

This approach is less efficient but easier to understand conceptually.

⏱ Time O(n²*m) Nested loops with list operations 💾 Space O(m*k) Temporary word lists

Input: s = "barfoothefoobarman", words = ["foo", "bar"]

IndexWindowTemp WordsStatus
0”barfoo”Extract “bar” → remove → [“foo”]. Extract “foo” → remove → [].✓ Match
1”arfoot”Extract “arf” → not in list.✗ No match
9”foobar”Extract “foo” → remove → [“bar”]. Extract “bar” → remove → [].✓ Match
function findSubstringBruteForce(s, words):
if words.empty() or s.empty():
return []
wordLen = len(words[0])
wordCount = len(words)
totalLen = wordLen * wordCount
result = []
// Check every possible starting position
for i from 0 to len(s) - totalLen:
substring = s[i : i + totalLen]
tempWords = copy(words)
valid = true
// Try to match all words in this window
for j from 0 to wordCount - 1:
word = substring[j*wordLen : (j+1)*wordLen]
if word in tempWords:
remove word from tempWords
else:
valid = false
break
if valid and tempWords.empty():
result.append(i)
return result
substring_with_concatenation_of_all_words_brute_force.py
from typing import List
from collections import Counter
from itertools import permutations
def find_substring_brute_force(s: str, words: List[str]) -> List[int]:
"""
Find all starting indices of substrings that are concatenations of all words.
Uses brute force by checking every possible concatenation.
Time: O(n² * m), where n = len(s), m = len(words)
Space: O(m * k), where k = average word length
"""
if not words or not s:
return []
word_len = len(words[0])
word_count = len(words)
total_len = word_len * word_count
result = []
# For each possible starting position in the string
for i in range(len(s) - total_len + 1):
# Extract the substring of exact length
substring = s[i:i + total_len]
# Check if this substring can be formed by concatenating all words
temp_words = words[:]
valid = True
for j in range(word_count):
word = substring[j * word_len:(j + 1) * word_len]
if word in temp_words:
temp_words.remove(word)
else:
valid = False
break
if valid and not temp_words:
result.append(i)
return result
# Test cases
if __name__ == "__main__":
# Example 1
s1 = "barfoothefoobarman"
words1 = ["foo", "bar"]
print(find_substring_brute_force(s1, words1)) # [0, 9]
# Example 2
s2 = "wordgoodgoodgoodbestword"
words2 = ["word", "good", "best", "word"]
print(find_substring_brute_force(s2, words2)) # []
# Example 3
s3 = "barfoofoobarthefoobarman"
words3 = ["bar", "foo", "the"]
print(find_substring_brute_force(s3, words3)) # [6, 9, 12]
✓ Simple

A third approach demonstrating an alternative technique for solving this problem. This implementation optimizes either time or space complexity compared to the previous approaches.

⏱ Time O(n) Optimized complexity 💾 Space O(1) Efficient space usage
function approach3():
// Implementation approach goes here
substring_with_concatenation_of_all_words_optimized.py
"""
Solution for Substring with Concatenation of All Words
"""
def solve():
"""Implementation for approach 3 of Substring with Concatenation of All Words"""
pass
if __name__ == "__main__":
print("Solution ready")