蓝桥杯2023年A组-试题D-平方差

0. 题目



1. 题解

1.1 基于中心扩展的字符串处理算法

思路

我们可以选定一个中心,然后从中心开始,向外扩展我们的子串,且能存储之前子串的部分性质(这里便于左等于右的情况)
0. 确定中心点
这里我们用外层一个大循环来表示,中心点即为变量i。

  1. 首先分为子串为奇数串和偶数串的情况
    奇数串的话比如像上图示例的我们选的是3作为中心, 但由于要求是连续子串,所以我们默认初始长度为3(长度为1的翻转也没有任何意义,不会改变大小)开始进行翻转。
    这里落实到程序中就是 L=i-1,R=i作为初始值,'L>=0&&R<str.length()'作为结束条件, L--,R++向外扩展。
    偶数串的话其实同理,见程序。

2.其次再考虑到何时可以翻转?
由于我们是由中心点向外扩展, 我们利用贪心的思想,从局部来看,何时能达到翻转后大小变小?

只要区域最左边的值大于最右边的值,结果就会变小。真的只有这样吗?
到了本题的一大难点:我们发现存在 2312 这种,虽然左值等于右值,但是翻转后 2132 > 2312的,就是说左等于右,还要再往里面看一层,就比较麻烦了,可能涉及到递归。

但是这就是我们为何使用从中心扩展的字符串处理,我们发现,只要我们在从中心向外扩展的过程中,加上一个flag标记,标记之前一个是否可以反转,就可以在下一次遇到左等于右的情况时,直接通过标记判断是否可以反转;

你问第一次就是左等于右? 由于为奇数串:121(怎么翻转都不会变), 偶数串:11(怎么翻都不会变) 我们设置flag初值为false(不可翻转)即可!

代码

#include<bits/stdc++.h>
using namespace std;
int ans = 0;
int main(){
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	string str;
	cin >> str;
	for (int i = 0; i < str.length(); i++){
		// 子串个数为偶数
		bool flag = false;
		for (int L = i - 1, R = i; L >= 0 && R < str.length(); L--, R++){
			if(str[L] > str[R]) flag = true; // 左大于右,代表可以交换 
			if(str[L] < str[R]) flag = false; // 右大于左,代表不可以交换 
			if (flag) ans++; // 这里隐含了左等于右的情况,flag情况跟之前保持相同,所以无需写出 
		} 
		
		// 子串个数为奇数 
		flag = false;
		for (int L = i - 1, R = i + 1; L >= 0 && R < str.length(); L--, R++){
			if(str[L] > str[R]) flag = true;
			if(str[L] < str[R]) flag = false;
			if (flag) ans++;
		}
	} 
	cout << ans;
	return 0;
}
posted @ 2024-04-08 16:45  DawnTraveler  阅读(11)  评论(0编辑  收藏  举报