LeetCode30(困难):串联所有单词的子串(Golang实现)

题目描述

给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。

注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。

示例

示例1(基本功能测试)

输入
s = “barfoothefoobarman”,
words = [“foo”,“bar”]
输出:[0,9]
解释
从索引 0 和 9 开始的子串分别是 “barfoor” 和 “foobar” 。
输出的顺序不重要, [9,0] 也是有效答案。

示例2(边界值测试)

输入
s = “wordgoodgoodgoodbestword”,
words = [“word”,“good”,“best”,“word”]
输出:[]

示例3(特殊输入测试)

输入
s = “foobarfoobar”,
words = [“foo”,“bar”]
输出:[0, 3, 6]

示例4(性能测试)

输入
s = “pjzkrkevzztxductzzxmxsvwjkxpvukmfjywwetvfnujhweiybwvvsrfequzkhossmootkmyxgjgfordrpapjuunmqnxxdrqrfgkrsjqbszgiqlcfnrpjlcwdrvbumtotzylshdvccdmsqoadfrpsvnwpizlwszrtyclhgilklydbmfhuywotjmktnwrfvizvnmfvvqfiokkdprznnnjycttprkxpuykhmpchiksyucbmtabiqkisgbhxngmhezrrqvayfsxauampdpxtafniiwfvdufhtwajrbkxtjzqjnfocdhekumttuqwovfjrgulhekcpjszyynadxhnttgmnxkduqmmyhzfnjhducesctufqbumxbamalqudeibljgbspeotkgvddcwgxidaiqcvgwykhbysjzlzfbupkqunuqtraxrlptivshhbihtsigtpipguhbhctcvubnhqipncyxfjebdnjyetnlnvmuxhzsdahkrscewabejifmxombiamxvauuitoltyymsarqcuuoezcbqpdaprxmsrickwpgwpsoplhugbikbkotzrtqkscekkgwjycfnvwfgdzogjzjvpcvixnsqsxacfwndzvrwrycwxrcismdhqapoojegggkocyrdtkzmiekhxoppctytvphjynrhtcvxcobxbcjjivtfjiwmduhzjokkbctweqtigwfhzorjlkpuuliaipbtfldinyetoybvugevwvhhhweejogrghllsouipabfafcxnhukcbtmxzshoyyufjhzadhrelweszbfgwpkzlwxkogyogutscvuhcllphshivnoteztpxsaoaacgxyaztuixhunrowzljqfqrahosheukhahhbiaxqzfmmwcjxountkevsvpbzjnilwpoermxrtlfroqoclexxisrdhvfsindffslyekrzwzqkpeocilatftymodgztjgybtyheqgcpwogdcjlnlesefgvimwbxcbzvaibspdjnrpqtyeilkcspknyylbwndvkffmzuriilxagyerjptbgeqgebiaqnvdubrtxibhvakcyotkfonmseszhczapxdlauexehhaireihxsplgdgmxfvaevrbadbwjbdrkfbbjjkgcztkcbwagtcnrtqryuqixtzhaakjlurnumzyovawrcjiwabuwretmdamfkxrgqgcdgbrdbnugzecbgyxxdqmisaqcyjkqrntxqmdrczxbebemcblftxplafnyoxqimkhcykwamvdsxjezkpgdpvopddptdfbprjustquhlazkjfluxrzopqdstulybnqvyknrchbphcarknnhhovweaqawdyxsqsqahkepluypwrzjegqtdoxfgzdkydeoxvrfhxusrujnmjzqrrlxglcmkiykldbiasnhrjbjekystzilrwkzhontwmehrfsrzfaqrbbxncphbzuuxeteshyrveamjsfiaharkcqxefghgceeixkdgkuboupxnwhnfigpkwnqdvzlydpidcljmflbccarbiegsmweklwngvygbqpescpeichmfidgsjmkvkofvkuehsmkkbocgejoiqcnafvuokelwuqsgkyoekaroptuvekfvmtxtqshcwsztkrzwrpabqrrhnlerxjojemcxel”
words =
[“dhvf”,“sind”,“ffsl”,“yekr”,“zwzq”,“kpeo”,“cila”,“tfty”,“modg”,“ztjg”,“ybty”,“heqg”,“cpwo”,“gdcj”,“lnle”,“sefg”,“vimw”,“bxcb”]
输出:[935]

分析

一开始我想到的算法是:先用递归求出words数组中所有单词组合的全排列,并存入一个数组中,然后在字符串s中进行查找,判断数组中的元素是否是s的子串(可以使用Go自带的strings包中的Contains函数),如果是则记下子串首字符的下标。这种算法的主要复杂度集中于求words数组元素的全排列,当words数组的长度较长时,算法的时间复杂度极高,示例4是肯定过不了的。

本题的关键在于这些单词的长度相同,这是一个很关键的条件,利用此条件,我们可以取words[0]这个字符串,借助它的下标进行遍历,可遍历完words中所有的字符串。我们可以使用两个Map来保存出现的字符串和次数。首先,在外循环中,遍历给定字符串s直到总长度减去单个待查找字符的长度len(s) - len(words) * len(words[0]));然后,在内循环中,按照每个单词的长度len(words[0])对给定字符串s进行切割,分别提取子串s[i + num * length : i + (num + 1) * length],其中i为字符串s的字符索引,num用来维护当前匹配的次数,lengthlen(words[0])。如果words数组中包含该子串,则num加1,继续查询;如果不包含该子串,则跳出内循环,从给定字符串s的下一个字符开始查询。最后,当内循环结束后,判断num是否等于len(words)(因为子串恰好是words数组里的元素的一种组合),如果是,则保存起始下标到结果数组中。

注意:Go语言中Map没有清空函数,清空一个Map的唯一方法是重新定义一个新的Map。

代码

正确答案

本文测试结果均由以下代码运行得到:

package main
import "fmt"

func main() {
	s := "barfoothefoobarman"
	words := []string{"foo","bar"}
	//s := "wordgoodgoodgoodbestword"
	//words := []string{"word","good","best","word"}
	res := findSubstring(s, words)
	fmt.Println(res)
}

func findSubstring(s string, words []string) []int {
	var res []int
	if s == "" || len(s) == 0 || words == nil || len(words) == 0 {
		return res
	}
	// words数组中每个单词字符串长度一致, 取第一个单词
	length := len(words[0])
	// 定义一个wordsMap用于存放words数组中每个单词出现的次数
	wordsMap := make(map[string]int)
	for _, w := range words {
		put(wordsMap, w)
	}
	for i := 0; i < len(s) - len(words) * length + 1; i++ {
		// 定义一个window用于存放匹配的单词出现的次数
		window := make(map[string]int)
		// 定义一个num维护当前匹配的次数
		var num int
		for num < len(words) {
			// 根据查找取子串
			word := s[i + num * length : i + (num + 1) * length]
			_, ok := wordsMap[word]
			if ok {
				put(window, word)
				// window中word出现的次数超过words数组中,则跳出本次循环
				if window[word] > wordsMap[word] {
					break
				}
			} else {
				// 没有查询到, 跳出本次循环, 查找下一个字符
				break
			}
			num++
		}
		if num == len(words) {
			res = append(res, i)
		}
	}
	return res
}

func put(myMap map[string]int, key string) {
	_, ok := myMap[key]
	if ok {
		myMap[key] += 1
	} else {
		myMap[key] = 1
	}
}

超时答案

即分析一节开始给出的解法,该解法无法在规定时间内通过示例4,其余三个示例均能通过。以下给出代码仅供参考。

package main
import (
	"fmt"
	"strings"
	"sort"
)

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

func findSubstring(s string, words []string) []int {
	var str []string
	var res []int
	permutation(words, 0, &str)
	for _, r := range str {
		res = append(res, index(s, r)...)
	}
	sort.Ints(res)
	return res
}

func permutation(words []string, begin int, pStr *[]string) {
	if words == nil || len(words) == 0 || begin < 0 || begin > len(words) - 1 {
		return
	}
	if begin == len(words) - 1 {
		strNew := strings.Join(words, "")
		if !contains(*pStr, strNew) {
			*pStr = append(*pStr, strNew)
		}
	} else {
		for i := 0; i < len(words); i++ {
			words[begin], words[i] = words[i], words[begin]
			permutation(words, begin + 1, pStr)
			words[begin], words[i] = words[i], words[begin]
		}
	}
}

func contains(str []string, s string) bool{
	for _, r := range str {
		if s == r {
			return true
		}
	}
	return false
}

func index(s1, s2 string) []int{
	var indexes []int
	if strings.Contains(s1, s2) {
		for i := 0; i <= len(s1) - len(s2); i++ {
			if s1[i: i + len(s2)] == s2 {
				indexes = append(indexes, i)
			}
		}
	}
	return indexes
}

运行结果

示例1(基本功能测试)
fig1
示例2(边界值测试)
fig2
示例3(特殊输入测试)
fig3
示例4(性能测试)
fig4

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值