Skip to content

Commit 97d7b82

Browse files
authored
feat: add solutions to lc problem: No.3617 (#4570)
No.3617.Find Students with Study Spiral Pattern
1 parent 6f7b0b3 commit 97d7b82

File tree

8 files changed

+1108
-0
lines changed

8 files changed

+1108
-0
lines changed

solution/3600-3699/3617.Find Students with Study Spiral Pattern/README.md

Lines changed: 427 additions & 0 deletions
Large diffs are not rendered by default.

solution/3600-3699/3617.Find Students with Study Spiral Pattern/README_EN.md

Lines changed: 430 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import pandas as pd
2+
from datetime import timedelta
3+
4+
5+
def find_study_spiral_pattern(
6+
students: pd.DataFrame, study_sessions: pd.DataFrame
7+
) -> pd.DataFrame:
8+
# Convert session_date to datetime
9+
study_sessions["session_date"] = pd.to_datetime(study_sessions["session_date"])
10+
11+
result = []
12+
13+
# Group study sessions by student
14+
for student_id, group in study_sessions.groupby("student_id"):
15+
# Sort sessions by date
16+
group = group.sort_values("session_date").reset_index(drop=True)
17+
18+
temp = [] # Holds current contiguous segment
19+
last_date = None
20+
21+
for idx, row in group.iterrows():
22+
if not temp:
23+
temp.append(row)
24+
else:
25+
delta = (row["session_date"] - last_date).days
26+
if delta <= 2:
27+
temp.append(row)
28+
else:
29+
# Check the previous contiguous segment
30+
if len(temp) >= 6:
31+
_check_pattern(student_id, temp, result)
32+
temp = [row]
33+
last_date = row["session_date"]
34+
35+
# Check the final segment
36+
if len(temp) >= 6:
37+
_check_pattern(student_id, temp, result)
38+
39+
# Build result DataFrame
40+
df_result = pd.DataFrame(
41+
result, columns=["student_id", "cycle_length", "total_study_hours"]
42+
)
43+
44+
if df_result.empty:
45+
return pd.DataFrame(
46+
columns=[
47+
"student_id",
48+
"student_name",
49+
"major",
50+
"cycle_length",
51+
"total_study_hours",
52+
]
53+
)
54+
55+
# Join with students table to get name and major
56+
df_result = df_result.merge(students, on="student_id")
57+
58+
df_result = df_result[
59+
["student_id", "student_name", "major", "cycle_length", "total_study_hours"]
60+
]
61+
62+
return df_result.sort_values(
63+
by=["cycle_length", "total_study_hours"], ascending=[False, False]
64+
).reset_index(drop=True)
65+
66+
67+
def _check_pattern(student_id, sessions, result):
68+
subjects = [row["subject"] for row in sessions]
69+
hours = sum(row["hours_studied"] for row in sessions)
70+
71+
n = len(subjects)
72+
73+
# Try possible cycle lengths from 3 up to half of the sequence
74+
for cycle_len in range(3, n // 2 + 1):
75+
if n % cycle_len != 0:
76+
continue
77+
78+
# Extract the first cycle
79+
first_cycle = subjects[:cycle_len]
80+
is_pattern = True
81+
82+
# Compare each following cycle with the first
83+
for i in range(1, n // cycle_len):
84+
if subjects[i * cycle_len : (i + 1) * cycle_len] != first_cycle:
85+
is_pattern = False
86+
break
87+
88+
# If a repeated cycle is detected, store the result
89+
if is_pattern:
90+
result.append(
91+
{
92+
"student_id": student_id,
93+
"cycle_length": cycle_len,
94+
"total_study_hours": hours,
95+
}
96+
)
97+
break # Stop at the first valid cycle found
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# Write your MySQL query statement below
2+
WITH
3+
ranked_sessions AS (
4+
SELECT
5+
s.student_id,
6+
ss.session_date,
7+
ss.subject,
8+
ss.hours_studied,
9+
ROW_NUMBER() OVER (
10+
PARTITION BY s.student_id
11+
ORDER BY ss.session_date
12+
) AS rn
13+
FROM
14+
study_sessions ss
15+
JOIN students s ON s.student_id = ss.student_id
16+
),
17+
grouped_sessions AS (
18+
SELECT
19+
*,
20+
DATEDIFF(
21+
session_date,
22+
LAG(session_date) OVER (
23+
PARTITION BY student_id
24+
ORDER BY session_date
25+
)
26+
) AS date_diff
27+
FROM ranked_sessions
28+
),
29+
session_groups AS (
30+
SELECT
31+
*,
32+
SUM(
33+
CASE
34+
WHEN date_diff > 2
35+
OR date_diff IS NULL THEN 1
36+
ELSE 0
37+
END
38+
) OVER (
39+
PARTITION BY student_id
40+
ORDER BY session_date
41+
) AS group_id
42+
FROM grouped_sessions
43+
),
44+
valid_sequences AS (
45+
SELECT
46+
student_id,
47+
group_id,
48+
COUNT(*) AS session_count,
49+
GROUP_CONCAT(subject ORDER BY session_date) AS subject_sequence,
50+
SUM(hours_studied) AS total_hours
51+
FROM session_groups
52+
GROUP BY student_id, group_id
53+
HAVING session_count >= 6
54+
),
55+
pattern_detected AS (
56+
SELECT
57+
vs.student_id,
58+
vs.total_hours,
59+
vs.subject_sequence,
60+
COUNT(
61+
DISTINCT
62+
SUBSTRING_INDEX(SUBSTRING_INDEX(subject_sequence, ',', n), ',', -1)
63+
) AS cycle_length
64+
FROM
65+
valid_sequences vs
66+
JOIN (
67+
SELECT a.N + b.N * 10 + 1 AS n
68+
FROM
69+
(
70+
SELECT 0 AS N
71+
UNION
72+
SELECT 1
73+
UNION
74+
SELECT 2
75+
UNION
76+
SELECT 3
77+
UNION
78+
SELECT 4
79+
UNION
80+
SELECT 5
81+
UNION
82+
SELECT 6
83+
UNION
84+
SELECT 7
85+
UNION
86+
SELECT 8
87+
UNION
88+
SELECT 9
89+
) a,
90+
(
91+
SELECT 0 AS N
92+
UNION
93+
SELECT 1
94+
UNION
95+
SELECT 2
96+
UNION
97+
SELECT 3
98+
UNION
99+
SELECT 4
100+
UNION
101+
SELECT 5
102+
UNION
103+
SELECT 6
104+
UNION
105+
SELECT 7
106+
UNION
107+
SELECT 8
108+
UNION
109+
SELECT 9
110+
) b
111+
) nums
112+
ON n <= 10
113+
WHERE
114+
-- Check if the sequence repeats every `k` items, for some `k >= 3` and divides session_count exactly
115+
-- We simplify by checking the start and middle halves are equal
116+
LENGTH(subject_sequence) > 0
117+
AND LOCATE(',', subject_sequence) > 0
118+
AND (
119+
-- For cycle length 3:
120+
subject_sequence LIKE CONCAT(
121+
SUBSTRING_INDEX(subject_sequence, ',', 3),
122+
',',
123+
SUBSTRING_INDEX(SUBSTRING_INDEX(subject_sequence, ',', 6), ',', -3),
124+
'%'
125+
)
126+
OR subject_sequence LIKE CONCAT(
127+
-- For cycle length 4:
128+
SUBSTRING_INDEX(subject_sequence, ',', 4),
129+
',',
130+
SUBSTRING_INDEX(SUBSTRING_INDEX(subject_sequence, ',', 8), ',', -4),
131+
'%'
132+
)
133+
)
134+
GROUP BY vs.student_id, vs.total_hours, vs.subject_sequence
135+
),
136+
final_output AS (
137+
SELECT
138+
s.student_id,
139+
s.student_name,
140+
s.major,
141+
pd.cycle_length,
142+
pd.total_hours AS total_study_hours
143+
FROM
144+
pattern_detected pd
145+
JOIN students s ON s.student_id = pd.student_id
146+
WHERE pd.cycle_length >= 3
147+
)
148+
SELECT *
149+
FROM final_output
150+
ORDER BY cycle_length DESC, total_study_hours DESC;

solution/DATABASE_README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@
322322
| 3586 | [寻找 COVID 康复患者](/solution/3500-3599/3586.Find%20COVID%20Recovery%20Patients/README.md) | `数据库` | 中等 | |
323323
| 3601 | [寻找燃油效率提升的驾驶员](/solution/3600-3699/3601.Find%20Drivers%20with%20Improved%20Fuel%20Efficiency/README.md) | `数据库` | 中等 | |
324324
| 3611 | [查找超预订员工](/solution/3600-3699/3611.Find%20Overbooked%20Employees/README.md) | | 中等 | |
325+
| 3617 | [Find Students with Study Spiral Pattern](/solution/3600-3699/3617.Find%20Students%20with%20Study%20Spiral%20Pattern/README.md) | | 困难 | |
325326

326327
## 版权
327328

solution/DATABASE_README_EN.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ Press <kbd>Control</kbd> + <kbd>F</kbd>(or <kbd>Command</kbd> + <kbd>F</kbd> on
320320
| 3586 | [Find COVID Recovery Patients](/solution/3500-3599/3586.Find%20COVID%20Recovery%20Patients/README_EN.md) | `Database` | Medium | |
321321
| 3601 | [Find Drivers with Improved Fuel Efficiency](/solution/3600-3699/3601.Find%20Drivers%20with%20Improved%20Fuel%20Efficiency/README_EN.md) | `Database` | Medium | |
322322
| 3611 | [Find Overbooked Employees](/solution/3600-3699/3611.Find%20Overbooked%20Employees/README_EN.md) | | Medium | |
323+
| 3617 | [Find Students with Study Spiral Pattern](/solution/3600-3699/3617.Find%20Students%20with%20Study%20Spiral%20Pattern/README_EN.md) | | Hard | |
323324

324325
## Copyright
325326

solution/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3627,6 +3627,7 @@
36273627
| 3614 | [用特殊操作处理字符串 II](/solution/3600-3699/3614.Process%20String%20with%20Special%20Operations%20II/README.md) | | 困难 | 第 458 场周赛 |
36283628
| 3615 | [图中的最长回文路径](/solution/3600-3699/3615.Longest%20Palindromic%20Path%20in%20Graph/README.md) | | 困难 | 第 458 场周赛 |
36293629
| 3616 | [Number of Student Replacements](/solution/3600-3699/3616.Number%20of%20Student%20Replacements/README.md) | | 中等 | 🔒 |
3630+
| 3617 | [Find Students with Study Spiral Pattern](/solution/3600-3699/3617.Find%20Students%20with%20Study%20Spiral%20Pattern/README.md) | | 困难 | |
36303631

36313632
## 版权
36323633

solution/README_EN.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3625,6 +3625,7 @@ Press <kbd>Control</kbd> + <kbd>F</kbd>(or <kbd>Command</kbd> + <kbd>F</kbd> on
36253625
| 3614 | [Process String with Special Operations II](/solution/3600-3699/3614.Process%20String%20with%20Special%20Operations%20II/README_EN.md) | | Hard | Weekly Contest 458 |
36263626
| 3615 | [Longest Palindromic Path in Graph](/solution/3600-3699/3615.Longest%20Palindromic%20Path%20in%20Graph/README_EN.md) | | Hard | Weekly Contest 458 |
36273627
| 3616 | [Number of Student Replacements](/solution/3600-3699/3616.Number%20of%20Student%20Replacements/README_EN.md) | | Medium | 🔒 |
3628+
| 3617 | [Find Students with Study Spiral Pattern](/solution/3600-3699/3617.Find%20Students%20with%20Study%20Spiral%20Pattern/README_EN.md) | | Hard | |
36283629

36293630
## Copyright
36303631

0 commit comments

Comments
 (0)