
CP Weekly Challenge
CCC 2025 J4 Sunny Days Solution: Longest Sunny Streak After One Fix
Learn how to solve Sunny Days with prefix and suffix streak counts, one-day correction logic, and Python/C++ implementations.
CP Weekly Challenge
CCC 2025 J4 Sunny Days 题解:一次修正,最长连续晴天
用 prefix/suffix 连续计数理解一次修正,处理最长连续晴天、全是 S 的特殊情况,并写出 Python/C++ 解法。
This problem asks for the longest possible streak of sunny days after correcting exactly one wrong record. Each day is either S for sunshine or P for precipitation.
The important detail is that exactly one day is wrong. If there is a P, changing it to S may connect a sunny block on the left with a sunny block on the right. If every day is already S, we must change one S to P, so the answer becomes N - 1.
What the Problem Is Asking
We are given N days of weather records. Each record is one of:
S: sunshineP: precipitation
Exactly one record is wrong. We must change exactly one day, either:
P -> S- or
S -> P
After that correction, we want the maximum possible length of a consecutive streak of S.
For example:
S S S P S S
^
If we change the middle P into S, the two sunny blocks join together, giving a streak of 6 sunny days.
Where Beginners Often Get Stuck
A common first attempt is to count the longest streak that already appears:
best = 0
cur = 0
for day in days:
if day == "S":
cur += 1
best = max(best, cur)
else:
cur = 0
That only answers: what is the longest sunny streak in the original record?
But this problem asks: what is the longest sunny streak after fixing one wrong day?
There is also one important edge case. If the input is already all sunny:
S S S S S
we still have to change exactly one day. So the best answer is not 5; it is 4.
Core Algorithm Idea
There are two natural ways to understand this problem:
- prefix/suffix consecutive counts
- sliding window with at most one
P
This article uses the prefix/suffix approach because it makes the merge idea very visible.
The prefix/suffix approach precomputes:
left[i] = number of consecutive S ending at i
right[i] = number of consecutive S starting at i
Then for every P at index i, the streak created by changing it to S is:
left[i - 1] + 1 + right[i + 1]
with boundary checks at the first and last positions.
In words: left sunny block, plus the corrected day, plus right sunny block.
State Definition
Suppose the weather record is:
S S P S S S P
The left array stores the length of the sunny streak ending at each position:
days: S S P S S S P
left: 1 2 0 1 2 3 0
The right array stores the length of the sunny streak starting at each position:
days: S S P S S S P
right: 2 1 0 3 2 1 0
Now look at the P at index 2:
S S P S S S
^
If we change it to S, the candidate answer is:
left[1] + 1 + right[3] = 2 + 1 + 3 = 6
Algorithm Steps
- Read
Nand theNweather records. - Count whether there is any
P. - Build
leftfrom left to right. - Build
rightfrom right to left. - Try changing every
PintoS. - If there is no
P, outputN - 1. - Otherwise, output the best merged streak.
Python Solution
N = int(input())
days = [input().strip() for _ in range(N)]
count_p = days.count("P")
left = [0] * N
for i in range(N):
if days[i] == "S":
left[i] = 1 + (left[i - 1] if i > 0 else 0)
right = [0] * N
for i in range(N - 1, -1, -1):
if days[i] == "S":
right[i] = 1 + (right[i + 1] if i < N - 1 else 0)
best = 0
for i in range(N):
if days[i] == "P":
left_block = left[i - 1] if i > 0 else 0
right_block = right[i + 1] if i < N - 1 else 0
best = max(best, left_block + 1 + right_block)
if count_p == 0:
print(N - 1)
else:
print(best)
C++ Solution
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int N;
cin >> N;
vector<char> days(N);
for (int i = 0; i < N; i++) {
cin >> days[i];
}
int countP = 0;
for (char day : days) {
if (day == 'P') countP++;
}
vector<int> left(N, 0), right(N, 0);
for (int i = 0; i < N; i++) {
if (days[i] == 'S') {
left[i] = 1 + (i > 0 ? left[i - 1] : 0);
}
}
for (int i = N - 1; i >= 0; i--) {
if (days[i] == 'S') {
right[i] = 1 + (i < N - 1 ? right[i + 1] : 0);
}
}
int best = 0;
for (int i = 0; i < N; i++) {
if (days[i] == 'P') {
int leftBlock = (i > 0) ? left[i - 1] : 0;
int rightBlock = (i < N - 1) ? right[i + 1] : 0;
best = max(best, leftBlock + 1 + rightBlock);
}
}
if (countP == 0) {
cout << N - 1 << "\n";
} else {
cout << best << "\n";
}
return 0;
}
Complexity Analysis
We scan the list three times:
- once to build
left - once to build
right - once to try every
P
The time complexity is:
O(N)
The memory complexity is:
O(N)
A sliding window solution can also solve this in O(N) time with O(1) extra memory, but prefix/suffix arrays are often easier for students to understand first.
Common Mistakes
- Forgetting that we must change exactly one day, so all-sunny input returns
N - 1. - Only counting the original longest sunny streak.
- Accessing
left[i - 1]orright[i + 1]without boundary checks. - Misunderstanding
left[i]: it means the sunny streak ending ati, not the total number of sunny days to the left. - Using nested loops to count left and right streaks from scratch for every position, which can become
O(N^2).
Review Questions
- Why do we usually focus on changing
PtoS? - What do
left[i]andright[i]represent? - Why does the formula use
left[i - 1] + 1 + right[i + 1]? - Why is the answer
N - 1when every day is already sunny? - Can you write the same solution using a sliding window?
这道题看起来只是数连续的 S,但真正容易错的地方在一句话:天气记录中“刚好有一天”是错的。也就是说,我们必须改动一个字符,然后问最多可能有多少天连续是 sunny。
如果你只是找原字符串里最长的连续 S,会漏掉最重要的情况:一个 P 可能夹在两个晴天段中间。把这个 P 改成 S,左右两段晴天就可以连起来。
题目要我们做什么
输入有 N 天的天气记录,每一天是:
S: sunshine,晴天P: precipitation,下雨或降水
但是其中正好有一天记录错了。我们可以把一天改成另一个字符:
P -> S- 或
S -> P
目标是让修正后的记录里,连续 S 的最长长度尽可能大。
例如:
S S S P S S
^
如果把中间这个 P 改成 S,就能得到连续 6 天晴天。
初学者容易卡在哪里
最常见的错误是只写一个普通的 longest streak:
best = 0
cur = 0
for day in days:
if day == "S":
cur += 1
best = max(best, cur)
else:
cur = 0
这段代码只能回答“原始记录里最长连续晴天是多少”。但题目问的是“修正一天之后,最长连续晴天可能是多少”。
另一个常见坑是全是 S 的情况。如果记录是:
S S S S S
因为题目说刚好有一天错了,我们必须把一个 S 改成 P。所以答案不是 5,而是 4。
核心算法概念
这道题可以用两个角度理解:
- prefix/suffix consecutive counts
- sliding window with at most one
P
本篇用 prefix/suffix,因为它能很清楚地解释“把一个 P 改成 S 后,左右两边怎么合并”。
我们预处理两个数组:
left[i] = 到 i 为止,以 i 结尾的连续 S 数量
right[i] = 从 i 开始,往右的连续 S 数量
如果第 i 天是 P,把它改成 S 以后,能形成的连续晴天长度是:
left[i - 1] + 1 + right[i + 1]
也就是左边连续晴天,加上被修正的这一天,再加上右边连续晴天。
状态定义
假设天气记录是:
S S P S S S P
那么 left 表示每个位置左侧连续晴天段的长度:
days: S S P S S S P
left: 1 2 0 1 2 3 0
right 表示每个位置右侧连续晴天段的长度:
days: S S P S S S P
right: 2 1 0 3 2 1 0
现在看第 2 个位置,也就是那个 P:
S S P S S S
^
改成 S 后,长度是:
left[1] + 1 + right[3] = 2 + 1 + 3 = 6
算法步骤
- 读入
N和N天天气。 - 统计是否存在
P。 - 从左到右建立
left数组。 - 从右到左建立
right数组。 - 枚举每个
P,计算改成S后能合并出的长度。 - 如果没有
P,答案是N - 1。 - 否则输出最大合并长度。
Python 解法
N = int(input())
days = [input().strip() for _ in range(N)]
count_p = days.count("P")
left = [0] * N
for i in range(N):
if days[i] == "S":
left[i] = 1 + (left[i - 1] if i > 0 else 0)
right = [0] * N
for i in range(N - 1, -1, -1):
if days[i] == "S":
right[i] = 1 + (right[i + 1] if i < N - 1 else 0)
best = 0
for i in range(N):
if days[i] == "P":
left_block = left[i - 1] if i > 0 else 0
right_block = right[i + 1] if i < N - 1 else 0
best = max(best, left_block + 1 + right_block)
if count_p == 0:
print(N - 1)
else:
print(best)
C++ 解法
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int N;
cin >> N;
vector<char> days(N);
for (int i = 0; i < N; i++) {
cin >> days[i];
}
int countP = 0;
for (char day : days) {
if (day == 'P') countP++;
}
vector<int> left(N, 0), right(N, 0);
for (int i = 0; i < N; i++) {
if (days[i] == 'S') {
left[i] = 1 + (i > 0 ? left[i - 1] : 0);
}
}
for (int i = N - 1; i >= 0; i--) {
if (days[i] == 'S') {
right[i] = 1 + (i < N - 1 ? right[i + 1] : 0);
}
}
int best = 0;
for (int i = 0; i < N; i++) {
if (days[i] == 'P') {
int leftBlock = (i > 0) ? left[i - 1] : 0;
int rightBlock = (i < N - 1) ? right[i + 1] : 0;
best = max(best, leftBlock + 1 + rightBlock);
}
}
if (countP == 0) {
cout << N - 1 << "\n";
} else {
cout << best << "\n";
}
return 0;
}
复杂度分析
我们只扫描了数组三次:
- 建立
left - 建立
right - 枚举每个
P
所以时间复杂度是:
O(N)
我们用了两个长度为 N 的数组:
O(N)
如果用 sliding window,也可以做到 O(N) 时间和 O(1) 额外空间。但 prefix/suffix 写法更直观,尤其适合第一次学习这类题的学生。
常见错误
- 忘记“必须改一天”,导致全是
S时输出N。 - 只找原字符串最长连续
S,没有尝试把P改成S。 - 访问
left[i - 1]或right[i + 1]时没有处理边界。 - 把
left[i]理解成“左边有几个 S”,其实它是“以 i 结尾的连续 S”。 - 写成双重循环,对每个位置重新向左右数,导致复杂度变成
O(N^2)。
复习问题
- 为什么只需要考虑把
P改成S,除了全是S的特殊情况? left[i]和right[i]分别表示什么?- 为什么第
i天是P时,答案候选是left[i - 1] + 1 + right[i + 1]? - 全部都是
S时,为什么答案是N - 1? - 你能用 sliding window 重新写一遍吗?
Questions or feedback?
Have a question about this article or want to suggest an improvement? Send us a private message.
有问题或建议?
如果你对本文有疑问,或希望提出改进建议,请给我们发送私密留言。
Related Learning
Continue exploring related learning paths.
These related pages help students and parents move from interest to the right next step.