【算法基础】真题实战 - 求A中第一个包含B中所有字符的最小字串

题目详情

输入一个字符串 A 和 一个字符集 B,请找出 A 中 第一个 包含 B中所有字符 的最小字串

题目分解/做题思路

1、输入两个字符串A 和 B
2、求 A 的 字串
3、子串需要注意的地方:
 1、B是可以有重复字母的
 2、A的子串必须包含B的所有字符
 3、B在 A 的子串中是可以 乱序,或者不连续的
 4、满足以上要求的第一次出现的子串

解题方法 == 〉双指针、滑动窗口

  根据题目分解后,明白了要求的主要就是 求一个 A的子串,然后 他 包含了 B的所有字符。可以使用滑动窗口,首先让窗口逐渐拉大,直到全部包含B中的字符。完全包含B中的字符之后,开始缩小窗口,直到达到临界值,然后记录窗口的起始位置和最小窗口的长度。最后返回这段字符

解题步骤

public static void main(String[] args) {
       String A = "ADOBECODEBANC";
       String B = "ABCC";
       String minimumWindowSubstring = findMinimumWindowSubstring(A, B);
       System.out.println("最小字串: " + minimumWindowSubstring);
   }
   public static String findMinimumWindowSubstring(String A, String B) {
       // 创建字符集B的频率映射
       Map<Character, Integer> charFreqMapB = new HashMap<>();
       for (char c : B.toCharArray()) {
           charFreqMapB.put(c, charFreqMapB.getOrDefault(c, 0) + 1);
       }

       int left = 0; // 窗口左边界
       int right = 0; // 窗口右边界
       int minWindowLength = Integer.MAX_VALUE; // 最小窗口长度,这样做是为了确保我们能够找到一个比任何可能的窗口长度都要小的初始值,从而在后续的逻辑中进行比较和更新。
       int minWindowStart = 0; // 最小窗口起始位置
       int count = charFreqMapB.size(); // 字符集B中字符的数量,用于判断当前窗口是否已经包含了字符集B中的所有字符。

       /**
        * 遍历字符串A,使用滑动窗口来寻找A 包含 B 的最小字串
        * 当窗口包含了B 的一个字符时,更新频率和count
        * 当窗口包含了B 的所有字符时,开始缩小窗口,更新窗口的长度和起始位置
        */
       // 遍历字符串A
       while (right < A.length()) {

           char c = A.charAt(right);
           //当窗口包含了B中的一个字符时,会将对应字符在charFreqMapB中的频率-1.如果减1后的频率为0
           //说明窗口已经完全包含了该字符,因此将 count 值 -1
           if (charFreqMapB.containsKey(c)) {
               charFreqMapB.put(c, charFreqMapB.get(c) - 1);
               if (charFreqMapB.get(c) == 0) { // 字符B的 字母X已经被全部包含
                   count--;  //B中字母的数量 -1
               }
           }

           // 当窗口包含了字符集B中的所有字符时,即count == 0
           while (count == 0) {
               // 更新最小窗口长度和起始位置
               //窗口的长度 right - left +1
               if (right - left + 1 < minWindowLength) {
                   minWindowLength = right - left + 1;
                   minWindowStart = left;
               }

               char leftChar = A.charAt(left); // 获取窗口左边界对应的字符
               if (charFreqMapB.containsKey(leftChar)) {//判断字符是否存在与B中
                   //将映射中的字符频率+1,表示在缩小窗口时,将原来在窗口中的字符排除在外
                   charFreqMapB.put(leftChar, charFreqMapB.get(leftChar) + 1);
                   //如果大于0,表示字符leftChar是窗口外的字符,将count+1,追踪未覆盖的字符数量
                   if (charFreqMapB.get(leftChar) > 0) {
                       count++;
                   }
               }
               left++;//缩小窗口
           }
           right++;//扩大窗口
       }

       // 如果没有找到包含字符集B的最小字串,则返回空字符串
       if (minWindowLength == Integer.MAX_VALUE) {
           return "";
       }

       // 返回最小字串
       return A.substring(minWindowStart, minWindowStart + minWindowLength);
    }

相关标签:子串 双指针 滑动窗口

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值