package main

func findSubstring(s string, words []string) []int {
	indices := []int{}
	strLen, wordCnt := len(s), len(words)
	if strLen == 0 || wordCnt == 0 {
		return indices
	}
	dict := make(map[string]int) // May contains duplicate words!
	for _, word := range words {
		dict[word]++
	}
	wordLen := len(words[0])
	for i := 0; i < wordLen; i++ {
		left, count := i, 0
		occur := make(map[string]int)
		for j := i; j <= strLen-wordLen; j += wordLen {
			word := s[j : j+wordLen]
			// If word is valid, accumulate results
			if _, ok := dict[word]; ok {
				occur[word]++
				if occur[word] <= dict[word] {
					count++
				} else {
					// One more word occur, shift the left side of window
					for occur[word] > dict[word] {
						leftWord := s[left : left+wordLen]
						occur[leftWord]--
						if occur[leftWord] < dict[leftWord] {
							count--
						}
						left += wordLen
					}
				}
				// Come to a result
				if count == wordCnt {
					indices = append(indices, left)
					// Advance left side by one word
					occur[s[left:left+wordLen]]--
					count--
					left += wordLen
				}
			} else {
				// Not in word dict, reset all
				occur = make(map[string]int)
				count = 0
				left = j + wordLen
			}
		}
	}
	return indices
}

// func main() {
// 	fmt.Println(findSubstring(
// 		"wordgoodgoodgoodbestword",
// 		[]string{"word", "good", "best", "good"}))
// 	fmt.Println(findSubstring(
// 		"foobarfoobar",
// 		[]string{"foo", "bar"}))
// }