题目一
给定三个字符串str1、str2和aim, 如果aim包含且仅包含来自str1和str2的所有字符,而且在aim中属于str1的字符 之间保持原来在str1中的顺序,属于str2的字符之间保持原来在str2中的顺序,那么称aim是str1和str2的交错组成。实现一个函数,判断aim是 否是str1和str2交错组成
[举例] str1="AB", str2="12"。 那么"AB12"、"A1B2"、 "A12B"、 "1A2B"和"1AB2"等都是str1和str2的交错组成
双指针法只能用在没有重复值的情况 如果有重复值 双指针法就不再适用了
首先如果长度不一致的话 aim!=str1+str2的时候 直接过滤掉
dp[i][j] 是str1 0....i和str2 0....j 能否交错组成aim 0.....i+j
basecase的话 第一行还算好找 第一列也是
经典字符串考虑 不要的情况 dp[0]位置全是str1一个字符都不要 不过这么做之后确实简单了不少 要不basecase就够喝一壶了
所以 相等就是可以 不等就是不行
然后dp[i][j]依赖于 它的前一个格子和上一个格子
就是你新加入了一个字符 那么我们就看这个新加的字符和aim对应位置的字符是否相同
因为是交错组成 假如说前面已经是两个字符串交错组成而来的话那我们只需要看最新的位置
if(s3.length()!=s1.length()+s2.length()) {return false;}char [] aim = s3.toCharArray();char [] chars1 = s1.toCharArray();char [] chars2 = s2.toCharArray();int length1 = chars1.length;int length2 = chars2.length;boolean [][] dp = new boolean [length1+1][length2+1];dp[0][0] = true;for(int i = 1;i<=length1;i++) {if(chars1[i-1]!=aim[i-1]) {break;}dp[i][0] = true; }for(int i = 1;i<=length2;i++) {if(chars2[i-1]!=aim[i-1]) {break;}dp[0][i] = true; }for(int i = 1;i<=length1;i++) {for(int j = 1;j<=length2;j++) {if ((chars1[i - 1] == aim[i + j - 1] && dp[i - 1][j])|| (chars2[j - 1] == aim[i + j - 1] && dp[i][j - 1])) {dp[i][j] = true;}}}return dp[length1][length2];}
题目二
给定一个无序数组arr.如果只能再一个子数组上排序 返回如果让arr整体有序,需要排序的最短子数组长度
无论中间的有序数组有多长 边上有元素无序都不行 所以我们看两侧不用排的有多少
public static int [] getMinLength(int[] arr) {if (arr == null || arr.length < 2) {return new int [] {-1,-1};}int N = arr.length;int leftno = -1;int max = arr[0];int righton = -1;int min = arr[N-1];for(int i = 1;i<N;i++) {if(max > arr[i]) {leftno = i-1;break;}max = arr[i];}for(int i = N-2;i>=0;i--) {if(min<arr[i]) {righton = i+1;break;}min = arr[i];}return new int [] {leftno,righton};}
我刚开始是这么写的 但是只是两边有序是不行的 例如 1,2,3...... 1,2,3 这样的 就是错的
从后往前找 找到最前面一个无序的 从前往后找 找最后一个无序的 这样才对 这样找出来的才是最边上的无序的
第三题
读题都得读一会
要注意我们要求的是整个数组的最小组成和 不是某个子数组的不可组成和 只要一个子数组可以组成 那么整体就可以组成
做一个dp数组 dp[i][j] 表示数组0...i的累加和能否得到j
啊 填第一列 肯定能 我一个不要 不就是累加和0
dp[i][j] 它依赖于dp[i-1][j-arr[i]] 如果只用i-1个元素 就能凑出j-arr[i]的话 那么我们再加上一个元素 就能凑出j
或者 dp[i-1][j] 如果四个元素都能推出j 那五个元素也能推出 我不要新的元素不就得了
所以这个格子的位置是 可不可以推出 而不是等不等于
可以有可以的推法被 可以是包含等于的 所以我们又有了个依赖dp[i-1][j]
既然我有 0....i了 那我就可以凑出任何一个位置的 累加和可不可以得到某个值(其实用不上 只要最后一行 所有元素都要能推出什么值)
然后我们拿最后一行 也就是说我们用所有元素 可以推出什么不可以推出什么
啊 所以最开始我们求sum的时候把min和一起求了 后面还有用(max不用求 就是sum)
public static int unformedSum(int[] arr) {if (arr == null || arr.length == 0) {return 1;}int N = arr.length;int Min = Integer.MAX_VALUE;int sum = 0;for (int i : arr) {Min = Math.min(Min,i);sum+=i;}boolean [][] dp = new boolean [N][sum+1];for(int i = 1;i<N;i++) {dp[i][0] = true;}for(int i = 0;i<N;i++) {for(int j = 1;j<sum+1;j++) {dp[i][j] = dp[i-1][j]||((j-arr[i]>=0)?dp[i-1][j-arr[i]]:false);}}for (int j = min; j <= sum; j++) {if (!dp[N - 1][j]) {return j;}}return sum + 1;}
那么升级版的问题怎么解
整数数组永远包含1 也就是最小不可组成和永远要从2开始算
先把整个数组从小到大排序
定义一个变量 range 含义是1.....range的数都可求
a是一个遍历数组的指针
如果a<=range+1
那么就 range = range+a 举个例子 a = 5 range = 20 也就是说range<=20的所有数都能求出来 那么就必然存在 20+5 19+5 18+5 +17+5 让它把21到25填好
如果a>range+1
a = 50; range = 30 50>31 前面的一堆玩意都凑不出来31 你比31还大 那更凑不出来了 第一个出现的大于就是最小不可求
为啥是range+1 当a==range+1 的时候 假如说a = 5 range = 4 这个时候 9完全可以通过之前的方式得出 如果a要是再大一点呢 6 a = 6 range仍等于4 那么4通过累加就无法得到5了 他们就缺了 这个东西本质是要求 range的最大范围要和a是相邻的 才能严格的推出每一个值
4 5 相邻 中间没有真空期是前面的累加不出来 加上后面的更累加不出来 range后面这个数 要一个数加上a才能累加出来 如果a本身就比这个大 那家多少都累加不出来
public static int unformedSum3(int[] arr) {if (arr == null || arr.length == 0) {return 0;}Arrays.sort(arr); // O (N * logN)int range = 1;// arr[0] == 1for (int i = 1; i != arr.length; i++) {if (arr[i] > range + 1) {return range + 1;} else {range += arr[i];}}return range + 1;}