【算法竞赛】回文字符串+思维模拟(蓝桥杯真题·回文字符串·代码清晰易懂)
回文在字符串中是一个非常常考的知识点,本题很考验思维和细节处理,可以作为回文问题的很好练习,后续也会总结一些字符串中回文的常见处理技巧和考法
目录
一、题目
二、思路解析
1. 允许的操作:
2. 我们将字符串划分为3部分:
3. 中间必须是回文:
4. 左侧是否匹配?
步骤如下:
5. 最终判断
三、知识点补充
1. 回文串判断
2. 字符串前缀/后缀提取
3. 模拟类字符串题常见技巧
四、代码
一、题目
P10905 [蓝桥杯 2024 省 C] 回文字符串 - 洛谷
二、思路解析
本题的关键在于理解允许的操作和回文串的定义:
1. 允许的操作:
只能在字符串的开头添加字符 'l'
、'q'
、'b'
这意味着我们可以"填补"原字符串的左边部分,而右边不能动
我们先思考:如果我们能在两端都插字符,那我们一定可以对称地填补每一侧,使其回文。
但由于限制只能在左边插,所以我们只能模拟“构造对称的左半边”:
2. 我们将字符串划分为3部分:
题目明确要求我们在左端添加字符的时候只能添加l ,q ,b 三种,所以:
(1)右端连续的l ,q ,b肯定可以被匹配,我们单独拎出来
(2)左端连续的l ,q ,b取决于在现有的(因为我们无法再在右端添加字符了)右端连续部分能否给它匹配,如果能则没问题,否则不合法
(3)剩余的中间部分必须得自己构成回文
故划分方式如下:
-
左端(s1):开头连续的
'l'
、'q'
、'b'
字符 -
右端(s3):结尾连续的
'l'
、'q'
、'b'
字符 -
中间(s2):剩下的部分
3. 中间必须是回文:
中间部分 s2
是我们既不能删也不能改的核心部分。
如果 s2 不是回文串,那无论你怎么填左边都没用
4. 左侧是否匹配?
步骤如下:
-
检查左边连续的 lqb 部分(s1)是否能被右边的 lqb(s3)匹配掉
-
因为我们只能在左边添加 lqb,所以我们不能用超过右边已有的字符
-
换句话说:左边已有的 lqb 部分必须“能被右边覆盖”,才能形成对称
-
5. 最终判断
只有同时满足下面上面条件的字符串才能输出 “Yes”:
-
中间
s2
是回文串 -
左端已有的合法字符
s1
能被右端s3
中的字符镜像匹配
三、知识点补充
1. 回文串判断
最常见的方法是双指针法:一个从左一个从右,逐字符比较
2. 字符串前缀/后缀提取
通过遍历 s
字符串前后,提取出所有的合法字符(即 lqb)
3. 模拟类字符串题常见技巧
-
字符提取和分段:识别出具有意义的字符串片段
-
子串处理:如
s.substr(start, len)
高效提取区间 -
模拟构造:将操作的限制通过条件逻辑写代码
-
双指针:处理对称性、滑动窗口、区间匹配等
四、代码
//只能在左侧连续添加,所以只能允许右端连续部分无法构成回文串
//且无法构成回文串的部分只包含l,q,b
//先去掉右端连续包含l,q,b的部分,看剩下的能否匹配
//此时要注意左端连续部分有可能要和去掉的右端连续部分匹配
//所以还要判断左端连续l,q,b部分能否被匹配
//注意:不是所有回文串都是按照先逆序再找公共子序列
#include <iostream>
#include <cstring>
using namespace std;
int n;
//判断是否是回文串
bool check(string s)
{
int n=s.size();
int i=0,j=n-1;
while(i<j)
{
if(s[i]!=s[j]) return false;
i++;
j--;
}
return true;
}
int main()
{
cin>>n;
for(int m=1;m<=n;m++)
{
bool flag=true;
string s;//原始字符串
cin>>s;
int n=s.size();
string s1,s2,s3;//左端lqb连续部分,中间,右端lqb连续部分
//左端连续lqb
//i:停留在左端第一个不匹配的位置
int i,j;
for(i=0;i<n;i++)
{
if(s[i]=='l'||s[i]=='q'||s[i]=='b') s1+=s[i];
else break;
}
//右端连续lqb
//j:停留在右端第一个不匹配的位置
for(j=n-1;j>=0;j--)
{
if(s[j]=='l'||s[j]=='q'||s[j]=='b') s3+=s[j];
else break;
}
//substr:(i,len)
//中间
s2=s.substr(i,j-i+1);
//检查中间是否是回文串
if(!check(s2)) flag=false;
//检查左端已有连续lqb部分能否被右端完全匹配
//s1和s3都倒序匹配
//指向s1
i=s1.size()-1;
//指向s3
j=s3.size()-1;
while(i>=0&&j>=0&&s1[i]==s3[j]){i--;j--;}
if(i>=0) flag=false;//左端不能被右端完全匹配
//输出
if(flag) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
return 0;
}
-
这道题考察了字符串模拟处理、回文串判断、双指针思想
-
在保证中间部分为回文串的同时,我们还要关注边界拼接对称性
-
注意细节:右端的连续部分既是参考标准,也是判断左端合法性的依据
如果你喜欢这种题解风格,欢迎关注我的博客/专栏,我会持续更新高质量的题解与算法思维讲解