蓝桥杯第十一届省赛C++B组真题解析
八、回文日期https://www.lanqiao.cn/problems/348/learning
方法一:暴力枚举所有的日期,记录有多少个回文日期。
#include <bits/stdc++.h>
using namespace std;
int month[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int s[9];
bool find(int year){
if(year%400==0||(year%4==0&&year%100!=0)) return true;
else return false;
}
bool tell(int s1[]){
bool flag = true;
for(int i=0; i<8; i++){
if(s1[i] != s1[7-i]){
flag = false;
break;
}
}
return flag;
}
void transform(int num, int kk){
if(kk==1){
s[0] = num/1000;
s[1] = num/100%10;
s[2] = num/10%10;
s[3] = num%10;
}else if(kk==2){
if(num<10) s[4] = 0;
else s[4] = num/10 ;
s[5] = num%10 ;
}else if(kk==3){
if(num<10) s[6] = 0;
else s[6] = num/10 ;
s[7] = num%10;
}
}
int main()
{
int ans=0;
long long day1,day2;
cin >> day1 >> day2;
int y1 = day1/10000,y2=day2/10000;
int m1 = day1%10000/100,m2=day2%10000/100;
int d1 = day1%100,d2 = day2%100;
if(y2>y1){
//特判第一年
transform(y1,1);
if(find(y1)) month[2] = 29;
for(int i=m1; i<13; i++){
transform(i,2);
int j=1;
if(i==m1) j = d1;
for(; j<=month[i]; j++){
transform(j,3);
if(tell(s)) ans++;
}
}
//特判最后一年
transform(y2,1);
if(find(y2)) month[2] = 29;
for(int i=1; i<=m2; i++){
transform(i,2);
for(int j=1; j<=month[i]; j++){
if(i == m2 && j>d2) break;
transform(j,3);
if(tell(s)) ans++;
}
}
}else{
transform(y2,1);
if(find(y2)) month[2] = 29;
for(int i=m1; i<=m2; i++){
transform(i,2);
int j=1;
if(i==m1) j = d1;
for(; j<=month[i]; j++){
if(i == m2 && j>d2) break;
transform(j,3);
if(tell(s)) ans++;
}
}
}
for(int i=y1+1;i<y2;i++){
if(find(i)) month[2] =29;
else month[2] = 28;
transform(i,1);
for(int j=1; j<13; j++)
{
transform(j,2);
for(int k=1; k<=month[j]; k++)
{
transform(k,3);
if(tell(s)) ans++;
}
}
}
cout << ans;
return 0;
}
方法二:用月份和日枚举所有的回文日期,判断是否在有效日期内。
⚠️不用特判闰年,因为二月份反转的年份为20,一定为闰年.
#include<bits/stdc++.h>
using namespace std;
//预处理月份对应天数
int a[]={0,31,29,31,30,31,30,31,31,30,31,30,31};
int main(){
int n,m;
cin>>n>>m;
int ans=0;
//根据月份和天数直接构造回文年份,看是是否在题目要求范围内
for(int i=1;i<=12;i++){
for(int j=1;j<=a[i];j++){
//年份
int y=j%10*1000+(j/10)*100+i%10*10+i/10;
//年份+月份+天数组成的回文串
int sum=y*10000+i*100+j;
if(sum>m||sum<n) continue;
else ans++;
}
}
cout<<ans;
return 0;
}
九、子串分值和https://www.lanqiao.cn/problems/1037/learning/
方法一:遍历+哈希表
#include <bits/stdc++.h>
using namespace std;
string s;
int main()
{
cin >> s;
int cnt=0;
for(int i=0; i<s.size(); i++){
unordered_map<char,int> m;
m[s[i]]++;
for(int j=i; j<s.size(); j++){
m[s[j]]++;
cnt += m.size();
}
}
cout << cnt;
return 0;
}
方法二:
核心观察:
每个字符 s[i] 在某个子字符串中第一次出现时,会为该子字符串的不同字符数 贡献1。统计所有这样的贡献次数。
实现方法:
使用数组 last[26] 记录每个字母上一次出现的位置。
对于每个字符 s[i],计算它能在多少个子字符串中作为第一次出现的该字符。
#include <bits/stdc++.h>
using namespace std;
string s;
int o_last[26];//记录26个字母上一次出现的位置
int main()
{
cin >> s;
long long cnt=0;
int l = s.size();
memset(o_last, -1, sizeof(o_last));
for(int i=0; i<l; i++){
int last = o_last[s[i]-'a'];
cnt += (long long)(l-i)*(i-last);//前一段乘后一段
o_last[s[i]-'a'] = i;//更新s[i]最新出现的位置
}
cout << cnt;
return 0;
}