【题型总结】使用双指针+哈希表寻找符合某种条件的字符串/数字的个数
创始人
2024-03-21 16:33:56
0次

文章目录

  • 双指针[滑动窗口]+哈希表
    • 变位词
      • 字符串中的排列【LC567】
      • 字符串中所有的变位词【LC438】
    • 不含重复字符的最长子字符串【LC3】
      • 哈希表+双指针
    • 含有所有字符的最短字符串/最小覆盖子串【LC76】
    • 字符串中不同整数的数目【LC1805】

双指针[滑动窗口]+哈希表

变位词

  • 定义

    变位词是指组成各个单词的字母及每个字母出现的次数完全相同,只是字母排列的顺序不同。如“pots”、“stop”和“tops”

  • 特点

    • 长度相同
    • 字母集合相同,并且每个字母出现的次数相同

字符串中的排列【LC567】

2022/10/27

给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的某个变位词。

换句话说,第一个字符串的排列之一是第二个字符串的 子串 。

  • 思路:s1的字符在s2中全部存在,并且连续(子串),返回true

    • 记录第一出现的字符的位置和最后一个出现的字符的位置,如果相减长度为s1的长度,那么证明是连续出现的子串
      • 不对 字母可能多次出现 并且重复出现
    • 逐一判断字符串s2中长度为n的子字符串是不是字符串s1的变位词
  • 实现

    • 使用哈希表存储s1中出现的字符及其次数

    • 使用双指针定位一个子字符串,其中一个指针left指向子字符串的第一个字符,另一个指针指向子字符串的最后一个字符

      • 每当在子字符串中添加一个字符时,就把哈希表中对应位置的值减1
      • 每当在子字符串中删除一个字符时,就把哈希表中对应位置的值加1
    • 如果子字符串对应的charToCount全为0,那么证明该子字符串为字符串s1的变位词,返回true

    • 代码

      class Solution {public boolean checkInclusion(String s1, String s2) {if (s1.length() > s2.length()){return false;}int len1 = s1.length();int[] charToCount = new int[26];for (int i = 0; i < len1; i++){charToCount[s1.charAt(i)-'a']++;charToCount[s2.charAt(i)-'a']--;}if (areAllZeor(charToCount)){return true;}for (int i = len1; i < s2.length(); i++){charToCount[s2.charAt(i-len1)-'a']++;// 删除左指针对应的字符charToCount[s2.charAt(i)-'a']--;// 添加右指针对应的字符if (areAllZeor(charToCount)){return true;}}return false;}public boolean areAllZeor(int[] charToCount){for (int i = 0; i < charToCount.length; i++){if (charToCount[i] != 0){return false;}}return true;}
      }
      
    • 复杂度

      • 时间复杂度:O(n+m)O(n+m)O(n+m)
      • 空间复杂度:O(1)O(1)O(1)

字符串中所有的变位词【LC438】

2022/10/27

给定两个字符串 s 和 p,找到 s 中所有 p 的 变位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

变位词 指字母相同,但排列不同的字符串。

  • 思路:逐一判断字符串s中长度为lenP的子字符串是不是字符串p的变位词,如果是则将起始索添加至结果集

  • 实现

    • 使用哈希表存储p中出现的字符及其次数
    • 使用双指针定位一个子字符串,其中一个指针left指向子字符串的第一个字符,另一个指针指向子字符串的最后一个字符
      • 每当在子字符串中添加一个字符时,就把哈希表中对应位置的值减1
      • 每当在子字符串中删除一个字符时,就把哈希表中对应位置的值加1
    • 如果子字符串对应的charToCount全为0,那么证明该子字符串为字符串p的变位词,则将起始索添加至结果集
    • 最后返回结果集
  • 代码

    class Solution {public boolean checkInclusion(String s1, String s2) {if (s1.length() > s2.length()){return false;}int len1 = s1.length();int[] charToCount = new int[26];for (int i = 0; i < len1; i++){charToCount[s1.charAt(i)-'a']++;charToCount[s2.charAt(i)-'a']--;}if (areAllZeor(charToCount)){return true;}for (int i = len1; i < s2.length(); i++){charToCount[s2.charAt(i-len1)-'a']++;// 删除左指针对应的字符charToCount[s2.charAt(i)-'a']--;// 添加右指针对应的字符if (areAllZeor(charToCount)){return true;}}return false;}public boolean areAllZeor(int[] charToCount){for (int i = 0; i < charToCount.length; i++){if (charToCount[i] != 0){return false;}}return true;}
    }
    
    • 复杂度

      • 时间复杂度:O(n+m)O(n+m)O(n+m)
      • 空间复杂度:O(1)O(1)O(1)

不含重复字符的最长子字符串【LC3】

给定一个字符串 s ,请你找出其中不含有重复字符的 最长连续子字符串 的长度。

2022/10/27

哈希表+双指针

  • 思路:使用双指针定位一个子字符串,使用哈希表判断子字符串是否存在重复元素,如果有重复元素,当出现重复字符,收缩左边界直到重复字符消失

  • 实现

    • 使用哈希set存储目前存放的字符
    • 使用双指针定位一个子字符串,其中一个指针left指向子字符串的第一个字符,另一个指针指向子字符串的最后一个字符
      • 每当当前字符不位于哈希表中时,向右移动right
      • 每当当前字符位于哈希表中时,
        • 判断是否需要更新res,此时子字符串的长度为right-left
        • 然后向右移动left,找到重复的位置,并找到新的初始位置[每移动一次left,需要将对应的字符在哈希表中删除]
    • 最后返回结果
  • 代码 hashset

    class Solution {public int lengthOfLongestSubstring(String s) {Set words = new HashSet<>();int res = 0;int left = 0;int right = 0;while ( left <= right && right < s.length()){if (!words.contains(s.charAt(right))){words.add(s.charAt(right));}else{res = Math.max(res,right - left);while (left < s.length() && s.charAt(left) != s.charAt(right)){words.remove(s.charAt(left));left++;}left++;}right++;}res = Math.max(res,right - left);return res;}
    }
    
    • 复杂度
      • 时间复杂度:O(n)O(n)O(n)
      • 空间复杂度:O(1)O(1)O(1),hashset最大的容量为ASCII码的多少,为256
  • 代码 boolean数组

    class Solution {public int lengthOfLongestSubstring(String s) {boolean[] words = new boolean[256];int res = 0;int left = 0;int right = 0;while ( left <= right && right < s.length()){if (!words[s.charAt(right)]){words[s.charAt(right)] = true;}else{res = Math.max(res,right - left);while (left < s.length() && s.charAt(left) != s.charAt(right)){words[s.charAt(left)] = false;left++;}left++;}right++;}res = Math.max(res,right - left);return res;}
    }
    

含有所有字符的最短字符串/最小覆盖子串【LC76】

给定两个字符串 s 和 t 。返回 s 中包含 t 的所有字符的最短子字符串。如果 s 中不存在符合条件的子字符串,则返回空字符串 "" 。

如果 s 中存在多个符合条件的子字符串,返回任意一个。

注意: 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。

[可以包括其他字符的最短’变位词’]

2022/10/29

  • 思路:使用双指针定位s的子字符串,使用哈希表记录出现的字母及次数

    • 当该子字符串包含t的所有字符时,判断是否是最短子字符串,是则记录左右边界
    • 如果左边界出现重复字符并且不是所需要的字符,那么收缩左边界
  • 实现:

    • 使用哈希表存储t中出现的字符及其次数
    • 使用双指针定位一个子字符串,其中一个指针left指向子字符串的第一个字符,另一个指针right指向子字符串的最后一个字符
      • 每当在子字符串中添加一个字符时,就把哈希表中对应位置的值减1
      • 每当在子字符串中删除一个字符时,就把哈希表中对应位置的值加1
    • 如果子字符串对应的charToCounts全为0,那么证明该子字符串包含t的所有字符,判断是否是最短子字符串,是则记录左右边界
    • 最后返回判断是否存在子字符串,是则返回s.sbstring(left,right),否则返回空字符串
  • 代码

    class Solution {public String minWindow(String s, String t) {if (s.length() < t.length()){return "";}int[] charToCounts = new int['z'-'A'+1];int left = 0;int right = 0;int minLeft = 0;// 左闭右闭int minRight = Integer.MAX_VALUE;for (; right < t.length(); right++){charToCounts[t.charAt(right)-'A']++;charToCounts[s.charAt(right)-'A']--;}if (isContainsAll(charToCounts)){minRight = right - 1;}while (right < s.length()){charToCounts[s.charAt(right)-'A']--;// 左边界有重复字符或者不是所需字符 收缩左边界while (left <= right && charToCounts[s.charAt(left)-'A'] < 0){     charToCounts[s.charAt(left)-'A']++;left++;}if (isContainsAll(charToCounts)){// 包含全部字符if (right - left < minRight - minLeft){minRight = right;minLeft = left;}}right++;}return minRight == Integer.MAX_VALUE ? "" : s.substring(minLeft,minRight+1);}public boolean isContainsAll(int[] charToCounts){for(int i = 0; i < charToCounts.length; i++){if (charToCounts[i] > 0){return false;}}return true;}
    }
    
  • 复杂度

    • 时间复杂度:O(n)O(n)O(n)
    • 空间复杂度:O(C)O(C)O(C),charToCounts的容量为’z’-‘A’+1

字符串中不同整数的数目【LC1805】

You are given a string word that consists of digits and lowercase English letters.

You will replace every non-digit character with a space. For example, "a123bc34d8ef34" will become " 123 34 8 34". Notice that you are left with some integers that are separated by at least one space: "123", "34", "8", and "34".

Return the number of different integers after performing the replacement operations on word.

Two integers are considered different if their decimal representations without any leading zeros are different.

2022/12/6

  • 思路:使用双指针定位字符串中整数的起始位置和结束位置,去除前导0后,将该整数放入哈希表中,最后返回哈希表的大小即可。

    • 如果左指针为字母,那么右移左指针,左指针为整数的起始位置
    • 如果右指针为数字,那么右移右指针,右指针为整数的结束位置+1
  • 实现

    class Solution {public int numDifferentIntegers(String word) {Set set = new HashSet<>();int len = word.length();int l = 0;        int r = 0;while (l < len && r < len){// 找到左边界while (l < len && word.charAt(l) >= 'a' && word.charAt(l) <= 'z'){l++;}if (l == len){break;}// 找到右边界r = l;while ( r < len && word.charAt(r) >= '0' && word.charAt(r) <= '9') {r++;}// 去除前导0while (l != r && word.charAt(l) == '0'){l++;}String num = word.substring(l, r);set.add(num);l = r + 1;}return set.size();}
    }
    
    • 复杂度

      • 时间复杂度:O(n)O(n)O(n)
      • 空间复杂度:$

相关内容

热门资讯

前端-session、jwt 目录:   (1)session (2&#x...
linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
关于测试,我发现了哪些新大陆 关于测试 平常也只是听说过一些关于测试的术语,但并没有使用过测试工具。偶然看到编程老师...
前缀和与对数器与二分法 1. 前缀和 假设有一个数组,我们想大量频繁的去访问L到R这个区间的和,...
nodejs:本地安装nvm实... 一、背景-使用不同版本node的原因 vue3+ts、nuxt3版本,node...
JAVA集合知识整理 Java集合知识整理 HashMap相关 HashMap的底层数据结构:jdk1.8之...
无刷直流电机介绍及单片机控制实... 无刷直流电机介绍及单片机控制实例前言基本概念优势与劣势使用寿命基本结构使用单片机控制实例电子调速器&...
fwdiary(2) dp2 1.传纸条  AcWing 275. 传纸条 - AcWing 走两条路,走一条最大的...
常用的DOS命令 常用的DOS命令 DOS(Disk Operating System,磁...
<C++> 类和对象(下) 1.const成员函数将const修饰的“成员函数”称之为const成员函数,cons...