https://leetcode-cn.com/problems/get-equal-substrings-within-budget/
思路一:考虑枚举转换的字符串的长度,那么在
O
(
s
.
l
e
n
g
t
h
)
O(s.length)
O(s.length)内即可计算出在
c
o
s
t
cost
cost限制下是否有解,那么可以从
1
1
1开始枚举直到不满足题意为止,但是这样做会超时。不难发现转换的字符串长度和消耗的代价是有单调性的(这里仅考虑最优解),所以可以上二分,那么复杂度大概为
O
(
n
l
g
n
)
O(nlgn)
O(nlgn),可以通过。
class Solution {
public:
int equalSubstring(string s, string t, int maxCost) {
int n=s.size();
int l=0,r=n,mid;
while(l<=r)
{
mid=(l+r)>>1;
bool flag=true;
int sum=0;
for(int i=0;i<mid;i++)
sum+=abs(s[i]-t[i]);
flag=sum>maxCost;
for(int i=mid;i<n&&flag;i++)
{
sum+=abs(s[i]-t[i])-abs(s[i-mid]-t[i-mid]);
flag=sum>maxCost;
}
if(flag)
r=mid-1;
else
l=mid+1;
}
return r;
}
};
思路二:直接维护一个满足题意的滑动窗口,分别移动右左边界记录最大值即可。复杂度 O ( n ) O(n) O(n)。
class Solution {
public:
int equalSubstring(string s, string t, int maxCost) {
int cost=0,l=0,r=0,n=s.size(),ans=0;
while(r<n)
{
cost+=abs(s[r]-t[r]);
++r;
while(cost>maxCost)
{
cost-=abs(s[l]-t[l]);
++l;
}
ans=max(ans,r-l);
}
return ans;
}
};
思路三:依然是滑动窗口,但是我们可以进一步优化。考虑思路二中的代码,每一次循环都会把右边界 + 1 +1 +1,然后依据限制条件调整左边界,最后我们得到了一个满足题意的区间 [ l , r ) [l,r) [l,r),由于最后要返回最大值,那么我们是否可以在滑动区间的同时维持区间长度单调不减呢?答案是可以的,只需要把修改左边界的那部分代码放到 i f if if语句中,这样在每次循环中,只有两种情况: 1. r = r + 1 , l 1.r=r+1,l 1.r=r+1,l不变; 2. r = r + 1 , l = l + 1 2.r=r+1,l=l+1 2.r=r+1,l=l+1。可以看到区间长度是单调不减的。正确性?完整的证明我是给不了了,不过可以这样理解,假设最优解对应的区间为 [ l 2 , r 2 ) [l_2,r_2) [l2,r2),我们现在位于区间 [ l 1 , r 1 ) [l_1,r_1) [l1,r1),显然: r 2 − l 2 > = r 1 − l 1 r_2-l_2>=r_1-l_1 r2−l2>=r1−l1,所以当 l 1 = l 2 l_1=l_2 l1=l2时一定有 r 1 < = r 2 r_1<=r_2 r1<=r2,这种情况下我们一定能得到新的最优解 [ l 2 , r 2 ) [l_2,r_2) [l2,r2),由于窗口长度是单调不减的,所以当 r 2 = n r_2=n r2=n时我们可以得到答案 n − l 2 n-l_2 n−l2。虽然总体复杂度依然是 O ( n ) O(n) O(n),不过可以减少一些运算次数。
class Solution {
public:
int equalSubstring(string s, string t, int maxCost) {
int cost=0,l=0,r=0,n=s.size();
while(r<n)
{
cost+=abs(s[r]-t[r]);
++r;
if(cost>maxCost)
{
cost-=abs(s[l]-t[l]);
++l;
}
}
return n-l;
}
};