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 thanS
, in YYYY/MM/DD format.S
is between January 1, 2001 and December 31, 2999 (inclusive).
Ex.
2022/01/01
gives2022/02/02
and2999/12/31
gives3000/03/03
.
Follow-up (stuck):
Find the closest good date instead; it can be either earlier or later than
S
.
My Questions:
- Is there a better way to solve the original problem? I used a triple loop, so...
- 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()'.