【周赛总结】优先队列,字典序计数+组合数,最长公共子串dp

本文总结了四道算法题目,涉及优先队列解决最远建筑问题、组合数计数、最长公共子串的动态规划及构造目标字符串的方案数。通过优先队列解决贪心问题,理解组合数计算,以及使用动态规划优化复杂度。
摘要由CSDN通过智能技术生成

1642. 可以到达的最远建筑(优先队列)

在这里插入图片描述在这里插入图片描述

最开始想到的方法是贪心,寻找合适的位置用梯子。但是这样会被hack的,没有想到使用优先队列的方法。但是想到了的话还是很自然的,维护最大的N个代价即可。

class Solution {
    public int furthestBuilding(int[] heights, int bricks, int ladders) {
        int n = heights.length, sum = 0;
        Queue<Integer> queue = new PriorityQueue<>();
        for(int i = 1; i < heights.length; i++) {
            int diff = heights[i] - heights[i - 1];
            if(diff > 0) {
                queue.offer(diff);
                if(queue.size() > ladders) {
                    sum += queue.poll();
                }
                if(sum > bricks)
                    return i - 1;
            }
        }
        return n - 1;
    }
}

1643. 第 K 条最小指令H(组合数计数)

在这里插入图片描述
在这里插入图片描述

是一道利用通过组合数问题比较好的练习题。也是记住组合数的模板,C[i][j]表示 C i j C_i^j Cij.

class Solution {
    int[][] C;
    public String kthSmallestPath(int[] d, int k) {
        int v = d[0];
        int h = d[1];
        C = new int[h+v+1][h+v+1];
        make(h+v);
        char[] ans = new char[h+v];
        int sum = h+v;
        for (int i = 0; i<sum;i++){
            if (C[h+v-1][v]>=k){// 注意这里的关键是等于,此时也是写H,不清楚就自己写一下。
                ans[i] = 'H';
                h--;
            }else{
                ans[i] = 'V';
                k -= C[h+v-1][v];
                v--;
            }
        }
        return String.valueOf(ans);

    }
    private void make(int n){
        for (int i = 0; i<=n;i++){
            for (int j = 0; j<=i;j++){
                if (j == 0) C[i][j] = 1;
                else C[i][j] = (C[i-1][j]+C[i-1][j-1]);
            }
        }
    }
}

1638. 统计只差一个字符的子串数目(最长公共子串DP)

在这里插入图片描述

比较暴力的方法是 n 4 n^4 n4的,依次枚举长度,s串的起始位置,t串的起始位置和匹配位置。比较聪明的方法是类似经典DP问题,最长公共子串的。dp[i][j] 表示s结尾i,t结尾j的情况下,一个不同的字符的数目。类似的我们还需要维护一个完全相同的字串的个数。

// dp方法,类似与字符串匹配
class Solution {
    public int countSubstrings(String s, String t) {
        int slength = s.length();
        int tlength = t.length();
        int[][] dp = new int[slength][tlength]; 
        int[][] dpEqual = new int[slength][tlength];// dp[i][j] 表示s结尾i,t结尾j的情况下,完全的数目
        int sum = 0;
        for(int i=0;i<slength;i++){
            for(int j=0;j<tlength;j++){
                if(i==0||j==0){// 初始
                    if(s.charAt(i)==t.charAt(j)){
                        dp[i][j]=0;
                        dpEqual[i][j]=1;
                    }else if(s.charAt(i)!=t.charAt(j)){
                        dp[i][j]=1;
                        dpEqual[i][j]=0;
                    }
                }else{
                    if(s.charAt(i)==t.charAt(j)){
                        dp[i][j]=dp[i-1][j-1]; // 这里是转移,继续眼神
                        dpEqual[i][j]=dpEqual[i-1][j-1]+1;// 维护最长的相同
                    }else if(s.charAt(i)!=t.charAt(j)){
                        dp[i][j]=dpEqual[i-1][j-1]+1; //之前最长的,加上自己
                        dpEqual[i][j]=0;
                    }
                }
                sum=sum+dp[i][j];
            }
        }
        return sum;
    }
}

1639. 通过给定词典构造目标字符串的方案数H(Dp)

在这里插入图片描述

在这里插入图片描述

是一道典型的DP问题,但是这个DP的设计决定了复杂度。比较好的设计思路是dp[i][j]第i位用前j个字符,转移方程dp[i][j] = dp[i-1][j-1]*maplen+dp[i][j-1]);这样复杂度是 n 2 n^2 n2。也可以设计为dp[i][j]表示target第i位用第j个字符,转移方程为dp[i][j] =dp[i-1][k]*maplen+dp[i][j],需要额外枚举k,表示第i-1位使用哪一个字符,这样的复杂度是 n 3 n^3 n3

//dp[i][j]第i位用前j个
class Solution {
    public int numWays(String[] words, String target) {
        int n = words.length;
        int m = words[0].length();
        int[][] map = new int[m][26];
        // 设计了查表,降低了复杂度
        for (int i = 0; i<n;i++){
            for (int j = 0; j<m;j++){
                map[j][(int)(words[i].charAt(j)-'a')]++;
            }
        }
        
        int Mod = 1000000007;
        int len = target.length();
        int[][] dp = new int[len][m];
        dp[0][0] = map[0][target.charAt(0)-'a'];
        for (int i = 1; i<m;i++){
            dp[0][i] = (int)((1L*dp[0][i-1] + map[i][target.charAt(0)-'a'])%Mod);
        }
        for (int i = 1;i<len;i++){
            for (int j = i;j<m;j++){ // 剪枝
                int maplen = map[j][(int)target.charAt(i)-'a'];
                dp[i][j] = (int)((1L*dp[i-1][j-1]*maplen+1L*dp[i][j-1])%Mod);                 
            }
        }
        return dp[len-1][m-1];
    }
}
=======================================================================================
// dp[i][j] 表示target第i位用第j个字符
class Solution {
    public int numWays(String[] words, String target) {
        int n = words.length;
        int m = words[0].length();
        int[][] map = new int[m][26];
        for (int i = 0; i<n;i++){
            for (int j = 0; j<m;j++){
                map[j][(int)(words[i].charAt(j)-'a')]++;
            }
        }
        
        int Mod = 1000000007;
        int len = target.length();
        int[][] dp = new int[len][m];
        

        for (int i = 0;i<len;i++){
            for (int j = i;j<=m-len+i;j++){ // 剪枝
                int maplen = map[j][(int)target.charAt(i)-'a'];
                if (maplen==0) continue;
                if (i == 0){
                    dp[i][j] = maplen;
                    continue;
                }
                for (int k = i-1; k<j;k++){
                    dp[i][j] = (int)((1L*dp[i-1][k]*maplen+1L*dp[i][j])%Mod);
                }                    
            }
        }
        int ans =0;
        for (int i = 0; i<m;i++){
            ans = (int)((1L*ans+dp[len-1][i])%Mod);
        }
        return ans;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值