2015编程之美挑战赛

本文详细解析了编程之美挑战赛中的两个问题:计算两个日期之间2月29日的个数,并求解给定字符串中的回文子串数目。对于日期问题,文章使用结构体数组和遍历方法进行解答;对于回文子串计数,采用递归方法深入讲解了解题思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2015编程之美挑战赛(资格赛)

一、求两个日期之间2月29日的个数

给定两个日期,计算这两个日期之间有多少个2月29日(包括起始日期)。
只有闰年有2月29日,满足以下一个条件的年份为闰年:
1. 年份能被4整除但不能被100整除
2. 年份能被400整除

输入
第一行为一个整数T,表示数据组数。
之后每组数据包含两行。每一行格式为”month day, year”,表示一个日期。month为{“January”, “February”, “March”, “April”, “May”, “June”, “July”, “August”, “September”, “October”, “November” , “December”}中的一个字符串。day与year为两个数字。
数据保证给定的日期合法且第一个日期早于或等于第二个日期。

输出
对于每组数据输出一行,形如”Case #X: Y”。X为数据组数,从1开始,Y为答案。

1.用了一个结构体数组来存年月日,其实也可以用二维整形数组,不过这样好理解一些;
2.在主计算函数里费了些时间,想着把两个端点及他们之间的年分开来算,但是一开始不知道怎样算之间有多少个闰年,后来用了遍历的方法可能会耗一点时。然后就是没有考虑两个年数相同以及同为闰年的情况。
3.main函数里主要有输入模块和对每个case 调用函数输出模块,将字符串月份转换为int本来想用switch,但是switch的参数必须为一个数,所以不行。好像枚举类型可行,但是最后采用了if-else这种丑陋的结构。

#include<iostream>
#include<vector>
#include<string>
using namespace std;

typedef struct mdy
{
    mdy(int m,int d,int y):month(m),day(d),year(y){}
    int month;
    int day;
    int year;
}mdy;

//判断是否为闰年
bool isLeap(int year)
{
    if(year%400==0||(year%4==0&&year%100!=0))return true;
    else return false;
}

//主计算函数
int computeLeap(int beginYear,int beginMonth,int beginDay,int endYear,int endMonth,int endDay)
{
    int leapNum=0;

    if(endYear==beginYear&&isLeap(beginYear))
    {
        if(((beginMonth<2)||(beginMonth==2&&beginDay<=29))&&((endMonth>2)||(endMonth==2&&endDay==29)))
            leapNum++;
    }
    for(int i=beginYear+1;i<endYear;i++)
    {
        if(isLeap(i))leapNum++;
    }
    if(endYear!=beginYear&&isLeap(beginYear))
    {
        if(beginMonth<2)leapNum++;
        else if(beginMonth==2&&beginDay<=29)leapNum++;
    }
    if(endYear!=beginYear&&isLeap(endYear))
    {
        if(endMonth>2)leapNum++;
        else if(endMonth==2&&endDay==29)leapNum++;
    }
    return leapNum;
}

int main()
{
    vector<mdy> vecMdy;
    int T;
    cin>>T;
    T*=2;

    //输入
    for(int i=0;i<T;i++)
    {
        string sMonth;
        int month,day,year;
        cin>>sMonth>>day;
        getchar();
        cin>>year;
        if(sMonth=="January")month=1;
        else if(sMonth=="February")month=2;
        else if(sMonth=="March")month=3;
        else if(sMonth=="April")month=4;
        else if(sMonth=="May" )month=5;
        else if(sMonth=="June" )month=6;
        else if(sMonth=="July")month=7;
        else if(sMonth=="August" )month=8;
        else if(sMonth=="September" )month=9;
        else if(sMonth=="October" )month=10;
        else if(sMonth=="November")month=11;
        else if(sMonth=="December")month=12;
        struct mdy mymdy(month,day,year);
        vecMdy.push_back(mymdy);
    }

    int k=1;
    vector<mdy>::iterator pos;
    for(pos=vecMdy.begin();pos!=vecMdy.end();++pos)
    {
        int leapnum=computeLeap(pos->year,pos->month,pos->day,(pos+1)->year,(pos+1)->month,(pos+1)->day);
        cout<<"Case #"<<k<<":"<<" "<<leapnum<<endl;
        k++;
        ++pos;
    }
    return 0;
}

二、回文子串数目

给定字符串,求它的回文子序列个数。回文子序列反转字符顺序后仍然与原序列相同。例如字符串aba中,回文子序列为”a”, “a”, “aa”, “b”, “aba”,共5个。内容相同位置不同的子序列算不同的子序列。

输入
第一行一个整数T,表示数据组数。之后是T组数据,每组数据为一行字符串。

输出
对于每组数据输出一行,格式为”Case #X: Y”,X代表数据编号(从1开始),Y为答案。答案对100007取模。

回文字符串相关的问题很容易在笔试或面试中出出来,这道题我当时一开始是不会的,所以也不打算做了,不过我觉得我应该弄懂它。

以下来自cbsheng的专栏
所谓回文字符串,就是一个字符串,从左到右读和从右到左读是完全一样的。
解法:递归
递归的作用在于把问题的规模不断缩少,直到问题缩少到能简单地解决
问:如何缩少问题规模?
答:通过观察可以知道,一个回文字符串其中内部也是回文。所以,我们只需要以去掉两端的字符的形式一层层检查,每一次的检查都去掉了两个字符,这样就达到了缩少问题规模的目的。

新问题与原问题有着相同的形式
当去掉两端字符后的字符串,其产生的新问题同样是检查这个字符串是否回文。

递归的结束需要简单情景
1. 字符串长度可能会奇数或偶数:
如果字符串长度是奇数,字符串会剩下最中间那位字符,但其不影响回文。当检查到长度为1的时候即代表此字符串是回文
如果字符串长度是偶数,当两端的字符串两两比较检查后不会剩下字符。即检查到长度为0的时候即代表此字符串是回文
2. 如果检查到两端两个字符不相同。则说明此字符串不是回文,直接返回0,不需要继续检查

递归跳跃的信任
此题的递归分解比较简单,所以对在递归过程中细节的实现,我们可以直接看出。但是一些较复杂的题目上,我们就没那么容易看出过程中细节的实现,这时候就需要我们递归跳跃的信任!

#include <iostream>
using namespace std;
int fun(int low, int high, char *str, int length)
{
    if (length == 0 || length == 1)
        return    1;
    if (str[low] != str[high])
        return    0;
    return fun(low+1, high-1, str, length-2);
}
int main()
{
    char    str[]="aaabdaaa";
    int     length = strlen(str);
    //返回1代表是, 0代表不是
    cout << fun(0, length-1, str, length) << endl;
    return    0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值