P9232 [蓝桥杯 2023 省 A] 更小的数

[蓝桥杯 2023 省 A] 更小的数

双指针模拟字符串的颠倒

终于本弱一次通关了一道研究生组别的题了[普及/提高−]
一道较为简单的双指针题,但一定有更好的解法.

题目描述

image

小蓝有一个长度均为 n n n 且仅由数字字符 0 ∼ 9 0 \sim 9 09 组成的字符串,下标从 0 0 0 n − 1 n-1 n1你可以将其视作是一个具有 n n n 位的十进制数字 n u m num num,小蓝可以从 n u m num num 中选出一段连续的子串并将子串进行反转,最多反转一次。小蓝想要将选出的子串进行反转后再放入原位置处得到的新的数字 n u m n e w num_{new} numnew 满足条件 n u m n e w < n u m num_{new}<num numnew<num,请你帮他计算下一共有多少种不同的子串选择方案,只要两个子串在 n u m num num 中的位置不完全相同我们就视作是不同的方案。

注意,我们允许前导零的存在,即数字的最高位可以是 0 0 0,这是合法的。

输入格式

输入一行包含一个长度为 n n n 的字符串表示 n u m num num(仅包含数字字符 0 ∼ 9 0 \sim 9 09),从左至右下标依次为 0 ∼ n − 1 0 \sim n-1 0n1

输出格式

输出一行包含一个整数表示答案。

样例 #1

样例输入 #1

210102

样例输出 #1

8

提示

【样例说明】

一共有 8 8 8 种不同的方案:

  1. 所选择的子串下标为 0 ∼ 1 0\sim1 01,反转后的 n u m n e w = 120102 < 210102 num_{new} = 120102 < 210102 numnew=120102<210102
  2. 所选择的子串下标为 0 ∼ 2 0\sim2 02,反转后的 n u m n e w = 012102 < 210102 num_{new} = 012102 < 210102 numnew=012102<210102
  3. 所选择的子串下标为 0 ∼ 3 0\sim3 03,反转后的 n u m n e w = 101202 < 210102 num_{new} = 101202 < 210102 numnew=101202<210102
  4. 所选择的子串下标为 0 ∼ 4 0\sim4 04,反转后的 n u m n e w = 010122 < 210102 num_{new} = 010122 < 210102 numnew=010122<210102
  5. 所选择的子串下标为 0 ∼ 5 0\sim5 05,反转后的 n u m n e w = 201012 < 210102 num_{new} = 201012 < 210102 numnew=201012<210102
  6. 所选择的子串下标为 1 ∼ 2 1\sim2 12,反转后的 n u m n e w = 201102 < 210102 num_{new} = 201102 < 210102 numnew=201102<210102
  7. 所选择的子串下标为 1 ∼ 4 1\sim4 14,反转后的 n u m n e w = 201012 < 210102 num_{new} = 201012 < 210102 numnew=201012<210102
  8. 所选择的子串下标为 3 ∼ 4 3\sim4 34,反转后的 n u m n e w = 210012 < 210102 num_{new} = 210012 < 210102 numnew=210012<210102
【评测用例规模与约定】

对于 20 % 20\% 20% 的评测用例, 1 ≤ n ≤ 100 1 \le n \le 100 1n100

对于 40 % 40\% 40% 的评测用例, 1 ≤ n ≤ 1000 1 \le n \le 1000 1n1000

对于所有评测用例, 1 ≤ n ≤ 5000 1 \le n \le 5000 1n5000

CODE

给个免费的赞吧谢谢ヽ( ̄ω ̄( ̄ω ̄〃)ゝ
写在前面:本来以为需要判定符号,后面删了符号判断也能过,说明不需要判断
#include<bits/stdc++.h>
using namespace std;
string s;int counter;
inline bool reverse_xyz(const string& str){
//双指针模拟颠倒
	for(auto its=str.cbegin(),ite=str.cend()-1;its<ite;--ite,++its){
		if(*its==*ite)continue;
		else if(*its<*ite)return false;//不值得颠倒 
		else return true;//值得颠倒	
	}
	return false;
}
int main(){
	cin>>s;
//j不是下标,而是substr需要的自起始的子串长度
	for(int i=0;i<s.size()-1;++i)for(int j=2;j<s.size()-i+1;++j){
		if(reverse_xyz(s.substr(i,j)))++counter;
	}
	cout<<counter; 
} 

note:

//重复量有点大
//可以优化:哈希表,记忆化,改日实现
//考虑如果当前子串的首尾已经比较出结果
//1.若不值得换,则大于等于当前子串尾的一定 不值得
//2.若值得去换,则小于等于当前子串尾的一定 也值得
/*
eg:
例如本题需要改变后小于则值得
12234
第一轮传入子串32,发现不值得(21101!<12101),并且向后查找第一个小于尾部元素2,find_if()没有,则以该位置起始的所有子串都不值得去换
23202
第一轮传入子串23,发现不值得(32102!<23102),则大于等于3的一定也不值得,故向后查找第一个小于3的元素再比较,find_if()找到2(序列第三的2),发现也不值得,则向后找第一个小于2的元素再比较,得到0,发现值得
12345
第一轮传入12,显然21!<12,且尾巴是2,后面没有一个小于2的,故序列第1的元素没有匹配的,直接返回,节省循环判断时间
如果值得,则可以hash表一下对应的序列第几桶,然后后面的遍历就不会到它.
*/
#include<bits/stdc++.h>
using namespace std;
string s;int counter;
inline bool reverse_xyz(const string& str){
	for(auto its=str.cbegin(),ite=str.cend()-1;its<ite;--ite,++its){
	//双指针内移,若相等继续判定,不相等则输出该子串值不值得reverse
		if(*its==*ite)continue;//相等则双指针内移 
		else if(*its<*ite)return false;//不值得颠倒 
		else return true;//值得颠倒	
	}
	return false;
}
//负数情况
inline bool reverse_xyf(const string& str){ 
	for(auto its=str.cbegin(),ite=str.cend()-1;its<ite;--ite,++its){
		if(*its==*ite)continue;
		else if(*its>*ite)return false;//首位是负号则是大于
		else return true;
	}
	return false;
}
int main(){
	cin>>s;
	//如果是纯数字,默认为无符号正数
	if(isdigit(s[0]))for(int i=0;i<s.size()-1;++i)for(int j=2;j<s.size()-i+1;++j){
		if(reverse_xyz(s.substr(i,j)))++counter;//根据左右端点传入子串
	}
	else if(s[0]=='+')for(int i=1;i<s.size()-1;++i)for(int j=2;j<s.size()-i+1;++j){
		if(reverse_xyz(s.substr(i,j)))++counter;
	}
	else if(s[0]=='-')for(int i=1;i<s.size()-1;++i)for(int j=2;j<s.size()-i+1;++j){
		if(reverse_xyf(s.substr(i,j)))++counter;
	}
	cout<<counter; 

} 
//s.substr(i,j);
//包括i索引的字符在内及其后j-1个字符组成j个字符长度 
  • 55
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值