GESP三级真题 202409-P2·回文拼接:枚举切分 + 双段回文判断完整拆解

四季读书网 1 0
GESP三级真题 202409-P2·回文拼接:枚举切分 + 双段回文判断完整拆解

这道题,拆两段,两段都要是回文

2024 年 9 月 GESP C++ 三级编程第二题「回文拼接」,是字符串枚举的典型题。

题目问:一个字符串能不能拆成前后两段,两段都是回文串,且每段长度至少为 2?

思路直接:枚举每一个合法的切分点 j,把字符串切成左段和右段,分别判断是不是回文——找到一个合法切分就输出 Yes,全试完都没有就输出 No

难点在两处:切分点的合法范围,以及怎么判断一段字符串是回文。


题目原文

回文拼接(GESP C++ 三级,2024 年 9 月,编程题 2)

给定 n 个仅含小写字母的字符串。对每个字符串,判断它是否能写成两个回文串的前后拼接,且两段长度都至少为 2

回文串:从前往后读与从后往前读完全相同的非空字符串。

输入格式:

  • 第一行一个正整数 n,表示字符串个数
  • 接下来 n 行,每行一个仅含小写字母的字符串

输出格式:

  • 对每个字符串输出一行: Yes 或 No

样例输入:

  1. 4
  2. abcd
  3. aabbb
  4. aaac
  5. abcdd

样例输出:

  1. No
  2. Yes
  3. No
  4. No

说明: aabbb 可拆为 aa 与 bbb,均为回文且长度均不少于 2;其余在同样规则下不成立。

数据范围: 1 ≤ n ≤ 10;字符串仅含小写字母。


家长速览

本篇核心:枚举切分点 j(从 2 到 m-2),用 s.substr 取左右两段,用 reverse 翻转后比较判断是否回文。堵点一:j 的范围是 2jm-2,保证两段各至少 2 个字符。堵点二: substr(0,j) 取前 j 个字符, substr(j,m-j) 取后 m-j 个字符,下标别搞混。堵点三:回文判断用翻转比较——复制一份、reverse、再与原串比较。

GESP三级真题 202409-P2·回文拼接:枚举切分 + 双段回文判断完整拆解-第1张图片-四季读书网

第一步:读懂题,手算样例

样例:aabbb(长度 m=5)

合法切分点 j 的范围:j 从 2 到 m-2=3,即 j ∈ {2, 3}。

j
左段 s1
s1 是回文?
右段 s2
s2 是回文?
两段都回文?
2
aa
(aa反转还是aa)
bbb
(bbb反转还是bbb)
3
aab
否(反转是baa)
bb

j=2 时两段都是回文,找到了,输出 Yes

样例:abcdd(长度 m=5)

j
左段
回文?
右段
回文?
结果
2
ab
cdd
3
abc
dd
否(左段不回文)

全部切分点都不满足,输出 No

为什么 aaac 也是 No?

j
左段
回文?
右段
回文?
2
aa
ac
3
aaa
c

j=3 时右段 c 长度为 1,不满足「至少为 2」的要求,不合法。输出 No


第二步:框架——枚举切分点

  1. 读入 n
  2. 循环 n 次:
  3. 读入字符串 s,设长度为 m
  4.     found =0
  5.  j 2 m-2
  6.         s1 = s 的前 j 个字符
  7.         s2 = s 的后 m-个字符
  8. 如果 s1 是回文 s2 是回文:
  9.             found =1break
  10. 输出YesNo

为什么 j 从 2 开始,到 m-2 结束?

  • j 是左段长度,左段至少 2 个字符 → j ≥ 2
  • 右段长度 = m - j,右段至少 2 个字符 → m - j ≥ 2 → j ≤ m - 2
  • 所以合法范围是 2jm-2

第三步:关键细节

细节一: substr 怎么用

  1. string s1 = s.substr(0, j);// 从位置 0 开始,取 j 个字符 = 前 j 个
  2. string s2 = s.substr(j, m - j);// 从位置 j 开始,取 m-j 个字符 = 后 m-j 个

两个参数:起始位置、取多少个。别写成 substr(j,m),那样会取到越界。

细节二:回文判断用 reverse 翻转比较

  1. string t1 = s1;
  2. reverse(t1.begin(), t1.end());// t1 变成 s1 的倒序
  3. if(t1 == s1){/* s1 是回文 */}

先复制一份( t1=s1),再 reverse,再与原串比较。不要直接 reverse(s1),那样原串就被改掉了。

细节三:j 的边界要严格,m < 4 时没有合法切分点

如果字符串长度 m < 4,则 m-2 < 2, for(j=2;j<=m-2;j++) 的循环一次也不进入,直接输出 No。这是正确行为,不需要特判。


第四步:完整代码

  1. #include<bits/stdc++.h>
  2. usingnamespace std;
  3. int main(){
  4. int n;
  5.     cin >> n;
  6. for(int i =0; i < n; i++){
  7.         string s;
  8.         cin >> s;
  9. int m =(int)s.size();
  10. int found =0;
  11. for(int j =2; j <= m -2; j++){
  12.             string s1 = s.substr(0, j);
  13.             string s2 = s.substr(j, m - j);
  14.             string t1 = s1, t2 = s2;
  15.             reverse(t1.begin(), t1.end());
  16.             reverse(t2.begin(), t2.end());
  17. if(t1 == s1 && t2 == s2){
  18.                 found =1;
  19. break;
  20. }
  21. }
  22. if(found){
  23.             cout <<"Yes\n";
  24. }else{
  25.             cout <<"No\n";
  26. }
  27. }
  28. return0;
  29. }

动手填一填:程序填空

  1. #include<bits/stdc++.h>
  2. usingnamespace std;
  3. int main(){
  4. int n;
  5.     cin >> n;
  6. for(int i =0; i < n; i++){
  7.         string s;
  8.         cin >> s;
  9. int m =(int)s.size();
  10. int found =0;
  11. for(int j = ___________; j <= ___________; j++){// ① 切分点范围
  12.             string s1 = s.substr(0, j);
  13.             string s2 = s.substr(___________, m - j);// ② 右段起始位置
  14.             string t1 = s1, t2 = s2;
  15.             reverse(t1.begin(), t1.end());
  16.             reverse(t2.begin(), t2.end());
  17. if(t1 == s1 && t2 == s2){ found =1;break;}
  18. }
  19. if(found) cout <<"Yes\n";
  20. else cout <<"No\n";
  21. }
  22. return0;
  23. }
考的是什么
答案
① 左边界
左段至少 2 个字符
2
① 右边界
右段至少 2 个字符,即 j ≤ m-2
m-2
右段从切分点 j 开始
j

这道题考察的核心能力

能力点
说明
字符串切分
substr(0,j)
 取前段, substr(j,m-j) 取后段
回文判断
复制 + reverse + 与原串比较
枚举切分点范围
j 从 2 到 m-2,保证两段各至少 2 个字符
找到即停
found=1 + break,不必穷举所有切分点
输出格式
Yes
No 大小写严格匹配

小结

「回文拼接」的框架是枚举切分点 + 双段回文判断。

记住这两个关键点

  1. for(int j =2; j <= m -2; j++)// 两端各至少 2 个字符
  2. string t1 = s1; reverse(t1.begin(), t1.end());// 复制再翻转,不改原串

与上一篇「二进制回文串」对比:数字回文用 while 逐位提取,字符串回文用 reverse 翻转比较——一数一串,两种写法都要熟练。

抱歉,评论功能暂时关闭!