返回博客
实用算法

哈希集合遍历与 LocalDate 类的使用——[蓝桥杯 2026 省 Java A 组] 连续打卡题解

我们规定,判断前一天是否存在。如果前一天不存在,那么这一天就是连续打卡的起始日期。那么,我们只需要循环判断,直到不连续,就可以计算出连续的天数,并且判断其是否为最大连续天数。

原题连接P16291 [蓝桥杯 2026 省 Java A 组] 连续打卡

关键词:日期,集合。

思路分析(最重要!)

存储

题目里面给到“可能有重复日期”实际上是一种提示——我们可以考虑使用集合来解决这个问题。哈希集合 的平均查找时间复杂度是 $O(1)$ 我们可以快速的判断一个日期是否存在于集合中。

所以,我们将日期都存进一个哈希集合。

遍历

我们使用 foreach 可以方便地遍历哈希集合的对象。设想现在我们取出了一个日期对象,要怎么判断它是否是连续的?对!就是看它的前一天或者后一天在不在集合里。

我们规定,判断前一天是否存在。如果前一天不存在,那么这一天就是连续打卡的起始日期。那么,我们只需要循环判断,直到不连续,就可以计算出连续的天数,并且判断其是否为最大连续天数。

伪代码:

for 每个 date in checkDates:
    if 集合不包含 date 的前一天:
        currentDays = 0
        nowDay = date

        while 集合包含 nowDay:
            currentDays += 1
            nowDay = nowDay.plusDays(1)

        ans = max(ans, currentDays)

前置知识

1. HashSet

  • 作用:存储不重复的元素,适合本题去重。
  • 常用方法:
    • add(x):添加元素。
    • contains(x):判断元素是否存在。
  • 时间复杂度:查找和插入的平均时间复杂度为 $O(1)$。

2. LocalDate

java.time.LocalDate 是 Java 8 引入的日期类,只表示年月日,不包含时间和时区。它是不可变对象,日期运算会返回新的对象。

本题常用方法:

方法作用
LocalDate.parse(text)将 yyyy-MM-dd 格式的字符串转换为日期
date.plusDays(1)获取后一天,自动处理跨月、跨年和闰年
date.minusDays(1)获取前一天

例如:

LocalDate date = LocalDate.parse("2026-01-01");
LocalDate nextDay = date.plusDays(1); // 2026-01-02

注意:plusDays() 和 minusDays() 不会修改原日期,需要使用返回值。

完整题解

Scanner 使用方便,但在大规模输入时通常比 BufferedReader 慢。对于竞赛中的大量输入,可以根据数据规模选择更高效的读取方式。所以在做竞赛题目时建议使用 BufferedReader + StringTokenizer 的组合。本题读入都是以行为单位,所以可以不使用 StringTokenizer 。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.time.LocalDate;
import java.util.HashSet;

public class Main {
	
	static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	
	static LocalDate nextDate() throws IOException {
		return LocalDate.parse(br.readLine());
		
	}
	
	public static void main(String[] args) throws IOException{
		int n = Integer.parseInt(br.readLine());
		HashSet<LocalDate> checkDates = new HashSet<>();
		for (int i = 0; i < n; i++) {
			checkDates.add(nextDate());
		}
		int ans = 0;
		for (LocalDate date : checkDates) {
			LocalDate nowDay = date;
			int currentDays = 0;
			if (!checkDates.contains(nowDay.minusDays(1))) {
				currentDays = 0;
				do {
					currentDays += 1;
					nowDay = nowDay.plusDays(1);
				} while (checkDates.contains(nowDay));
				ans = Math.max(ans, currentDays);
			}
		}
		System.out.println(ans);
	}
}

复杂度总结

时间复杂度: 设去重后的日期数量为 $m$ ,外层遍历需要 $O(m)$ 次。由于每个连续区间只会从起点开始扫描一次,每个日期最多参与一次区间扫描,因此在 HashSet 操作具有平均常数时间的前提下,整体期望时间复杂度为 $O(n)$ 。

空间复杂度: 使用 HashSet 存储日期,需要 $O(n)$ 的额外空间。

感谢阅读。

交流与讨论

写一条评论