|
|
package class41;
|
|
|
|
|
|
import java.util.Arrays;
|
|
|
import java.util.HashMap;
|
|
|
|
|
|
// 来自小红书
|
|
|
// 有四种诗的韵律分别为: AABB、ABAB、ABBA、AAAA
|
|
|
// 比如 : 1 1 3 3就属于AABB型的韵律、6 6 6 6就属于AAAA型的韵律等等
|
|
|
// 一个数组arr,当然可以生成很多的子序列,如果某个子序列一直以韵律的方式连接起来,我们称这样的子序列是有效的
|
|
|
// 比如, arr = { 1, 1, 15, 1, 34, 1, 2, 67, 3, 3, 2, 4, 15, 3, 17, 4, 3, 7, 52, 7, 81, 9, 9 }
|
|
|
// arr的一个子序列为{1, 1, 1, 1, 2, 3, 3, 2, 4, 3, 4, 3, 7, 7, 9, 9}
|
|
|
// 其中1, 1, 1, 1是AAAA、2, 3, 3, 2是ABBA、4, 3, 4, 3是ABAB、7, 7, 9, 9是AABB
|
|
|
// 可以看到,整个子序列一直以韵律的方式连接起来,所以这个子序列是有效的
|
|
|
// 给定一个数组arr, 返回最长的有效子序列长度
|
|
|
// 题目限制 : arr长度 <= 4000, arr中的值<= 10^9
|
|
|
// 离散化之后,arr长度 <= 4000, arr中的值<= 4000
|
|
|
public class Code02_PoemProblem {
|
|
|
|
|
|
// arr[i.....]符合规则连接的最长子序列长度
|
|
|
// public static int zuo(int[] arr, int i) {
|
|
|
// if (i + 4 > arr.length) {
|
|
|
// return 0;
|
|
|
// }
|
|
|
// // 最终的,符合规则连接的最长子序列长度,就是不要i位置的字符
|
|
|
// int p0 = zuo(arr, i + 1);
|
|
|
// // p1使用for循环搞定的!
|
|
|
// int p1 = 找到arr[i..s]是最短的,且能搞出AABB来的(4个) + zuo(arr, s + 1);
|
|
|
// // p2使用for循环搞定的!
|
|
|
// int p2 = 找到arr[i..t]是最短的,且能搞出ABAB来的(4个) + zuo(arr, t + 1);
|
|
|
// // p3使用for循环搞定的!
|
|
|
// int p3 = 找到arr[i..k]是最短的,且能搞出ABBA来的(4个) + zuo(arr, k + 1);
|
|
|
// // p4没用
|
|
|
// int p4 = 找到arr[i..f]是最短的,且能搞出AAAA来的(4个) + zuo(arr, f + 1);
|
|
|
// return p0~p4的最大值
|
|
|
// }
|
|
|
|
|
|
// AABB
|
|
|
// ABAB
|
|
|
// ABBA
|
|
|
// AAAA
|
|
|
public static int maxLen1(int[] arr) {
|
|
|
if (arr == null || arr.length < 4) {
|
|
|
return 0;
|
|
|
}
|
|
|
int[] path = new int[arr.length];
|
|
|
return process1(arr, 0, path, 0);
|
|
|
}
|
|
|
|
|
|
public static int process1(int[] arr, int index, int[] path, int size) {
|
|
|
if (index == arr.length) {
|
|
|
if (size % 4 != 0) {
|
|
|
return 0;
|
|
|
} else {
|
|
|
for (int i = 0; i < size; i += 4) {
|
|
|
if (!valid(path, i)) {
|
|
|
return 0;
|
|
|
}
|
|
|
}
|
|
|
return size;
|
|
|
}
|
|
|
} else {
|
|
|
int p1 = process1(arr, index + 1, path, size);
|
|
|
path[size] = arr[index];
|
|
|
int p2 = process1(arr, index + 1, path, size + 1);
|
|
|
return Math.max(p1, p2);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public static boolean valid(int[] p, int i) {
|
|
|
// AABB
|
|
|
// ABAB
|
|
|
// ABBA
|
|
|
// AAAA
|
|
|
return (p[i] == p[i + 1] && p[i + 2] == p[i + 3])
|
|
|
|| (p[i] == p[i + 2] && p[i + 1] == p[i + 3] && p[i] != p[i + 1])
|
|
|
|| (p[i] == p[i + 3] && p[i + 1] == p[i + 2] && p[i] != p[i + 1]);
|
|
|
}
|
|
|
|
|
|
// 0 : [3,6,9]
|
|
|
// 1 : [2,7,13]
|
|
|
// 2 : [23]
|
|
|
// [
|
|
|
// [3,6,9]
|
|
|
// ]
|
|
|
public static int maxLen2(int[] arr) {
|
|
|
if (arr == null || arr.length < 4) {
|
|
|
return 0;
|
|
|
}
|
|
|
int n = arr.length;
|
|
|
int[] sorted = Arrays.copyOf(arr, n);
|
|
|
Arrays.sort(sorted);
|
|
|
HashMap<Integer, Integer> vmap = new HashMap<>();
|
|
|
int index = 0;
|
|
|
vmap.put(sorted[0], index++);
|
|
|
for (int i = 1; i < n; i++) {
|
|
|
if (sorted[i] != sorted[i - 1]) {
|
|
|
vmap.put(sorted[i], index++);
|
|
|
}
|
|
|
}
|
|
|
int[] sizeArr = new int[index];
|
|
|
for (int i = 0; i < n; i++) {
|
|
|
arr[i] = vmap.get(arr[i]);
|
|
|
sizeArr[arr[i]]++;
|
|
|
}
|
|
|
int[][] imap = new int[index][];
|
|
|
for (int i = 0; i < index; i++) {
|
|
|
imap[i] = new int[sizeArr[i]];
|
|
|
}
|
|
|
for (int i = n - 1; i >= 0; i--) {
|
|
|
imap[arr[i]][--sizeArr[arr[i]]] = i;
|
|
|
}
|
|
|
return process2(arr, imap, 0);
|
|
|
}
|
|
|
|
|
|
// AABB
|
|
|
// ABAB
|
|
|
// ABBA
|
|
|
// AAAA
|
|
|
public static int process2(int[] varr, int[][] imap, int i) {
|
|
|
if (i + 4 > varr.length) {
|
|
|
return 0;
|
|
|
}
|
|
|
int p0 = process2(varr, imap, i + 1);
|
|
|
// AABB
|
|
|
int p1 = 0;
|
|
|
int rightClosedP1A2 = rightClosed(imap, varr[i], i);
|
|
|
if (rightClosedP1A2 != -1) {
|
|
|
for (int next = rightClosedP1A2 + 1; next < varr.length; next++) {
|
|
|
if (varr[i] != varr[next]) {
|
|
|
int rightClosedP1B2 = rightClosed(imap, varr[next], next);
|
|
|
if (rightClosedP1B2 != -1) {
|
|
|
p1 = Math.max(p1, 4 + process2(varr, imap, rightClosedP1B2 + 1));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// ABAB
|
|
|
int p2 = 0;
|
|
|
for (int p2B1 = i + 1; p2B1 < varr.length; p2B1++) {
|
|
|
if (varr[i] != varr[p2B1]) {
|
|
|
int rightClosedP2A2 = rightClosed(imap, varr[i], p2B1);
|
|
|
if (rightClosedP2A2 != -1) {
|
|
|
int rightClosedP2B2 = rightClosed(imap, varr[p2B1], rightClosedP2A2);
|
|
|
if (rightClosedP2B2 != -1) {
|
|
|
p2 = Math.max(p2, 4 + process2(varr, imap, rightClosedP2B2 + 1));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// ABBA
|
|
|
int p3 = 0;
|
|
|
for (int p3B1 = i + 1; p3B1 < varr.length; p3B1++) {
|
|
|
if (varr[i] != varr[p3B1]) {
|
|
|
int rightClosedP3B2 = rightClosed(imap, varr[p3B1], p3B1);
|
|
|
if (rightClosedP3B2 != -1) {
|
|
|
int rightClosedP3A2 = rightClosed(imap, varr[i], rightClosedP3B2);
|
|
|
if (rightClosedP3A2 != -1) {
|
|
|
p3 = Math.max(p3, 4 + process2(varr, imap, rightClosedP3A2 + 1));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
// AAAA
|
|
|
int p4 = 0;
|
|
|
int rightClosedP4A2 = rightClosed(imap, varr[i], i);
|
|
|
int rightClosedP4A3 = rightClosedP4A2 == -1 ? -1 : rightClosed(imap, varr[i], rightClosedP4A2);
|
|
|
int rightClosedP4A4 = rightClosedP4A3 == -1 ? -1 : rightClosed(imap, varr[i], rightClosedP4A3);
|
|
|
if (rightClosedP4A4 != -1) {
|
|
|
p4 = Math.max(p4, 4 + process2(varr, imap, rightClosedP4A4 + 1));
|
|
|
}
|
|
|
return Math.max(p0, Math.max(Math.max(p1, p2), Math.max(p3, p4)));
|
|
|
}
|
|
|
|
|
|
public static int rightClosed(int[][] imap, int v, int i) {
|
|
|
int left = 0;
|
|
|
int right = imap[v].length - 1;
|
|
|
int ans = -1;
|
|
|
while (left <= right) {
|
|
|
int mid = (left + right) / 2;
|
|
|
if (imap[v][mid] <= i) {
|
|
|
left = mid + 1;
|
|
|
} else {
|
|
|
ans = mid;
|
|
|
right = mid - 1;
|
|
|
}
|
|
|
}
|
|
|
return ans == -1 ? -1 : imap[v][ans];
|
|
|
}
|
|
|
|
|
|
public static int maxLen3(int[] arr) {
|
|
|
if (arr == null || arr.length < 4) {
|
|
|
return 0;
|
|
|
}
|
|
|
int n = arr.length;
|
|
|
int[] sorted = Arrays.copyOf(arr, n);
|
|
|
Arrays.sort(sorted);
|
|
|
HashMap<Integer, Integer> vmap = new HashMap<>();
|
|
|
int index = 0;
|
|
|
vmap.put(sorted[0], index++);
|
|
|
for (int i = 1; i < n; i++) {
|
|
|
if (sorted[i] != sorted[i - 1]) {
|
|
|
vmap.put(sorted[i], index++);
|
|
|
}
|
|
|
}
|
|
|
int[] sizeArr = new int[index];
|
|
|
for (int i = 0; i < n; i++) {
|
|
|
arr[i] = vmap.get(arr[i]);
|
|
|
sizeArr[arr[i]]++;
|
|
|
}
|
|
|
int[][] imap = new int[index][];
|
|
|
for (int i = 0; i < index; i++) {
|
|
|
imap[i] = new int[sizeArr[i]];
|
|
|
}
|
|
|
for (int i = n - 1; i >= 0; i--) {
|
|
|
imap[arr[i]][--sizeArr[arr[i]]] = i;
|
|
|
}
|
|
|
int[] dp = new int[n + 1];
|
|
|
for (int i = n - 4; i >= 0; i--) {
|
|
|
int p0 = dp[i + 1];
|
|
|
// AABB
|
|
|
int p1 = 0;
|
|
|
int rightClosedP1A2 = rightClosed(imap, arr[i], i);
|
|
|
if (rightClosedP1A2 != -1) {
|
|
|
for (int next = rightClosedP1A2 + 1; next < arr.length; next++) {
|
|
|
if (arr[i] != arr[next]) {
|
|
|
int rightClosedP1B2 = rightClosed(imap, arr[next], next);
|
|
|
if (rightClosedP1B2 != -1) {
|
|
|
p1 = Math.max(p1, 4 + dp[rightClosedP1B2 + 1]);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// ABAB
|
|
|
int p2 = 0;
|
|
|
for (int p2B1 = i + 1; p2B1 < arr.length; p2B1++) {
|
|
|
if (arr[i] != arr[p2B1]) {
|
|
|
int rightClosedP2A2 = rightClosed(imap, arr[i], p2B1);
|
|
|
if (rightClosedP2A2 != -1) {
|
|
|
int rightClosedP2B2 = rightClosed(imap, arr[p2B1], rightClosedP2A2);
|
|
|
if (rightClosedP2B2 != -1) {
|
|
|
p2 = Math.max(p2, 4 + dp[rightClosedP2B2 + 1]);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// ABBA
|
|
|
int p3 = 0;
|
|
|
for (int p3B1 = i + 1; p3B1 < arr.length; p3B1++) {
|
|
|
if (arr[i] != arr[p3B1]) {
|
|
|
int rightClosedP3B2 = rightClosed(imap, arr[p3B1], p3B1);
|
|
|
if (rightClosedP3B2 != -1) {
|
|
|
int rightClosedP3A2 = rightClosed(imap, arr[i], rightClosedP3B2);
|
|
|
if (rightClosedP3A2 != -1) {
|
|
|
p3 = Math.max(p3, 4 + dp[rightClosedP3A2 + 1]);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
// AAAA
|
|
|
int p4 = 0;
|
|
|
int rightClosedP4A2 = rightClosed(imap, arr[i], i);
|
|
|
int rightClosedP4A3 = rightClosedP4A2 == -1 ? -1 : rightClosed(imap, arr[i], rightClosedP4A2);
|
|
|
int rightClosedP4A4 = rightClosedP4A3 == -1 ? -1 : rightClosed(imap, arr[i], rightClosedP4A3);
|
|
|
if (rightClosedP4A4 != -1) {
|
|
|
p4 = Math.max(p4, 4 + dp[rightClosedP4A4 + 1]);
|
|
|
}
|
|
|
dp[i] = Math.max(p0, Math.max(Math.max(p1, p2), Math.max(p3, p4)));
|
|
|
}
|
|
|
return dp[0];
|
|
|
}
|
|
|
|
|
|
// 课堂有同学提出了贪心策略(这题还真是有贪心策略),是正确的
|
|
|
// AABB
|
|
|
// ABAB
|
|
|
// ABBA
|
|
|
// AAAA
|
|
|
// 先看前三个规则:AABB、ABAB、ABBA
|
|
|
// 首先A、A、B、B的全排列为:
|
|
|
// AABB -> AABB
|
|
|
// ABAB -> ABAB
|
|
|
// ABBA -> ABBA
|
|
|
// BBAA -> 等同于AABB,因为A和B谁在前、谁在后都算是 : AABB的范式
|
|
|
// BABA -> 等同于ABAB,因为A和B谁在前、谁在后都算是 : ABAB的范式
|
|
|
// BAAB -> 等同于ABBA,因为A和B谁在前、谁在后都算是 : ABBA的范式
|
|
|
// 也就是说,AABB、ABAB、ABBA这三个规则,可以这么用:
|
|
|
// 只要有两个不同的数,都出现2次,那么这一共4个数就一定符合韵律规则。
|
|
|
// 所以:
|
|
|
// 1) 当来到arr中的一个数字num的时候,
|
|
|
// 如果num已经出现了2次了, 只要之前还有一个和num不同的数,
|
|
|
// 也出现了两次,则一定符合了某个规则, 长度直接+4,然后清空所有的统计
|
|
|
// 2) 当来到arr中的一个数字num的时候,
|
|
|
// 如果num已经出现了4次了(规则四), 长度直接+4,然后清空所有的统计
|
|
|
// 但是如果我去掉某个规则,该贪心直接报废,比如韵律规则变成:
|
|
|
// AABB、ABAB、AAAA
|
|
|
// 因为少了ABBA, 所以上面的化简不成立了, 得重新分析新规则下的贪心策略
|
|
|
// 而尝试的方法就更通用(也就是maxLen3),只是减少一个分支而已
|
|
|
// 这个贪心费了很多心思,值得点赞!
|
|
|
public static int maxLen4(int[] arr) {
|
|
|
// 统计某个数(key),出现的次数(value)
|
|
|
HashMap<Integer, Integer> map = new HashMap<>();
|
|
|
// tow代表目前有多少数出现了2次
|
|
|
int two = 0;
|
|
|
// ans代表目前符合韵律链接的子序列增长到了多长
|
|
|
int ans = 0;
|
|
|
// 当前的num出现了几次
|
|
|
int numTimes = 0;
|
|
|
for (int num : arr) {
|
|
|
// 对当前的num,做次数统计
|
|
|
map.put(num, map.getOrDefault(num, 0) + 1);
|
|
|
// 把num出现的次数拿出来
|
|
|
numTimes = map.get(num);
|
|
|
// 如果num刚刚出现了2次, 那么目前出现了2次的数,的数量,需要增加1个
|
|
|
two += numTimes == 2 ? 1 : 0;
|
|
|
// 下面的if代表 :
|
|
|
// 如果目前有2个数出现2次了,可以连接了
|
|
|
// 如果目前有1个数出现4次了,可以连接了
|
|
|
if (two == 2 || numTimes == 4) {
|
|
|
ans += 4;
|
|
|
map.clear();
|
|
|
two = 0;
|
|
|
}
|
|
|
}
|
|
|
return ans;
|
|
|
}
|
|
|
|
|
|
// 为了测试
|
|
|
public static int[] randomArray(int len, int value) {
|
|
|
int[] arr = new int[len];
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
arr[i] = (int) (Math.random() * value);
|
|
|
}
|
|
|
return arr;
|
|
|
}
|
|
|
|
|
|
// 为了测试
|
|
|
public static void main(String[] args) {
|
|
|
|
|
|
// 1111 2332 4343 7799
|
|
|
int[] test = { 1, 1, 15, 1, 34, 1, 2, 67, 3, 3, 2, 4, 15, 3, 17, 4, 3, 7, 52, 7, 81, 9, 9 };
|
|
|
System.out.println(maxLen1(test));
|
|
|
System.out.println(maxLen2(test));
|
|
|
System.out.println(maxLen3(test));
|
|
|
System.out.println(maxLen4(test));
|
|
|
System.out.println("===========");
|
|
|
|
|
|
int len = 16;
|
|
|
int value = 10;
|
|
|
int[] arr = randomArray(len, value);
|
|
|
int[] arr1 = Arrays.copyOf(arr, arr.length);
|
|
|
int[] arr2 = Arrays.copyOf(arr, arr.length);
|
|
|
int[] arr3 = Arrays.copyOf(arr, arr.length);
|
|
|
int[] arr4 = Arrays.copyOf(arr, arr.length);
|
|
|
System.out.println(maxLen1(arr1));
|
|
|
System.out.println(maxLen2(arr2));
|
|
|
System.out.println(maxLen3(arr3));
|
|
|
System.out.println(maxLen4(arr4));
|
|
|
|
|
|
System.out.println("===========");
|
|
|
|
|
|
long start;
|
|
|
long end;
|
|
|
int[] longArr = randomArray(4000, 20);
|
|
|
start = System.currentTimeMillis();
|
|
|
System.out.println(maxLen3(longArr));
|
|
|
end = System.currentTimeMillis();
|
|
|
System.out.println("运行时间(毫秒) : " + (end - start));
|
|
|
System.out.println("===========");
|
|
|
|
|
|
start = System.currentTimeMillis();
|
|
|
System.out.println(maxLen4(longArr));
|
|
|
end = System.currentTimeMillis();
|
|
|
System.out.println("运行时间(毫秒) : " + (end - start));
|
|
|
System.out.println("===========");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|