LeetCode 滑动窗口题型练习
字符串的排列
题目
给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。
换句话说,第一个字符串的排列之一是第二个字符串的子串。
示例1:
输入: s1 = “ab” s2 = “eidbaooo”
输出: True
解释: s2 包含 s1 的排列之一 (“ba”).
示例2:
输入: s1= “ab” s2 = “eidboaoo”
输出: False
注意:
输入的字符串只包含小写字母
两个字符串的长度都在 [1, 10,000] 之间
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutation-in-string
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
代码
public static boolean checkInclusion(String s1, String s2) {
if (s1 == null || s2 == null || s1.length() == 0 || s2.length() == 0 || s1.length() > s2.length()){
return false ;
}
char[] chars1 = s1.toCharArray() ;
char[] chars2 = s2.toCharArray() ;
//s1中字符的数量记录
int[] map1 = new int[26] ;
//s2中字符的数量记录
int[] map2 = new int[26] ;
//s1和s2中相同字符对应数量相等的记录
int count = 0 ;
for (int i=0 ; i<chars1.length ; i++){
map1[chars1[i] - 'a'] ++ ;
//初始化第一个窗口
map2[chars2[i] - 'a'] ++ ;
}
//先判断第一个窗口与s1的对应
for (int i=0 ; i<26 ; i++){
if (map1[i] == map2[i]){
count ++ ;
}
}
int r , l ;
int len = chars2.length - chars1.length ;
for (int i=0 ; i<len ; i++){
r = chars2[i + chars1.length] - 'a' ;
l = chars2[i] - 'a' ;
if (count == 26){
return true ;
}
//核心
//右边加入的在map2中对应++
map2[r] ++ ;
if (map1[r] == map2[r]){
count ++ ;
}else if (map1[r] == map2[r] - 1){
count -- ;
}
//左边弹出的在map2中对应--
map2[l] -- ;
if (map1[l] == map2[l]){
count ++ ;
}else if (map1[l] == map2[l] + 1){
count -- ;
}
}
return count == 26 ;
}
替换后的最长重复字符
题目
给你一个仅由大写英文字母组成的字符串,你可以将任意位置上的字符替换成另外的字符,总共可最多替换 k 次。在执行上述操作后,找到包含重复字母的最长子串的长度。
注意:
字符串长度 和 k 不会超过 104。
示例 1:
输入:
s = “ABAB”, k = 2
输出:
4
解释:
用两个’A’替换为两个’B’,反之亦然。
示例 2:
输入:
s = “AABABBA”, k = 1
输出:
4
解释:
将中间的一个’A’替换为’B’,字符串变为 “AABBBBA”。
子串 “BBBB” 有最长重复字母, 答案为 4。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-repeating-character-replacement
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
代码
public int characterReplacement(String s, int k) {
if (s == null || s.length() == 0){
return 0 ;
}
int left = 0 ;
int right = 0 ;
int historyMax = 0 ;
//记录当前窗口中每种字符的个数
int[] dp = new int[26] ;
//思路:
//当前窗口是遍历过的多大的窗口,如果当前内容非法,就移动窗口。如果当前的窗口不违法,扩容。
for (right = 0 ; right < s.length() ; right ++){
//
dp[s.charAt(right) - 'A'] ++ ;
//当前窗口最多的字符
historyMax = Math.max(historyMax , dp[s.charAt(right) - 'A']) ;
//窗口违法
if (historyMax +k < right - left + 1){
dp[s.charAt(left) - 'A'] -- ;
left ++ ;
}
}
return s.length() - left ;
}
最大连续1的个数
题目
给定一个由若干 0 和 1 组成的数组 A,我们最多可以将 K 个值从 0 变成 1 。
返回仅包含 1 的最长(连续)子数组的长度。
示例 1:
输入:A = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出:6
解释:
[1,1,1,0,0,1,1,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 6。
示例 2:
输入:A = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3
输出:10
解释:
[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 10。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/max-consecutive-ones-iii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
代码
public int longestOnes(int[] A, int K) {
if (A == null || A.length == 0){
return 0 ;
}
int historyMax = 0 ;
int left = 0 ;
int right = 0 ;
int[] dp = new int[2] ;
//类似于上一道题
for ( ; right < A.length ; right ++){
dp[A[right]] ++ ;
historyMax = Math.max(historyMax , dp[1]) ;
if (historyMax + K < right - left + 1){
dp[A[left]] -- ;
left ++ ;
}
}
return historyMax + K < A.length ? historyMax + K : A.length ;
}
可获得的最大点数
题目
张卡牌 排成一行,每张卡牌都有一个对应的点数。点数由整数数组 cardPoints 给出。
每次行动,你可以从行的开头或者末尾拿一张卡牌,最终你必须正好拿 k 张卡牌。
你的点数就是你拿到手中的所有卡牌的点数之和。
给你一个整数数组 cardPoints 和整数 k,请你返回可以获得的最大点数。
示例 1:
输入:cardPoints = [1,2,3,4,5,6,1], k = 3
输出:12
解释:第一次行动,不管拿哪张牌,你的点数总是 1 。但是,先拿最右边的卡牌将会最大化你的可获得点数。最优策略是拿右边的三张牌,最终点数为 1 + 6 + 5 = 12 。
示例 2:
输入:cardPoints = [2,2,2], k = 2
输出:4
解释:无论你拿起哪两张卡牌,可获得的点数总是 4 。
示例 3:
输入:cardPoints = [9,7,7,9,7,7,9], k = 7
输出:55
解释:你必须拿起所有卡牌,可以获得的点数为所有卡牌的点数之和。
示例 4:
输入:cardPoints = [1,1000,1], k = 1
输出:1
解释:你无法拿到中间那张卡牌,所以可以获得的最大点数为 1 。
示例 5:
输入:cardPoints = [1,79,80,1,1,1,200,1], k = 3
输出:202
提示:
1 <= cardPoints.length <= 10^5
1 <= cardPoints[i] <= 10^4
1 <= k <= cardPoints.length
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-points-you-can-obtain-from-cards
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
代码
public int maxScore(int[] cardPoints, int k) {
if (cardPoints == null || cardPoints.length == 0 || k == 0){
return 0 ;
}
//构造第一个窗口
int size = cardPoints.length - k ;
int count = 0 ;
int min = 0 ;
int sum = 0 ;
for (int i=0 ; i<size ; i++){
count += cardPoints[i] ;
}
sum = count ;
min = count ;
for (int i=size ; i < cardPoints.length ; i++){
count -= cardPoints[i - size] ;
count += cardPoints[i] ;
min = Math.min(min , count) ;
sum += cardPoints[i] ;
}
return sum - min ;
}
满足条件的子序列数目(超时)
题目
给你一个整数数组 nums 和一个整数 target 。
请你统计并返回 nums 中能满足其最小元素与最大元素的 和 小于或等于 target 的 非空 子序列的数目。
由于答案可能很大,请将结果对 10^9 + 7 取余后返回。
示例 1:
输入:nums = [3,5,6,7], target = 9
输出:4
解释:有 4 个子序列满足该条件。
[3] -> 最小元素 + 最大元素 <= target (3 + 3 <= 9)
[3,5] -> (3 + 5 <= 9)
[3,5,6] -> (3 + 6 <= 9)
[3,6] -> (3 + 6 <= 9)
示例 2:
输入:nums = [3,3,6,8], target = 10
输出:6
解释:有 6 个子序列满足该条件。(nums 中可以有重复数字)
[3] , [3] , [3,3], [3,6] , [3,6] , [3,3,6]
示例 3:
输入:nums = [2,3,3,4,6,7], target = 12
输出:61
解释:共有 63 个非空子序列,其中 2 个不满足条件([6,7], [7])
有效序列总数为(63 - 2 = 61)
示例 4:
输入:nums = [5,2,4,1,7,6,8], target = 16
输出:127
解释:所有非空子序列都满足条件 (2^7 - 1) = 127
提示:
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^6
1 <= target <= 10^6
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/number-of-subsequences-that-satisfy-the-given-sum-condition
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
代码
public int numSubseq(int[] nums, int target) {
if (nums == null || nums.length == 0){
return 0 ;
}
//双指针
//1.sort一下
//2.
//3.while left <= right
//4.找到right最大位置
//5.计算子序列的个数
//6.left ++
long mod = 1_0000_0000_7 ;
long count = 0 ;
Arrays.sort(nums);
int left = 0 ;
int right = nums.length - 1 ;
while(left <= right){
//找到right的位置
while(right >= 0 &&nums[left] + nums[right] > target){
right -- ;
}
//System.out.println("范围:" + nums[left] + " - " + nums[right]);
//cal
int l = right - left + 1 ;
if (right < left){
break ;
}
count += cal(l , mod);
//System.out.println("总数量:"+count);
left ++ ;
}
return (int) (count % mod);
}
//移位的时候可能会溢出
public long cal(int l , long mod){
long k = 1 ;
for (int i=0 ; i<l-1 ; i++){
k = k << 1 ;
if (k > mod){
k = k % mod ;
}
}
return k ;
}