Skip to content

Commit 9a94e60

Browse files
Create 0030-0030-Substring-with-Concatenation-of-All-Words
Adding Solution to the leetcode problem 0030-Substring-with-Concatenation-of-All-Words
1 parent 2f6e3e7 commit 9a94e60

File tree

1 file changed

+377
-0
lines changed

1 file changed

+377
-0
lines changed
Lines changed: 377 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,377 @@
1+
---
2+
id: Substring with Concatenation of All Words
3+
title: Substring with Concatenation of All Words
4+
sidebar_label: 0030-Substring-with-Concatenation-of-All-Words
5+
tags:
6+
- Hard
7+
- String
8+
- Hash Table
9+
- Two Pointers
10+
description: A concatenated string is a string that exactly contains all the strings of any permutation of `words` concatenated.
11+
---
12+
13+
### Problem Description:
14+
| Problem Statement | Solution Link | LeetCode Profile |
15+
| :------------------------------------------------------ | :------------------------------------------------------------------------- | :------------------------------------------------------ |
16+
| [Substring with Concatenation of All Words](https://leetcode.com/problems/0030-Substring-with-Concatenation-of-All-Words/description/) | [Substring with Concatenation of All Words Solution on LeetCode](https://leetcode.com/problems/0030-Substring-with-Concatenation-of-All-Words/solutions/) | [Nikita Saini](https://leetcode.com/u/Saini_Nikita/) |
17+
18+
## Problem Description
19+
You are given a string `s` and an array of strings `words`. All the strings in `words` are of the same length.
20+
21+
### Examples
22+
23+
**Example 1:**
24+
25+
- **Input:** `s = "barfoothefoobarman"`, `words = ["foo","bar"]`
26+
- **Output:** `[0,9]`
27+
28+
- **Explanation:**
29+
- The substring starting at 0 is "barfoo". It is the concatenation of `["bar","foo"]` which is a permutation of `words`.
30+
- The substring starting at 9 is "foobar". It is the concatenation of `["foo","bar"]` which is a permutation of `words`.
31+
32+
**Example 2:**
33+
34+
- **Input:** `s = "wordgoodgoodgoodbestword"`, `words = ["word","good","best","word"]`
35+
- **Output:** `[]`
36+
37+
- **Explanation:**
38+
- There is no concatenated substring.
39+
40+
**Example 3:**
41+
42+
- **Input:** `s = "barfoofoobarthefoobarman"`, `words = ["bar","foo","the"]`
43+
- **Output:** `[6,9,12]`
44+
45+
- **Explanation:**
46+
- The substring starting at 6 is "foobarthe". It is the concatenation of `["foo","bar","the"]`.
47+
- The substring starting at 9 is "barthefoo". It is the concatenation of `["bar","the","foo"]`.
48+
- The substring starting at 12 is "thefoobar". It is the concatenation of `["the","foo","bar"]`.
49+
50+
### Constraints
51+
52+
- 1 <= s.length <= 10^4
53+
- 1 <= words.length <= 5000
54+
- 1 <= words[i].length <= 30
55+
- s and words[i] consist of lowercase English letters.
56+
57+
## Approach
58+
We will use a sliding window approach to solve this problem.
59+
1. First, we calculate the length of each word in the `words` array and the total length of the concatenated substring.
60+
2. We create a hashmap to store the count of each word in `words`.
61+
3. We iterate over each possible starting index in the string `s` using a sliding window of size equal to the total length of the concatenated substring.
62+
4. For each substring, we check if it can be formed by concatenating all the words in `words`.
63+
5. To check the validity of each window, we use another hashmap to count occurrences of words in the current window and compare it to the word count map.
64+
6. If the current window is valid, we add its starting index to the result list.
65+
66+
## Solution in Java
67+
```java
68+
import java.util.*;
69+
70+
public class Solution {
71+
public List<Integer> findSubstring(String s, String[] words) {
72+
List<Integer> result = new ArrayList<>();
73+
if (s == null || s.length() == 0 || words == null || words.length == 0) {
74+
return result;
75+
}
76+
77+
int wordLength = words[0].length();
78+
int wordCount = words.length;
79+
int totalLength = wordLength * wordCount;
80+
81+
// Create a map to count the words
82+
Map<String, Integer> wordMap = new HashMap<>();
83+
for (String word : words) {
84+
wordMap.put(word, wordMap.getOrDefault(word, 0) + 1);
85+
}
86+
87+
// Slide the window over the string `s`
88+
for (int i = 0; i <= s.length() - totalLength; i++) {
89+
String currentSubstring = s.substring(i, i + totalLength);
90+
if (isValid(currentSubstring, wordMap, wordLength, wordCount)) {
91+
result.add(i);
92+
}
93+
}
94+
95+
return result;
96+
}
97+
98+
private boolean isValid(String substring, Map<String, Integer> wordMap, int wordLength, int wordCount) {
99+
Map<String, Integer> seen = new HashMap<>();
100+
for (int j = 0; j < wordCount; j++) {
101+
int wordStartIndex = j * wordLength;
102+
String word = substring.substring(wordStartIndex, wordStartIndex + wordLength);
103+
if (!wordMap.containsKey(word)) {
104+
return false;
105+
}
106+
seen.put(word, seen.getOrDefault(word, 0) + 1);
107+
if (seen.get(word) > wordMap.get(word)) {
108+
return false;
109+
}
110+
}
111+
return true;
112+
}
113+
}
114+
```
115+
116+
### Solution in Python
117+
```python
118+
class Solution:
119+
def findSubstring(self, s: str, words: List[str]) -> List[int]:
120+
result = []
121+
if not s or not words:
122+
return result
123+
124+
word_length = len(words[0])
125+
word_count = len(words)
126+
total_length = word_length * word_count
127+
128+
word_map = {}
129+
for word in words:
130+
word_map[word] = word_map.get(word, 0) + 1
131+
132+
for i in range(len(s) - total_length + 1):
133+
current_substring = s[i:i + total_length]
134+
if self.is_valid(current_substring, word_map, word_length, word_count):
135+
result.append(i)
136+
137+
return result
138+
139+
def is_valid(self, substring, word_map, word_length, word_count):
140+
seen = {}
141+
for j in range(word_count):
142+
word_start_index = j * word_length
143+
word = substring[word_start_index:word_start_index + word_length]
144+
if word not in word_map:
145+
return False
146+
seen[word] = seen.get(word, 0) + 1
147+
if seen[word] > word_map[word]:
148+
return False
149+
return True
150+
```
151+
152+
### Solution in CPP
153+
```cpp
154+
#include <iostream>
155+
#include <vector>
156+
#include <string>
157+
#include <unordered_map>
158+
159+
using namespace std;
160+
161+
class Solution {
162+
public:
163+
vector<int> findSubstring(string s, vector<string>& words) {
164+
vector<int> result;
165+
if (s.empty() || words.empty()) {
166+
return result;
167+
}
168+
169+
int wordLength = words[0].length();
170+
int wordCount = words.size();
171+
int totalLength = wordLength * wordCount;
172+
173+
unordered_map<string, int> wordMap;
174+
for (const string& word : words) {
175+
wordMap[word]++;
176+
}
177+
178+
for (int i = 0; i <= s.length() - totalLength; i++) {
179+
string currentSubstring = s.substr(i, totalLength);
180+
if (isValid(currentSubstring, wordMap, wordLength, wordCount)) {
181+
result.push_back(i);
182+
}
183+
}
184+
185+
return result;
186+
}
187+
188+
bool isValid(string substring, unordered_map<string, int>& wordMap, int wordLength, int wordCount) {
189+
unordered_map<string, int> seen;
190+
for (int j = 0; j < wordCount; j++) {
191+
string word = substring.substr(j * wordLength, wordLength);
192+
if (wordMap.find(word) == wordMap.end()) {
193+
return false;
194+
}
195+
seen[word]++;
196+
if (seen[word] > wordMap[word]) {
197+
return false;
198+
}
199+
}
200+
return true;
201+
}
202+
};
203+
204+
int main() {
205+
Solution solution;
206+
string s = "barfoothefoobarman";
207+
vector<string> words = {"foo", "bar"};
208+
vector<int> result = solution.findSubstring(s, words);
209+
for (int idx : result) {
210+
cout << idx << " ";
211+
}
212+
cout << endl;
213+
return 0;
214+
}
215+
```
216+
217+
### Solution in C
218+
```c
219+
#include <stdio.h>
220+
#include <stdlib.h>
221+
#include <string.h>
222+
223+
#define MAX_WORD_LENGTH 30
224+
225+
int isValid(char* substring, char** words, int* wordMap, int wordLength, int wordCount) {
226+
int seen[5000] = {0};
227+
for (int j = 0; j < wordCount; j++) {
228+
char* word = substring + j * wordLength;
229+
int index = -1;
230+
for (int k = 0; k < wordCount; k++) {
231+
if (strcmp(word, words[k]) == 0) {
232+
index = k;
233+
break;
234+
}
235+
}
236+
if (index == -1 || wordMap[index] == 0) {
237+
return 0;
238+
}
239+
seen[index]++;
240+
if (seen[index] > wordMap[index]) {
241+
return 0;
242+
}
243+
}
244+
return 1;
245+
}
246+
247+
int* findSubstring(char* s, char** words, int wordsSize, int* returnSize) {
248+
*returnSize = 0;
249+
if (!s || !words || wordsSize == 0) {
250+
return NULL;
251+
}
252+
253+
int wordLength = strlen(words[0]);
254+
int wordCount = wordsSize;
255+
int totalLength = wordLength * wordCount;
256+
257+
int* result = (int*)malloc(sizeof(int) * 5000);
258+
if (!result) {
259+
return NULL;
260+
}
261+
262+
int wordMap[5000] = {0};
263+
for (int i = 0; i < wordCount; i++) {
264+
int found = 0;
265+
for (int j = 0; j < i; j++) {
266+
if (strcmp(words[i], words[j]) == 0) {
267+
found = 1;
268+
wordMap[j]++;
269+
break;
270+
}
271+
}
272+
if (!found) {
273+
wordMap[i]++;
274+
}
275+
}
276+
277+
for (int i = 0; i <= strlen(s) - totalLength; i++) {
278+
char currentSubstring[totalLength + 1];
279+
strncpy(currentSubstring, s + i, totalLength);
280+
currentSubstring[totalLength] = '\0';
281+
if (isValid(currentSubstring, words, wordMap, wordLength, wordCount)) {
282+
result[(*returnSize)++] = i;
283+
}
284+
}
285+
286+
return result;
287+
}
288+
289+
int main() {
290+
char* s = "barfoothefoobarman";
291+
char* words[2] = {"foo", "bar"};
292+
int wordsSize = 2;
293+
int returnSize;
294+
int* result = findSubstring(s, words, wordsSize, &returnSize);
295+
for (int i = 0; i < returnSize; i++) {
296+
printf("%d ", result[i]);
297+
}
298+
printf("\n");
299+
free(result);
300+
return 0;
301+
}
302+
```
303+
304+
### Solution in JavaScript
305+
```js
306+
/**
307+
* @param {string} s
308+
* @param {string[]} words
309+
* @return {number[]}
310+
*/
311+
var findSubstring = function(s, words) {
312+
const result = [];
313+
if (!s || !words || words.length === 0) {
314+
return result;
315+
}
316+
317+
const wordLength = words[0].length;
318+
const wordCount = words.length;
319+
const totalLength = wordLength * wordCount;
320+
321+
const wordMap = {};
322+
words.forEach(word => {
323+
if (wordMap[word]) {
324+
wordMap[word]++;
325+
} else {
326+
wordMap[word] = 1;
327+
}
328+
});
329+
330+
for (let i = 0; i <= s.length - totalLength; i++) {
331+
const currentSubstring = s.substring(i, i + totalLength);
332+
if (isValid(currentSubstring, wordMap, wordLength, wordCount)) {
333+
result.push(i);
334+
}
335+
}
336+
337+
return result;
338+
};
339+
340+
function isValid(substring, wordMap, wordLength, wordCount) {
341+
const seen = {};
342+
for (let j = 0; j < wordCount; j++) {
343+
const word = substring.substr(j * wordLength, wordLength);
344+
if (!wordMap[word]) {
345+
return false;
346+
}
347+
if (!seen[word]) {
348+
seen[word] = 1;
349+
} else {
350+
seen[word]++;
351+
}
352+
if (seen[word] > wordMap[word]) {
353+
return false;
354+
}
355+
}
356+
return true;
357+
}
358+
359+
// Example usage:
360+
const s = "barfoothefoobarman";
361+
const words = ["foo", "bar"];
362+
console.log(findSubstring(s, words)); // Output: [0, 9]
363+
```
364+
365+
### Step by Step Algorithm
366+
1. Initialize an empty list `result` to store the starting indices of all the concatenated substrings.
367+
2. Check for base cases: if `s` or `words` is empty, return the empty list `result`.
368+
3. Calculate the length of each word in the `words` array and the total length of the concatenated substring.
369+
4. Create a hashmap `word_map` to store the count of each word in `words`.
370+
5. Iterate over each possible starting index in the string `s` using a sliding window of size equal to the total length of the concatenated substring.
371+
6. For each substring, check if it can be formed by concatenating all the words in `words`.
372+
7. To check the validity of each window, use another hashmap `seen` to count occurrences of words in the current window and compare it to the `word_map`.
373+
8. If the current window is valid, add its starting index to the `result` list.
374+
9. Return the `result` list.
375+
376+
### Conclusion
377+
This problem can be efficiently solved using a sliding window approach. By using a hashmap to store the count of words and iterating over each possible starting index in the string `s`, we can find all the concatenated substrings in `s` that are formed by concatenating all the strings of any permutation of `words`. The time complexity of this solution is O(NML), where N is the length of the string `s`, M is the number of words, and L is the length of each word.

0 commit comments

Comments
 (0)