蓝桥杯2023年A组-试题D-平方差
0. 题目
1. 题解
1.1 基于中心扩展的字符串处理算法
思路
我们可以选定一个中心,然后从中心开始,向外扩展我们的子串,且能存储之前子串的部分性质(这里便于左等于右的情况)
0. 确定中心点
这里我们用外层一个大循环来表示,中心点即为变量i。
- 首先分为子串为奇数串和偶数串的情况
奇数串的话比如像上图示例的我们选的是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;
}