``` // 寻找符合要求的最长子串 // 题目描述 // 给定一个字符串 𝑠,要求找出满足以下条件的最长子串: // 1.该子串中任意一个字符最多出现 2 次。 // 2.该子串不包含指定的某个字符。 // 请计算满足上述条件的最长子串的长度。 // 输入格式 // 第一行包含一个字符,表示指定不能包含的字符,取值范围为 [0-9a-zA-Z]。 // 第二行为字符串 𝑠,每个字符的取值范围为 [0-9a-zA-Z]。 // 输出格式 // 输出一个整数,表示满足条件的最长子串的长度。如果不存在满足条件的子串,则输出 0。 // 样例输入1 // D // ABC132 // 样例输出1 // 6 // 样例输入2 // D // ABACA123D // 样例输出2 // 7 // 样例解释 // 样例 解释说明 // 样例1 整个字符串 "ABC132" 满足条件,长度为 6。 // 样例2 子串 "ABACA12" 满足条件,长度为 7。 // 数据范围 // 字符串 𝑠的长度范围:1≤∣𝑠∣≤10000 // 字符取值范围:[0-9a-zA-Z] #include <iostream> using namespace std; #include <unordered_map> #include <vector> // 不定长滑动窗口,吐血,先试试暴力,果然超时,通过25/30,5个超时 // 还是得用滑动窗口 // 自己写的一塌糊涂,deepseek一改就AC了 // 复刻博客代码,通过14/30 // n // rycspizmjhxanzjjwsolvvtraboqzuzurzopjohpcfdztvsz // 结果应是:19 // 遇到x的情况计数器没有清空,加上清空还是不行,换成哈希映射,可以了 int main() { // 指定不能包含的字符,[0-9a-zA-Z] char x; // [0-9a-zA-Z] string s; while (cin >> x >> s) { // 注意 while 处理多个 case int res = 0; // 最长子串的长度 int n = s.size(); // 窗口[left,right) // 初始化 int left = 0; int right = 0; // vector<int> cnt(128, 0); // 字符计数 unordered_map<char, int> cnt; while (right < n) { if (s[right] == x) { // 强制截断,整个窗口跳过right // 当前[left,right) res = max(res, right - left); right++; left = right; // 计数器清空 cnt.clear(); // clear()方法只会将size设为0,但不会改变capacity continue; } cnt[s[right]]++; if (cnt[s[right]] > 2) { // 字符计数>2 // 当前[left,right) res = max(res, right - left); } // 收缩 while (cnt[s[right]] > 2) { cnt[s[left]]--; left++; } right++; // 向右扩展 } // 最后一个窗口,没有x也没有字符计数>2的情况 // 当前[left,right),此时right==n res = max(res, right - left); cout << res; } return 0; } // 64 位输出请用 printf("%lld")```为什么用vector<int> cnt(128, 0)计数会有问题
时间: 2025-07-10 17:43:53 浏览: 4
<think>好的,我现在需要理解为什么在给出的代码中使用vector<int> cnt(128, 0)作为字符计数会导致问题,而改用unordered_map<char, int>就能解决。这个问题出现在一个寻找最长子串的问题中,子串需要满足两个条件:每个字符最多出现两次,且不包含某个特定字符。
首先,我得回忆一下代码的逻辑。这段代码使用了滑动窗口的方法,维护一个窗口[left, right),窗口内的字符需要满足条件。当遇到禁止的字符x时,窗口会被重置,计数器清空。另外,当某个字符的出现次数超过2次时,左指针left需要向右移动,直到该字符的出现次数不超过2次。
现在的问题是,原本使用vector<int> cnt(128, 0)作为计数器,但结果不正确,而换成unordered_map<char, int>后问题解决。为什么会这样?
首先,vector<int> cnt(128, 0)的大小是128,这可能是因为ASCII字符共有128个。每个字符的ASCII码作为索引,对应的值是该字符的出现次数。而unordered_map则是动态存储出现的字符及其计数。
可能的问题点在于,当遇到禁止字符x时,代码会清空计数器。比如,当使用vector时,cnt.clear()会把所有元素置为0。但是,在C++中,vector的clear()方法确实会移除所有元素,然后将size变为0,但capacity保持不变。然而,在这段代码中,cnt是局部变量,在每次循环中,当遇到x时,会执行cnt.clear(),然后继续循环。但这里的问题可能不是vector的clear方法,而是后续的使用方式。
或者,可能是在某些情况下,当使用vector时,某些字符的计数没有被正确重置?比如,当窗口被强制截断时,left和right都被设置为新的位置,此时cnt需要全部清零。使用vector的clear()方法可能并没有正确重置所有位置为0,而是将size变为0?或者,原来的代码中可能vector被错误地初始化或处理?
仔细看代码,原代码中的注释提到:“计数器清空,clear()方法只会将size设为0,但不会改变capacity”。这可能是误解。实际上,vector的clear()方法会删除所有元素,size变为0,但capacity不变。此时,如果后续再次使用vector时,比如通过operator[]访问元素,可能会导致未定义行为,因为索引可能超出了当前size的范围。例如,当vector的size为0时,访问cnt[s[right]]会导致越界,因为operator[]不会检查索引是否在size范围内,而是假设索引有效。这时,如果vector的capacity足够大,可能不会崩溃,但此时的行为是未定义的,会导致错误的结果。
比如,假设原来的vector的size是128,在调用clear()之后,size变为0。然后,当代码执行cnt[s[right]]++时,s[right]对应的ASCII值可能超过当前的size(此时size是0),导致operator[]访问的是未初始化的内存区域,从而引发错误。
而unordered_map的operator[]则不同,如果键不存在,会插入一个默认构造的值(对于int来说是0),这样即使之前调用了clear(),每次访问都会自动处理不存在的键,保证计数正确。
所以,问题在于当使用vector时,在调用clear()之后,vector的size变为0,此时通过索引访问元素(如cnt['A'])实际上是在访问超出size的位置,导致未定义行为。而unordered_map则不会有这个问题,因为每次访问不存在的键时会自动插入并初始化为0。
举个例子,当遇到禁止字符x后,执行cnt.clear(),此时vector的size变为0。然后,在后续的循环中,处理新的字符,例如s[right]的ASCII码是65('A')。当执行cnt[s[right]]++时,vector的operator[]访问的是索引65的位置,但由于此时vector的size是0,这实际上是一个越界访问,导致未定义行为(可能修改了vector后面的内存,或者返回一个不确定的值)。而unordered_map在这种情况下,会创建键'A',并初始化其值为0,然后递增到1,这是正确的。
因此,使用vector<int> cnt(128, 0)的正确做法应该是在每次清空时,重新初始化所有128个元素为0,而不是调用clear()。或者,在每次需要重置时,重新将整个vector赋值为vector<int>(128, 0)。但是原来的代码中使用的是cnt.clear(),这会导致vector的size变为0,导致后续的索引访问越界。
所以,正确的做法应该是,当需要清空计数器时,不调用clear(),而是将所有元素置0,比如使用fill方法。例如:
cnt.assign(128, 0);
或者在每次遇到x时,重新初始化vector:
cnt = vector<int>(128, 0);
而原来的代码中使用的是cnt.clear(),这导致vector的size变为0,之后用operator[]访问的时候越界,导致未定义行为,从而计数错误,最终导致答案不正确。
总结起来,问题出在vector的clear()方法会使得size变为0,而后续通过operator[]访问元素时,如果索引超过当前size,会导致越界访问,从而引发错误。而unordered_map在这种情况下会自动处理不存在的键,所以使用unordered_map可以避免这个问题。</think>在代码中使用 `vector<int> cnt(128, 0)` 作为字符计数器时,问题主要出在以下两点:
---
### 1. **`clear()` 方法的行为误解**
- **`vector` 的 `clear()`**:调用 `clear()` 会将 `size` 设为 `0`,但**不会改变 `capacity`**。此时,虽然内存未被释放,但通过 `operator[]` 访问元素时,**索引必须满足 `0 ≤ index < size`**。若 `size` 为 `0`,访问任何索引都会导致**越界**,引发未定义行为(如脏数据或崩溃)。
- **`unordered_map` 的 `clear()`**:调用 `clear()` 后,所有键值对均被删除。但后续通过 `operator[]` 访问键时,若键不存在,会自动插入并初始化为 `0`,保证了正确性。
---
### 2. **越界访问导致计数错误**
- **滑动窗口强制截断时**:遇到禁止字符 `x` 时,代码会清空计数器。若使用 `vector`,`clear()` 后 `size` 变为 `0`。后续处理新字符时,假设字符的 ASCII 码为 `k`,访问 `cnt[k]` 会导致越界(因为 `k ≥ size = 0`),此时计数器的值不可预测。
- **`unordered_map` 的自动初始化**:即使调用 `clear()`,访问新字符时,`cnt[s[right]]++` 会自动插入该字符并初始化为 `0`,确保计数正确。
---
### 正确解法
若坚持使用 `vector`,需在清空时**重置所有字符计数为 0**,而非调用 `clear()`。例如:
```cpp
cnt.assign(128, 0); // 重置所有元素为 0
```
或直接重新初始化:
```cpp
cnt = vector<int>(128, 0);
```
---
### 总结
- **根本问题**:`vector` 的 `clear()` 导致 `size` 为 `0`,后续越界访问引发脏数据。
- **解决思路**:改用 `unordered_map` 或修正 `vector` 的清空方式。
阅读全文
相关推荐

















