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;
}
posted @ 2025-04-16 20:00  LteShuai  阅读(22)  评论(0)    收藏  举报