LeetCode 944. 删列造序 / 面试题 01.05. 一次编辑 / 691. 贴纸拼词(记忆化搜索+状压dp)

944. 删列造序

2022.5.12 每日一题

题目描述

给你由 n 个小写字母字符串组成的数组 strs,其中每个字符串长度相等。

这些字符串可以每个一行,排成一个网格。例如,strs = [“abc”, “bce”, “cae”] 可以排列为:

abc
bce
cae

你需要找出并删除 不是按字典序升序排列的 列。在上面的例子(下标从 0 开始)中,列 0(‘a’, ‘b’, ‘c’)和列 2(‘c’, ‘e’, ‘e’)都是按升序排列的,而列 1(‘b’, ‘c’, ‘a’)不是,所以要删除列 1 。

返回你需要删除的列数。

示例 1:

输入:strs = [“cba”,“daf”,“ghi”]
输出:1
解释:网格示意如下:
cba
daf
ghi
列 0 和列 2 按升序排列,但列 1 不是,所以只需要删除列 1 。

示例 2:

输入:strs = [“a”,“b”]
输出:0
解释:网格示意如下:
a
b
只有列 0 这一列,且已经按升序排列,所以不用删除任何列。

示例 3:

输入:strs = [“zyx”,“wvu”,“tsr”]
输出:3
解释:网格示意如下:
zyx
wvu
tsr
所有 3 列都是非升序排列的,所以都要删除。

提示:

n == strs.length
1 <= n <= 100
1 <= strs[i].length <= 1000
strs[i] 由小写英文字母组成

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/delete-columns-to-make-sorted
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

简单模拟

class Solution {
    public int minDeletionSize(String[] strs) {
        //直接按列遍历判断就行了
        int n = strs.length;
        int l = strs[0].length();
        int del = 0;
        for(int j = 0; j < l; j++){
            char pre = strs[0].charAt(j);
            for(int i = 1; i < n; i++){
                if(strs[i].charAt(j) < pre){
                    del++;
                    break;
                }
                pre = strs[i].charAt(j);
            }
        }
        return del;
    }
}

面试题 01.05. 一次编辑

2022.5.13 每日一题

题目描述

字符串有三种编辑操作:插入一个字符、删除一个字符或者替换一个字符。 给定两个字符串,编写一个函数判定它们是否只需要一次(或者零次)编辑。

示例 1:

输入:
first = “pale”
second = “ple”
输出: True

示例 2:

输入:
first = “pales”
second = “pal”
输出: False

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/one-away-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

我的思路,从左到右遍历找第一个不匹配的字符,从右到左再找一遍,如果两个找到的位置相同,那么说明可以一次编辑,否则不行
好像题解都是双指针,从一个方向找的

class Solution {
    public boolean oneEditAway(String first, String second) {
        //先看长度,如果差一个就是添加或者删除,如果长度相同就是修改
        int l1 = first.length();
        int l2 = second.length();
        if(Math.abs(l1- l2) > 1)
            return false;
        
        //保证first的长度是长的
        if(l2 > l1){
            String temp = first;
            first = second;
            second = temp;
            int t = l1;
            l1 = l2;
            l2 = t;
        }

        int left = 0;
        int right = l1 - 1;

        //从左到右找第一个不匹配的字符
        while(left < l1 && left < l2 && first.charAt(left) == second.charAt(left)){
            left++;
        }
        //从右到左找第一个不匹配的字符
        while(right >= 0 && l2 - (l1 - right) >= 0 && first.charAt(right) == second.charAt(l2 - (l1 - right))){
            right--;
        }
        //如果左边找到的下标大于等于右边找到的下标,说明仅有一个不同或者都相同
        if(left >= right)
            return true;
        else
            return false;
    }
}

691. 贴纸拼词

2022.5.14 每日一题

题目描述

我们有 n 种不同的贴纸。每个贴纸上都有一个小写的英文单词。

您想要拼写出给定的字符串 target ,方法是从收集的贴纸中切割单个字母并重新排列它们。如果你愿意,你可以多次使用每个贴纸,每个贴纸的数量是无限的。

返回你需要拼出 target 的最小贴纸数量。如果任务不可能,则返回 -1 。

注意:在所有的测试用例中,所有的单词都是从 1000 个最常见的美国英语单词中随机选择的,并且 target 被选择为两个随机单词的连接。

示例 1:

输入: stickers = [“with”,“example”,“science”], target = “thehat”
输出:3
解释:
我们可以使用 2 个 “with” 贴纸,和 1 个 “example” 贴纸。
把贴纸上的字母剪下来并重新排列后,就可以形成目标 “thehat“ 了。
此外,这是形成目标字符串所需的最小贴纸数量。

示例 2:

输入:stickers = [“notice”,“possible”], target = “basicbasic”
输出:-1
解释:我们不能通过剪切给定贴纸的字母来形成目标“basicbasic”。

提示:

n == stickers.length
1 <= n <= 50
1 <= stickers[i].length <= 10
1 <= target <= 15
stickers[i] 和 target 由小写英文单词组成

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/stickers-to-spell-word
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

第一时间看到这个题感觉是动态规划,但是想到的是统计target中每一个字符的数量,用一个数组统计,然后dfs看达到每个状态,状态也就是这个数组,需要的最少单词。然后发现统计这样的状态还需要写个类,重写equals,非常麻烦,就感觉不对
然后看题解才发现是状态压缩,因为target长度最长也就是15,还没有26个字母长,所以直接状态压缩,统计每个状态所需要的最少单词来进行动态规划。
遍历stickers中所有单词,如果这个单词中有target中出现的单词,那么就将状态改变,继续递归
这样一个记忆化搜索加状压dp就完成了
应该能自己做出来的。。

class Solution {
    public int minStickers(String[] stickers, String target) {
        //感觉像是完全背包问题,即每个物品可以用多次

        //递归的去遍历所有单词,然后从target中去除存在的字母
        //去除的时候可以选一次这个单词也可以选多次

        int l = target.length();
        //memo表示达到这个状态最少需要几个单词
        int[] memo = new int[1 << l];
        Arrays.fill(memo, -1);
        memo[0] = 0;
        int res = dfs(target, stickers, memo, (1 << l) - 1);
        return res <= l ? res : -1;

    }

    public int dfs(String target, String[] stickers, int[] memo, int mask){
        int l = target.length();
        //如果当前状态是没有被赋值的,那么进行处理
        if(memo[mask] < 0){
            //先将res赋予不可能的最大值(最大值就是每个字符都由一个单词贡献,即 l)
            int res = l + 1;
            //遍历所有单词,看哪个构成最小
            for(String s : stickers){
                int init = mask;
                int[] count = new int[26];
                for(char c : s.toCharArray()){
                    count[c - 'a']++;
                }
                //查看这个单词在target中有几个字符出现,将这些字符从mask中去除
                for(int i = 0; i < l; i++){
                    char c = target.charAt(i);
                    if(((mask >> i) & 1) == 1 && count[c - 'a'] > 0){
                        count[c - 'a']--;
                        init ^= 1 << i; 
                    }
                }
                //如果这个单词能构成target,那么继续递归
                if(init < mask){
                    res = Math.min(res, dfs(target, stickers, memo, init) + 1);
                }
            }
            memo[mask] = res;
        }
        return memo[mask];
    }
}

你可能感兴趣的:(LeetCode,leetcode,java)