这次基本都是滑动窗口类型的题目了捏
2.10 字符串中的变位词
题目描述:给定两个字符串
s1
和s2
,写一个函数来判断s2
是否包含s1
的某个变位词。换句话说,第一个字符串的排列之一是第二个字符串的 子串
题目链接:剑指 Offer II 014. 字符串中的变位词 - 力扣(LeetCode)
题目难度:✨
AC题解
总体的思路:
- 当s2字符串长度小于s1字符串,果断
false
- 滑动窗口的思想,引入对于len1长度子串的统计数组v1和v2,c++中可以直接判断v1和v2是否相等,相等则返回
true
否则继续滑动,更新v2
class Solution {
public:
bool checkInclusion(string s1, string s2) {
//第一个字符串的排位之一是第二个字符串的子串
vector<int>v1(26,0);
vector<int>v2(26,0);
int len1=s1.length();
int len2=s2.length();
if(len2<len1){
return false;
}
for(int i=0;i<len1;i++){
v1[s1[i]-'a']++;
}
bool flag=false;
int left=0;
int right=len1;
for(;left<len1;left++){
v2[s2[left]-'a']++;
}
if(v1==v2){
return true;
}else{
left=0;
//滑动窗口
for(;right<len2;right++){
v2[s2[left]-'a']--;
v2[s2[right]-'a']++;
left++;
if(v1==v2){
return true;
}
}
return false;
}
}
};
2.11 字符串中的所有变位词
题目描述:给定两个字符串 s 和 p,找到 s 中所有 p 的 变位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
变位词 指字母相同,但排列不同的字符串。
题目链接:剑指 Offer II 015. 字符串中的所有变位词 - 力扣(LeetCode)
题目难度:✨
AC题解
思路同上一题,滑动窗口,只不过输出的内容不一样~
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int>ans;
int len1=p.length();
int len2=s.length();
if(len1>len2){
return ans;
}else{
vector<int>v1(26,0);
vector<int>v2(26,0);
for(int i=0;i<len1;i++){
v1[p[i]-'a']++;
v2[s[i]-'a']++;
}
int left=0;
int right=len1;
for(;right<len2;right++){
if(v1==v2){
ans.push_back(left);
}
//滑动窗口
v2[s[left]-'a']--;
v2[s[right]-'a']++;
left++;
}
if(v2==v1){
ans.push_back(left);
}
return ans;
}
}
};
2.12 不含重复字符的最长子字符串
题目描述:给定一个字符串
s
,请你找出其中不含有重复字符的 最长连续子字符串 的长度。题目链接:剑指 Offer II 016. 不含重复字符的最长子字符串 - 力扣(LeetCode)
题目难度:✨
AC题解
根据百度我们知道ASCII码有128个,于是定义pos
数组存储当前指针之前出现某个字符的最近的位置
思路依然是滑动窗口,left
指向窗口最左边,当前指针i
(指向窗口最右边)所指的字符在之前出现过且在窗口中(即pos[s[i]]>=left)窗口滑动,更新left
每次都需要计算当前窗口大小,若大于ans
则更新ans
,每一次指针移动都要更新pos
数组的信息
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int len=s.length();
vector<int>pos(128,-1);//存放某个字符遍历到某个位置之前最后一次出现的位置
int ans=0;
int left=0;
for(int i=0;i<len;i++){
if(pos[s[i]]!=-1&&pos[s[i]]>=left){
left=pos[s[i]]+1;
}
pos[s[i]]=i;
if(i-left+1>ans){
ans=i-left+1;
}
}
return ans;
}
};
2.13 含所有字符的最短字符串
题目描述:给定两个字符串 s 和 t 。返回 s 中包含 t 的所有字符的最短子字符串。如果 s 中不存在符合条件的子字符串,则返回空字符串 “” 。
如果 s 中存在多个符合条件的子字符串,返回任意一个。
题目链接:剑指 Offer II 017. 含有所有字符的最短字符串 - 力扣(LeetCode)
题目难度:✨✨
AC题解
纪念一下独自做出hard(虽然不是最简洁的解法)
依然是滑动窗口的思路,每次向右移动根据记录的数组v1
和v2
决定要不要移动左边窗口位置……不是很难理解,直接上代码
class Solution {
public:
string minWindow(string s, string t) {
int len1=t.length();
int len2=s.length();
if(len1>len2){
return "";
}else if(len1==len2&&len1==1){
if(s==t)
return s;
else
return "";
}
vector<int>v1(60,0);
vector<int>v2(60,0);
for(int i=0;i<len1;i++){
if(t[i]<='z'&&t[i]>='a'){
v1[t[i]-'a']++;
}else{
v1[t[i]-'A'+26]++;
}
}
string ans="";
int min_len=len2*2;
int left=0;
int right=0;
for(;right<len2;right++){
//更新右边
if(s[right]<='z'&&s[right]>='a'){
v2[s[right]-'a']++;
}else{
v2[s[right]-'A'+26]++;
}
//更新左边
while(left<right){
int id;
if(s[left]<='z'&&s[left]>='a'){
id=s[left]-'a';
}else{
id=s[left]-'A'+26;
}
if(v2[id]>v1[id]){
v2[id]--;
left++;
}else{
break;
}
}
//判断v1和v2是否符合条件
bool flag=true;
for(int x=0;x<52;x++){
if(v2[x]<v1[x]){
flag=false;
break;
}
}
if(flag){
if(right-left+1<min_len){
min_len=right-left+1;
ans=s.substr(left,min_len);
}
//printf("合法:%d-%d:%d\n",left,right,right-left+1);
}
}
bool flag=true;
for(int x=0;x<52;x++){
if(v2[x]<v1[x]){
flag=false;
break;
}
}
if(flag){
if(right-left+1<min_len){
min_len=right-left+1;
ans=s.substr(left,min_len);
}
//printf("合法:%d-%d:%d\n",left,right,right-left+1);
}
return ans;
}
};