522. 最长特殊序列 II / 剑指 Offer II 101. 分割等和子集

522. 最长特殊序列 II【中等题】【每日一题】

思路:【枚举】

过程写在代码注释里,参考自官方题解。但我的执行耗时是2ms~

代码:

class Solution {
    public int findLUSlength(String[] strs) {
        /**
         * 对字符串数组中某一字符串 strs[i] 来说 如果它的一个子序列 sub 是特殊序列,即不为数组中其他字符串的子序列
         * 那么这个字符串 strs[i]也是一个特殊序列
         * 证明:反证法
         *      假设 sub 为特殊序列,那么 sub 在其他字符串中均不会以子序列形式出现
         *      那么给 sub 添加任意个字符,它依然不会以子序列形式出现在其他字符串中
         * 由于题目要求寻找最长特殊序列,因此我们只需枚举数组中每个字符串,筛选出长度最长的符合特殊序列的字符串,即为我们要寻找的最长特殊序列
         * 于是现在问题简化为两步:
         *      1.遍历每个字符串,判断其是否特殊序列
         *      2.如果字符串满足特殊序列要求,更新最长特殊序列长度 max,函数最后返回 max
         * 对于第1步来说,可以转化为,判断当前字符串是否是数组中除自己之外的其他字符串的子序列,是 则说明当前字符串是特殊序列,否 则说明不是特殊序列
         */
        int max = -1,n = strs.length;
        //双循环遍历字符串数组,筛选strs[i]是否是特殊序列
        for (int i = 0; i < n; i++) {
            boolean flag = true;
            for (int j = 0; j < n; j++) {
                if (i == j){
                    continue;
                }
                String s1 = strs[i],s2 = strs[j];
                int n1 = s1.length(),n2 = s2.length();
                //s1的长度比s2还长,s1不可能是s2的子序列,不用接着判断了
                if (n1 > n2){
                    continue;
                }else if (n1 == n2){
                    //s1的长度与s2相等
                    //如果s1与s2不等,则s1不是s2的子序列
                    //如果s1与s2相等,则s1是s2的子序列,标志位置为flase,内层for循环退出
                    if (s1.equals(s2)){
                        flag = false;
                        break;
                    }else {
                        continue;
                    }
                }
                //判断s1是否是s2的子序列(此时 n1 < n2)
                int p1 = 0,p2 = 0;
                while (p1 < n1 && p2 < n2){
                    //如果p1字符与p2字符相等,则p1与p2均右移
                    if (s1.charAt(p1) == s2.charAt(p2)){
                        p1++;
                        p2++;
                    }else {
                        //否则,只将p2右移
                        p2++;
                    }
                }
                //如果s1正常遍历结束,说明s1是s2的子序列,标志位置为false,内层for循环退出
                if (p1 == n1){
                    flag = false;
                    break;
                }
            }
            //经过筛选,strs[i]经受住了考验,是特殊序列,则更新最长特殊序列的长度
            if (flag){
                max = Math.max(max,strs[i].length());
            }
        }
        return max;
    }
}

剑指 Offer II 101. 分割等和子集【简单题】

思路:【动态规划】

一阶dp和二阶dp,均看答案写~,这是个动态规划题我是真没想到。

代码:【二阶dp】

class Solution {
    public boolean canPartition(int[] nums) {
        //要分成两部分,那么数组长度至少为2,
        int n = nums.length;
        if (n < 2){
            return false;
        }
        //要分成等和的两部分,那每一部分的和肯定是总和的一半,所以应该先求出数组的总和sum,这样就找到了目标和
        int sum = 0,max = 0;
        for (int num : nums) {
            max = Math.max(max,num);
            sum += num;
        }
        //如果sum是奇数,那么数组肯定不可能被分为元素和相等的两部分
        if(sum % 2 != 0){
            return false;
        }
        int target = sum / 2;
        //max > target,不可能将数组分为元素和相等的两部分
        if (max > target){
            return false;
        }
        //max == target 一定可以分为两部分,max为一部分,其余元素为另一部分
        if (max == target){
            return true;
        }

        //定义dp数组 dp[i][j]表示数组下标 [0,i) 范围内,选取若干个元素是否能使元素和达到j 那么我们只需要求出dp[n-1][target]是否为true即可
        boolean[][] dp = new boolean[n][target+1];
        //边界条件: j=0时,dp[i][0] = true ; i=0时,dp[0][nums[0]] = true。
        dp[0][nums[0]] = true;
        for (int i = 0; i < n; i++) {
            dp[i][0] = true;
        }
        for (int i = 1; i < n; i++) {
            for (int j = 1; j <= target; j++) {
                if (j >= nums[i]){
                    //不选择nums[i]元素,则dp[i][j]取决于 dp[i-1][j]
                    //选择 nums[i]元素,则dp[i][j]取决于 dp[i-1][j-nums[i]]
                    dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i]];
                }else {
                    //由于 j < nums[i] 不可能选择nums[i]元素
                    dp[i][j] = dp[i-1][j];
                }
            }
        }
        return dp[n-1][target];
    }
}

代码:【一阶dp】

class Solution {
    public boolean canPartition(int[] nums) {
        //要分成两部分,那么数组长度至少为2,
        int n = nums.length;
        if (n < 2){
            return false;
        }
        //要分成等和的两部分,那每一部分的和肯定是总和的一半,所以应该先求出数组的总和sum,这样就找到了目标和
        int sum = 0,max = 0;
        for (int num : nums) {
            max = Math.max(max,num);
            sum += num;
        }
        //如果sum是奇数,那么数组肯定不可能被分为元素和相等的两部分
        if(sum % 2 != 0){
            return false;
        }
        int target = sum / 2;
        //max > target,不可能将数组分为元素和相等的两部分
        if (max > target){
            return false;
        }
        //max == target 一定可以分为两部分,max为一部分,其余元素为另一部分
        if (max == target){
            return true;
        }
        //定义dp数组 dp[j]表示当前数组可选范围内,选择若干个元素是否能使元素和达到j,那么我们求出当可选范围为整个数组时,dp[target]是否为true即可
        boolean[] dp = new boolean[target+1];
        //边界条件:j = 0 时,必为 true
        dp[0] = true;
        for (int num : nums) {
            for (int j = target; j >= num; j--) {
                /*if (j >= nums[i]){
                    //选择nums[i]元素,则dp[j]取决于dp[j-nums[i]]
                    //不选择nums[i]元素,则dp[j]不动
                    dp[j] = dp[j] || dp[j-nums[i]];
                }else {
                    //j < nums[i]时,不可能选择nums[i] 于是 dp[j]保持不动
                    dp[j] = dp[j];
                }
                于是转移方程整理归纳如下
                */
                dp[j] = dp[j] || dp[j - num];
            }
        }
        return dp[target];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值