CCC 2025 J4 Sunny Days Solution: Longest Sunny Streak After One Fix



Start NorthStar

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++ 解法。

Share this article

Copy the link, open your phone share sheet, scan the QR code for WeChat, or send by email.




Email

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: sunshine
  • P: 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

  1. Read N and the N weather records.
  2. Count whether there is any P.
  3. Build left from left to right.
  4. Build right from right to left.
  5. Try changing every P into S.
  6. If there is no P, output N - 1.
  7. 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

  1. Forgetting that we must change exactly one day, so all-sunny input returns N - 1.
  2. Only counting the original longest sunny streak.
  3. Accessing left[i - 1] or right[i + 1] without boundary checks.
  4. Misunderstanding left[i]: it means the sunny streak ending at i, not the total number of sunny days to the left.
  5. Using nested loops to count left and right streaks from scratch for every position, which can become O(N^2).

Review Questions

  1. Why do we usually focus on changing P to S?
  2. What do left[i] and right[i] represent?
  3. Why does the formula use left[i - 1] + 1 + right[i + 1]?
  4. Why is the answer N - 1 when every day is already sunny?
  5. 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

算法步骤

  1. 读入 NN 天天气。
  2. 统计是否存在 P
  3. 从左到右建立 left 数组。
  4. 从右到左建立 right 数组。
  5. 枚举每个 P,计算改成 S 后能合并出的长度。
  6. 如果没有 P,答案是 N - 1
  7. 否则输出最大合并长度。

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 写法更直观,尤其适合第一次学习这类题的学生。

常见错误

  1. 忘记“必须改一天”,导致全是 S 时输出 N
  2. 只找原字符串最长连续 S,没有尝试把 P 改成 S
  3. 访问 left[i - 1]right[i + 1] 时没有处理边界。
  4. left[i] 理解成“左边有几个 S”,其实它是“以 i 结尾的连续 S”。
  5. 写成双重循环,对每个位置重新向左右数,导致复杂度变成 O(N^2)

复习问题

  1. 为什么只需要考虑把 P 改成 S,除了全是 S 的特殊情况?
  2. left[i]right[i] 分别表示什么?
  3. 为什么第 i 天是 P 时,答案候选是 left[i - 1] + 1 + right[i + 1]
  4. 全部都是 S 时,为什么答案是 N - 1
  5. 你能用 sliding window 重新写一遍吗?

Questions or feedback?

Have a question about this article or want to suggest an improvement? Send us a private message.

Send Feedback

有问题或建议?

如果你对本文有疑问,或希望提出改进建议,请给我们发送私密留言。

发送反馈

Back to CP Weekly Challenge
返回 CP Weekly Challenge

滚动至顶部