C. awoo's Favorite Problem_思维+set
题目大意
给字符串s和t。现有两种操作,操作1使“ab”变成“ba”,操作2使“bc”变成“cb”。问经过若干次操作后s有无可能变成t。
思路和代码
将两个字符串一位一位进行匹配。然后就有四种情况:
- s_i==t_i 则匹配
- s_i == 'a' and t_i == 'b' 则表明i位置的b需要从[i,n]中的某个位置(假设是j)移动而来。考虑要从j一路移到i,则表明[i,j]中没有字符c(不然就断掉了)。而可以贪心的想到,j位置就是[i,n]区间中字符b的第一次出现位置。
- s_i == 'b' and t_i == 'c' 和上一种情况类似。
- else
那么考虑如何编码:
做三个set,表示abc三种字符在[i,n]区间中的下标序列。然后两个字符串一位一位的对齐匹配,出现不匹配情况就尝试去将ti移动到si位置。总体是一个模拟的思路。
string solve2(){
cin >> n ;
string s , t ;
cin >> s >> t ;
s = " " + s ;
t = " " + t ;
set<ll> idx[3] ;
rep(i , 1 , n)
idx[s[i] - 'a'].insert(i) ;
//[i,n]的abc三种字母的升序下标序列
rep(i , 1 , n){
// cout << i << "=>" << s << "\n" ;
if(s[i] == t[i]){//直接匹配
idx[s[i] - 'a'].erase(i) ;
continue ;
}
if(s[i] == 'a' && t[i] == 'b'){
//aaab 后面的b移到i位置
//baaa
if(!idx[1].size()) return "NO\n" ;//没有b可以换到i位置
int posb = *idx[1].begin() ;
int posc = *idx[2].begin() ;
if(idx[2].size() && posc < posb) return "NO\n" ;
idx[0].erase(i) ;
idx[1].erase(posb) ;
idx[0].insert(posb) ;
swap(s[i] , s[posb]) ;
// cout << i << " <- " << posb << "\n" ;
continue ;
}
if(s[i] == 'b' && t[i] == 'c'){
//bbbc
//cbbb
if(!idx[2].size()) return "NO\n" ;
int posc = *idx[2].begin() ;
int posa = *idx[0].begin() ;
if(idx[0].size() && posa < posc) return "NO\n" ;
idx[1].erase(i) ;
idx[2].erase(posc) ;
idx[1].insert(posc) ;
swap(s[i] , s[posc]) ;
continue ;
}
return "NO\n" ;
}
return "YES\n" ;
}
接下来是堆的解法:
仔细思索一下整个解题步骤:
遇到不匹配的时候尝试从后面换一个匹配的字符过来。而这个交换就是在字符的下标序列里面插入一个新的点。而每次交换的时候,换过来的字符肯定是所有同种字符里的第一个。就是每次取头,再插入。所以优先队列完美胜任。要注意在交换的时候对堆的维护,细节写在代码里了。
string solve3(){
string s , t ;
cin >> n >> s >> t ;
s = " " + s ;
t = " " + t ;
priority_queue<ll , vct<ll> , greater<ll> > idx[3] ;
rep(i , 1 , n) idx[s[i] - 'a'].push(i) ;
rep(i , 1 , n){
// cout << i << " " << s << "\n" ;
if(s[i] == t[i]){
// cout << idx[s[i] - 'a'].top() << "\n" ;
idx[s[i] - 'a'].pop() ;
continue ;
}
if(s[i] == 'a' && t[i] == 'b'){
if(!idx[1].size()) return "NO\n" ;//后面没有b可以交换到第i位
int posb = idx[1].top() ;
if(idx[2].size()){
int posc = idx[2].top() ;
if(posc < posb) return "NO\n" ;//中间被c截断了,b移不到i位置
}
// cout << idx[s[i] - 'a'].top() << "\n" ;
idx[s[i] - 'a'].pop() ;
swap(s[i] , s[posb]) ;//交换
idx[1].pop() ;
idx[0].push(posb) ;
continue ;
}
if(s[i] == 'b' && t[i] == 'c'){
if(!idx[2].size()) return "NO\n" ;//后面没有c可以交换到第i位
int posc = idx[2].top() ;
if(idx[0].size()){
int posa = idx[0].top() ;
if(posa < posc) return "NO\n" ;//中间被a截断了,c移不到i位置
}
// cout << idx[s[i] - 'a'].top() << "\n" ;
idx[s[i] - 'a'].pop() ;
swap(s[i] , s[posc]) ;//交换
idx[2].pop() ;
idx[1].push(posc) ;
continue ;
}
return "NO\n" ;
}
return "YES\n" ;
}
小结
考试周军训周终于结束了,可以好好开始准备比赛,先复建两天,捋一捋知识点。做一做思维题,我尤其需要锻炼思考的能力(我只会打板子)
其实这题并不难,而且set和堆的解法其实类似。写这么多可能显的有点累赘,但是我的目的是做到100%ok,整个题目要思考清楚。
有的时候潜意识里会在做题的时候偷懒,蒙结论,猜对了就直接过。这样确实爽但是没有任何效果,是在纯纯浪费时间。不能欺骗自己啊,加油。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端