一个字符串 最少分隔几个部分 让它全都是回文字符串
做一个dp[][]数组 dp[i][j] 表示i....j位置上的字符串是否为回文字符串 依赖于dp[i+1][j-1] 也就是左边界缩一个格子 右边界缩一个格子
basecase 对角线 dp[i][i]只有一个字符 必为回文
最后一列 倒数第二行的格子 也就是对角线右边的一条线 依赖左下的话 是空的 所以一次填两条斜线万无一失
第二条斜线也好办 就两个元素 相等为回文 不等为非回文
然后填好每个格子 除了一条斜线一条斜线的填 我们也可以从下往上填 每一行的开始点都是行+2 因为本来是行=列开始的 从图中我们也可以看出 每行都填好了两个格子
额 这样我们就生成好了一张表 来看每一个区域是不是回文
然后在来一个一维的dp数组
dp[i]位置表示 i...结尾位置最少分隔几个成为回文字符串
然后我们枚举i 就是i为开头 然后在每个i中 枚举每个结尾 就相当于枚举每个子数组了 不过为啥不枚举结尾呢 一定要从后往前枚举开头呢
dp[i]是i...结尾位置最少分隔几个成为回文字符串的话 dp1[end+1]是 这个结尾的
这个MAX_VaLUE这一步很关键 因为最开始都是0 没填过的格子会被min识别成有效且最小的
当i...end为回文的时候 我们就要考虑 要不要切这么一刀 当前位置的分隔次数肯定是 它分隔前的次数+1刀 因为是吧i...end这个区间作为一个新的区间 但是呢 这一刀也不一定要
比如说 abakfk i==5 的时候
dp[5] MAX和0+1 哪个更小 dp[5] = 1
i==4的时候
从end==4开始结算 MAX和 1+1 选择2 这种每次都是+1的选择是最坏的可能性 我上一个格子的分隔区间 直接加一(我自身独立成一个格子) 接下来的每种走法 都是看能不能和之前的格子连接上 形成回文了 如果可以形成回文 也不一定是当前区间最长就是最小的 可能是 5长度 1 长度 1长度 1长度 四个区间 而4长度 三长度 两个区间才是最优解 所以要遍历每一个可能的区间 然后往回推 看看到底算上当前区间要分几个区间
如果是分割次数 那就是分割区间减一喽
public int minCut(String s) {
if (s == null || s.length() == 0) {
return 0;
}
if (s.length() == 1) {
return 0;
}
char [] chars = s.toCharArray();
int N = chars.length;
boolean [][] dp = new boolean [N][N];
for(int i = 0;i= 0; row--) {
for (int col = row + 2; col < N; col++) {//开始的两列填完了
dp[row][col] = chars[row] == chars[col] && dp[row + 1][col - 1];
}
}
int[] dp1 = new int[N + 1];
for (int i = 0; i <= N; i++) {
dp1[i] = Integer.MAX_VALUE;
}
dp1[N] = 0;
for (int i = N - 1; i >= 0; i--) {
for (int end = i; end < N; end++) {
// i..end
if (dp[i][end]) {
dp1[i] = Math.min(dp1[i], 1 + dp1[end + 1]);
}
}
}
return dp1[0]-1;
}
力扣链接-力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
给定两个有序数组arr1和arr2,再给定一个正数K
求两个数累加和最大的前K个,两个数必须分别来自arr1和arr2 可以重复使用数 但是同一个组合只能使用一次
额 做一张二维表 里面有所有数的累加和 dp[i][j]为 arr1 i位置+arr2 j位置的和
然后再有一张同样格式的表
然后做一个大根堆 大根堆最大容量为K 先把右下角的放进去 右下角一定是最大的 然后看这个值的上面和左面哪个值大 把它放入堆中 因为它是一个有序数组 两个数组都取最大的值 肯定是最大的 左侧和上侧 分别代表 arr1取倒数第二个和arr2取倒数第一个 arr1取倒数第一个和arr2取倒数第二个 看看他俩哪个大 然后把它放在堆中 然后再看它的左侧和上侧 wrong 两个都放进去 如果超额的放了 那么 它是一个大根堆 会把大的放在前面 然后只取较大的
记得不要重复加入 like this
这个D点就是重复加入了 by the way 这个B点 A点 一定分别大于CD和ED 这个倒是没问题 但是当C大于E 且只要五个元素的情况呢 我们会先加入最开始的星 弹出星 +B +A 然后弹出B +E +D 然后弹出A +D +C 每次加入两个元素 我担心的 还没加入本层元素 就收集完毕的情况不存在 收集元素到第N层的时候 外面那层已经加入完毕了并参与排序了 假如说我们A B为第二层 我们收集元素肯定是在第二层 在第二层收集完成之前 不会进入下一层 而当前层收集一遍的时候 就把所有下一层元素加进去了
public static int [] TopKsum(int [] arr1,int [] arr2,int K) {
if (arr1 == null || arr2 == null || K < 1) {
return null;
}
// arr1 50 arr2 20 1000 topk 100万
K = Math.min(K, arr1.length * arr2.length);
int length1 = arr1.length;
int length2 = arr2.length;
int cur1 = length1-1;
int cur2 = length2-1;
PriorityQueue stack = new PriorityQueue(new mycompare());
stack.add(new Node(cur1, cur2, arr1[cur1]+arr2[cur2]));
int count = 0;
int [] res = new int [K];
boolean [][] isin = new boolean[arr1.length][arr2.length];
while(count<=K) {
/*res[count] = stack.poll().value;
count++;
if(cur1-1>=0&&isin[cur1-1][cur2]==false) {
cur1--;
stack.add(new Node(cur1, cur2, arr1[cur1]+arr2[cur2]));
isin[cur1][cur2] = true;
}
if(cur2-1>=0&&isin[cur1][cur2-1]==false) {
cur2--;
stack.add(new Node(cur1, cur2, arr1[cur1]+arr2[cur2]));
isin[cur1][cur2] = true; 这么做不对 逻辑就错了 每次只能走两个 只针对第一次循环是正确的
}*/
Node node = stack.poll();
res[count] = node.value;
count++;
cur1 = node.i1;
cur2 = node.i2;
if(cur1-1>=0&&isin[cur1-1][cur2]==false) {
cur1--;
stack.add(new Node(cur1, cur2, arr1[cur1]+arr2[cur2]));
isin[cur1][cur2] = true;
}
if(cur2-1>=0&&isin[cur1][cur2-1]==false) {
cur2--;
stack.add(new Node(cur1, cur2, arr1[cur1]+arr2[cur2]));
isin[cur1][cur2] = true;
}
}
return res;
}
记得洗一下参数 如果拢共笛卡尔积都没有K那么多 那就不用找那么多
给定一个正数数组arr,返回该数组能不能分成4个部分,并且每个部分的累加
和相等,切分位置的数不要。
例如:
arr=[3, 2,4, 1,4,9, 5, 10,1, 2, 2]返回true三个切割点下标为2, 5, 7.切出的四个子数组为[3,2]. [1,4]. [5], [1,2,2],累加和都是5
首先 如果它长度不大于等于七 就是白给
然后我们的思路是枚举每一刀的位置
首先第一刀 1....N-6上面选 再后面就凑不齐三刀了
假设我们第一刀的位置为a 那么a的前缀和为x
那么第二刀的位置它的前缀和一定是2x+a
第三刀的前缀和一定是 3x+a+b
然后剩下的 也得是x
那就得做个累加和预处理数组了 然后遍历每个位置
或者我们主动出击 做个hashmap 看看需要多少累加和 那就直接找到
public static boolean canSplits(int[] arr) {
if (arr.length<7) {
return false;
}
HashMap map = new HashMap();
int sum = arr[0];
for (int i = 1; i < arr.length; i++) {
map.put(sum, i);
sum += arr[i];
}
int lsum = arr[0]; // 第一刀左侧的累加和 就是x拉
for (int s1 = 1; s1 < arr.length - 5; s1++) { // s1是第一刀的位置
int checkSum = lsum * 2 + arr[s1]; // 100 x 100 100*2 + x
if (map.containsKey(checkSum)) {
int s2 = map.get(checkSum); // j -> y
checkSum += lsum + arr[s2];
if (map.containsKey(checkSum)) { // 100 * 3 + x + y
int s3 = map.get(checkSum); // k -> z
if (checkSum + arr[s3] + lsum == sum) {
return true;
}
}
}
lsum += arr[s1];
}
return false;
}