算法日常训练10.17

 题目很长,总结下来就是找出最多只包含两个数的子数组,竟然是子数组问题,第一时间想到滑动窗口,固定左窗口,遍历右窗口,在加入第三个数时,维护左窗口,左窗口维护时,需要注意

1 0 1 2 2 2 1 2这种,在遍历到2时,应该维护到0后面,而不是1后面,在我写的程序中用set保存子数组的数字,很好理解,但是并不是优秀的写法,复杂度比较高

class Solution {
    public int totalFruit(int[] fruits) {
        //找到数组中只包含两个数的最长子数组
        Set<Integer> set=new HashSet<>();//保存篮子里的水果
        Map<Integer,Integer> map=new HashMap<>();//保存每个数最后出现的位置
        int i=0;//左窗口
        int max=0;//最长的数组
        for(int j=0;j<fruits.length;){//右窗口
            if(set.size()==2&&!set.contains(fruits[j])){//如果已经有了两种水果并且遍历到了新的水果
                int fru=0;//需要移出去的水果
                int index=Integer.MAX_VALUE;//需要移出去水果的下标
                for(int a:set){
                    if(map.get(a)<index){//哪个水果最后出现的位置在前面就把哪个水果移出去
                        fru=a;
                        index=map.get(a);
                    }
                }
                set.remove(fru);//移出要移出的水果
                i=index+1;//维护左窗口
            }
            else{
            map.put(fruits[j],j);//更新水果最后出现的位置
            set.add(fruits[j]);//添加水果
            max=Math.max(max,j-i+1);//维护最大值
            j++;//维护右窗口
            }
        }
        return max;
    }
}

这里再附上官方的写法,也是滑动数组,复杂度也比较高,但是这种写法的map和我写法的map不一样,这里的map是保存水果的数量,再种类超过2以后,左侧窗口维护,可以理解成一个一个扔出去,知道把一个种类的水果全扔去了,也很好理解

class Solution {
    public int totalFruit(int[] fruits) {
        int n = fruits.length;//数组长度
        Map<Integer, Integer> cnt = new HashMap<Integer, Integer>();//保存水果的个数

        int left = 0, ans = 0;
        for (int right = 0; right < n; ++right) {
            cnt.put(fruits[right], cnt.getOrDefault(fruits[right], 0) + 1);//记录水果个数
            while (cnt.size() > 2) {//已经存在了超过两种水果
                cnt.put(fruits[left], cnt.get(fruits[left]) - 1);//左侧窗口不断维护
                if (cnt.get(fruits[left]) == 0) {//知道维护到一种完全出去了
                    cnt.remove(fruits[left]);
                }
                ++left;
            }
            ans = Math.max(ans, right - left + 1);//维护最大值
        }
        return ans;
    }
}

再写一个欠下的LCIS(最长公共递增子序列),参考LCS和LIS,这个综合应用比较难理解,但是对LIS和LCS很熟练地话,用点比较蠢得方法还是能写,dp的定义和最长公共子序列相似

dp[i][j]:a的前i个字符和b的前j个字符最长的公共递增子序列

首先

a[i-1]!=b[j-1]时,dp[i][j]=dp[i-1][j],这个比较好理解,因为a[i-1]!=b[j-1],所以他们一定不是公共的,所以就是dp就等于不取当前字符的长度

当a[i-1]==b[j-1],就比较难理解,我这里用了三层循环,为了好理解,当他们相等时,就要找到以他们为结尾的最长递增序列,这里就和最长递增子序列很相似

public class LCIS {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int N= sc.nextInt();
        int M=sc.nextInt();
        int[] a=new int[N];
        int[] b=new int[M];
        for(int i=0;i<N;i++){
            a[i]=sc.nextInt();
        }
        for (int j=0;j<M;j++){
            b[j]=sc.nextInt();
        }
        int ans=0;//保存结果
        int[][] dp=new int[N+1][M+1];//dp:a的前i个字符和b的前j个字符最长的LCIS
        for(int i=1;i<=N;i++){
            for(int j=1;j<=M;j++){
                if(a[i-1]==b[j-1]){//如果相等
                    int maxv=1;
                    for (int k=1;k<j;k++){//找到最长的递增序列
                        if(a[i-1]>b[k-1])//if(arr[j]<arr[i]) dp[i]=Math.max(dp[i],dp[j]+1 );和这个相似,不过说是两组数,但是因为两数相等,实际是差不多的
                            maxv=Math.max(maxv,dp[i-1][k]+1);
                        dp[i][j]=Math.max(dp[i][j],maxv);//维护最大值
                    }
                }
                else//如果a[i-1]不等于b[j-1]
                dp[i][j]=dp[i-1][j];//那么就和a的i-1到b的j的dp是一样的,因为i-1不等于j-1,是不能公共的
                ans=Math.max(ans,dp[i][j]);//维护最大值
            }
        }
        System.out.println(ans);
    }
}

两层循环就是把找递增的一步在第二层循环里操作,有些细节理解的不是很好

public class LCIS_最长公共上升子序列 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int N= sc.nextInt();
        int M=sc.nextInt();
        int[] a=new int[N];
        int[] b=new int[M];
        for(int i=0;i<N;i++){
            a[i]=sc.nextInt();
        }
        for (int j=0;j<M;j++){
            b[j]=sc.nextInt();
        }
        int ans=0;
        int[][] dp=new int[N+1][M+1];//dp:a的前i个字符和b的前j个字符最长的LCIS
        for(int i=1;i<=N;i++){
            int val=0;//记录以[i-1]结尾的最长公共递增长度
            for (int j=1;j<=M;j++){
                if(a[i-1]==b[j-1]){
                    dp[i][j]=val+1;
                    ans=Math.max(ans,dp[i][j]);
                }
                else {
                   dp[i][j]=dp[i-1][j];
                   if(a[i-1]>b[j-1]){
                       val=Math.max(val,dp[i-1][j]);
                   }
                }
            }
        }
        System.out.println(ans);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值