[Java][算法 滑动窗口]Day 03---LeetCode 热题 100---08~09

news/2024/5/20 7:26:25 标签: leetcode, 算法, 滑动窗口, Java, java

第一题  无重复字符串的最长子串

思路

其实就是在字符串S中 找到没有重复的最长子串的长度  这道题的难点就是在于如何判断最长并且无重复

首先 最长长度  可以使用变量max记录保存   

再者 判断有无重复  最简单的方法就是  暴力遍历法  即对于每次找的子串都再次寻找遍历一次  判断是否已有字符  自然  这种方法判断的话 时间复杂度会不是一般的高

当然 算法优化我们慢慢再讨论  最直接的思路就是如此

解法一:暴力法

我们的暴力当然和上述思路不太一样  我们对于是否重复  可以直接利用Map集合key不能重复的特点  然后使用containKey()方法直接进行判断

class Solution {
   public static int lengthOfLongestSubstring(String s) {
// 对于S的特殊情况直接返回0
        if(s==null || s.length()==0) return 0;
//将S转化为char数组[]  遍历该数组 然后进行判断
        char[] charArray = s.toCharArray();int max=1;
        Map<Character,Integer> map=new HashMap<>();
//遍历  每次遍历开始就是认定为子串的起始字符
        for(int i=0;i< charArray.length;i++){
//每次记录完一次后 需要把map清空
            map.clear();
            map.put(charArray[i],i); //将第一个字符放入
            for(int j=i+1;j< charArray.length;j++){
                if(map.containsKey(charArray[j])){ // 利用containKey方法直接判断
                    if(map.size()>max) max=map.size(); // 判断当前map长度和max进行比较
                    break;
                }else{
                    map.put(charArray[j],j);
                }
            }
            if(map.size()>max) max=map.size();
        }
        return max;
    }
}

解法二:滑动窗口

所谓滑动窗口,其实就是在字符串中先选定一段,把这段作为一个可以滑动的窗口,这个窗口类似于队列,每次判断队列中字符是否满足题目条件,不满足即向左滑动(这是整体情况下 一般是向左  自然 也可以只会滑动右边界),滑动窗口的时候,自然左边会少一位,右边会多一位。

java">class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s.length()==0) return 0;
        HashMap<Character, Integer> map = new HashMap<Character, Integer>();
        int max = 0;
        int left = 0;
        for(int i = 0; i < s.length(); i ++){
            if(map.containsKey(s.charAt(i))){ //判断是否存在已有队列中
//有的话 找到map中该重复的元素的索引位置 判断两者索引大小 通过对索引的判断 不断向右滑动变化起点
                left = Math.max(left,map.get(s.charAt(i)) + 1);  
            }
            map.put(s.charAt(i),i);//不存在则添加到map中
            max = Math.max(max,i-left+1); // 更新最大长度
        }
        return max;
        
    }
}

第二题  找到字符串中所有字母异位词

思路

和第一题类似 也是遍历找子串  只是这个题目判断依据  不是重复 而是找异位词  异位词的定义我们之前也遇到过 简单来说 就是字母组成一样   如果是异位词 那么排序后的字符串两者一定是一样的  所以我们对其的判断方式就是排序后是否一样  那么这道题的解题思路就很明确

需要注意字符串长度  判null等特殊情况

解法一:暴力法

同样的 直接暴力遍历  然后判断是否为异位词就行即可

java">class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        if(s==null||p.length()>s.length()) return new ArrayList<>();
        char[] pArray = p.toCharArray();
        Arrays.sort(pArray);
        ArrayList<Integer> list = new ArrayList<>();
        int i=0;int l=p.length();
        //   abvdef   ab
        for(i=0;i<=s.length()-l;i++){
            char[] sArray = s.substring(i, i + l).toCharArray();
            Arrays.sort(sArray);
            if(String.valueOf(pArray).equals(String.valueOf(sArray))){
                list.add(i);
            }
        }
        return list;
    }
}

我们是采用排序的方式进行判断异位词  当然也可以采用哈希表映射 统计字母次数的方式  代码如下

java">class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        int sLen = s.length(), pLen = p.length();

        if (sLen < pLen) {
            return new ArrayList<Integer>();
        }

        List<Integer> ans = new ArrayList<Integer>();
        int[] sCount = new int[26];
        int[] pCount = new int[26];
        for (int i = 0; i < pLen; ++i) {
            ++sCount[s.charAt(i) - 'a'];
            ++pCount[p.charAt(i) - 'a'];
        }

        if (Arrays.equals(sCount, pCount)) {
            ans.add(0);
        }

        for (int i = 0; i < sLen - pLen; ++i) {
            --sCount[s.charAt(i) - 'a'];
            ++sCount[s.charAt(i + pLen) - 'a'];

            if (Arrays.equals(sCount, pCount)) {
                ans.add(i + 1);
            }
        }

        return ans;
    }
}

解法二:滑动窗口

我们不再分别统计滑动窗口和字符串 p 中每种字母的数量,而是统计滑动窗口和字符串 p 中每种字母数量的差;并引入变量 differ来记录当前窗口与字符串 p 中数量不同的字母的个数,并在滑动窗口的过程中维护它。

在判断滑动窗口中每种字母的数量与字符串 ppp 中每种字母的数量是否相同时,只需要判断 differ是否为零即可

java">class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> res = new ArrayList<Integer>();
        int sLen = s.length();
        int pLen = p.length();

        if(sLen < pLen) {
            return res;
        }
        //表示字母出现次数差距
        //count[x] = 0  表示 s与p中字母x出现次数相同 都出现了n次(n>=0)
        //count[x] = n  表示 在s中字母x出现次数比p多 多出现了n次(n>0)
        //count[x] = -n 表示 在s中字母x出现次数比p少 少出现了n次(n>0)
        int[] count = new int[26];

        for(int i = 0; i < pLen; i++){
            ++count[s.charAt(i)-'a'];
            --count[p.charAt(i)-'a'];
        }
        //表示字母差异个数
        int differ = 0;
        for(int j = 0; j < 26; j++){
            if(count[j]!=0)++differ;
        }

        if(differ==0){
            res.add(0);
        }
        //向右滑动
        for(int i = 0; i < sLen - pLen; i++){
            //缩减时只考虑count[x]==1与count[x]==0的情况
            //因为缩减时字母x减少,count[x]会减去1
            //(1)count[x]==1时(次数差距1次,不相同)  
            //count[x]==0 -> 次数相同 -> 不相同变相同,字母差异个数减少1 -> differ--

            //(2)count[x]==0时(次数相同)  
            //count[x]==-1 -> 次数差距变为1次->相同变不相同 ,字母差异个数增加1 -> differ++

            //(3)count[x]==-1时(次数不相同) -> count[x]==-2 次数还是不相同-> 字母差异数不变

            //(4)count[x]==2时(次数不相同)  -> count[x]==1 次数还是不相同-> 字母差异数不变


            //左缩减一位,i
            if (count[s.charAt(i) - 'a'] == 1) {  
                //窗口中s子串左边减少一个s[i]的数量(把原来多出来的1个s[i]去掉,变得相同)
                //两个字符串字母差距缩小
                --differ;
            } else if (count[s.charAt(i) - 'a'] == 0) {  
                //窗口中s子串左边减少一个s[i]的数量(把原来相同数量的s[i]的减少了1个,数量变得不相同)
                //两个字符串字母差距增大
                ++differ;
            }
            //窗口中s子串左边减少一个字母s[i]
            --count[s.charAt(i) - 'a'];

            //添加时只考虑count[x]==-1与count[x]==0的情况,原因分析与缩减时类似
            //右添加一位,i+pLen
            if (count[s.charAt(i + pLen) - 'a'] == -1) {  
                //窗口中s子串右边增加一个s[i+pLen]的数量(把原来缺少的1个s[i]加上,数量变得相同)
                //两个字符串字母差距缩小
                --differ;
            } else if (count[s.charAt(i + pLen) - 'a'] == 0) {  
                //窗口中s子串右边增加一个s[i+pLen]的数量(把原来相同数量的s[i]多加了1个,变得不相同)
                //两个字符串字母差距增大
                ++differ;
            }
            //窗口中s子串右边增加一个字母s[i+pLen]
            ++count[s.charAt(i + pLen) - 'a'];
            //两个字符串字母差距为0
            if (differ == 0) {
                res.add(i + 1);
            }
        }
        return res;  
    }
}


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

相关文章

解决Ubuntu23.10中WPS的字体问题

WPS版本&#xff1a;wps-office_11.1.0.11711_amd64 此版本在安装过后存在两个问题&#xff1a; 1. 字体库不完整&#xff0c;在打开一些文档的时候会有以下提示&#xff1a; 缺失字体“Symbol、Wingdings、Wingdings 2...“ 2. 粗体字显示不正确&#xff0c;显示成过粗的黑…

【Linux】单机可建立的最大TCP连接数

【Linux】单机可建立的最大TCP连接数 背景介绍环境客户端服务端总结 背景 本文内容大多基于网上其他参考文章及资料整理后所得&#xff0c;并非原创&#xff0c;目的是为了需要时方便查看。 介绍 本文介绍Linux单机作为客户端或服务端时可建立的最大TCP连接数。 环境 分类…

可视化低代码表单设计器

JNPF 表单设计器是一款在线可视化表单建模工具&#xff0c;基于VueSpringboot技术开发&#xff0c;具有组件丰富、操作简单、所见即所得等特性&#xff0c;既能够设计普通的数据录入表单&#xff0c;也能够配合流程设计出各类审批流转表单。 应用地址&#xff1a;https://www.j…

计算机专业必看《编程之神》

前言 这是一部关于数学家艾伦图灵&#xff08;Alan Turing&#xff09;的人物传记电影,非常值得一看. 影片中&#xff0c;艾伦图灵被描绘成一个富有创造力、勇气和独立思考的人物。他的天才思维和对计算机的理解在纠缠复杂的密码解密过程中发挥了重要作用。 图灵的天才 提出图灵…

IP地址冲突检查工具-arp-scan

arp-scan 是一个命令行工具&#xff0c;用于通过ARP&#xff08;地址解析协议&#xff09;扫描局域网中的设备。它发送ARP请求到指定的IP地址范围&#xff0c;然后监听响应&#xff0c;以此来识别网络上活动的设备。这个工具可以帮助网络管理员发现网络上的设备&#xff0c;包括…

C语言---指针进阶

1.字符指针 int main() {char str1[] "hello world";char str2[] "hello world";const char* str3 "hello world.";const char* str4 "hello world.";if (str3 str4){//常量字符串在内存里面是无法修改的&#xff0c;所以没必要…

oracle和mysql语句有哪些异同点?

Oracle和MySQL是两个流行的关系型数据库管理系统&#xff0c;它们都有SQL&#xff08;结构化查询语言&#xff09;作为主要的查询语言。尽管它们共享许多基本的SQL功能&#xff0c;但它们之间也存在一些关键的差异。以下是一些Oracle和MySQL语句的异同点&#xff1a; 数据类型…

java 水印测试工具

水印测试工具 介绍 在程序中添加了一个增加水印的操作&#xff0c;本地测试都ok,但是在实际使用中发现&#xff0c;服务器不行&#xff0c;打印出来都是方块&#xff0c;经过验证发现是没有安装中文字体&#xff0c;安装字体后就ok了。 你以为这就结束了吗&#xff1f; 当然…