算法——滑动窗口之找到字符串中所有的字母异位词,串联所有单词的子串

news/2024/6/18 21:40:18 标签: 算法, java, leetcode

6.找到字符串中所有的字母异位词

题目:. - 力扣(LeetCode)

6.1如何快速判断两个字符串是否是异位词

假设现在有s1 = 'aabca',s2 = 'abaca',那么这两个就是异位词,容易想到的判断方法就是将两个字符串按照字典序排序,再依次比较,但是时间复杂度很高;我们看看下面这种解法:不难发现如果两个字符串为异位词,那么他们之间的相同字符的个数也是相同的,如a有3个,b一个,c一个.那么我们就可以通过判断两个字符串中每种字符的个数是否相同,相同则是异位词,这个功能我们可以利用hash表来实现

6.2解决问题
 s = "cbaebabacd", p = "abc"

暴力解法当然是找出s中的所有长度为p.length 的子串,通过hash来判断是否为异位词,

我们在暴力解法上进行优化:

当我们判断前三个字符后,没必要将right回到b的位置再继续列举,因为实际上我们下一个要判断的是字符串"bae",但是我们在hash2中已经记录了b a的信息,我们只需将c移除hash2,再将a移入hash2即可.这就是滑动窗口的问题,与前面的题目不同的是,这里我们维护的窗口长度是固定的

滑动窗口步骤:

in表示right的元素,out表示left的元素

(1)进窗口 :hash2[in]++

(2)判断 如果 right - left + 1 > p.length,那么就要出窗口

(3)出窗口:hash2[out]--

(4)更新结果:判断hash1 和 hash2 字符情况是否相同

在此步骤判断的时候,如果按照上述的步骤来,我们可以建立两个长度为26的数组作为hash表,每次变量两个数组来判断是否相同,加上滑动窗口,时间复杂度为O(26n),即O(n)

这里是因为判断的为单个字符的数量,但是如果题目是要判断字符串的数量呢??

因此我们进行优化:利用count 来统计窗口中"有效字符"的个数

假设我们的字符串为:

我们的步骤是:

在进窗口时:

如此时,在hash2[in] ++之后,我们需要判断hash2[in] 与hash1[in] 的大小关系,如果hash2[in] <= hash1[in],那么说明我们加进来的是一个"有效字符",所谓的有效字符即我们这个字符能低效掉hash1中的某个字符

我们将right++,

此时由于hash2[in] > hash1[in],说明不是一个有效字符,那么count++,同理right再++

为有效字符,count更新为2

我们只需判断count 是否 等于 p.length即可,如果相等,那么即为异位词,并且我们这个判断可以穿插在每一次外循环中,因为只有count = p.length才能更新结果

当我们再次right++后,

这时候就要出窗口了,再出窗口时,我们依旧要判断hash2[out] 与hash1[out] 的大小关系,如果hash2[out] <= hash1[out],那么说明我们移出去的是一个"有效字符",那么count--,反之则不用

题解:

java"> public static List<Integer> findAnagrams(String s, String p) {
         List<Integer> ret = new ArrayList<>();
         int[] hash1 = new int[26];
         int[] hash2 = new int[26];
         for(int i = 0; i < p.length(); i++){
             hash1[p.charAt(i) - 'a']++;
         }
         for(int left = 0,right = 0,count = 0; right < s.length(); right++){
             int in = s.charAt(right) - 'a';
             hash2[in]++;
             if(hash2[in] <= hash1[in]){
                 count++;
             }
             if(right - left + 1 > p.length()){
                 int out = s.charAt(left++) - 'a' ;
                 if(hash2[out] <= hash1[out]) {
                     count--;
                 }
                 hash2[out]--;
             }
             if(count == p.length()){
                 ret.add(left);
             }
         }
         return ret;
     }

7.串联所有单词的子串

题目:. - 力扣(LeetCode)

理解题目后,如果我们做过滑动窗口的第6题,就会发现这道题和异位词实际上非常相似,只不过将原先的一个个单词转化为一个个字符串

具体的算法原理在第6题已经说过,我们来看看不同点

(1)哈希表

本题的hash表需要用Map<String,int>来实现

(2)left和right的次数

题目规定,words里面的单词都是等长的,我们记这个长为len,那么我们的left和right每次就都要移动len个字符,以实现跨过一个单词

(3)滑动窗口的执行次数

题目可能会出现"lingmindraboofooowingdingbarrwingmonkeypoundcake" ["fooo","barr","wing","ding","wing"]这样的情况,即left和right可能不是从0下标开始移动的

如上图,我们会发现找不到答案,而事实上还有下面几种可能:

当我们left从m开始的时候,会发现和第一种是一样的,因此我们只需要进行4次,而刚好是执行次数刚好是字符串的长度

也是由于这个问题,我们right的循环结束条件应该为:right + len <= s.length

题解:

java"> class Solution {
     public List<Integer> findSubstring(String s, String[] words) {
         List<Integer> list = new ArrayList<>();
         Map<String,Integer> hash1 = new HashMap<>();
         for(String str : words){
             hash1.put(str,hash1.getOrDefault(str,0)+1);
         }
         int len = words[0].length(),m = words.length;
         for(int i = 0; i < len; i++){
             Map<String,Integer> hash2 = new HashMap<>();
             for(int left = i,right = i,count = 0; right + len <= s.length(); right += len){
                 String in = s.substring(right,right+len);
                 hash2.put(in,hash2.getOrDefault(in,0)+1);
                 if(hash2.get(in) <= hash1.getOrDefault(in,0)){
                     count++;
                 }
                 if(right-left+1 > len * m){
                     String out = s.substring(left,left+len);
                     if(hash2.get(out) <= hash1.getOrDefault(out,0)){
                         count--;
                     }
                     hash2.put(out,hash2.get(out)-1);
                     left += len;
                 }
                 if(count == m){
                     list.add(left);
                 }
             } 
         }
         return list;
     }
 }


http://www.niftyadmin.cn/n/5420731.html

相关文章

OD_2024_C卷_200分_3、电脑病毒感染【JAVA】【图论 / 单源最短路径(dijkstra)】

题目描述 一个局域网内有很多台电脑&#xff0c;分别标注为 0 ~ N-1 的数字。相连接的电脑距离不一样&#xff0c;所以感染时间不一样&#xff0c;感染时间用 t 表示。 其中网络内一台电脑被病毒感染&#xff0c;求其感染网络内所有的电脑最少需要多长时间。如果最后有电脑不…

HBase安装,配置,启动,检查

目录: 一、HBase安装&#xff0c;配置 1、下载HBase安装包 2、解压&#xff0c;配置环境变量并激活 3、hbase 配置 4、将hadoop和zookeeper的配置文件创建软连接放在hbase配置目录 5、配置 regionserver 二、HBase启动与关闭&#xff0c;安装检验 1、启动关闭hbase的命令 2、 检…

Vue3+Vue Router使用<transition>过渡动画实现左右分栏后台布局

摘要 利用Vue3及其配套的Vue Router实现后台管理系统中的页面过渡动画。文章首先简要介绍了Vue3的特性和Vue Router的基本用法&#xff0c;利用Vue3提供的组件以及Vue Router的路由钩子函数来实现页面过渡效果。 代码结构 在 components 里有4个组件&#xff0c;其中 Layout…

【原创教程】S7-1200配方程序编写方法

1 绪论 1.1 本文的目的 在生产中我们的一台设备往往需要 对应很多种不同工艺或不同尺寸的设备,这就要求我们设备的参数需要经常变化。我们将每一种产品对应的参数保存起来,下一次再生产同种产品时可以迅速一键调用,而不是一个一个的去设置,这种功能就叫做配方(Recipe)。…

3.10 log | 647. 回文子串

647. 回文子串&#xff0c;516.最长回文子序列 class Solution { public:int countSubstrings(string s) {vector<vector<bool>> dp(s.size(),vector<bool>(s.size(),false));int result0;for(int is.size()-1;i>0;i--){for(int ji;j<s.size();j){if(…

POS 之 奖励机制

为什么需要有奖惩机制 如果没有奖励&#xff0c;就不会有节点参与POS&#xff0c;运营节点有成本&#xff0c;而奖励正是让运营者获利的方式 如果没有惩罚&#xff0c;网络上会充斥着很多无效节点&#xff0c;会扰乱甚至破坏网络 所有奖励和惩罚在每个 Epoch 实施一次 奖励 什…

鸿蒙OS应用开发之显示图片组件11

前面学习了像素降级处理的方法,这样方便一个图片可以显示在不同大小屏幕的技术,同样不会失真。现在来学习另外一个重要的技术,就是图片处理。图片处理是一个很范的名词,一般来说图片处理都会采用预处理的方法,比如在电脑上采用图形处理软件进行处理,然后再使用到手机的软…

深入理解React中的useReducer:管理复杂状态逻辑的利器

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…