leetcode中几道与维护窗口相关的问题

news/2024/7/8 3:08:54

leetcode中有几道题使用同一个思路,大致是先维护一个窗口,每次只移动窗口左侧或者右侧的边界,然后针对这个窗口内的元素进行处理。这种方式使用两个指针,可以将问题的运行时间降到O(n)内。

 

Longest Substring Without Repeating Characters:https://leetcode.com/problems/longest-substring-without-repeating-characters/

这道题我们维护一个窗口,每次将右边的窗口向前移动一位。另外维护一个hash表,标记当前窗口中的字符的位置,一旦右边的窗口遇到了当前窗口中的字符,说明有重复,将左边窗口移到当前窗口中重复字符的下一位,更新重复字符的位置为有窗口的位置。每次移动右窗口都更新当前的最大长度,知道有窗口移到最后一位。

这里hash table有一个对于字符串常用的用法,就是声明一个长度为256的数组,正好对应256个字符。

 1 class Solution {
 2 public:
 3     int lengthOfLongestSubstring(string s) {
 4         vector<int> visit(256, -1);
 5         int start = 0, end = 0, len = 0;
 6         while(end < s.length()) {
 7             if(visit[s[end]] >= start) {
 8                 start = visit[s[end]] + 1;
 9             }
10             visit[s[end]] = end;
11             len = max(end + 1 - start, len);
12             end++;
13         }
14         return len;
15     }
16 };
View Code

 

substring with concatenation of all words leetcode:https://leetcode.com/problems/substring-with-concatenation-of-all-words/

这道题的思路也是维护一个窗口和hash表。但是由于这道题的特殊性,需要维护两个hash,一个是不能改变的hash,用来存储字典;另一个是会变化的,都用来存储当前的窗口中访问的word以及每个word访问的次数。

为了是算法在O(n)时间内运行,这里还有一个技巧,(这个技巧也是使得这道题更难的原因),假设源字符串的长度为n,字典中单词的长度为wlen。因为不是一个字符,所以我们需要对源字符串所有长度为wlen的子串进行判断。做法是i从0到wlen-1个字符开始,得到初始考察的end为i, i+wlen, i+2*wlen, ...的长度为wlen的单词。这样就可以保证判断到所有的满足条件的串。因为每次扫描的时间复杂度是O(2*n/wlen)(每个单词不会被访问多于两次,一次是窗口右端,一次是窗口左端),总共扫描l次(i=0, ..., wlen-1),所以总复杂度是O(2*n/l*l)=O(n),是一个线性算法。

 1 class Solution {
 2 public:
 3     vector<int> findSubstring(string s, vector<string>& words) {
 4         unordered_map<string, int> stable_dict;
 5         for(int i = 0; i < words.size(); i++) {
 6             if(stable_dict.find(words[i]) == stable_dict.end())
 7                 stable_dict.insert(make_pair(words[i], 1));
 8             else
 9                 stable_dict[words[i]]++;
10         }
11         vector<int> res;
12         int wlen = words[0].size();
13         int i = 0;
14         while(i < wlen) {
15             int end = i;
16             int start = i;
17             int count = 0;
18             unordered_map<string, int> dict;
19             while(end < s.length()) {
20                 string cur = s.substr(end, wlen);
21                 string start_cur = s.substr(start, wlen);
22                 if(stable_dict.find(cur) != stable_dict.end()) {
23                     if(dict.find(cur) == dict.end())
24                         dict.insert(make_pair(cur, 0));
25                     if(dict[cur] < stable_dict[cur]) {
26                         dict[cur]++;
27                         count++;
28                         end += wlen;
29                     }
30                     else {
31                         if(count == words.size()) 
32                             res.push_back(start);
33                         dict[start_cur]--;
34                         count--;
35                         start += wlen;
36                     }
37                 }
38                 else {
39                     end += wlen;
40                     count = 0;
41                     start = end;
42                     dict.clear();
43                 }
44             }
45             i++;
46         }
47         return res;
48     }
49 };
View Code

 

Minimum Window Substring: https://leetcode.com/problems/minimum-window-substring/

这道题的思路和上一道很相似。区别是每次循环都要右移右指针,而移动左指针的条件是当所有的字典中的字符都在窗口中,停止移动的条件是有字符不在窗口中。这里要注意的一点是由于我们采取了纪录所有出现在窗口中的字典中字符的个数,所以如果某个字符的这个个数比字典中应有的个数多,也应当让左指针移动,并且减少这个值直到和字典的字符数一样多。

 1 class Solution {
 2 public:
 3     string minWindow(string s, string t) {
 4         string res = "";
 5         int start = 0, end = 0;
 6         int count = 0;
 7         vector<int> record(256, 0);
 8         vector<int> dict(256, 0);
 9         for(int i = 0; i < t.length(); i++) {
10             dict[t[i]]++;
11         }
12         while(end < s.length()) {
13             if(dict[s[end]] > 0) {
14                 record[s[end]]++;
15                 if(record[s[end]] <= dict[s[end]]) {
16                     count++;
17                 }
18                 if(count == t.length()) {
19                     while(dict[s[start]] == 0 || record[s[start]] > dict[s[start]]) {
20                         record[s[start]]--;
21                         start++;
22                     }
23                     res = res.length() == 0 ? s.substr(start, end + 1 - start) : res.length() < (end + 1 - start) ? res : s.substr(start, end + 1 - start);
24                     record[s[start]]--;
25                     count--;
26                     start++;
27                 }
28             }
29             end++;
30         }
31         return res;
32     }
33 };
View Code

 

Minimum Size Subarray Sum: https://leetcode.com/problems/minimum-size-subarray-sum/

这道题也是用一个窗口,在窗口中的数的和小于s的时候移动右窗口,否则考察当前的子数组是否最小, 并移动左窗口。这道题比之前几道题更简单是因为这里不需要hash table去存窗口信息,只需一个int存窗口中数的和即可。

 1 class Solution {
 2 public:
 3     int minSubArrayLen(int s, vector<int>& nums) {
 4         int start = 0, end = 0, len = nums.size() + 1, sum = 0;
 5         while(end < nums.size() || sum >= s) {
 6             if(sum < s) {
 7                 sum += nums[end];
 8                 end++;
 9             }
10             if(sum >= s) {
11                 sum -= nums[start];
12                 len = min(len, end - start);
13                 start++;
14             }
15         }
16         return len == nums.size() + 1 ? 0 : len;
17     }
18 };
View Code

 

可以看出这类窗口的问题其实是双指针扫描,关键点是判断两个指针的移动条件以及考虑使用hash table等额外保存窗口内元素的信息。

转载于:https://www.cnblogs.com/walcottking/p/4539763.html


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

相关文章

动手实现编译器(六)——实现全局变量

我们在上一节中实现了对语句的编译&#xff0c;在这一节中&#xff0c;我们希望向语句中加入全局变量。实现类似以下语句&#xff1a; int a; int b; int c; int d; int e; int f; int g; a 2; b 3; c 4; d 5; e 6; f 7; g 8; print a b * c / d % e - f g;这需要变量…

POJ 2027

#include<iostream> using namespace std; int main() {int time;cin>>time;int a;int b;while(time--){cin>>a;cin>>b;if(a<b)cout<<"NO BRAINS"<<endl;elsecout<<"MMM BRAINS"<<endl;} } 关注我的公…

Hibernate查询简介

创建实体类 在介绍Hibernate查询语言之前&#xff0c;首先我们来建立一下数据库。这里直接使用了MySQL自带的样例数据库world。如果你没有安装MySQL那么需要安装一下&#xff0c;并且在安装的时候选择安装样例数据库。 安装完成之后&#xff0c;应该能在MySQL中看到一个名为wor…

动手实现编译器(七)——比较运算符

上一节中&#xff0c;我们已经实现了全局变量的声明、赋值和计算&#xff0c;本节中我们将会实现计较运算符的计算。 , !, <, >, <, >修改词法分析 首先增加新的单词类型 // 单词类型 enum {T_EOF,T_ADD, T_SUB, T_MUL, T_DIV, T_MOD, T_EQ, T_NE, T_LT, T_GT, …

linux软件编译流程

首先先总结下linux下一些概念1&#xff0c;gcc可以说是史上最强大的c语言编译器起作用是将软件程序的源代码&#xff08;纯文本文件&#xff09;和利用已存在的函数库通过其本身gcc编译成计算机可识别的二进制文件。该过程为程序编译的普遍流程 2&#xff0c;环境检测程序&…

动手实现编译器(八)——IF条件语句

在上一节中&#xff0c;我们实现了比较运算符的计算&#xff0c;现在&#xff0c;我们可以容易地实现IF条件语句。 首先我们看IF语句的SysY语言的定义&#xff1a; 语句&#xff1a; Stmt → LVal ‘’ Exp ‘;’ | ‘if’ ( Cond ‘)’ Stmt [ ‘else’ Stmt ] 修改词法分析 …

嗅探、中间人sql注入、反编译--例说桌面软件安全性问题

嗅探、中间人sql注入、反编译--例说桌面软件安全性问题 今天这篇文章不准备讲太多理论&#xff0c;讲我最近遇到的一个案例。从技术上讲&#xff0c;这个例子没什么高深的&#xff0c;还有一点狗屎运的成分&#xff0c;但是它又足够典型&#xff0c;典型到我可以讲出很多大道理…

动手实现编译器(九)——WHILE语句

在上一节中&#xff0c;我们实现了IF语句&#xff0c;在这一节中&#xff0c;我们将进一步实现WHILE循环。从某种意义上说&#xff0c;WHILE循环非常类似于IF语句没有“else”子句&#xff0c;除了总是跳回顶部的循环。 WHILE语句的SysY语法如下 Stmt → ‘while’ ‘(’ Cond …