Enumerating Palindromic Dates Efficiently for NOIP Popularization Group 2016

Online Judge Reference:

http://ybt.ssoier.cn:8088/problem_show.php?pid=1974

Core Concepts

The problem requires identifying dates that are both valid calendar dates and palindromes. A naive approach iterates through every 8-digit number between the start and end dates, resulting in a complexity of O(10^8). While this might pass, it is computationally expensive.

A string is a palindrome if it reads the same forwards and backwards. The simplest check in C++ involves comparing the string to its reversed version:

str == string(str.rbegin(), str.rend())

However, creating a reversed string object adds overhead. An alternative is to manually reverse the string or compare characters directly.

Solution 1: Brute Force with String Reversal

This method iterates through every number in the range [start, end]. It validates the date first, then checks if the string representation is a palindrome.

#include <bits/stdc++.h>
using namespace std;

int startRange, endRange, counter = 0;
int daysInMonth[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

bool isLeapYear(int yr) {
    return (yr % 4 == 0 && yr % 100 != 0) || (yr % 400 == 0);
}

bool isValidDate(int fullDate) {
    int yr = fullDate / 10000;
    int mon = (fullDate % 10000) / 100;
    int dy = fullDate % 100;

    if (mon == 0 || mon > 12 || dy == 0) return false;

    if (mon == 2) {
        if (isLeapYear(yr)) return dy <= 29;
        else return dy <= 28;
    }
    
    return dy <= daysInMonth[mon];
}

void inspectDate(int num) {
    if (!isValidDate(num)) return;

    string numStr = to_string(num);
    string revStr = numStr;
    reverse(revStr.begin(), revStr.end());

    if (numStr == revStr) {
        counter++;
    }
}

int main() {
    cin >> startRange >> endRange;
    for (int i = startRange; i <= endRange; i++) {
        inspectDate(i);
    }
    cout << counter << endl;
    return 0;
}

Solution 2: Direct Character Comparison

Instead of constructing a new reversed string, we can extract the digits into an array and compare symmetric positions (index 1 vs 8, 2 vs 7, etc.).

#include <bits/stdc++.h>
using namespace std;

int startRange, endRange, counter = 0;
int daysInMonth[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int year, month, day;

bool isLeapYear(int yr) {
    return (yr % 4 == 0 && yr % 100 != 0) || (yr % 400 == 0);
}

bool isValidDate() {
    if (month == 0 || month > 12 || day == 0) return false;

    if (month == 2) {
        if (isLeapYear(year)) return day <= 29;
        else return day <= 28;
    }
    
    return day <= daysInMonth[month];
}

void inspectDate(int num) {
    year = num / 10000;
    month = (num % 10000) / 100;
    day = num % 100;

    if (!isValidDate()) return;

    int digits[9];
    digits[1] = year / 1000;
    digits[2] = (year % 1000) / 100;
    digits[3] = (year % 100) / 10;
    digits[4] = year % 10;
    digits[5] = month / 10;
    digits[6] = month % 10;
    digits[7] = day / 10;
    digits[8] = day % 10;

    for (int i = 1; i <= 4; i++) {
        if (digits[i] != digits[9 - i]) return;
    }
    counter++;
}

int main() {
    cin >> startRange >> endRange;
    for (int i = startRange; i <= endRange; i++) {
        inspectDate(i);
    }
    cout << counter << endl;
    return 0;
}

Solution 3: Constructing Palindromes (Optimized)

The most efficietn method recognizes that an 8-digit palindrome is defined entirely by its first 4 digits (the year). We iterate through possible years (e.g., 2000 to 9999), construct the full 8-digit palindrome (e.g., 2001 -> 20011002), and then check if that constructed date is valid and falls within the input range.

#include <bits/stdc++.h>
using namespace std;

int startRange, endRange, counter = 0;
int daysInMonth[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

bool isLeapYear(int yr) {
    return (yr % 4 == 0 && yr % 100 != 0) || (yr % 400 == 0);
}

void checkValidity(int fullDate) {
    int yr = fullDate / 10000;
    int mon = (fullDate / 100) % 100;
    int dy = fullDate % 100;

    if (mon == 0 || mon > 12 || dy == 0) return;

    if (mon == 2) {
        if (isLeapYear(yr) && dy > 29) return;
        if (!isLeapYear(yr) && dy > 28) return;
    } else if (dy > daysInMonth[mon]) {
        return;
    }
    
    counter++;
}

int main() {
    cin >> startRange >> endRange;
    
    // Iterate through possible years
    for (int yr = startRange / 10000; yr <= 9999; yr++) {
        // Extract digits of the year
        int d1 = yr / 1000;
        int d2 = (yr / 100) % 10;
        int d3 = (yr / 10) % 10;
        int d4 = yr % 10;

        // Build the palindrome: YYYY + MMDD (where MM = d4d3, DD = d2d1)
        int constructedDate = yr * 10000 + d4 * 1000 + d3 * 100 + d2 * 10 + d1;

        if (constructedDate >= startRange && constructedDate <= endRange) {
            checkValidity(constructedDate);
        }
    }

    cout << counter << endl;
    return 0;
}

Key Takeaways

When facing time limits in competitive programming, consider if enumerating the result space (constructing the answer) is faster than filtering the input space. In this case, constructing the 10^4 possible palindromes is significantly faster than checking 10^8 potential dates.

Tags: C++ NOIP palindrome Date Validation Algorithm Optimization

Posted on Sat, 30 May 2026 23:30:16 +0000 by greg252