得分最高的单词集合[二进制枚举]

前言

二进制枚举是把每个节点当作二进制的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栈调用开销。

参考资料

[1] LeetCode 得分最高的单词集合

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值