【Q1371】(md) 每个元音包含偶数次的最长字符串
给你一个字符串 s ,请你返回满足以下条件的最长子字符串的长度:每个元音字母,即 ‘a’,‘e’,‘i’,‘o’,‘u’ ,在子字符串中都恰好出现了偶数次。
示例 1:输入:s = “eleetminicoworoep”
输出:13
解释:最长子字符串是 “leetminicowor” ,它包含 e,i,o 各 2 个,以及 0 个 a,u 。
示例 2:输入:s = “leetcodeisgreat”
输出:5
解释:最长子字符串是 “leetc” ,其中包含 2 个 e 。
示例 3:输入:s = “bcbcbc”
输出:6
解释:这个示例中,字符串 “bcbcbc” 本身就是最长的,因为所有的元音 a,e,i,o,u都出现了 0 次。
提示: 1 <= s.length <= 5
class Solution {
/*
* 【状态压缩】【哈希表】【前缀和】
*
* 对于aexye,遍历一遍,发现每个位置都可以用一串数字表示状态(利用异或(^)可以写出每个位置的状态):
* 00000(-1位置), 10000, 11000, 11000, 11000,10000
* 又发现,在a位置,和第二个e的位置,其状态是相同的!!!我们把这两个位置相减就得到了一个符合条件的字符串!
* 基本思路:
* 第一次出现此状态时,就需要记录到下标,然后下次遇到相同状态,计算最大长度(我们记录的是第一次出现此状态的位置,不能再去更新它!)
* 即字典保存状态一次出现的位置(★☆★)
*
* 遍历 :
* 如果是aeiou:
* 对状态进行一次异或(偶数次的a就能回到原先的状态,e..同理。因此用异或)
* 用putIfAbsent(如果是第一次遇到该状态,就记录它及其位置;如果不是一次遇到,不要更新它)
* 不管是不是aeiou,只要此时状态之前出现过,就尝试更新最大长度
*
*/
public int findTheLongestSubstring(String s) {
int maxLen = 0;
int currentState = 0x00000;
Map<Character, Integer> vowel = new HashMap<Character, Integer>();
Map<Integer, Integer> state = new HashMap<Integer, Integer>();
vowel.put('a', 0x00001);
vowel.put('e', 0x00010);
vowel.put('i', 0x00100);
vowel.put('o', 0x01000);
vowel.put('u', 0x10000);
state.put(0, -1);
for(int i = 0 ; i < s.length() ; i++) {
if(vowel.containsKey(s.charAt(i))) {
currentState ^= vowel.get(s.charAt(i));
state.putIfAbsent(currentState, i); // 使用putIfAbsent方法添加键值对,如果已经存在对应的值,则依旧为原来的值
}
if(state.containsKey(currentState)) {
maxLen = Math.max(maxLen, i - state.get(currentState));
}
}
return maxLen;
}
}
【Q39】(md) 组合总和
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target的组合。
candidates 中的数字可以无限制重复被选取。
说明:所有数字(包括 target)都是正整数。 解集不能包含重复的组合。
示例 1:
输入: candidates = [2,3,6,7], target = 7, 所求解集为: [ [7], [2,2,3] ]
示例2:输入: candidates = [2,3,5], target = 8, 所求解集为: [ [2,2,2,2], [2,3,3], [3,5] ]
class Solution {
/*
* 【回溯算法】
*
* 画一画回溯图,思路其实很清晰的。但是我们从这题中,可以发现一些极其重要的细节:
* 如果暂存串是个字符串或者是个数字,那么traceback(...str + 'a'..)和traceback(...n+1..)
* 这样写毫无问题
* 但当暂存串是个StringBuffer或者ArrayList时,我们就能向上面那样【修改和传递直接一起写在traceback中】
* 我们需要先【add,再traceback,再remove】,即需要手动让暂存串回到刚才的位置——毕竟回溯时用的是一个公共暂存串
* 另外,在给res进行add时,add进去的一定是此时的暂存串的拷贝!否则add进去的是个引用,它随时变化!!!
*
* 此题【回溯】时的遇到的问题,在【Leetcode 46 全排列】中同样出现(个人记录8中)
*/
public List<List<Integer>> combinationSum(int[] candidates, int target) {
Arrays.sort(candidates);
int len = candidates.length;
List<Integer> tempList = new ArrayList<Integer>();
traceback(tempList, candidates, 0, len, 0, target);
return res;
}
public List<List<Integer>> res = new ArrayList<>();
private void traceback(List<Integer> tempList, int[] candidates, int n, int len, int sum, int target) {
if(sum == target) {
res.add(new ArrayList<Integer>(tempList));
return;
}
if(sum > target) {
return;
}
for(int i = n ; i < len ; i++) {
tempList.add(candidates[i]);
traceback(tempList, candidates, i, len, sum + candidates[i], target);
tempList.remove(tempList.size() - 1);
}
}
}
【Q40】(md) 组合总和Ⅱ
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明:所有数字(包括目标数)都是正整数。 解集不能包含重复的组合。
示例 1:输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为: [ [1, 7], [1, 2, 5], [2, 6], [1, 1, 6] ]
示例 2:输入: candidates = [2,5,2,1,2], target = 5,
所求解集为: [ [1,2,2], [5] ]
class Solution {
/*
* 和上题【Leetcode39组合总和】的思路几乎完全一致的【回溯算法】
*
* 首先和上题一样,有需要手动回溯状态的地方、以及add进的是tempList的拷贝而不是引用本身的问题,都需要万分注意
* 一个不同点是,每个数只能用一次,因此traceback内的for循环起始点不一样(这并不难)
*
* 真正的难点在于【避免重复】
* 以[1, 2, 2, 3, 5],target=8 为例,我们正常的回溯算法会出现[1, 2, 5][1, 2, 5]的重复
* 我们或许可以这样解决:
* 在for循环时,如果candidates[i] == candidates[i - 1],也就是此时用到的数字在上一个已经用过了,就continue(跳过此时重复的数字)
* 这有效避免了[1, 2, 5]的重复——因为第二个2被跳过了;但是[1, 2, 2, 3]这个结果就没了(因为第二个2被无情的跳过了)
*
* ★☆★ 一个精妙的语句:if(candidates[i] == candidates[i - 1] && i > layer)
* 这使得i==layer时候此时的数字不会被跳过,也就是说每轮for循环的第一个起始数字,必定合法!!
* 这样[1,2,2,3]的第二个2,没有被跳过,因为它是顺序写下来的(for循环的第一个);对于[1,2,5],2是for循环第二个才遍历到的,符合if条件,被跳过
*
*/
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
ArrayList<Integer> tempList = new ArrayList<>();
traceback(tempList, candidates, 0, 0, target);
return res;
}
private List<List<Integer>> res = new ArrayList<>();
private void traceback(ArrayList<Integer> tempList, int[] candidates, int layer, int sum, int target) {
if(sum == target) {
res.add(new ArrayList<Integer>(tempList));
return;
}
if(sum > target) {
return; // 剪枝
}
for(int i = layer ; i < candidates.length ; i++) {
if(i > 0 && candidates[i] == candidates[i - 1] && i > layer) {
continue;
}
tempList.add(candidates[i]);
traceback(tempList, candidates, i + 1, sum + candidates[i], target);
tempList.remove(tempList.size() - 1); // 手动回溯tempList的状态
}
}
// 在重复一遍本题避免重复的关键代码:if(candidates[i] == candidates[i - 1] && i > layer)
}
Qs from https://leetcode-cn.com
♥ loli suki
♣ end