Skip to main content

Substring with Concatenation of All Words

LeetCode 30 | Difficulty: Hard​

Hard

Problem Description​

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 any permutation of words concatenated.

  • For example, if words = ["ab","cd","ef"], then "abcdef", "abefcd", "cdabef", "cdefab", "efabcd", and "efcdab" are all concatenated strings. "acdbef" is not a concatenated string because it is not the concatenation of any permutation of words.

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

Example 1:

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

Output: [0,9]

Explanation:

The substring starting at 0 is "barfoo". It is the concatenation of ["bar","foo"] which is a permutation of words.

The substring starting at 9 is "foobar". It is the concatenation of ["foo","bar"] which is a permutation of words.

Example 2:

Input: s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]

Output: []

Explanation:

There is no concatenated substring.

Example 3:

Input: s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]

Output: [6,9,12]

Explanation:

The substring starting at 6 is "foobarthe". It is the concatenation of ["foo","bar","the"].

The substring starting at 9 is "barthefoo". It is the concatenation of ["bar","the","foo"].

The substring starting at 12 is "thefoobar". It is the concatenation of ["the","foo","bar"].

Constraints:

  • 1 <= s.length <= 10^4

  • 1 <= words.length <= 5000

  • 1 <= words[i].length <= 30

  • s and words[i] consist of lowercase English letters.

Topics: Hash Table, String, Sliding Window


Approach​

Sliding Window​

Maintain a window [left, right] over the array/string. Expand right to include new elements, and shrink left when the window violates constraints. Track the optimal answer as the window slides.

When to use

Finding subarrays/substrings with a property (max length, min length, exact count).

Hash Map​

Use a hash map for O(1) average lookups. Store seen values, frequencies, or indices. The key question: what should I store as key, and what as value?

When to use

Need fast lookups, counting frequencies, finding complements/pairs.


Solutions​

Solution 1: C# (Best: 1091 ms)​

MetricValue
Runtime1091 ms
Memory45 MB
Date2022-01-26
Solution
public class Solution {
public IList<int> FindSubstring(string s, string[] words) {
int n = words.Length;
bool[] used = new bool[n];
int wordLen = words[0].Length;
IList<int> indexes = new List<int>();
for (int i = 0; i <= s.Length-(n*wordLen); i++)
{
if(FindDfs(s, i, wordLen, words, used, n))
indexes.Add(i);
}
return indexes;
}

private bool FindDfs(string s, int index, int wordLen, string[] words, bool[] used, int count)
{
if(count==0) return true;
string substr = s.Substring(index, wordLen);
for(int i=0;i<words.Length;i++)
{
if(words[i].Equals(substr) && !used[i])
{
used[i] = true;
bool result = FindDfs(s, index + wordLen, wordLen, words, used, count - 1);
used[i] = false;
return result;
}
}
return false;
}
}
πŸ“œ 2 more C# submission(s)

Submission (2022-01-19) β€” 1335 ms, 44.7 MB​

public class Solution {
public IList<int> FindSubstring(string s, string[] words) {
int n = words.Length;
bool[] used = new bool[n];
int wordLen = words[0].Length;
IList<int> indexes = new List<int>();
for (int i = 0; i <= s.Length-(n*wordLen); i++)
{
if(FindDfs(s, i, wordLen, words, used, n))
indexes.Add(i);
}
return indexes;
}

private bool FindDfs(string s, int index, int wordLen, string[] words, bool[] used, int count)
{
if(count==0) return true;
string substr = s.Substring(index, wordLen);
for(int i=0;i<words.Length;i++)
{
if(words[i].Equals(substr) && !used[i])
{
used[i] = true;
bool result = FindDfs(s, index + wordLen, wordLen, words, used, count - 1);
used[i] = false;
return result;
}
}
return false;
}
}

Submission (2022-01-26) β€” 1421 ms, 44.9 MB​

public class Solution {
public IList<int> FindSubstring(string s, string[] words)
{
int n = words.Length;
bool[] used = new bool[n];
int wordLen = words[0].Length;
IList<int> indexes = new List<int>();
for (int i = 0; i <= s.Length-(n*wordLen); i++)
{
if(FindDfs(s, i, wordLen, words, used, n))
indexes.Add(i);
}
return indexes;
}

private bool FindDfs(string s, int index, int wordLen, string[] words, bool[] used, int count)
{
if(count==0) return true;
string substr = s.Substring(index, wordLen);
for(int i=0;i<words.Length;i++)
{
//handling duplicate words condition by goinf over all words which are equal and not used
if(words[i].Equals(substr) && !used[i])
{
used[i] = true;
bool result = FindDfs(s, index + wordLen, wordLen, words, used, count - 1);
used[i] = false;
return result;
}
}
return false;
}
}

Complexity Analysis​

ApproachTimeSpace
Sliding WindowO(n)O(n)O(k)O(k)
Hash MapO(n)O(n)O(n)O(n)

Interview Tips​

Key Points
  • Break the problem into smaller subproblems. Communicate your approach before coding.
  • Clarify what makes a window "valid" and what triggers expansion vs shrinking.
  • Hash map gives O(1) lookup β€” think about what to use as key vs value.