1. 题目背景与需求分析
LeetCode 1507题"Reformat Date"是一个典型的字符串处理问题,主要考察开发者对日期格式转换和字符串操作的能力。题目要求将输入的日期字符串从"Day Month Year"格式转换为"YYYY-MM-DD"格式,其中:
- 输入格式示例:"20th Oct 2052"
- 输出格式示例:"2052-10-20"
这类问题在实际开发中非常常见,比如处理不同地区的日期显示格式、日志文件的时间戳转换等。题目看似简单,但隐藏着几个关键挑战:
- 日期中的"th"、"st"、"nd"、"rd"等后缀需要去除
- 月份需要从英文缩写转换为数字表示
- 单数日期(如1st)需要补前导零
- 需要处理不同长度的输入字符串
2. 核心解题思路
2.1 字符串分割与提取
第一步是将输入字符串分割成可处理的组成部分。以"20th Oct 2052"为例:
python复制parts = date.split() # 得到['20th', 'Oct', '2052']
这种分割方式可以处理中间有多个空格的情况,比直接按空格位置索引更健壮。对于日期部分,我们需要:
- 去除最后两个字符(后缀)
- 将剩余部分转换为整数
- 格式化为两位数(补零)
python复制day = parts[0][:-2].zfill(2) # '20th' → '20' → '20'
# 对于'1st' → '1' → '01'
2.2 月份映射处理
月份转换是本题的关键之一。我们需要将英文缩写映射为对应的数字:
python复制month_map = {
"Jan": "01", "Feb": "02", "Mar": "03", "Apr": "04",
"May": "05", "Jun": "06", "Jul": "07", "Aug": "08",
"Sep": "09", "Oct": "10", "Nov": "11", "Dec": "12"
}
month = month_map[parts[1]] # 'Oct' → '10'
使用字典(哈希表)进行映射是最直观高效的方式,时间复杂度为O(1)。相比数组查找或条件判断,这种方法代码更简洁,扩展性更好。
2.3 年份处理
年份部分相对简单,直接从分割后的第三部分获取即可:
python复制year = parts[2] # '2052'
3. 完整代码实现
3.1 Python解决方案
python复制class Solution:
def reformatDate(self, date: str) -> str:
month_map = {
"Jan": "01", "Feb": "02", "Mar": "03", "Apr": "04",
"May": "05", "Jun": "06", "Jul": "07", "Aug": "08",
"Sep": "09", "Oct": "10", "Nov": "11", "Dec": "12"
}
parts = date.split()
day = parts[0][:-2].zfill(2)
month = month_map[parts[1]]
year = parts[2]
return f"{year}-{month}-{day}"
3.2 Java解决方案
java复制import java.util.*;
class Solution {
public String reformatDate(String date) {
Map<String, String> monthMap = new HashMap<>();
monthMap.put("Jan", "01");
monthMap.put("Feb", "02");
monthMap.put("Mar", "03");
monthMap.put("Apr", "04");
monthMap.put("May", "05");
monthMap.put("Jun", "06");
monthMap.put("Jul", "07");
monthMap.put("Aug", "08");
monthMap.put("Sep", "09");
monthMap.put("Oct", "10");
monthMap.put("Nov", "11");
monthMap.put("Dec", "12");
String[] parts = date.split(" ");
String day = parts[0].substring(0, parts[0].length() - 2);
if (day.length() == 1) {
day = "0" + day;
}
String month = monthMap.get(parts[1]);
String year = parts[2];
return year + "-" + month + "-" + day;
}
}
3.3 C++解决方案
cpp复制#include <string>
#include <unordered_map>
#include <sstream>
#include <iomanip>
using namespace std;
class Solution {
public:
string reformatDate(string date) {
unordered_map<string, string> monthMap = {
{"Jan", "01"}, {"Feb", "02"}, {"Mar", "03"}, {"Apr", "04"},
{"May", "05"}, {"Jun", "06"}, {"Jul", "07"}, {"Aug", "08"},
{"Sep", "09"}, {"Oct", "10"}, {"Nov", "11"}, {"Dec", "12"}
};
istringstream iss(date);
string day, month, year;
iss >> day >> month >> year;
day = day.substr(0, day.size() - 2);
if (day.size() == 1) {
day = "0" + day;
}
return year + "-" + monthMap[month] + "-" + day;
}
};
4. 时间复杂度与空间复杂度分析
4.1 时间复杂度
所有解决方案的时间复杂度都是O(1),因为:
- 字符串分割操作的时间与输入长度成正比,但题目中输入的日期字符串长度固定(最大约15字符)
- 哈希表查找操作是O(1)
- 字符串拼接操作的时间也是固定的
4.2 空间复杂度
空间复杂度也是O(1),因为我们只使用了固定大小的额外空间:
- 月份映射表的大小固定(12个月)
- 临时存储分割后的字符串部分
- 最终结果字符串的大小固定(10字符)
5. 边界条件与测试用例
5.1 常见测试用例
python复制测试用例1:
输入: "20th Oct 2052"
输出: "2052-10-20"
测试用例2:
输入: "6th Jun 1933"
输出: "1933-06-06"
测试用例3:
输入: "1st Jan 2023"
输出: "2023-01-01"
5.2 特殊边界情况
-
单数日期(需要补零):
- 输入: "1st Mar 2000" → 输出: "2000-03-01"
-
日期为个位数(不带前导零):
- 输入: "2nd Apr 1999" → 输出: "1999-04-02"
-
月份缩写大小写(题目保证输入规范,实际开发中需要考虑):
- 输入: "31st DEC 2010" → 可能需要转换为小写处理
-
无效输入处理(题目不要求,但实际开发需要考虑):
- 空字符串
- 格式错误的日期
- 不存在的月份缩写
6. 实际应用场景
这种日期格式转换在实际开发中非常常见,比如:
- 日志处理系统:不同服务可能使用不同格式记录日志时间,需要统一格式存储和分析
- 国际化应用:根据用户地区偏好显示不同格式的日期
- 数据迁移:将旧系统中的日期格式迁移到新系统
- API接口:统一前后端或不同服务间的日期格式约定
7. 优化与扩展思路
7.1 性能优化
虽然当前解法已经足够高效,但在极端性能要求下可以考虑:
-
使用数组代替哈希表:将月份缩写转换为固定索引
python复制months = ["Jan", "Feb", ..., "Dec"] month_num = str(months.index(parts[1]) + 1).zfill(2) -
手动解析字符串:避免分割操作,直接按固定位置提取
python复制# 假设输入格式严格为"DDth MMM YYYY" day = date[:2] if date[1].isdigit() else "0" + date[0]
7.2 功能扩展
-
支持更多日期格式:
- "October 20, 2052"
- "20/10/2052"
-
添加输入验证:
- 检查日期是否有效(如2月30日)
- 处理大小写不敏感的月份缩写
-
支持多语言:
- 不同语言的月份名称
- 不同地区的日期格式偏好
8. 常见错误与调试技巧
8.1 常见错误
-
忘记处理日期后缀:
- 错误:直接使用
parts[0]作为日期 - 正确:
parts[0][:-2]去除最后两个字符
- 错误:直接使用
-
月份映射错误:
- 错误:将"Jan"映射为"1"而不是"01"
- 正确:确保所有月份都是两位数
-
单数日期未补零:
- 错误:"1st" → "1"
- 正确:"1st" → "01"
8.2 调试技巧
-
打印中间结果:
python复制print(f"Raw day: {parts[0]}, Processed day: {day}") print(f"Month: {parts[1]}, Mapped: {month}") -
使用断言验证:
python复制assert len(month) == 2, "Month should be two digits" assert len(day) == 2, "Day should be two digits" -
边界测试:
- 测试1日和31日
- 测试不同月份
- 测试不同年份
9. 不同语言的实现差异
9.1 字符串处理差异
-
Python:
- 字符串切片简单直观
zfill()方法方便补零
-
Java:
- 需要使用
substring和手动补零 - 字符串不可变,拼接效率较低
- 需要使用
-
C++:
substr方法类似Java- 可以使用
std::setw和std::setfill补零
9.2 哈希表实现差异
-
Python:
- 字典语法简洁
- 字面量初始化方便
-
Java:
- 需要显式创建
HashMap - 类型系统更严格
- 需要显式创建
-
C++:
unordered_map性能高- 初始化语法较新版本支持较好
10. 相关题目推荐
-
LeetCode 1185. 一周中的第几天
- 类似日期处理问题
- 需要了解Zeller公式或API使用
-
LeetCode 1360. 日期之间隔几天
- 更复杂的日期计算
- 需要考虑闰年等规则
-
LeetCode 539. 最小时间差
- 时间格式转换与计算
- 需要处理循环情况
-
LeetCode 1154. 一年中的第几天
- 日期解析与计算
- 需要了解各月份天数
11. 个人解题心得
在实际解决这个问题时,我有几点体会:
-
问题拆解是关键。将大问题分解为:分割字符串、处理日期、映射月份、组合结果等小步骤,每个步骤单独解决。
-
选择合适的数据结构能大幅简化代码。使用哈希表映射月份比条件判断或数组查找更清晰。
-
边界条件容易被忽视。特别是单数日期的补零处理,第一次提交时我就漏掉了这个细节。
-
代码可读性很重要。虽然可以写得更简洁(如一行代码解决),但分步处理更易理解和维护。
-
测试驱动很有帮助。先写出测试用例再编码,能更快发现逻辑漏洞。
这个问题虽然简单,但很好地展示了如何系统化地处理字符串转换问题。在实际工程中,这类问题往往更复杂,需要考虑更多边界情况和性能要求,但基本的解决思路是相通的。
