diff --git a/solution/0500-0599/0547.Number of Provinces/Solution.py b/solution/0500-0599/0547.Number of Provinces/Solution.py index 6ff12cd515..f076345dfa 100644 --- a/solution/0500-0599/0547.Number of Provinces/Solution.py +++ b/solution/0500-0599/0547.Number of Provinces/Solution.py @@ -3,9 +3,9 @@ class Solution: def dfs(i: int): vis[i] = True for j, x in enumerate(isConnected[i]): - if not vis[j] and x: + if not vis[j] and x: dfs(j) - + n = len(isConnected) vis = [False] * n ans = 0 @@ -13,4 +13,4 @@ class Solution: if not vis[i]: dfs(i) ans += 1 - return ans \ No newline at end of file + return ans diff --git a/solution/0800-0899/0887.Super Egg Drop/Solution.py b/solution/0800-0899/0887.Super Egg Drop/Solution.py index 4ccba265b8..676d3170fc 100644 --- a/solution/0800-0899/0887.Super Egg Drop/Solution.py +++ b/solution/0800-0899/0887.Super Egg Drop/Solution.py @@ -17,4 +17,4 @@ class Solution: r = mid - 1 return max(dfs(l - 1, j - 1), dfs(i - l, j)) + 1 - return dfs(n, k) \ No newline at end of file + return dfs(n, k) diff --git a/solution/1100-1199/1116.Print Zero Even Odd/Solution.py b/solution/1100-1199/1116.Print Zero Even Odd/Solution.py index 2d931b714b..42c4bc7e06 100644 --- a/solution/1100-1199/1116.Print Zero Even Odd/Solution.py +++ b/solution/1100-1199/1116.Print Zero Even Odd/Solution.py @@ -1,13 +1,14 @@ from threading import Semaphore + class ZeroEvenOdd: def __init__(self, n): self.n = n self.z = Semaphore(1) self.e = Semaphore(0) self.o = Semaphore(0) - - # printNumber(x) outputs "x", where x is an integer. + + # printNumber(x) outputs "x", where x is an integer. def zero(self, printNumber: 'Callable[[int], None]') -> None: for i in range(self.n): self.z.acquire() @@ -16,13 +17,13 @@ class ZeroEvenOdd: self.o.release() else: self.e.release() - + def even(self, printNumber: 'Callable[[int], None]') -> None: for i in range(2, self.n + 1, 2): self.e.acquire() printNumber(i) self.z.release() - + def odd(self, printNumber: 'Callable[[int], None]') -> None: for i in range(1, self.n + 1, 2): self.o.acquire() diff --git a/solution/1400-1499/1419.Minimum Number of Frogs Croaking/Solution.py b/solution/1400-1499/1419.Minimum Number of Frogs Croaking/Solution.py index 6ba76f9b10..54956e46b1 100644 --- a/solution/1400-1499/1419.Minimum Number of Frogs Croaking/Solution.py +++ b/solution/1400-1499/1419.Minimum Number of Frogs Croaking/Solution.py @@ -16,4 +16,4 @@ class Solution: cnt[i - 1] -= 1 if i == 4: x -= 1 - return -1 if x else ans \ No newline at end of file + return -1 if x else ans diff --git a/solution/2400-2499/2432.The Employee That Worked on the Longest Task/Solution.py b/solution/2400-2499/2432.The Employee That Worked on the Longest Task/Solution.py index 6a1c92f04e..ac77c7d83d 100644 --- a/solution/2400-2499/2432.The Employee That Worked on the Longest Task/Solution.py +++ b/solution/2400-2499/2432.The Employee That Worked on the Longest Task/Solution.py @@ -6,4 +6,4 @@ class Solution: if mx < t or (mx == t and ans > uid): ans, mx = uid, t last += t - return ans \ No newline at end of file + return ans diff --git a/solution/CONTEST_README.md b/solution/CONTEST_README.md index 63bb0083ea..7e716f42a9 100644 --- a/solution/CONTEST_README.md +++ b/solution/CONTEST_README.md @@ -8,18 +8,18 @@ 如果竞赛积分处于段位的临界值,在每周比赛结束重新计算后会出现段位升级或降级的情况。段位升级或降级后会自动替换对应的荣誉勋章。 -| 段位 | 比例 | 段位名 | 国服分数线 | 勋章 | -| ----- | ------ | -------- | --------- | --------------------------------------------------------------------------- | -| LV3 | 5% | Guardian | ≥2251.88 |

| -| LV2 | 20% | Knight | ≥1879.80 |

| -| LV1 | 75% | - | - | - | +| 段位 | 比例 | 段位名 | 国服分数线 | 勋章 | +| ---- | ---- | -------- | ----------- | ----------------------------------------------------------------------------------------------------------------------- | +| LV3 | 5% | Guardian | ≥2251.88 |

| +| LV2 | 20% | Knight | ≥1879.80 |

| +| LV1 | 75% | - | - | - | 力扣竞赛 **全国排名前 10** 的用户,全站用户名展示为品牌橙色。 ## 赛后估分网站 -- https://lcpredictor.herokuapp.com -- https://lccn.lbao.site +- https://lcpredictor.herokuapp.com +- https://lccn.lbao.site ## 往期竞赛 diff --git a/solution/CONTEST_README_EN.md b/solution/CONTEST_README_EN.md index 971d85c8cb..69a50f5dd2 100644 --- a/solution/CONTEST_README_EN.md +++ b/solution/CONTEST_README_EN.md @@ -11,11 +11,11 @@ If you are in the top 5% of the contest rating, you’ll get the “Guardian” If you are in the top 25% of the contest rating, you’ll get the “Knight” badge. -| Level | Proportion | Badge | Rating | | -| ----- | ---------- | ---------- | -------------- | ----------------------------------------------------------------------------------------------------------------------- | -| LV3 | 5\% | Guardian | ≥2228.90 |

| -| LV2 | 20\% | Knight | ≥1842.73 |

| -| LV1 | 75\% | - | - | - | +| Level | Proportion | Badge | Rating | | +| ----- | ---------- | -------- | ----------- | ----------------------------------------------------------------------------------------------------------------------- | +| LV3 | 5\% | Guardian | ≥2228.90 |

| +| LV2 | 20\% | Knight | ≥1842.73 |

| +| LV1 | 75\% | - | - | - | For top 10 users (excluding LCCN users), your LeetCode ID will be colored orange on the ranking board. You'll also have the honor with you when you post/comment under discuss. diff --git a/solution/README.md b/solution/README.md index 559fb5e5de..4380a31eb5 100644 --- a/solution/README.md +++ b/solution/README.md @@ -2687,4 +2687,4 @@ ## 版权 -著作权归 [GitHub 开源社区 Doocs](https://github.com/doocs) 所有,商业转载请联系 [@yanglbme](mailto:contact@yanglibin.info) 获得授权,非商业转载请注明出处。 +著作权归 [GitHub 开源社区 Doocs](https://github.com/doocs) 所有,商业转载请联系 [@yanglbme](mailto:contact@yanglibin.info) 获得授权,非商业转载请注明出处。 \ No newline at end of file diff --git a/solution/README_EN.md b/solution/README_EN.md index cc1080d5e0..44f1019e1d 100644 --- a/solution/README_EN.md +++ b/solution/README_EN.md @@ -2685,4 +2685,4 @@ Press Control+F(or Command+F on the ## Copyright -[@Doocs](https://github.com/doocs) +[@Doocs](https://github.com/doocs) \ No newline at end of file diff --git a/solution/main.py b/solution/main.py index 31b81c06bd..e53932d34c 100644 --- a/solution/main.py +++ b/solution/main.py @@ -1,3 +1,4 @@ +import json import time from datetime import timezone, timedelta, datetime @@ -21,6 +22,8 @@ weekly_range = range(83, 500) biweekly_range = range(1, 300) WEEKLY_URL = 'https://leetcode.cn/contest/api/info/weekly-contest-{}/' BIWEEKLY_URL = 'https://leetcode.cn/contest/api/info/biweekly-contest-{}/' +WEEKLY_SLUG = 'weekly-contest-{}' +BIWEEKLY_SLUG = 'biweekly-contest-{}' class Spider: @@ -57,18 +60,18 @@ class Spider: form1 = { 'operationName': 'globalData', 'query': 'query globalData {\n feature {\n questionTranslation\n subscription\n signUp\n ' - 'discuss\n mockInterview\n contest\n store\n book\n chinaProblemDiscuss\n ' - 'socialProviders\n studentFooter\n cnJobs\n enableLsp\n enableWs\n ' - 'enableDebugger\n enableDebuggerAdmin\n enableDarkMode\n tasks\n ' - 'leetbook\n __typename\n }\n userStatus {\n isSignedIn\n isAdmin\n ' - 'isStaff\n isSuperuser\n isTranslator\n isPremium\n isVerified\n ' - 'isPhoneVerified\n isWechatVerified\n checkedInToday\n username\n ' - 'realName\n userSlug\n groups\n avatar\n optedIn\n ' - 'requestRegion\n region\n activeSessionId\n permissions\n notificationStatus {\n ' - 'lastModified\n numUnread\n __typename\n }\n completedFeatureGuides\n ' - 'useTranslation\n accountStatus {\n isFrozen\n inactiveAfter\n __typename\n ' - '}\n __typename\n }\n siteRegion\n chinaHost\n websocketUrl\n userBannedInfo {\n ' - 'bannedData {\n endAt\n bannedType\n __typename\n }\n __typename\n }\n}\n', + 'discuss\n mockInterview\n contest\n store\n book\n chinaProblemDiscuss\n ' + 'socialProviders\n studentFooter\n cnJobs\n enableLsp\n enableWs\n ' + 'enableDebugger\n enableDebuggerAdmin\n enableDarkMode\n tasks\n ' + 'leetbook\n __typename\n }\n userStatus {\n isSignedIn\n isAdmin\n ' + 'isStaff\n isSuperuser\n isTranslator\n isPremium\n isVerified\n ' + 'isPhoneVerified\n isWechatVerified\n checkedInToday\n username\n ' + 'realName\n userSlug\n groups\n avatar\n optedIn\n ' + 'requestRegion\n region\n activeSessionId\n permissions\n notificationStatus {\n ' + 'lastModified\n numUnread\n __typename\n }\n completedFeatureGuides\n ' + 'useTranslation\n accountStatus {\n isFrozen\n inactiveAfter\n __typename\n ' + '}\n __typename\n }\n siteRegion\n chinaHost\n websocketUrl\n userBannedInfo {\n ' + 'bannedData {\n endAt\n bannedType\n __typename\n }\n __typename\n }\n}\n', 'variables': {}, } headers = { @@ -83,20 +86,20 @@ class Spider: 'operationName': 'questionData', 'variables': {'titleSlug': question_title_slug}, 'query': 'query questionData($titleSlug: String!) {\n question(titleSlug: $titleSlug) {\n ' - 'questionId\n questionFrontendId\n categoryTitle\n boundTopicId\n title\n ' - 'titleSlug\n content\n translatedTitle\n translatedContent\n isPaidOnly\n ' - 'difficulty\n likes\n dislikes\n isLiked\n similarQuestions\n ' - 'contributors {\n username\n profileUrl\n avatarUrl\n __typename\n ' - '}\n langToValidPlayground\n topicTags {\n name\n slug\n ' - 'translatedName\n __typename\n }\n companyTagStats\n codeSnippets {\n ' - 'lang\n langSlug\n code\n __typename\n }\n stats\n hints\n ' - 'solution {\n id\n canSeeDetail\n __typename\n }\n status\n ' - 'sampleTestCase\n metaData\n judgerAvailable\n judgeType\n mysqlSchemas\n ' - 'enableRunCode\n envInfo\n book {\n id\n bookName\n pressName\n ' - 'source\n shortDescription\n fullDescription\n bookImgUrl\n ' - 'pressImgUrl\n productUrl\n __typename\n }\n isSubscribed\n ' - 'isDailyQuestion\n dailyRecordStatus\n editorType\n ugcQuestionId\n style\n ' - 'exampleTestcases\n __typename\n }\n}\n', + 'questionId\n questionFrontendId\n categoryTitle\n boundTopicId\n title\n ' + 'titleSlug\n content\n translatedTitle\n translatedContent\n isPaidOnly\n ' + 'difficulty\n likes\n dislikes\n isLiked\n similarQuestions\n ' + 'contributors {\n username\n profileUrl\n avatarUrl\n __typename\n ' + '}\n langToValidPlayground\n topicTags {\n name\n slug\n ' + 'translatedName\n __typename\n }\n companyTagStats\n codeSnippets {\n ' + 'lang\n langSlug\n code\n __typename\n }\n stats\n hints\n ' + 'solution {\n id\n canSeeDetail\n __typename\n }\n status\n ' + 'sampleTestCase\n metaData\n judgerAvailable\n judgeType\n mysqlSchemas\n ' + 'enableRunCode\n envInfo\n book {\n id\n bookName\n pressName\n ' + 'source\n shortDescription\n fullDescription\n bookImgUrl\n ' + 'pressImgUrl\n productUrl\n __typename\n }\n isSubscribed\n ' + 'isDailyQuestion\n dailyRecordStatus\n editorType\n ugcQuestionId\n style\n ' + 'exampleTestcases\n __typename\n }\n}\n', } try: @@ -120,7 +123,11 @@ class Spider: except Exception as e: print(e) time.sleep(2) - return self.get_question_detail(question_title_slug, retry - 1) if retry > 0 else {} + return ( + self.get_question_detail(question_title_slug, retry - 1) + if retry > 0 + else {} + ) @staticmethod def format_question_detail(question_detail: dict) -> dict: @@ -149,13 +156,18 @@ class Spider: 'url_en': url_en, 'relative_path_cn': path_cn, 'relative_path_en': path_en, - 'title_cn': question_detail.get('translatedTitle') or question_title_en or '', + 'title_cn': question_detail.get('translatedTitle') + or question_title_en + or '', 'title_en': question_title_en or '', 'question_title_slug': question_title_slug, 'content_en': question_detail.get('content'), - 'content_cn': question_detail.get('translatedContent') or question_detail.get('content') or '', + 'content_cn': question_detail.get('translatedContent') + or question_detail.get('content') + or '', 'tags_en': [e['name'] for e in topic_tags if e['name']] or [], - 'tags_cn': [e['translatedName'] for e in topic_tags if e['translatedName']] or [], + 'tags_cn': [e['translatedName'] for e in topic_tags if e['translatedName']] + or [], 'difficulty_en': question_detail.get('difficulty'), 'difficulty_cn': difficulty.get(question_detail.get('difficulty')), 'code_snippets': question_detail.get('codeSnippets') or [], @@ -187,10 +199,18 @@ class Contest: def __init__(self, contest_seq: int, contest_type: int = 1): double = contest_type % 2 == 0 url_pattern = BIWEEKLY_URL if double else WEEKLY_URL + slug_pattern = BIWEEKLY_SLUG if double else WEEKLY_SLUG self.contest_type = contest_type self.contest_url = url_pattern.format(contest_seq) - self.contest_title = f'第 {contest_seq} 场双周赛' if double else f'第 {contest_seq} 场周赛' - self.contest_title_en = f'Biweekly Contest {contest_seq}' if double else f'Weekly Contest {contest_seq}' + self.contest_title_slug = slug_pattern.format(contest_seq) + self.contest_title = ( + f'第 {contest_seq} 场双周赛' if double else f'第 {contest_seq} 场周赛' + ) + self.contest_title_en = ( + f'Biweekly Contest {contest_seq}' + if double + else f'Weekly Contest {contest_seq}' + ) @staticmethod def format_time(timestamp: int) -> str: @@ -212,7 +232,7 @@ class Contest: 'contest_start_time': res['contest']['origin_start_time'], 'contest_duration': res['contest']['duration'], 'user_num': res['user_num'], - 'question_slugs': question_slugs + 'question_slugs': question_slugs, } except Exception as e: print(e) @@ -229,27 +249,37 @@ class Contest: duration = data['contest_duration'] cost_minutes = duration // 60 user_num = data['user_num'] - rows = [f'#### {title}({Contest.format_time(start_time)}, {cost_minutes} 分钟) 参赛人数 {user_num}\n'] + rows = [ + f'#### {title}({Contest.format_time(start_time)}, {cost_minutes} 分钟) 参赛人数 {user_num}\n' + ] rows_en = [f'#### {title_en}\n'] for question in data['question_list']: - frontend_question_id, title_cn, title_en, relative_path_cn, relative_path_en = question + ( + frontend_question_id, + title_cn, + title_en, + relative_path_cn, + relative_path_en, + ) = question rows.append(f'- [{frontend_question_id}. {title_cn}]({relative_path_cn})') - rows_en.append(f'- [{frontend_question_id}. {title_en}]({relative_path_en})') - return [ - start_time, - '\n'.join(rows), - '\n'.join(rows_en) - ] + rows_en.append( + f'- [{frontend_question_id}. {title_en}]({relative_path_en})' + ) + return [start_time, '\n'.join(rows), '\n'.join(rows_en)] -def get_contests() -> List: - res = [] +def get_contests(fetch_new=True) -> List: + res = [] if fetch_new else load_contest_result() t = 0 + d = {x.get('contest_title_slug'): x for x in res} for r in (weekly_range, biweekly_range): t += 1 cnt = 0 for i in r: - contest_data = Contest(i, contest_type=t).get_data(retry=3) + c = Contest(i, contest_type=t) + if c.contest_title_slug in d: + continue + contest_data = c.get_data(retry=3) if not contest_data: cnt += 1 if cnt > 2: @@ -257,6 +287,8 @@ def get_contests() -> List: continue print(contest_data) res.append(contest_data) + d[c.contest_title_slug] = contest_data + save_contest_result(res) return res @@ -265,17 +297,28 @@ def get_contests() -> List: cookie_cn, cookie_en = load_cookies() spider = Spider(cookie_cn, cookie_en) -# 题目详情列表 +# 是否刷新所有题目 +refresh_all = load_refresh_config() + question_details = {} +if not refresh_all: + for item in load_result(): + slug = item.get('question_title_slug') + if slug: + question_details[slug] = item + for q in spider.get_all_questions(retry=4): slug = q['stat']['question__title_slug'] + if slug in question_details: + print(f'{slug} 已存在, 跳过') + continue detail = spider.get_question_detail(slug, retry=4) time.sleep(0.3) question_details[slug] = Spider.format_question_detail(detail) # 周赛场次列表 -contest_list = get_contests() +contest_list = get_contests(refresh_all) cls = [] for contest in contest_list: contest_title = contest['contest_title'] @@ -289,8 +332,13 @@ for contest in contest_list: detail['md_table_row_en'][4] = contest_title_en # 给周赛信息添加题目详情 - add = [detail['frontend_question_id'], detail['title_cn'], - detail['title_en'], detail['relative_path_cn'], detail['relative_path_en']] + add = [ + detail['frontend_question_id'], + detail['title_cn'], + detail['title_en'], + detail['relative_path_cn'], + detail['relative_path_en'], + ] contest_question_list.append(add) contest['question_list'] = contest_question_list @@ -307,4 +355,5 @@ generate_summary(ls) generate_contest_readme(cls) # 刷新题目文件 -refresh(ls) +if refresh_all: + refresh(ls) diff --git a/solution/util.py b/solution/util.py index 81093445cc..341b0094fc 100644 --- a/solution/util.py +++ b/solution/util.py @@ -29,10 +29,12 @@ contest_readme_en = load_template('contest_readme_template_en') def load_cookies() -> Tuple[str, str]: - cookie_cn, cookie_en = None, None - with open("./.env", "r") as f: + cookie_cn, cookie_en = '', '' + env_file = './.env' + if not os.path.exists(env_file): + return cookie_cn, cookie_en + with open(env_file, "r") as f: lines = f.readlines() - for line in lines: if line.startswith("COOKIE_CN"): parts = line.split("=")[1:] @@ -46,17 +48,49 @@ def load_cookies() -> Tuple[str, str]: return cookie_cn, cookie_en +def load_refresh_config() -> bool: + env_file = './.env' + if not os.path.exists(env_file): + return False + with open(env_file, "r") as f: + lines = f.readlines() + for line in lines: + if line.startswith("REFRESH"): + parts = line.split("=")[1:] + return "=".join(parts).strip().strip('"') == 'True' + return False + + def load_result() -> List[dict]: - with open('./result.json', 'r', encoding='utf-8') as f: + result_file = './result.json' + if not os.path.exists(result_file): + return [] + with open(result_file, 'r', encoding='utf-8') as f: res = f.read() return json.loads(res) +def load_contest_result() -> List[dict]: + contest_result_file = './contest.json' + if not os.path.exists(contest_result_file): + return [] + with open(contest_result_file, 'r', encoding='utf-8') as f: + res = f.read() + if res: + return json.loads(res) + return [] + + def save_result(data: List[dict]): with open('./result.json', 'w', encoding='utf-8') as f: f.write(json.dumps(data)) +def save_contest_result(data: List[dict]): + with open('./contest.json', 'w', encoding='utf-8') as f: + f.write(json.dumps(data)) + + def select_templates(category): if category == 'Shell': return [bash_readme_cn, bash_readme_en] @@ -248,4 +282,4 @@ def generate_contest_readme(result: List): f.write(content_cn) content_en = contest_readme_en.format(content_en) with open('./CONTEST_README_EN.md', 'w', encoding='utf-8') as f: - f.write(content_en) + f.write(content_en) \ No newline at end of file