1 条题解
-
0
Vasya 与字符串 题解
算法说明
最终的最长相同字符子串只可能全部为
a或全部为b。分别计算把某个连续子串变成全a、全b时能够取得的最大长度,再取两者的最大值。对于固定的目标字符
target,使用双指针维护窗口 ,并记录窗口中不等于target的字符数changed:- 将
s[right]加入窗口;若它不等于target,则changed加一。 - 当
changed>k时,不断右移left;移出的字符不等于target时,changed减一。 - 此时窗口至多需要修改 个字符,用窗口长度更新答案。
正确性证明
先固定目标字符
target。算法在每次更新答案前都保证窗口内不等于
target的字符数不超过 ,所以将这些字符修改为target后,整个窗口均为target。因此算法记录的每个长度都是合法的。对于任意右端点 ,如果加入新字符后窗口需要修改的字符数超过 ,算法会一直右移左端点,直到窗口重新合法。停止时的 是使窗口合法的最小左端点:更靠左的窗口包含更多字符,且在停止前仍有超过 个非目标字符。因此,算法取得了以 为右端点、能够变成全
target的最长合法窗口。枚举所有右端点后,算法便求出了能够变成全
target的最长连续子串。题目中相同字符子串的字符只能是a或b,分别以二者为目标计算并取最大值,就得到全局最优答案。复杂度分析
对每个目标字符,左右指针都只会单调移动至多 次,时间复杂度为 。只使用常数个变量,额外空间复杂度为 。
边界与易错点
- 时不能修改任何字符,答案是原串最长连续相同段。
- 时可以把整个字符串改成同一个字符,答案为 。
- 必须同时计算变成全
a和全b的情况。 - 窗口中非目标字符数恰好等于 时仍然合法;只有大于 才收缩。
- 收缩窗口时,只有移出的字符不是目标字符,才减少
changed。
参考代码
#include <iostream> #include <string> using namespace std; int longest(const string &s, int k, char target) { int left = 0; int changed = 0; int answer = 0; for (int right = 0; right < static_cast<int>(s.size()); ++right) { if (s[right] != target) ++changed; while (changed > k) { if (s[left] != target) --changed; ++left; } int length = right - left + 1; if (length > answer) answer = length; } return answer; } int main() { ios::sync_with_stdio(false); cin.tie(0); int n, k; string s; if (!(cin >> n >> k >> s)) return 0; int answer_a = longest(s, k, 'a'); int answer_b = longest(s, k, 'b'); cout << (answer_a > answer_b ? answer_a : answer_b) << '\n'; return 0; } - 将
- 1
信息
- ID
- 95
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 4
- 已通过
- 2
- 上传者