水果放进篮子的问题,
fruits数组里面的数字是每个水果的类型,当前位置对应一个水果。
只有2个篮子,每个篮子只能放一种水果,不能几种水果混放,不限制每个篮子放的水果个数。
从左到右遍历数组,可以选择一个开始的index, 从这个index开始,每经历一个数字,都要把该数字类型的水果放进篮子里,那水果不是不能几种类型混着放么,要是和篮子里面的水果类型不匹配怎么办,那就到此为止,结束。
统计两个篮子里最多能放多少个水果。
思路:
方法1: DP
因为只有2个篮子,降低了复杂度,两个篮子就对应前面出现的2种类型(2个数字)。
因为只有2个数字,当无法再继续放的时候(当前数字和前面2个数字不匹配),更新之前放的水果的最大值,然后从头开始(保留前面的一个数字和当前数字,前面第1个数字抛弃)。
这样说可能一头雾水,举个例子吧,
pr1, pr2表示保存的当前两个篮子里面放的类型。
dp表示到当前index时2个篮子里的水果数。
prCnt表示当前index-1处的数字连续出现的个数。这是为了统计当index处的水果和2个篮子里的水果不匹配时,需要从index-1处重新开始放,而index-1处的数字已经连续出现了多少次(前面一种类型的水果已经放了多少个)。
fruits: 3 3 3 1 2 1 1 2 3 3 4
dp: 1 2 3 4 2 3 4 5 2 3 3
pr1: 3 3 3 3 1 2 2 1 2 2 3
pr2: -1 -1 -1 1 2 1 1 2 3 3 4
prCnt: 1 2 3 1 1 1 2 1 1 2 1
res: 1 2 3 4 4 4 4 5 5 5 5
所以,看明白了吗。刚开始的时候只有pr1=fruits[0], pr2=-1,
dp[0] = 1, 因为这时候只有一个水果,
到第2个的时候,还是3,跟pr1一样,所以pr2还是-1,
prCnt是它前面一个数字3出现的次数,只出现了一次,所以是1,
因为3和pr1相同,所以dp[1] = dp[0] + 1,
第3个3和上面一样,来看看第4个数字1,
它既不等于pr1, 也不等于pr2, 而现在只有pr1有值,pr2还是-1,所以更新pr2=1,
现在它和pr1或pr2相等了,所以dp[3] = dp[2] + 1 = 4,
这是和pr1或pr2相等的情况。
那么再看和pr1和pr2都不相等的情况,第5个数字2,
因为它和前两个篮子的水果都不匹配,所以要放弃类型为3的水果,
取它的前一个类型2,从2开始,
那么2已经连续出现了几次呢,就是prCnt=1
这是dp[4] = prCnt + 1 = 2.
第6个数字1来的时候,它和pr1相等,dp[5] = dp[4] + 1,
但是我们要保证pr1和pr2的顺序(不然可能新出现的数字被放弃了),1现在是最新出现的,应该保存在pr2,
所以把pr1和pr2交换一下顺序。
public int totalFruit(int[] fruits) {
int n = fruits.length;
int res = 1;
int pr1 = fruits[0], pr2 = -1;
int[] dp = new int[n];
int prCnt = 1;
dp[0] = 1;
if(n > 1) {
dp[1] = 2;
res = 2;
if(fruits[1] != pr1) {
pr2 = fruits[1];
} else {
prCnt = 2;
}
}
for(int i = 2; i < n; i++) {
if(pr2 == -1 && fruits[i] != pr1) {
pr2 = fruits[i];
}
if(fruits[i] == pr1 || fruits[i] == pr2) {
if(fruits[i] == pr1) {
int tmp = pr1;
pr1 = pr2;
pr2 = tmp;
}
if(fruits[i] == fruits[i-1]) prCnt ++;
else prCnt = 1;
dp[i] = dp[i-1] + 1;
} else {
dp[i] = prCnt + 1;
prCnt = 1;
pr1 = pr2;
pr2 = fruits[i];
}
res = Math.max(res, dp[i]);
}
return res;
}
方法2: 双指针
上面的DP方法需要统计前一个数字出现的次数prCnt,也可以用双指针记录前一个数字出现的起点和终点,用终点减去起点就是前一个数字出现的次数。
其他的都差不多。
public int totalFruit(int[] fruits) {
if (fruits.length == 1) {
return 1;
}
int left = 0;
int res = 0;
int[] currFr = new int[]{fruits[0], -1};
for (int right = 1; right < fruits.length; ++right) {
int fr = fruits[right];
if (fr != currFr[0] && fr != currFr[1]) {
if (currFr[1] == -1) {
currFr[1] = fr;
continue;
}
//统计前一个数字出现的次数
res = Math.max(res, right - left);
//更新新的前一个数字起点和终点指针
int prevFr = fruits[right - 1];
left = right - 1;
while (fruits[left - 1] == prevFr) {
left--;
}
//更新最新的2个数字
currFr[0] = prevFr;
currFr[1] = fr;
}
}
return Math.max(fruits.length - left, res);
}