【11.8力扣打卡】(力扣1255)得分最高的单词集合(DFS)

5 篇文章 0 订阅

题目

你将会得到一份单词表 words,一个字母表 letters (可能会有重复字母),以及每个字母对应的得分情况表 score。

请你帮忙计算玩家在单词拼写游戏中所能获得的「最高得分」:能够由 letters 里的字母拼写出的 任意 属于 words 单词子集中,分数最高的单词集合的得分。

单词拼写游戏的规则概述如下:

玩家需要用字母表 letters 里的字母来拼写单词表 words 中的单词。
可以只使用字母表 letters 中的部分字母,但是每个字母最多被使用一次。
单词表 words 中每个单词只能计分(使用)一次。
根据字母得分情况表score,字母 ‘a’, ‘b’, ‘c’, … , ‘z’ 对应的得分分别为 score[0], score[1], …, score[25]。
本场游戏的「得分」是指:玩家所拼写出的单词集合里包含的所有字母的得分之和。

示例1

输入:words = [“dog”,“cat”,“dad”,“good”], letters = [“a”,“a”,“c”,“d”,“d”,“d”,“g”,“o”,“o”], score = [1,0,9,5,0,0,3,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0]
输出:23
解释:
字母得分为 a=1, c=9, d=5, g=3, o=2
使用给定的字母表 letters,我们可以拼写单词 “dad” (5+1+5)和 “good” (3+2+2+5),得分为 23 。
而单词 “dad” 和 “dog” 只能得到 21 分。

示例2

输入:words = [“xxxz”,“ax”,“bx”,“cx”], letters = [“z”,“a”,“b”,“c”,“x”,“x”,“x”], score = [4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,10]
输出:27
解释:
字母得分为 a=4, b=4, c=4, x=5, z=10
使用给定的字母表 letters,我们可以组成单词 “ax” (4+5), “bx” (4+5) 和 “cx” (4+5) ,总得分为 27 。
单词 “xxxz” 的得分仅为 25 。

示例3

输入:words = [“leetcode”], letters = [“l”,“e”,“t”,“c”,“o”,“d”], score = [0,0,1,1,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0]
输出:0
解释:
字母 “e” 在字母表 letters 中只出现了一次,所以无法组成单词表 words 中的单词。

提示

1 <= words.length <= 14
1 <= words[i].length <= 15
1 <= letters.length <= 100
letters[i].length == 1
score.length == 26
0 <= score[i] <= 10
words[i] 和 letters[i] 只包含小写的英文字母。

代码

func maxScoreWords(words []string, letters []byte, score []int) int {
	var res int
	var wordsQuanju []string
	var scoreQuanju []int
	//全局变量
	wordsQuanju=words
	scoreQuanju=score
	//1.先统计字母数量
	charNumber:=make([]int,len(score))

	for _,value:=range letters{
		if charNumber[value-'a']!=0{
			charNumber[value-'a']++
		}else{
			charNumber[value-'a']=1
		}
	}
	dfs(0,0,charNumber,wordsQuanju,scoreQuanju,&res)
	return res
}
func dfs(index int,number int,charNumber []int,wordsQuanju []string,scoreQuanju []int,res *int){
	if index>=len(wordsQuanju){//递归下标超过word切片里面的个数,递归出口
		return
	}
	//遍历每个字母
	var charNumbertmp []int
	for _,value:=range charNumber{
		charNumbertmp=append(charNumbertmp,value)
	}
	//charNumbertmp=charNumber//先复制一份完整的没遍历过的单词表
	//fmt.Println(charNumbertmp)
	sumtmp:=0
	var j int
	for j=0;j<len(wordsQuanju[index]);j++{
		if charNumber[wordsQuanju[index][j]-'a']>0{//说明还有字母没被用
			sumtmp=sumtmp+scoreQuanju[wordsQuanju[index][j]-'a']//计算sumtmp,后面取max
			charNumber[wordsQuanju[index][j]-'a']--//使用之后个数-1
		}else{
			dfs(index+1,number,charNumbertmp,wordsQuanju,scoreQuanju,res)//放入一个没遍历过的单词表
			return //退出
		}
	}

	if *res<number+sumtmp{//number+sumtmp为这次递归的总和,看是否有比最大的大
		*res=number+sumtmp
	}
	dfs(index+1,number,charNumbertmp,wordsQuanju,scoreQuanju,res)
	dfs(index+1,number+sumtmp,charNumber,wordsQuanju,scoreQuanju,res)

}

解题思路

  1. 首先这里dfs函数的参数较多,这是因为测试用例是不可以用全局变量,其他语言不知道,至少我用Go语言的全局测试用例它就会报错
  2. wordsQuanju是保存单词的切片,scoreQuanju是保存成绩的切片,不需要保存具体的修改内容,因此不需要传递切片指针,而res是我们需要内容,因此需要传递一个整型指针,这样修改的内容外部才能得到
  3. 解释完了一些参数之后,可以开始具体的代码思路了,首先就是要设置一个map,用这个map来统计letters中单词的个数,因为题目说了一个单词只能用一次,必须考虑到使用map,map具有很多优良的特性,如保存唯一key,统计使用个数等,这里用map,当他的value=0时,说明用完了,这就可以去下一个单词了
  4. 每次遍历words中的一个单词,index就是单词的下标,去map下寻找出现的字母是否还有,两种情况,有就计算分数(map要对应减掉使用的单词),没有就index+1递归(使用原先没有减掉的map)。
  5. 这里有一个深坑!要使用原先没有减掉的map,进行循环遍历之前肯定要复制一份,重点来了。***Go语言中所有的传参都是值传递(传值),都是一个副本,一个拷贝。因为拷贝的内容有时候是非引用类型(int、string、struct等这些),这样就在函数中就无法修改原内容数据;有的是引用类型(指针、map、slice、chan等这些),这样就可以修改原内容数据。可以看到charNumber是一个切片,他是一种引用传递,因此如果直接赋值给charNumbertmp,那么两者就会保持引用关系,也就是说charNumber改变charNumbertmp也会随之改变,这样我们递归是不是就失去了意义?因为我们递归要传递的就是原先没有改变的切片,如果一直在变,那么就没办法保证能到达原先的状态。
  6. 说完了最坑的地方,接下来就是计算我们要的结果res,这个res是一个指针,因此我们只要保证每次dfs的时候它都找判断最大值就行了,因为是指针嘛,你改了其他地方都会改了。
  7. 之后就是一个背包问题了,为了取得所有的可能,我们index+1之后,考虑放入或者不放入,放入则使用改变后的charNumber,不放入则使用原先的charNumbertmp,这样就可以保证每种可能都遍历到,且res是指针,那么我们的结果肯定是所有可能中的最大值了,这就是我们要的结果了。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值