二、阅读程序(程序输入不超过数组或字符串定义的范围;判断题正确填 A,错误填 B;除特殊说明外,判断题 $2$ 分,选择题 $3$ 分,共计 $40$ 分)
#include <cstdio>
#include <cstring>
using namespace std;
char s[10000];
int cnt[26];
int main() {
scanf("%s", s);
for (int i = 0; i < strlen(s); ++i) {
if (cnt[s[i] - 'a'] <= 50) {
s[strlen(s)] = s[i];
}
++cnt[s[i] - 'a'];
}
printf("%s\n", s);
return 0;
}假设设初始时输入的字符串长度不超过 $500$,且不是空串。完成下面的判断题和单选题。
1) 将程序第 11 行中的 ++i 改为 i++,程序运行结果不会改变( ) 2) 将程序第 11 行改为 for(int i=0,len=strlen(s);i<len;++i), 程序的运行结果不会改变,同时程序的运行效率将得到提升( ) 3) 对于任意一个出现了 $\texttt a$ 到 $\texttt z$ 中所有的字符、且各字符出现的次数不小于 50 的字符串 $b$,总存在一个字符串 $a$,使得将字符串 $a$ 输入程序后的运行结果为字符串 $b$。( ) 4) 程序的输出字符串长度一定不小于 $1300$(注:$1300=50\times 26$)。( )
5) 设输入字符串长度为 $x (1\leq x\leq 500)$,输出字符串长度为 $y$,则关于 $x$ 和 $y$ 的大小关系正确的是( )。 A. 对于全部的输入字符串,都有 $x=y$。 B. 对于全部的输入字符串,都有 $x<y$。 C. 存在一部分输入字符串,使得 $x=y$;也存在一部分输入字符串,使得
(本题为阅读程序 / 完善程序,完整题目与测试数据见平台)
这题别急着看代码,先搞清楚程序到底在干什么:它每读一个字符就统计一次,如果之前这个字符统计数没超过 50,就把这个字符再追加到字符串末尾。所以核心就是模拟这个‘计数 + 条件追加’的过程,然后对着每个选项推演字符串长度变化。
最容易栽跟头的地方,一是把循环里 s[strlen(s)] = s[i] 当成在原有长度基础上直接赋值,其实 strlen(s) 是动态的,赋值后字符串长度就变了,但循环条件里的 strlen(s) 是每次迭代重新算的,所以后面判断时用的是新长度,这导致逻辑上容易想成‘只要总计数<=50就追加’,其实追加本身也会增加计数,题目里计数是先判断再自增,顺序不能搞反。二是看到选项 2 觉得改成 len 就不会错,其实原循环里每次 strlen(s) 都重新扫一遍,改成预处理 len 确实省时间,但题目问的是‘结果不会改变且效率提升’,原循环里因为追加操作,strlen(s) 在循环中会不断变大,而改成固定 len 后,后面追加的字符根本不会被再处理,结果就变了,所以不能简单替换。三是选项 3 和 4 容易想当然认为只要每个字符都>=50 次,输出就>=1300,其实原程序里追加的条件是 cnt<=50,一旦某个字符累积到 51 次就不再追加了,所以即使输入里每个字符都出现了 50 次,输出里每个字符最多也就 50 次,总长度最多 1300,但题目说‘一定不小于 1300’,这忽略了输入可能少于 50 次的情况,比如只输入 a 到 z 各 49 次,输出就是 49*26=1274,显然小于 1300。四是选项 5 容易误选 C 或 D,以为重复两次输入就对应 52 或 53 次输出,其实要仔细模拟:输入 w 重复两次,每个字符出现 2 次,第一次循环每个字符 cnt 从 0 到 2,都<=50,所以每个字符被追加 2 次,输出变成 w 重复 4 次;第二次循环每个字符 cnt 从 4 到 6,还是<=50,追加 2 次,输出变成 w 重复 6 次;以此类推,每次循环每个字符追加 2 次,直到 cnt 达到 50,也就是需要 25 次循环才能让每个字符从 0 到 50,此时输出是 w 重复 50 次;第 26 次循环时 cnt 从 50 到 52,不再追加,所以最终输出是 w 重复 50 次,不是 51 或 52 或 53。
这道题考查的是对程序执行过程的模拟能力,以及对字符串长度动态变化和计数逻辑的细致理解。
命题人特别喜欢在循环里埋坑:利用 strlen(s) 在循环条件中每次重新计算的特性,结合 s[strlen(s)] = s[i] 这种动态追加操作,让学生误以为可以简单替换成固定长度循环,或者误以为计数逻辑是“先加后判”而不是“先判后加”。
++i 改成 i++,结果变不变?原循环是 for(int i=0;i<strlen(s);++i)。++i 和 i++ 在数值上完全一样,都是让 i 从 0 递增到 strlen(s)-1,然后循环终止条件 i<strlen(s) 判断失败。唯一的区别是 i++ 会先使用旧值再自增,++i 是先自增再使用新值,但在这个 for 循环里,i 只在循环体开始时被使用,且每次迭代 i 只增加 1,所以两者行为完全一致。
结论:正确(A)。