30. Substring with Concatenation of All Words #
Problem #
You are given a string, s, and a list of words, words, that are all of the same length. Find all starting indices of substring(s) in s that is a concatenation of each word in words exactly once and without any intervening characters.
Example 1:
Input:
s = "barfoothefoobarman",
words = ["foo","bar"]
Output: [0,9]
Explanation: Substrings starting at index 0 and 9 are "barfoor" and "foobar" respectively.
The output order does not matter, returning [9,0] is fine too.
Example 2:
Input:
s = "wordgoodgoodgoodbestword",
words = ["word","good","best","word"]
Output: []
Problem Summary #
Given a source string s and an array of strings, find the starting indices in the source string of continuous strings formed by various combinations of the string array. If there are multiple, all of them need to be output in the result.
Solution Approach #
This problem seems difficult, but there are 2 constraints that make it not particularly hard. 1. The strings in the string array are all of the same length. 2. The strings in the string array are required to be concatenated continuously, and their order can be any permutation.
The solution approach is to first store all strings in the string array in a map and accumulate their occurrence counts. Then scan the source string from the beginning. Each time, determine whether all strings in the string array have been used up (whether the counts are 0). If they have all been used up, and the length is exactly the total length of any permutation of the string array, record the starting index of this combination. If it does not match, continue examining the next character of the source string until the entire source string has been scanned.
Code #
package leetcode
func findSubstring(s string, words []string) []int {
if len(words) == 0 {
return []int{}
}
res := []int{}
counter := map[string]int{}
for _, w := range words {
counter[w]++
}
length, totalLen, tmpCounter := len(words[0]), len(words[0])*len(words), copyMap(counter)
for i, start := 0, 0; i < len(s)-length+1 && start < len(s)-length+1; i++ {
//fmt.Printf("sub = %v i = %v lenght = %v start = %v tmpCounter = %v totalLen = %v\n", s[i:i+length], i, length, start, tmpCounter, totalLen)
if tmpCounter[s[i:i+length]] > 0 {
tmpCounter[s[i:i+length]]--
//fmt.Printf("******sub = %v i = %v lenght = %v start = %v tmpCounter = %v totalLen = %v\n", s[i:i+length], i, length, start, tmpCounter, totalLen)
if checkWords(tmpCounter) && (i+length-start == totalLen) {
res = append(res, start)
continue
}
i = i + length - 1
} else {
start++
i = start - 1
tmpCounter = copyMap(counter)
}
}
return res
}
func checkWords(s map[string]int) bool {
flag := true
for _, v := range s {
if v > 0 {
flag = false
break
}
}
return flag
}
func copyMap(s map[string]int) map[string]int {
c := map[string]int{}
for k, v := range s {
c[k] = v
}
return c
}