藍橋杯2023年A組-試題D-平方差

DawnTraveler發表於2024-04-08

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;
}

相關文章