算法:
方法一:暴力hash
时间复杂度O(len(s) * subStringLength),这种朴素的想法,怎么的都先试一下,然后再进行优化
func findSubstring(s string, words []string) []int {
wordCount := make(map[string]int)
for i := range words {
wordCount[words[i]] ++
}
n := len(words)
wordLen := len(words[0])
subStringLength := n * wordLen
ans := make([]int, 0)
for i := 0; i + subStringLength <= len(s); i ++ {
cur := make(map[string]int)
for j := i; j + wordLen <= i + subStringLength; j = j + wordLen {
str := s[j:j + wordLen]
cur[str] ++
if cur[str] > wordCount[str] {
goto NEXT
}
}
if equal(cur, wordCount) {
ans = append(ans, i)
}
NEXT:
}
return ans
}
func equal(m1, m2 map[string]int) bool {
// fmt.Println(m1,m2)
if len(m1) != len(m2) {
return false
}
for k, v := range m1 {
if v != m2[k] {
return false
}
}
for k, v := range m2 {
if v != m1[k] {
return false
}
}
return true
}
方法二:双指针
暴力中的内部的for循环进行了多次重复计算,我们能不能想办法制造一个窗口,进来word长度则wordCount[word] ++,移除一个word则wordCount[word] --,比较wordCount和cur两个map是否相等。
func findSubstring(s string, words []string) []int {
wordCount := make(map[string]int)
for i := range words {
wordCount[words[i]] ++
}
n := len(words)
wordLen := len(words[0])
subStringLength := n * wordLen
ans := make([]int, 0)
// 起步遍历wordLen长度就可以了,内部的循环会遍历到s最后
for i := 0; i < wordLen; i ++ {
cur := make(map[string]int)
for j := i; j + wordLen <= len(s); j = j + wordLen {
// 进来一个word,次数加一
str := s[j: j + wordLen]
cur[str] ++
// 长度超过窗口了,窗口左边的移除一个word,更新cur word计数
if j >= subStringLength {
preStr := s[j - subStringLength : j - subStringLength + wordLen]
if cur[preStr] == 1 {
delete(cur, preStr)
} else {
cur[preStr] --
}
}
if equal(cur, wordCount) {
// 移入最后一个字符串s[j: j + wordLen]后,两个map相等了
ans = append(ans, j - (n - 1) * wordLen)
}
}
}
return ans
}
func equal(m1, m2 map[string]int) bool {
// fmt.Println(m1,m2)
if len(m1) != len(m2) {
return false
}
for k, v := range m1 {
if v != m2[k] {
return false
}
}
for k, v := range m2 {
if v != m1[k] {
return false
}
}
return true
}