2024-01-17:lc的30. 串联所有单词的子串
2024-01-17:用go語言,給定一個字符串 s 和一個字符串數組 words。 words 中所有字符串 長度相同。
s 中的 串聯子串 是指一個包含 words 中所有字符串以任意順序排列連接起來的子串。
例如,如果 words = ["ab","cd","ef"],
那么 "abcdef", "abefcd","cdabef",
"cdefab","efabcd", 和 "efcdab" 都是串聯子串,
"acdbef" 不是串聯子串,因為他不是任何 words 排列的連接。
返回所有串聯字串在 s 中的開始索引。
你可以以 任意順序 返回答案。
1 <= s.length <= 10^4,
1 <= words.length <= 5000,
1 <= words[i].length <= 30。
words[i] 和 s 由小寫英文字母組成。
輸入:s = "barfoothefoobarman", words = ["foo","bar"]。
輸出:[0,9]。
來自lc的30. 串聯所有單詞的子串。
答案2024-01-17:
來自左程云。
靈捷3.5
大體過程如下:
-
定義一些常量和變量,包括
BASE和MAXN,以及存儲結果的切片ans。 -
實現
hashValue函數,用于計算字符串的哈希值。這里使用一個基于索引的簡單哈希函數將字符串映射為一個唯一的整數。 -
實現
buildHash函數,用于構建字符串的前綴哈希數組。通過動態規劃的方式計算每個位置的哈希值。 -
實現
hashValueRange函數,用于計算子串的哈希值。利用前綴哈希數組,根據子串的起始和結束位置計算哈希值。 -
創建一個哈希表
mapCount用于存儲words中每個單詞的出現次數。 -
構建字符串
s的前綴哈希數組hash。 -
創建一個數組
pow,用于存儲 BASE 的冪次方,便于后續計算子串的哈希值。 -
創建一個滑動窗口
window,用于記錄當前窗口中每個單詞出現的次數。 -
循環遍歷
s中每個起始位置的可能性(即從 0 到wordLen-1)。 -
在每個起始位置,初始化一個變量
debt用于記錄還需要湊齊的單詞數。 -
在每個起始位置,遍歷
words中的單詞,依次將其添加到窗口中,并更新debt的值。 -
如果
debt等于 0,表示窗口中已經包含了所有words中的單詞,則將當前起始位置加入結果數組ans中。 -
對于每個起始位置,向右移動窗口,同時更新窗口中單詞的出現次數。
-
檢查窗口中的哈希值和單詞出現次數是否符合要求,如果符合則將當前起始位置加入結果數組
ans中。 -
清空滑動窗口
window。 -
返回結果數組
ans。
總的時間復雜度:O(n * m * k),其中 n 是字符串 s 的長度,m 是 words 的長度,k 是單詞的平均長度。
總的額外空間復雜度:O(n),其中 n 是字符串 s 的長度,主要用于存儲哈希表、前綴哈希數組和結果數組。
go完整代碼如下:
package main
import (
"fmt"
)
var BASE = 499
var MAXN = 10001
func hashValue(str string) int64 {
if str == "" {
return 0
}
n := len(str)
ans := int64(str[0]-'a') + 1
for j := 1; j < n; j++ {
ans = ans*int64(BASE) + int64(str[j]-'a') + 1
}
return ans
}
func buildHash(str string) []int64 {
hash := make([]int64, len(str))
hash[0] = int64(str[0]-'a') + 1
for j := 1; j < len(str); j++ {
hash[j] = hash[j-1]*int64(BASE) + int64(str[j]-'a') + 1
}
return hash
}
func hashValueRange(l, r int, hash []int64, pow []int64) int64 {
ans := hash[r-1]
if l > 0 {
ans -= hash[l-1] * pow[r-l]
}
return ans
}
func findSubstring(s string, words []string) []int {
var ans []int
if len(s) == 0 || len(words) == 0 {
return ans
}
wordLen := len(words[0])
wordNum := len(words)
allLen := wordLen * wordNum
mapCount := make(map[int64]int)
for _, key := range words {
v := hashValue(key)
mapCount[v]++
}
hash := buildHash(s)
pow := make([]int64, MAXN)
pow[0] = 1
for j := 1; j < MAXN; j++ {
pow[j] = pow[j-1] * int64(BASE)
}
window := make(map[int64]int)
for init := 0; init < wordLen && init+allLen <= len(s); init++ {
debt := wordNum
for l, r, part := init, init+wordLen, 0; part < wordNum; l += wordLen {
cur := hashValueRange(l, r, hash, pow)
window[cur]++
if window[cur] <= mapCount[cur] {
debt--
}
r += wordLen
part++
}
if debt == 0 {
ans = append(ans, init)
}
for l1, r1, l2, r2 := init, init+wordLen, init+allLen, init+allLen+wordLen; r2 <= len(s); l1, r1, l2, r2 = l1+wordLen, r1+wordLen, l2+wordLen, r2+wordLen {
out := hashValueRange(l1, r1, hash, pow)
in := hashValueRange(l2, r2, hash, pow)
window[out]--
if window[out] < mapCount[out] {
debt++
}
window[in]++
if window[in] <= mapCount[in] {
debt--
}
if debt == 0 {
ans = append(ans, r1)
}
}
for key := range window {
delete(window, key)
}
}
return ans
}
func main() {
s := "barfoothefoobarman"
words := []string{"foo", "bar"}
result := findSubstring(s, words)
fmt.Println(result)
}
總結
以上是生活随笔為你收集整理的2024-01-17:lc的30. 串联所有单词的子串的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: lucene实现全文检索(四):分词器
- 下一篇: Okhttp请求工具类