不容易解的题10.15

news2024/11/20 13:17:28

395.至少有K个重复字符的最长字串

395. 至少有 K 个重复字符的最长子串 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/longest-substring-with-at-least-k-repeating-characters/description/?envType=list&envId=ZCa7r67M自认为是不好做的题。尤其是使用滑动窗口解法,思路很难想

一开始的想法很简单,思路也是该ac的思路,但是测试用例太长,超时了

思路是取该字符串上的每一个子串,这里的实现是用双for确定子串的开始和结束位置,将该字串传递进一个自写函数,该函数作用是用一个数组做哈希表来判断每个字符是否出现了k次及以上,如果是返回true,并且判断当前子串长度是否是被记录最长,如果不是更新答案,遍历完之后返回正确的长度即可。

class Solution {
public:
    
    int longestSubstring(string s, int k) {
        int res=0;
        for(int i=0;i<s.size();i++){
            for(int j=i;j<s.size();j++){
                if(fun(s,i,j,k)&&res<j-i+1)res=j-i+1;
            }
        }
        return res;
    }
    bool fun(string &s,int left,int right,int k){
        int arr[26]={0};
        for(int i=left;i<=right;i++)arr[s[i]-'a']++;
        for(int i=0;i<26;i++){
            if(arr[i]!=0&&arr[i]<k)return false;
        }
        return true;
    }
};

做的时候一直以为该思路是可以通过的,然后做了很多次剪枝,发现通不过去。

下面来看正确的滑动窗口实现,是我看一个网友写的,思路十分清晰易懂,后来发现官方题解的滑窗也是这样的思路,但是官方题解一般很难看得懂。

class Solution {
public:
    int longestSubstring(string s, int k) {
        int res=0;int arr[26]={0};
        for(int i=1;i<=26;i++){
            int left=0,right=0;
            int diff=0,count=0;
            memset(arr,0,sizeof(arr));
            while(right<s.size()){
                
                arr[s[right]-'a']++;
                if(arr[s[right]-'a']==1)diff++;
                if(arr[s[right]-'a']==k)count++;
                right++;
            
            while(left<right&&diff>i){
                arr[s[left]-'a']--;
                if(arr[s[left]-'a']==0)diff--;
                if(arr[s[left]-'a']==k-1)count--;
                left++;
            
            }
            if(diff==i&&diff==count)res=max(res,right-left);
            }
        }
        return res;
    }
};

我们先给出代码,以代码来分析。

循环是以26个不同的字母为限制的,即每次只允许窗口内存在n个不同的字母
设置变量来分别存放当前窗口内不同种类字符的个数、符合该种字符个数等于k的字符有多少个、窗口左边界、窗口右边界
变量设置好后,开始进入第一个while,它是扩张右窗口的
如果当前数组位置为1,diff++,说明有不同的字符第一次加进来
如果当前位置数值为k则count++说明当前字符出现次数第一次达到k,注意这里并不是大于等于k时候count++,这样的话后续加进来该字符会导致重复计数
如果当前窗口出现的字符种类大于i,进入左边界缩小的阶段,进入的是循环而不是if判断语句
循环中如果左边界在右边界左侧,说明该窗口有缩减的必要,并且还要保证此时窗口字符种类大于i
进入之后是和上面类似的判断,不停增大left直到字符种类在允许的范围内
值得注意的有两点:
第一:判断窗口种类个数是否超过i的循环,应该在扩大右边界循环内部,每扩大一次右边界,并完成相应变量增加后,就立即判断此时左边界是否应该缩小
第二:判断完左右边界之后,立即进行的是一句判断
如果当前所允许的字符种类个数等于当前窗口的字符种类个数,并且窗口里任意字符的个数都大于或等于k,则判断是否应该更新答案值
为什么要判断现在最大允许的字符种类个数呢?
因为该循环是由1开始到26结束,每次判断的是最大允许范围这能保证当前窗口前提下,找到的是最大的数值,这样才可能需要更新数据
一定要注意不要把更新数据这条语句不小心写到循环外面了,这样如果在窗口移动时有新数据,就添加不上了

memset每次清理数组很重要,上一次遗留的数据不能作为下一次扩大i限制的哈希表,这会造成不同字母为1时和字母为2时的哈希表累加,这在逻辑上也说不过去
right++可以放在最后,这样使代码更容易理解,当然代码也应该写成right-left+1
注意vector数组的clear不能起到相同的作用
还有就是不要把-‘a’写成-‘0’

还有一种方法是递归:

这种方法,十分巧妙,但是更难想,我一般倾向于能不用递归就不用递归,递归一般思路不好想,而且出bug也不太容易找。

也是先给出代码再看思路

class Solution {
public:
    int longestSubstring(string s, int k) {
        unordered_set<char> set(s.begin(),s.end());
        unordered_map<char,int>map;
        for(char ch:s)map[ch]++;
        for(char ch:set){
            vector<string>path;
            if(map[ch]<k){
                split(s,path,ch);
                int res=0;
                for(string tn:path){
                    res=max(longestSubstring(tn,k),res);
                }
                return res;
            }
        }
        return s.size();
    }
    void split(const string&s,vector<string>&path,const char flag=' '){
    path.clear();
    istringstream iss(s);
    string temp;
    while(getline(iss,temp,flag)){
        path.push_back(temp);
    }
}
};

大概的理解:
map记录的是s字符串中每个出现过的字符都有多少个,如果全都大于k那么进不到if循环,直接返回s.size()了,因为整个字符串就符合题解
如果有小于k的说明只要包含该字符的子串都不能满足条件,因为map存储的是s这个字符串中该字符出现的总次数,如果小于那么进行分割,分割出不含该字符的若干字串,将这些子串全部放入一个数组里,然后进行递归,找这些子串中最长的满足条件的长度是多少
如果此时分割出来的子串中含有小于k次频率的字符怎么办?
不用担心,res=max这一行,会再次进入函数,进行分割,直到子串全部满足大于等于k的出现频率
不必深究代码究竟如何进行递归,我们只需要搞清楚分割的条件,以及答案将返回什么即可

怎么样是不是理解起来比滑窗稍难一些,也可能是我个人使用递归很少,所以感觉很难想,用哈希表存每个字符出现频次,然后再把不满足频次的字符分割开,这种思路很巧妙。


都看到这里了如果对您有用的话别忘了一键三连哦,如果是互粉回访我也会做的!

大家有什么想看的题解,或者想看的算法专栏、数据结构专栏,可以去看看往期的文章,有想看的新题目或者专栏也可以评论区写出来,讨论一番,本账号将持续更新。
期待您的关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1094722.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自动化的采集链接和自动推送必应的在线工具

搜索LMCJL在线工具 进入后点击站长工具类型&#xff0c;选择必应自动推送 进去后&#xff0c;添加域名&#xff0c;点击数据管理&#xff0c;输入必应的token 然后开启推送&#xff0c;就可以实现&#xff0c;自动化采集链接&#xff0c;自动推送给必应。 必应的站长后台官网…

基于吉萨金字塔建造优化的BP神经网络(分类应用) - 附代码

基于吉萨金字塔建造优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于吉萨金字塔建造优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.吉萨金字塔建造优化BP神经网络3.1 BP神经网络参数设置3.2 吉萨金字…

16.SpringBoot前后端分离项目之简要配置一

SpringBoot前后端分离项目之简要配置一 前面对后端所需操作及前端页面进行了了解及操作&#xff0c;这一节开始前后端分离之简要配置 为什么要前后端分离 为了更低成本、更高效率的开发模式。 前端有一个独立的服务器。 后端有一个独立的服务器。两个服务器之间实时数据交换…

关于YOLOv8不显示GFlops的问题解决

屏幕显示GFlops需要安装此pip库: thop。但是没有安装&#xff0c;则不显示&#xff0c;导致debug比较困难。

Linux:进程调度的O(1)算法

文章目录 并发的理解程序运行时的数据进程切换的过程 内核的调度队列和调度原理 并发的理解 前面总结到了&#xff0c;关于并发的概念&#xff0c;并发针对的是单核的CPU上同时运行很多情况&#xff0c;并不是某个程序在CPU上运行就一直运行&#xff0c;而是根据一定的时间片和…

Linux C/C++ 嗅探数据包并显示流量统计信息

嗅探数据包并显示流量统计信息是网络分析中的一种重要技术&#xff0c;常用于网络故障诊断、网络安全监控等方面。具体来说&#xff0c;嗅探器是一种可以捕获网络上传输的数据包&#xff0c;并将其展示给分析人员的软件工具。在嗅探器中&#xff0c;使用pcap库是一种常见的方法…

怎么启动MySQL服务

你可能也遇到这样的问题&#xff0c;打开navicat&#xff0c;但是点击数据库连接不上&#xff0c;这就有可能是数据库服务没有启。报错如下图所示 解决方法一win11为例&#xff0c;右键此电脑&#xff0c;找到管理。 找到服务和应用吃程序&#xff0c;点击服务。 往下找到MySQL…

ti am335 RT-LINUX测试

RT-Linux是一个基于Linux内核的实时操作系统&#xff0c;它在满足Linux操作系统的通用性的同时兼顾 实时性能&#xff0c;它的核心是Linux内核的一个实时扩展&#xff0c;它为实时任务提供了必要的调度机制和时间管理。通过采用抢占式调度策略&#xff0c;高优先级的实时任务可…

肉眼无法读懂是二进制独有的浪漫——一篇博客学懂文件操作(C语言)

目录 一、为什么使用文件 二、什么是文件 2.1程序文件 2.2数据文件 2.3文本文件和二进制文件 2.4文件名 三、文件的打开和关闭 3.1 文件指针 3.2 文件的打开和关闭 3.3文件的顺序读写函数 3.3.1流的概念 3.3.2输入输出的概念 3.3.3函数操作 3.4文件的随机读写函…

miniblink学习

1.基本使用 main.cpp #include "webwidget.h" #include <QApplication> #include "wke.h" //工作目录是指当前目录&#xff0c;运行目录是指exe所在路径。 int main(int argc, char *argv[]) {QApplication a(argc, argv);//设置miniblink的全路径文…

C# GFPGAN 图像修复

效果 项目 代码 using Microsoft.ML.OnnxRuntime; using Microsoft.ML.OnnxRuntime.Tensors; using OpenCvSharp; using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.Windows.Forms;namespace 图像修复 {pu…

leetcode-62.不同路径

1. 题目 2. 解答 dp[i][j]表示机器人位于第i&#xff0c;j位置的时候&#xff0c;有多少路径 如果i 0&#xff0c;dp[i][j] 1;如果j 0&#xff0c;dp[i][j] 1;其他情况dp[i][j] dp[i-1][j] dp[i][j - 1] #include <stdio.h>int solve(int m, int n) {int dp[m][…

一场直播脚本的策划及话术怎么写?

一场直播脚本的策划及话术参考 直播流程安排示范:120 分钟直播流程设计(过款型) 标准化直播话术单元(单款产品话术模板) I 直播 120 分钟标准化流程 I 分解为 4 个 30 分钟直播单元 I 30 分钟前期介绍 2-3 个款作为起步 每款持续时长 10 分钟,10 分钟的时间里 ①卖点引出 2 …

找不到msvcp100.dll无法继续执行此代码怎么解决,快速修复dll问题的5个方法

电脑已经成为我们生活和工作中不可或缺的一部分&#xff0c;在我们使用电脑的时候&#xff0c;总会遇到一些技术问题&#xff0c;其中之一就是“找不到msvcp100.dll”。msvcp100.dll是一个动态链接库文件&#xff0c;它是Microsoft Visual C 2010 Redistributable Package的一部…

G.711语音编解码器详解

语音编解码利用人听觉上的冗余对语音信息进行压缩从而达到节省带宽的目的。值得注意的是,本文说的是语音编解码器,也就Speech codec,而常用的还有另一种编解码器称作音频编解码器,英文是Audio codec,它们的区别如下。 以前在学校的时候研究了很多VoIP的编解码器从G.723到A…

java.sql.SQLFeatureNotSupportedException解决方法

使用MyBatis访问数据库查询数据时报错&#xff1a; Caused by: java.sql.SQLFeatureNotSupportedExceptionat com.alibaba.druid.pool.DruidPooledResultSet.getObject(DruidPooledResultSet.java:1771)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun…

在node中操作mysql数据库

目录 前言 在node中安装mysql模块 引入绑定数据库 验证mysql模块能否正常工作 增 便捷方式 改 便捷方式 删 查 前言 本文介绍在node中对数据库使用sql语句进行增删改查 在node中安装mysql模块 npm i mysql 引入绑定数据库 导入mysql模块 const mysql require(m…

2、Windows下安装

目录 一.安装 1、双击下载的程序&#xff1a; 2、加载完成后&#xff0c;会进入如下界面&#xff08;选第一个Developer Default&#xff09; 3、然后点击Next 点击Execute 然后Next 4.继续next注意端口为3306 5.继续next&#xff0c;输入账户密码&#xff08;要有大小写…

C# 图解教程 第5版 —— 第4章 类型、存储和变量

文章目录 4.1 C# 程序是一组类型声明4.2 类型是一种模板&#xff08;*&#xff09;4.3 实例化类型4.4 数据成员和函数成员4.5 预定义类型4.6 用户定义类型4.7 堆和栈&#xff08;*&#xff09;4.8 值类型和引用类型4.9 变量4.9.1 变量声明4.9.2 多变量声明&#xff08;*&#x…

“视频剪辑:如何分割与转换视频格式,一探究竟!

如今&#xff0c;视频已成为我们生活中不可或缺的一部分。无论是记录生活点滴、分享兴趣爱好&#xff0c;还是传递信息&#xff0c;视频都已经成为一种非常有效的表达方式。而在视频制作过程中&#xff0c;剪辑是至关重要的一环。通过剪辑&#xff0c;我们可以去掉不需要的内容…