先给题

尽可能使字符串相等

给你两个长度相同的字符串,s 和 t。

将 s 中的第 i 个字符变到 t 中的第 i 个字符需要 |s[i] - t[i]| 的开销(开销可能为 0),也就是两个字符的 ASCII 码值的差的绝对值。

用于变更字符串的最大预算是 maxCost。在转化字符串时,总开销应当小于等于该预算,这也意味着字符串的转化可能是不完全的。

如果你可以将 s 的子字符串转化为它在 t 中对应的子字符串,则返回可以转化的最大长度。

如果 s 中没有子字符串可以转化成 t 中对应的子字符串,则返回 0。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/get-equal-substrings-within-budget
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

有兴趣的可以先做一下

本人用的C++的代码

1.首先先说暴力破解

(这是我的第一想法,很蠢,但还是要写出来) 我首先想到的是从第一位开始,依次进行比较,超出最大则从记录的地址(a)值下一位开始

 

尽可能使字符串相等(滑动窗口)_字符串尽可能使字符串相等(滑动窗口)_字符串_02
 1 class Solution {
 2 
 3 public:
 4 int equalSubstring(string s, string t, int maxCost) {
 5 int sum = 0;
 6 int min = 0, n = 0;//min 记录最大的长度 n 记录当前长度
 7 int a = 0;//用来记录地址
 8 for (int i = 0; i < s.length(); i++) {
 9 n++;
10 sum += abs((int)(s[i] - t[i]));
11 if (sum > maxCost) {
12 i = ++a;
13 n--;
14 min = n > min ? n : min;
15 n = 0;
16 sum = 0;
17 }
18 }
19 //如果字符串最后一位正好达到max 还需要在进行一次比较
20 return (n > min ? n : min);
21 }
22 };
View Code

 

 


毫无疑问,暴力破解是超时的,那么接下来就得优化过程,我想的是让地址值a的移动进行一些适当的改变。
在优化过程中,我看了一下题解,这时恍然大悟,可以转换一下,转换成自己做过的题目。
我们可以把s和t第i个字符ASCII 码值的差的绝对值先算出来,存储在一个数组中,这样问题就转换成了最大子数组问题(只不过加了一些限制,没有负数和有最大上限)。
算法导论p38-p42 有对最大子数组的描述,分治法和一个线性时间的算法。(以此为参考来讨论这个题)
可以观看我的博客,我对最大子数组问题也进行过分析。

2.说一下我对暴力的优化

这是我根据转化后的数组进行的一个修改,虽然通过了,但是时间还是很长,只能说勉强过了,我先解释一下我的这个代码,交代一下我的思路,然后再说我从其他博客以及题解中获得的思想

尽可能使字符串相等(滑动窗口)_字符串尽可能使字符串相等(滑动窗口)_字符串_02
 1 class Solution {
 2 public:
 3 int equalSubstring(string s, string t, int maxCost) {
 4 int length = s.length();
 5 int* num = new int[length + 1];
 6 for (int i = 1; i < length + 1; i++) {
 7 num[i] = abs((int)(s[i - 1] - t[i - 1]));
 8 cout << num[i] << " ";
 9 }
10 int sum = 0, maxsum = 0;
11 int low = 0;
12 int maxlow = 0, maxhigh = 0;
13 for (int i = 1; i <= length; i++) {
14 sum += num[i];
15 while (sum > maxCost)
16 {
17 low++;
18 sum -= num[low];
19 }
20 if (((i - low) > (maxhigh - maxlow)))
21 {
22 maxhigh = i;
23 maxlow = low;
24 maxsum = sum;
25 }
26 }
27 return maxhigh - maxlow;
28 }
29 };
View Code

这是暴力破解的一个改良版,让判定的sum值更少了一些(我忽略了本题想要解的东西,本题只想解出最大长度,而我却一直在纠结最大子数组是哪个)
我们直接举例子来说明我的代码吧
假设1 2 3 的总值是小于maxCost的 而 1 2 3 4的总值是大于maxCost的,那么接下来我们就要让它减小到maxCost即让low++ 缩减到2 3 4,判断是否达到最小(等会看后面的讲解这一步其实是没必要循环的)
如果长度大于我们记录的最大长度,那么替换相应的值即可。
相比较暴力破解,我们省去了2 和 3 和 2 3 这样的比较过程。

3.看题解了解了滑动窗口这样的解法。

https://leetcode-cn.com/problems/get-equal-substrings-within-budget/solution/shuang-zhi-zhen-hua-dong-chuang-kou-by-c-3ktr/
https://leetcode-cn.com/problems/longest-repeating-character-replacement/solution/ti-huan-hou-de-zui-chang-zhong-fu-zi-fu-eaacp/
以上是参考
其实我的想法和答案很接近了,但唯一差出的就是判断sum>maxCost时,我一直想要让sum的值降下来,好求最大子数组坐标,但本题并没有让你去求这个坐标,只需要知道长度即可,
那么改为if的话,如果大于maxCost low++ 我们所记录的最大的长度是没有改变的,只有当sum<=maxCost时,长度才会增加,所以我们没必要让sum始终<=maxCost,只要保证最后的是最大长度即可。
那么接下来修改代码

尽可能使字符串相等(滑动窗口)_字符串尽可能使字符串相等(滑动窗口)_字符串_02
 1 int equalSubstring(string s, string t, int maxCost) {
 2 int length = s.length();
 3 int* num = new int[length + 1];
 4 for (int i = 1; i < length + 1; i++) {
 5 num[i] = abs((int)(s[i - 1] - t[i - 1]));
 6 }
 7 int sum = 0;
 8 int low = 1;
 9 int i = 1;
10 for (; i <= length; i++) {
11 sum += num[i];
12 if (sum > maxCost)
13 {
14 sum -= num[low];
15 low++;
16 }
17 }
18 return i - low;
19 }
View Code


这是修改后的代码,耗时还是长,应该是开辟数组的缘故,我们就不开辟数组了。

尽可能使字符串相等(滑动窗口)_字符串尽可能使字符串相等(滑动窗口)_字符串_02
 1 int equalSubstring(string s, string t, int maxCost) {
 2 int length = s.length();
 3 int sum = 0;
 4 int low = 0;
 5 int i = 0;
 6 for (; i < length; i++) {
 7 sum += abs((int)(s[i] - t[i]));
 8 if (sum > maxCost)
 9 {
10 sum -= abs((int)(s[low] - t[low]));
11 low++;
12 }
13 }
14 return i - low;
15 }
View Code


速度与内存都得到了改善。