一、問題描述
給你一個僅由大寫英文字母組成的字符串,你可以將任意位置上的字符替換成另外的字符,總共可最多替換 k 次。在執行上述操作後,找到包含重複字母的最長子串的長度。
注意:
字符串長度 和 k 不會超過 。
示例 1:
輸入:
s = "ABAB", k = 2
輸出:
4
解釋:
用兩個'A'替換爲兩個'B',反之亦然。
示例 2:
輸入:
s = "AABABBA", k = 1
輸出:
4
解釋:
將中間的一個'A'替換爲'B',字符串變爲 "AABBBBA"。
子串 "BBBB" 有最長重複字母, 答案爲 4。
題目來源:LeetCode 鏈接:https://leetcode-cn.com/problems/longest-repeating-character-replacement
二、解題思路
1.暴力法,一般時間都會超限
class Solution {
public:
int characterReplacement(string s, int k) {
int k2=k, maxlen=0, first=s.length();
int i=0, j=0; //一個左邊界i,一個右邊界j
while(i<s.length()&&j<s.length())
{
if(s[j]!=s[i])
{
if(k2>0)
{
k2--;
first = first< j ? first: j; //first記錄第一個跟s[i]不相同的元素下標
//後面可以直接把i重新賦值爲first,而不是i+1
j++;
}
else //如果k次修改機會用完
{
int temp = j-i;
maxlen = maxlen<temp?temp:maxlen; //記錄最長字符串長度
if((s.length()-first)>maxlen) //只有first後面的字符串大於目前的maxlen
{ //繼續執行纔有意義,否則直接break
i = first; //把i賦值爲first
j = i+1; //j爲i+1
k2 = k; //k2復原爲原值
}
else
break;
}
}
else
{
j++;
}
int temp = j-i;
maxlen = maxlen<temp?temp:maxlen;
}
return maxlen;
}
};
2.滑動窗口法
上面的暴力法最壞情況了的時間複雜度其實可以達到, 這是因爲當k次修改機會用完又遇到跟左邊界值s[i]不同的元素時,採取的策略是往回走,把i賦值爲first,j賦值爲i+1。而滑動窗口的思想就是一直往前滑,爭取達到O(n)的時間複雜度。而這要怎麼才能做到?
該方法時記錄滑動窗口內的最多個數的字符即charMax,只要滿足right - left + 1 > charMax + k即可繼續向右擴張,即right++,不滿足的時候就窗口整體向右移動,即同時right++,left++。這時候,假設後面都沒有更長的替換後連續字符串的話,那麼這個窗口長度會一直不變,知道循環結束(right走到終點),而如果後面還有更長的替換後連續字符串的話,這個窗口就會再次擴張。最後返回窗口大小,就是所求的替換後最長連續字符串的長度。
class Solution {
public:
int characterReplacement(string s, int k) {
if(s.length()==0)
return 0;
int map[26]={0}; //map用來記錄字符數
int i=0, charmax=0; //map數組下標爲鍵,內容爲值
for(int j=0;j<s.length();j++)
{
int index = s[j]-'A';
map[index]++; //向右擴張一步
charmax=charmax>map[index]?charmax:map[index]; //更新charmax
if((j-i+1)>charmax+k)
{
map[s[i]-'A']--; //如果不符合條件,左邊收縮
//保持窗口長度不變
i++;
}
}
return s.length()-i;
}
};
三、總結
1.滑動窗口通常由擴張、收縮、移動三種行爲,根據解題過程的不同階段採取不同的行爲
2.滑動窗口經常搭配map使用 ,意在記錄滑動窗口中的信息
3.滑動窗口很適合解決字符串類的題目