下面是对今晚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-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;
}
}