找到给定回文日期的最近回文日期

find closest palindrome date of a given palindrome date

给定一个回文日期,我们必须找到与该给定日期最接近的回文日期。结果日期应在给定日期之前。

日期格式为YYYY/MM/DD

一种蛮力解决方案是(给出伪代码,可能效率不高)。

Convert the string into a number
For each number less than this number
   If the number is a valid date then
    If number is a palindrome
         print and return

我认为一定有一些高效的算法可以解决这个问题。

例子:输入日期:2030/03/02

输出日期:2021/12/02

如果考虑回文日期写成YYYY/MM/DD,比如2121/12/12,可以得出一些结论:

一个月中的一天在 01 到 31 之间,一个月在 01 到 12 之间。所以你推断日期回文匹配以下正则表达式:

([0-9][0-2]|[01]3)([0-9]0|[12]1)-(0[0-9]|1[12])-([0-2][0-9]|3[01])
       1.  & 2.         3 & 4           4 & 3           2 & 1

我把重复的四个字母标出来了

输入日期之前的回文为

  • 尽可能递减 n°4(去年和十个月前)

  • 尽可能递减数字 n°3(10 年零 1 个月之前)

  • 尽可能递减数字 n°2(100 年零 10 天之前)

  • 如果可能的话递减数字 n°1(1000 年零 1 天之前)

停止

在给定的一年中,只有一个回文日期。因此,要找到以前的回文日期,您需要减少年份并将其转换为回文日期。

示例:对于 2030 年,有效的月份和日期将为 03 和 02,这将导致 2030/03/02。

所以,必须清楚,这种情况下的回文日期只有在以下情况下才有可能:

  1. 年份是 4 位数字(不是 10000)因为月份和天数是两位数
  2. 年份的最后两位数应为 {10, 20, 30, 40, 50, 60, 70, 80, 90, 01, 11, 21}
  3. 年份的前两位数字应该是1-31的镜像(有月份和年份的有效期)。

使用规则 2 和 3,可以在 O(1) 中检查给定年份是否具有给定格式的回文日期。让我们假设年份的有效范围是从 9999-0001,那么可以按线性顺序找到以前的回文日期。

void find_prev_date(const char* input, char * output) {
    int year = extract_year(input); //Extract first 4 digits
    --year;
    //limit is 101 because 0000 will not give you valid month and day.
    for(;year>=101;--year) {
      if(haspalindromic(year))
      {
        write_date(year, output);
      }
    }
}

bool haspalindromic(int year)
{
   int first2 = year%100;
   int last2 = year/100;
   bool second_valid = false;
   bool first_valid = false;

   switch(last2){
       case 10: case 20: case 30: case 40: case 50: case 60: case 70: case 80:
       case 90: case 1: case 11: case 21: second_valid = true; break;
   }
   if(!second_valid)
      return false;

   switch(first2) {
      case 10: case 20: case 30: case 40: case 50: case 60: case 70: case 80: case 90: case 1: case 11: case 21: case 31: case 41:    case 51: case 61: case 71: case 81: case 91:
      case 2: case 12: case 22: case 32: case 42: case 52: case 62: case 72: case 82: case 92:
      case 3: case 13:
         first_valid = true;
      break;
   }

   if(!first_valid)
      return false;

   //reverse digit such that 2 yields 20
   int month = reverse_digits_on100(last2);
   int day = reverse_digits_on100(first2);

   if(month==2) {
      if(day<=28)
         return true;
      if(isleapyear(year) && day==29)
         return true;
      if(day>28)
        return false;
   }

   switch(month) {
        case 4: case 6: case 9: case 11:
        if(day>30)
           return false;
   }
   //For remaining months, check is already made as first part of year is gives day in range of [1..31].
   return true;
}

有些功能没有实现,但名字明显暗示了意图。