4.16 PDD笔试第二题记录

下面是对今晚PDD笔试第二题的回忆记录,只做学习交流。具体细节记不太清了,但是大概意思能够描述清楚。

题目描述

如果一个数组中,任意相邻的两个值都不相同,那么这个数组称为活跃数组,活跃数组中所有元素之和称为这个活跃数组的活跃度

现在用户会输入一个长度为n(1~10^4)的数组,要求返回一个int类型数值,该数值为给定数组的所有长度大于1的连续子数组的活跃度。

示例1:

[1, 3, 5, 3] 

长度为2的子数组有[1, 3], [3, 5], [5, 3],都为活跃数组,活跃度分别为4, 8, 8;

长度为3的子数组有[1, 3, 5], [3, 5, 3],都为活跃数组,活跃度分别为9, 11;

长度为4的子数组只有[1, 3, 5, 4],为活跃数组,活跃度为12。

将上述活跃度求和,最后预期返回数值为:(4+8+8)+(9+11)+12 = 52。

示例2:

[1,4,4,1]

长度为2的子数组有[1, 4], [4, 4], [4, 1],其中[1, 4], [4, 1]为活跃数组,活跃度分别为5, 5;

长度为3的子数组有[1, 4, 4], [4, 4, 1],都不是活跃数组;

长度为4的子数组只有[1, 4, 4, 1],也不是活跃数组。

将上述活跃度求和,最后预期返回数值为:5 + 5 = 10。

思路

首先,数组长度较大,使用回溯等暴力枚举再对结果进行筛选的方法肯定不可取。又因为题目最终要求的是活跃度,其实也就是满足要求的子数组的和,因此考虑前缀和方法。

经过一阵小递推后,发现有一个小规律(先暂时不考虑数组活跃与否):
当求子数组长度为2的总和时,第一个元素和最后一个元素只参与运算一次,中间元素运算了两次。假设原数组前n项和为Sn,那么Sn + Sn-1 - S1即为所求。

……

当子数组长度为k时,Sn+Sn-1+……+Sn-k+1 - S1- S2 -……-Sk-1即为所求。

最后,需要考虑一个数组是否满足活跃数组要求。不难发现,若有两个元素连续出现,那么包括这两个元素的长度为任何的数组都不能算作活跃数组,也就是这两个数组一定出现在两个数组运算集中。因此,将连续两次出现的数字进行拆分,例如x1,x2, x3,……,10,10,xk,xk+1,……,那么将x1,x2, x3,……,10和10,xk,xk+1,……当做两个独立的数组分别计算所有的子数组之和即可。

贴上代码,一方面防止自己遗忘,一方面可以互相交流学习。

import java.util.*;


public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int nLength = Integer.parseInt(sc.nextLine());
        ArrayList<ArrayList<Integer>> allArray = new ArrayList<>();
        ArrayList<ArrayList<Integer>> allPreSum = new ArrayList<>();
        ArrayList<Integer> array = new ArrayList<>();
        ArrayList<Integer> preSum = new ArrayList<>();
        int preElement = -1;
        for(int i = 0;i<nLength;i++)
        {
            int temp = sc.nextInt();
            if(preElement == temp)
            {
                allArray.add(new ArrayList<>(array));
                allPreSum.add(new ArrayList<>(preSum));
                array.clear();
                preSum.clear();
            }
            array.add(temp);
            if(preSum.isEmpty())preSum.add(temp);
            else preSum.add(temp+preSum.getLast());

            preElement = temp;
        }
        //temp里面剩下的全部装入
        allArray.add(array);
        allPreSum.add(preSum);
        int FinalResult = 0;
        for(int i = 0;i<allArray.size();i++)
        {
            FinalResult += getTempResult(allArray.get(i), allPreSum.get(i));
        }
        System.out.println(FinalResult);
    }
    public static int getTempResult(ArrayList<Integer> array, ArrayList<Integer> preSum)
    {
        //不考虑相邻情况
        int FinalResult = 0;
        int nLength = array.size();
        for(int i = 2;i<=nLength;i++)
        {
            int tempTimes = i;
            while(tempTimes >0)
            {
                FinalResult += preSum.get(nLength-tempTimes);
                tempTimes--;
            }
            //此时tempTimes == 0
            while(tempTimes < i-1)
            {
                FinalResult -= preSum.get(tempTimes);
                tempTimes++;
            }
        }
        return FinalResult;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值