前言
二进制枚举是把每个节点当作二进制的1bit,0表示未选择,1表示选择,就像是寻找子集一样,这样就可以减少DFS调用栈的开销。
一、得分最高的单词集合
二、字符串组合
1、DFS
class Solution {
public int maxScoreWords(String[] words, char[] letters, int[] score) {
List<String> arr = new ArrayList<>();
for(String word : words) arr.add(word);
arr.sort((p,q)->q.length()- p.length());
// 记录字符组数
int[] cnt = new int[26];
for(char c : letters) cnt[c - 97]++;
// dfs
dfs(arr,0,0,cnt,score);
return max;
}
int max = 0;
public void dfs(List<String> arr,int i,int all,int[] cnt,int[] score){
if(i == arr.size()){
max = Math.max(max,all);
return;
}
for(int j = i;j < arr.size();j++){
if(isOk(arr.get(j),cnt)){
dfs(arr,j + 1,all + updateCnt(arr.get(j),cnt,score),cnt,score);
// 回溯,信息复位。
setCnt(arr.get(j),cnt);
}else {
max = Math.max(max,all);
}
}
}
public void setCnt(String str,int[] cnt){
for(int i = 0;i < str.length();i++) cnt[str.charAt(i) - 97]++;
}
public int updateCnt(String str,int[] cnt,int[] score){
int sum = 0;
for(int i = 0;i < str.length();i++) {
sum += score[str.charAt(i) - 97];
cnt[str.charAt(i) - 97]--;
}
return sum;
}
public boolean isOk(String str,int[] cnt){
int[] count = new int[26];
for(int i = 0;i < str.length();i++) count[str.charAt(i) - 97]++;
for(int i = 0;i < 26;i++) if(count[i] > cnt[i]) return false;
return true;
}
}
2、二进制枚举
// 只有14个单词,进行二进制枚举。
// 枚举之前,可以先预处理每个words的cnt以及分数,预处理letters的字符分组统计。
func maxScoreWords(words []string, letters []byte, score []int) int {
wordsCnt,wordsScore := processWords(words,score)
lettersCnt := processLetters(letters)
// 二进制枚举
m := 0
for mask := 0;mask < 1 << len(words);mask++ {
curCnt,curSum := [26]int{},0
for i := 0;i < len(words);i++ {
if (1 << i) & mask == 0 {
continue
}
// 合计该mask下的所有word的cnt情况。
for j := 0;j < 26;j++ {
curCnt[j] += wordsCnt[i][j]
}
curSum += wordsScore[i]
}
// 是否合法
if isOk(curCnt,lettersCnt) {
m = max(m,curSum)
}
}
return m
}
func isOk(curCnt [26]int,lettersCnt [26]int) bool {
for i := 0;i < 26;i++ {
if curCnt[i] > lettersCnt[i] {
return false
}
}
return true
}
func processLetters(letters []byte) [26]int {
lettersCnt := [26]int{}
for _,l := range letters {
lettersCnt[l - 97]++
}
return lettersCnt
}
func processWords(words []string,score []int)([][26]int,[]int){
wordsCnt,wordsScore := make([][26]int,len(words)),make([]int,len(words))
for i,w := range words {
for j := 0;j < len(w);j++ {
wordsCnt[i][w[j] - 97]++
wordsScore[i] += score[w[j] - 97]
}
}
return wordsCnt,wordsScore
}
func max(x,y int) int {
if x > y {
return x
}
return y
}
// 只有14个单词,进行二进制枚举。
// 枚举之前,可以先预处理每个words的cnt以及分数,预处理letters的字符分组统计。
总结
1)将字符串转换成一位二进制,从而将DFS转换为二进制枚举,从而减少DFS栈调用开销。