Home > Enterprise >  Finding a "good date" which has two or fewer different digits with an additional requireme
Finding a "good date" which has two or fewer different digits with an additional requireme

Time:12-28

I have a coding problem with determining a "good date" (more description below).

I solved the original problem but got stuck on the follow-up problem. I attached my questions and solution to the original problem below. Thank you for your help in advance.

Original Problem:

YYYY/MM/DD format is a notation of date consisting of the zero-padded 4-digit year, zero-padded 2-digit month, and zero-padded 2-digit day, separated by slashes.

A good date has two or fewer different digits in YYYY/MM/DD format.

Given a date S in YYYY/MM/DD format. Print the first good day not earlier than S, in YYYY/MM/DD format. S is between January 1, 2001 and December 31, 2999 (inclusive).

Ex. 2022/01/01 gives 2022/02/02 and 2999/12/31 gives 3000/03/03.

Follow-up (stuck):

Find the closest good date instead; it can be either earlier or later than S.

My Questions:

  1. Is there a better way to solve the original problem? I used a triple loop, so...
  2. How can I tackle the follow-up problem? I can only think of finding all good dates and comparing them to S...

My Solution to the Original Problem:

class Solution:
    @staticmethod
    def good_date(s: str) -> str:
        s = ''.join(s.split("/"))
        starting_year = eval(s   ' // 10000')

        for yyyy in range(starting_year, 3001):
            for mm in range(1, 13):
                for dd in range(1, 32):

                    date = str(yyyy)   f"{mm:02}"   f"{dd:02}"

                    if len(set(date)) == 2 and s <= date:
                        return date[:4]   "/"   date[4:6]   "/"   date[6:]


if __name__ == '__main__':
    S = input()
    print(Solution.good_date(S))

CodePudding user response:

To answer コリン's comment: Think about the job your 'eval' solution has to do:

  • concatenate 2 strings
  • in eval():
    • split the string into 3 tokens
    • detect it is an integer division
    • convert 2 numbers to binary (using int)
    • do integer division
    • return value

It would be lighter to use:

starting_year = int( s[0:4])

And I mention "bad habit" because it's dangerous to use eval() with user input. For example, if user enter '1/(1-1)' as a date, it will crash your program. But he(she) may even do worse. Every time input() is used, it should be sanitized and it's critical if the input is sent to eval().

CodePudding user response:

Here is a solution with a single-level loop.
It take care of leap years and irregular days_per_month.
And you can easily modify number of different_digits and end_date.

from datetime import date, timedelta

def next_good_date( udate:date):->date
    day_duration = timedelta( days=1)
    while True:
        #udate_asc = f"{udate.year}{udate.month}{udate.day}" # use if leading zeros are non-significant
        udate_asc = f"{udate.year:4}{udate.month:2}{udate.day:2}" # use if leading zeros are significant
        if len( set( udate_asc)) <= 2:
            return udate
        udate  = day_duration
        if udate == date.fromisoformat( '3001-01-01'): return None

if __name__ == '__main__':
    udate = date.fromisoformat( input( "date(yyyy/mm/dd)> ").replace('/','-')) # ISO use '-'
    udate = next_good_date( udate)
    if udate:
        print( udate.strftime( "%Y/%m/%d"))
    else:
        print( "No answer")

And it will be easy to create 'previous_good_date()'.

  • Related