P10058 Reverse and Rotate-左/右两种写法题解
题目

前言
讲下为什么两次翻转没用
比如
abc 站一块儿 翻一次 cba 第二次 abc
为什么会这样?其实就是因为翻转没有改变旁边邻居,只是改变了左右/相对位置,所以
ab ba ab又回来了
下面都是对abcde进行分析
我的做法w
暴力枚举 :
详见
我信心慢慢交上去,心里还纳闷就这还黄,一看tle人傻了。还是太菜了
然后正解思路 我是看题解的 还看了半天没看懂。硬是靠着好心人帮助,才弄懂的
下面是正确思路
详见
讲的很仔细
如果是正常思路 其实就是>加<减 就行了
然后你会发现 无论怎么转怎么移动
其实某个字母旁边的邻居都是不变的,所以只要标记首字母的最终位置,
由于是一个环形队列,最终输出就很简单了,
我觉得是正常人都会最终再翻转把 应该没什么人在统计完直接翻转了然后再移动(substr)吧 反正我是这样的
对于这个右+的思路 要注意几点
只执行一次操作且比如 只向左
<2
这样的 x会变成4的
因为取模的原因我们是不要负数的 所以变成4了 通过l-x+1=2 拿来表示ab这一段
有rev和没有rev对于输出的方式是两回事对于右移动而言
l-x+1表示最终位置 也可以拿来做长度使用,
当有了rev这个操作 substr也会发生改变 此时x变成长度又当位置了。
其实靠模拟都是能发现这个规律的 在执行rev 记得一起更新x的值 就这样吧
#include<iostream>
#include<algorithm>
using namespace std;
string s;
int n;
string c;
int x=1;
int num;
bool rev=0;
int main()
{
cin>>s;
cin>>n;
int l=s.size();
for(int i=1;i<=n;i++)
{
cin>>c;
if(c==">")
{
cin>>num;
x=(x+num%l)%l;
}
else if(c=="<")
{
cin>>num;
x=((x-num)%l+l)%l;
}
else {
rev=!rev;
x=l-x+1;
}
}
if(rev)
{
string s1=s.substr(0,x);
string s2=s.substr(x);
s=s2+s1;
//x/l-x+1表示长度 也表示选择的下标
reverse(s.begin(),s.end());
cout<<s;
}
else
{
string s1=s.substr(l-x+1);
string s2=s.substr(0,l-x+1);
s=s1+s2;
cout<<s;
}
return 0;
}
对于向左的做法分析如下
我觉得这个最麻烦了 也是最不想写的
这里我们也是最后计算翻转的
一定要明白一个道理
对于abcde 先翻转后右移动1=先左移1后翻转
可以模拟下 然后我们在对左移动sum的求和中 就可以对每一时刻是加还是减就有了清晰的认知了
rev是0 不用说
rev是1 如果向右移动 对于此时已经是翻转了 所以sum(左)+比较左翻转=翻转右
rev是1 如果向左移动 此时
比如 翻转 左移2 =右2+翻转 所以是sum-因为相当于 此时进来的是右没有翻转的情况
最终核心代码就是这样的 非常感谢评论区的解答 否则我都做不出来
最后代码substr就是对向左就行处理 不用担心sum是负数 因为
前面说过了 如果是负数 比如
(相当于右移动1后翻转)
<2
rev
<3
最终得到sum=4
其实就是向左4=向右(len-4)=1操作结果是一样的 只不过取模保证了正整数而已
好题,是一道很好的题目 我觉得这道题 然后我后面还要上一篇双端队列的题解上来
头好晕.
#include <iostream>
#include<algorithm>
using namespace std;
string s;
int n;
string c;
int rev = 0;
int x;
int sum;
int main() {
cin >> s;
cin >> n;
int l = s.size();
for (int i = 1; i <= n; i++) {
cin >> c;
if (c == ">") {
cin >> x;
if (rev == 0) {
sum = ((sum - x) % l + l) % l;
//-2%5==3
//右移2 相当于左移动3
//
} else {
sum = (sum + x) % l;
}
} else if (c == "<") {
cin >> x;
if (rev == 0)sum = (sum + x) % l;
else {
sum = ((sum - x) % l + l) % l;
}
} else {
rev = !rev;
}
}
//向左后翻转 此题我要写3种解法
string s1=s.substr(sum);
string s2=s.substr(0,sum);
s=s1+s2;
if(rev)reverse(s.begin(),s.end());
cout<<s;
return 0;
}
------------
------------
------------
最后
//这题没什么严格定义向左翻转=翻转再右 这种不同的做题思路
//并没有说翻转的顺序严格规定 反正都是环型队列
//只要你最终划分的字段正确
//最终翻转后得到的都是正确答案
//没有说我用了左后翻
//然后用右边的思路去做题
//就一定在输出的过程中要先翻转了 再右边移动然后输出正确答案
//不是这样的 只是右边做起来更简单
//对于左边的 我们使用翻右=左翻这个规律
//进行每次状态改变的sum值维护而已
//这个sum表示左移动的值
//最终用这个sum把字段分好就行
//不用担心只右移动的问题
//因为我们发现左移动=len-右
//这样一定能写出来
附别的写法
前言
我发现其实翻转放后面还是前面都其实没关系的,只要操作正常就没事
然后这个代码 cnt有正负意义的 所以不要用
((sum-x)%p+len)%p变成正的
#include<iostream>
#include<deque>
#include<algorithm>
using namespace std;
deque<char>q;
int n;
string s;
long long cnt=0;
string c;
int x;
bool rev=0;
int main()
{
cin>>s;
cin>>n;
int len=s.size();
for(int i=0;i<len;i++)q.push_back(s[i]);
for(int i=1;i<=n;i++)
{
cin>>c;
if(c==">")
{
cin>>x;
cnt+=(x%len);
}
else if(c=="<")
{
cin>>x;
cnt=cnt-(x%len);
//不用用那个%+len%len,因为要考虑cnt为负数 使用pushfrot这些操作
//可能考虑了 也可以做吧 反正我不会 主要是想学习下双端队列用法
}
else
{
rev=!rev;
cnt=-1*cnt;
}
}
if(rev)reverse(q.begin(),q.end());
if(cnt<0)
{
for(int i=1;i<=(-1*cnt)%len;i++)
{
q.push_back(q.front());
q.pop_front();
}
}
else if(cnt>0) {
for(int i=1;i<=cnt%len;i++)
{
q.push_front(q.back());
q.pop_back();
}
}
for(auto i:q)
{
cout<<i;
}
return 0;
}

浙公网安备 33010602011771号