算法打卡第二天

81 阅读5分钟

算法思路

1717. 删除子字符串的最大得分思路:我的初版逻辑因为只有两种条件一个是ab一个是ba那么我的思路就是首先我们获取只包含ab并且长度大于2的字串,然后返回然后根据xy的优先级,哪个高优先级就是哪个,但是这个的时间复杂度实在是太高了On²的复杂度因为先获取字串需要一层循环然后遍历又是循环时间复杂度太高了导致超时。

class Solution {
   public int maximumGain(String s, int x, int y) {
        int ans = 0;
        List<String> strings = extractValidABSubstrings(s);
        String up = null,lp = null;
        int high = 0,low = 0;
        if (x>y){
            up = "ab";
            lp = "ba";
            high = x;
            low = y;
        }else {
            up = "ba";
            lp = "ab";
            high = y;
            low = x;
        }
        for (int i = 0; i < strings.size(); i++) {
            String str = strings.get(i);
            while (str.contains("a")&&str.contains("b")) {
                if (str.contains(up)) {
                    int index = str.indexOf(up);
                    str = str.substring(0, index) + str.substring(index + 2);
                    ans+=high;
                }else if (str.contains(lp)) {
                    int index = str.indexOf(lp);
                    str = str.substring(0, index) + str.substring(index + 2);
                    ans+=low; 
                }
            }
        }
        return ans;
    }
    //获取只包含ab的字串
    public static List<String> extractValidABSubstrings(String input) {
        List<String> result = new ArrayList<>();
        StringBuilder current = new StringBuilder();
        for (int i = 0; i < input.length(); i++) {
            char c = input.charAt(i);
            if (c == 'a' || c == 'b') {
                current.append(c);
            } else {
                // 遇到非a/b字符,检查并保存当前累积的子串
                if (current.length() >= 2) {//只有当长度大于2的情况下才获取到这个字串
                    result.add(current.toString());
                }
                current.setLength(0); // 重置
            }
        }
        // 检查字符串末尾可能存在的有效子串
        if (current.length() >= 2) {
            result.add(current.toString());
        }
        return result;
    }
}

看了别人的思路优化了一下代码,使用栈加两次循环即可,优先循环处理原先字符串里面满足高优先级的然后把其余不满足的存储起来到第二次循环在使用就可也只是用两次循环也就是On的时间复杂度即可

class Solution {
    public int maximumGain(String s, int x, int y) {
        int ans = 0;
        boolean isABPriority = x > y;
        String highStr = isABPriority ? "ab" : "ba";
        String lowStr = isABPriority ? "ba" : "ab";
        int highScore = Math.max(x, y);
        int lowScore = Math.min(x, y);

        // 处理高优先级
        StringBuilder sb = new StringBuilder();
        for (char c : s.toCharArray()) {
            if (sb.length() > 0 &&
                    sb.charAt(sb.length() - 1) == highStr.charAt(0) &&
                    c == highStr.charAt(1)) {
                // 出栈
                sb.deleteCharAt(sb.length() - 1);//如果满足条件就不如栈然后删除上一个关联的字符
                ans += highScore;
            } else {
                // 入栈
                sb.append(c);
            }
        }

        // 处理低优先级
        StringBuilder sb2 = new StringBuilder();
        for (char c : sb.toString().toCharArray()) {
            if (sb2.length() > 0 &&
                    sb2.charAt(sb2.length() - 1) == lowStr.charAt(0) &&
                    c == lowStr.charAt(1)) {
                // 出栈
                sb2.deleteCharAt(sb2.length() - 1);
                ans += lowScore;
            } else {
                // 入栈
                sb2.append(c);
            }
        }
        return ans;
    }
}

2134. 最少交换次数来组合所有的 1 II思路:想不出来看了大佬的题解自己理了一下,先求出所有的1的个数,那么这个个数k就是这个滑动窗口的大小然后每次取这个固定大小的窗口然后里面的值加起来因为只会是1和0然后加起来k减去这个总和就是需要交换的次数然后求最小值,但是由于题目是有头尾相邻的这个条件那么就需要计算头尾相邻的这个窗口也就是使用nums[(i+k-1)%n] - nums[i-1]这个是用来nums[(i+k-1)]是最新达到的右边界,然后-nums[i-1]是去除掉左边界溢出的窗口值。然后第一次小于k的时候就已经建立了一个固定大小为k的窗口然后下面的循环就是按着这个窗口来扩展。

class Solution {
    public int minSwaps(int[] nums) {
        int n = nums.length;
        int res = Integer.MAX_VALUE;
        int k = 0;
        for(int i = 0; i < n; i++){
            if(nums[i] == 1){
                k++;
            }
        }
        int sum = 0;
        for(int i = 0; i < k; i++){
            sum += nums[i];
        }
        res = Math.min(res, k - sum);
        for(int i = 1; i < n; i++){
            sum += nums[(i+k-1)%n] - nums[i-1];
            res = Math.min(res, k - sum);
        }
        return res;
    }
}

1297. 子串的最大出现次数思路:四个参数中maxSize起到了一个装饰品的作用啊哈哈哈哈,思路是这样的来着,创建两个哈希列表,一个用来存储出现过的字母次数,然后继续开始创建窗口,右窗口往右边延申,如果已经满足了最小条件也就是等于需要的尺寸minSize那么就判断是否是我们需要的字符串如果是就存储起来并更新ans取最大如果大于这个了就收缩左窗口,至于为什么用不到maxSize因为如果这个长度的字母个数小于参数给的那么他在小一点也一定满足。

class Solution {
    public int maxFreq(String s, int maxLetters, int minSize, int maxSize) {
        int ans = 0;
        HashMap<Character, Integer> charCount = new HashMap<>();
        HashMap<String, Integer> substringCount = new HashMap<>();
        int left = 0, right = 0;
        for (right = 0; right < s.length(); right++) {
            char c = s.charAt(right);
            charCount.put(c, charCount.getOrDefault(c, 0) + 1);
            // 当窗口大小超过minSize时,移动左指针缩小窗口
            while (right - left + 1 > minSize) {
                char leftChar = s.charAt(left);
                charCount.put(leftChar, charCount.get(leftChar) - 1);
                if (charCount.get(leftChar) == 0) {
                    charCount.remove(leftChar);
                }
                left++;
            }
            if (right - left + 1 == minSize && charCount.size() <= maxLetters) {
                String substring = s.substring(left, right + 1);
                substringCount.put(substring, substringCount.getOrDefault(substring, 0) + 1);
                ans = Math.max(ans, substringCount.get(substring));
            }
        }
        return ans;
    }
}

2269. 找到一个数字的 K 美丽值思路:简单的一个滑动窗口就是不能除零有个坑其他很简单就是单纯的滑动窗口做法找窗口符合加结果就可以了

class Solution {
  public int divisorSubstrings(int num, int k) {
        int ans = 0;
        String s = String.valueOf(num);
        int left = 0;
        for (int right = 0; right < s.length(); right++) {
            while (right - left + 1 > k) {
                left++;
            }
            if (right - left + 1 == k) {
                String substring = s.substring(left, right + 1);
                int sb = Integer.parseInt(substring);
                if (sb != 0 && num % sb == 0) {
                    ans++;
                }
            }
        }
        return ans;
    }
}

1358. 包含所有三种字符的子字符串数目思路:只包含a,b,c三种不同的字母,至少出现过一次如果出现了至少出现过一次的窗口,那么这个[L,R]的窗口更大的窗口也一定符合也就是如果找到了符合条件的窗口然后直接总长度减去右边闭合窗口的位置就可以了。一开始忘记**left++**了调试了半天,总之就是越大越好,滑到符合条件的窗口然后获取这个窗口可以扩大的所有窗口相加就可以。

class Solution {
 public int numberOfSubstrings(String s) {
        int ans = 0;
        HashMap<Character, Integer> charCount = new HashMap<>();
        int n = s.length();
        int left = 0;
        for (int right = 0; right < s.length(); right++) {
            char c = s.charAt(right);
            charCount.put(c, charCount.getOrDefault(c, 0) + 1);
            while (charCount.size()==3){
                char leftChar = s.charAt(left);
                ans+= n-right;
                charCount.put(leftChar, charCount.get(leftChar) - 1);
                if (charCount.get(leftChar) == 0) {
                    charCount.remove(leftChar);
                }
                left++;
            }
        }
        return ans;
    }
}