30. 串联所有单词的子串

69 阅读1分钟

题目:
leetcode.cn/problems/su…

算法:

方法一:暴力hash
时间复杂度O(len(s) * subStringLength),这种朴素的想法,怎么的都先试一下,然后再进行优化

image.png

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
}