Skip to content

Commit ebeca22

Browse files
authored
feat: add solutions to lc problems: No.1625,3716 (#4792)
1 parent 086ab25 commit ebeca22

File tree

8 files changed

+528
-6
lines changed

8 files changed

+528
-6
lines changed

solution/1600-1699/1625.Lexicographically Smallest String After Applying Operations/README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,35 @@ func findLexSmallestString(s string, a int, b int) string {
216216
}
217217
```
218218

219+
#### TypeScript
220+
221+
```ts
222+
function findLexSmallestString(s: string, a: number, b: number): string {
223+
const q: string[] = [s];
224+
const vis = new Set<string>([s]);
225+
let ans = s;
226+
let i = 0;
227+
while (i < q.length) {
228+
s = q[i++];
229+
if (ans > s) {
230+
ans = s;
231+
}
232+
const t1 = s
233+
.split('')
234+
.map((c, j) => (j & 1 ? String((Number(c) + a) % 10) : c))
235+
.join('');
236+
const t2 = s.slice(-b) + s.slice(0, -b);
237+
for (const t of [t1, t2]) {
238+
if (!vis.has(t)) {
239+
vis.add(t);
240+
q.push(t);
241+
}
242+
}
243+
}
244+
return ans;
245+
}
246+
```
247+
219248
<!-- tabs:end -->
220249

221250
<!-- solution:end -->
@@ -364,6 +393,41 @@ func findLexSmallestString(s string, a int, b int) string {
364393
}
365394
```
366395

396+
#### TypeScript
397+
398+
```ts
399+
function findLexSmallestString(s: string, a: number, b: number): string {
400+
let ans = s;
401+
const n = s.length;
402+
let arr = s.split('');
403+
for (let _ = 0; _ < n; _++) {
404+
arr = arr.slice(-b).concat(arr.slice(0, -b));
405+
for (let j = 0; j < 10; j++) {
406+
for (let k = 1; k < n; k += 2) {
407+
arr[k] = String((Number(arr[k]) + a) % 10);
408+
}
409+
if (b & 1) {
410+
for (let p = 0; p < 10; p++) {
411+
for (let k = 0; k < n; k += 2) {
412+
arr[k] = String((Number(arr[k]) + a) % 10);
413+
}
414+
const t = arr.join('');
415+
if (ans > t) {
416+
ans = t;
417+
}
418+
}
419+
} else {
420+
const t = arr.join('');
421+
if (ans > t) {
422+
ans = t;
423+
}
424+
}
425+
}
426+
}
427+
return ans;
428+
}
429+
```
430+
367431
<!-- tabs:end -->
368432

369433
<!-- solution:end -->

solution/1600-1699/1625.Lexicographically Smallest String After Applying Operations/README_EN.md

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ There is no way to obtain a string that is lexicographically smaller than &quot;
9191

9292
<!-- solution:start -->
9393

94-
### Solution 1
94+
### Solution 1: BFS
95+
96+
Since the data scale of this problem is relatively small, we can use BFS to brute-force search all possible states and then take the lexicographically smallest state.
9597

9698
<!-- tabs:start -->
9799

@@ -212,13 +214,50 @@ func findLexSmallestString(s string, a int, b int) string {
212214
}
213215
```
214216

217+
#### TypeScript
218+
219+
```ts
220+
function findLexSmallestString(s: string, a: number, b: number): string {
221+
const q: string[] = [s];
222+
const vis = new Set<string>([s]);
223+
let ans = s;
224+
let i = 0;
225+
while (i < q.length) {
226+
s = q[i++];
227+
if (ans > s) {
228+
ans = s;
229+
}
230+
const t1 = s
231+
.split('')
232+
.map((c, j) => (j & 1 ? String((Number(c) + a) % 10) : c))
233+
.join('');
234+
const t2 = s.slice(-b) + s.slice(0, -b);
235+
for (const t of [t1, t2]) {
236+
if (!vis.has(t)) {
237+
vis.add(t);
238+
q.push(t);
239+
}
240+
}
241+
}
242+
return ans;
243+
}
244+
```
245+
215246
<!-- tabs:end -->
216247

217248
<!-- solution:end -->
218249

219250
<!-- solution:start -->
220251

221-
### Solution 2
252+
### Solution 2: Enumeration
253+
254+
We observe that for the addition operation, a digit will return to its original state after at most $10$ additions; for the rotation operation, the string will also return to its original state after at most $n$ rotations.
255+
256+
Therefore, the rotation operation produces at most $n$ states. If the rotation count $b$ is even, the addition operation only affects digits at odd indices, resulting in a total of $n \times 10$ states; if the rotation count $b$ is odd, the addition operation affects both odd and even index digits, resulting in a total of $n \times 10 \times 10$ states.
257+
258+
Thus, we can directly enumerate all possible string states and take the lexicographically smallest one.
259+
260+
The time complexity is $O(n^2 \times 10^2)$ and the space complexity is $O(n)$, where $n$ is the length of string $s$.
222261

223262
<!-- tabs:start -->
224263

@@ -352,6 +391,41 @@ func findLexSmallestString(s string, a int, b int) string {
352391
}
353392
```
354393

394+
#### TypeScript
395+
396+
```ts
397+
function findLexSmallestString(s: string, a: number, b: number): string {
398+
let ans = s;
399+
const n = s.length;
400+
let arr = s.split('');
401+
for (let _ = 0; _ < n; _++) {
402+
arr = arr.slice(-b).concat(arr.slice(0, -b));
403+
for (let j = 0; j < 10; j++) {
404+
for (let k = 1; k < n; k += 2) {
405+
arr[k] = String((Number(arr[k]) + a) % 10);
406+
}
407+
if (b & 1) {
408+
for (let p = 0; p < 10; p++) {
409+
for (let k = 0; k < n; k += 2) {
410+
arr[k] = String((Number(arr[k]) + a) % 10);
411+
}
412+
const t = arr.join('');
413+
if (ans > t) {
414+
ans = t;
415+
}
416+
}
417+
} else {
418+
const t = arr.join('');
419+
if (ans > t) {
420+
ans = t;
421+
}
422+
}
423+
}
424+
}
425+
return ans;
426+
}
427+
```
428+
355429
<!-- tabs:end -->
356430

357431
<!-- solution:end -->
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
function findLexSmallestString(s: string, a: number, b: number): string {
2+
const q: string[] = [s];
3+
const vis = new Set<string>([s]);
4+
let ans = s;
5+
let i = 0;
6+
while (i < q.length) {
7+
s = q[i++];
8+
if (ans > s) {
9+
ans = s;
10+
}
11+
const t1 = s
12+
.split('')
13+
.map((c, j) => (j & 1 ? String((Number(c) + a) % 10) : c))
14+
.join('');
15+
const t2 = s.slice(-b) + s.slice(0, -b);
16+
for (const t of [t1, t2]) {
17+
if (!vis.has(t)) {
18+
vis.add(t);
19+
q.push(t);
20+
}
21+
}
22+
}
23+
return ans;
24+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
function findLexSmallestString(s: string, a: number, b: number): string {
2+
let ans = s;
3+
const n = s.length;
4+
let arr = s.split('');
5+
for (let _ = 0; _ < n; _++) {
6+
arr = arr.slice(-b).concat(arr.slice(0, -b));
7+
for (let j = 0; j < 10; j++) {
8+
for (let k = 1; k < n; k += 2) {
9+
arr[k] = String((Number(arr[k]) + a) % 10);
10+
}
11+
if (b & 1) {
12+
for (let p = 0; p < 10; p++) {
13+
for (let k = 0; k < n; k += 2) {
14+
arr[k] = String((Number(arr[k]) + a) % 10);
15+
}
16+
const t = arr.join('');
17+
if (ans > t) {
18+
ans = t;
19+
}
20+
}
21+
} else {
22+
const t = arr.join('');
23+
if (ans > t) {
24+
ans = t;
25+
}
26+
}
27+
}
28+
}
29+
return ans;
30+
}

solution/3700-3799/3716.Find Churn Risk Customers/README.md

Lines changed: 114 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ tags:
2020

2121
<pre>
2222
+------------------+---------+
23-
| Column Name | Type |
23+
| Column Name | Type |
2424
+------------------+---------+
2525
| event_id | int |
2626
| user_id | int |
@@ -157,14 +157,126 @@ monthly_amount 表示此次事件后的月度订阅费用。
157157

158158
<!-- solution:start -->
159159

160-
### 方法一
160+
### 方法一:分组统计 + 连接 + 条件筛选
161+
162+
我们先通过窗口函数获取每个用户按照事件日期和事件 ID 降序排列的最后一条记录,得到每个用户的最新事件信息。然后,我们通过分组统计每个用户的订阅历史信息,包括订阅开始日期、最后事件日期、历史最高订阅费用以及降级事件的数量。最后,我们将最新事件信息与历史统计信息进行连接,并根据题目要求的条件进行筛选,得到流失风险客户列表。
161163

162164
<!-- tabs:start -->
163165

164166
#### MySQL
165167

166168
```sql
169+
WITH
170+
user_with_last_event AS (
171+
SELECT
172+
s.*,
173+
ROW_NUMBER() OVER (
174+
PARTITION BY user_id
175+
ORDER BY event_date DESC, event_id DESC
176+
) AS rn
177+
FROM subscription_events s
178+
),
179+
user_history AS (
180+
SELECT
181+
user_id,
182+
MIN(event_date) AS start_date,
183+
MAX(event_date) AS last_event_date,
184+
MAX(monthly_amount) AS max_historical_amount,
185+
SUM(
186+
CASE
187+
WHEN event_type = 'downgrade' THEN 1
188+
ELSE 0
189+
END
190+
) AS downgrade_count
191+
FROM subscription_events
192+
GROUP BY user_id
193+
),
194+
latest_event AS (
195+
SELECT
196+
user_id,
197+
event_type AS last_event_type,
198+
plan_name AS current_plan,
199+
monthly_amount AS current_monthly_amount
200+
FROM user_with_last_event
201+
WHERE rn = 1
202+
)
203+
SELECT
204+
l.user_id,
205+
l.current_plan,
206+
l.current_monthly_amount,
207+
h.max_historical_amount,
208+
DATEDIFF(h.last_event_date, h.start_date) AS days_as_subscriber
209+
FROM
210+
latest_event l
211+
JOIN user_history h ON l.user_id = h.user_id
212+
WHERE
213+
l.last_event_type <> 'cancel'
214+
AND h.downgrade_count >= 1
215+
AND l.current_monthly_amount < 0.5 * h.max_historical_amount
216+
AND DATEDIFF(h.last_event_date, h.start_date) >= 60
217+
ORDER BY days_as_subscriber DESC, l.user_id ASC;
218+
```
167219

220+
#### Pandas
221+
222+
```python
223+
import pandas as pd
224+
225+
226+
def find_churn_risk_customers(subscription_events: pd.DataFrame) -> pd.DataFrame:
227+
subscription_events["event_date"] = pd.to_datetime(
228+
subscription_events["event_date"]
229+
)
230+
subscription_events = subscription_events.sort_values(
231+
["user_id", "event_date", "event_id"]
232+
)
233+
last_events = (
234+
subscription_events.groupby("user_id")
235+
.tail(1)[["user_id", "event_type", "plan_name", "monthly_amount"]]
236+
.rename(
237+
columns={
238+
"event_type": "last_event_type",
239+
"plan_name": "current_plan",
240+
"monthly_amount": "current_monthly_amount",
241+
}
242+
)
243+
)
244+
245+
agg_df = (
246+
subscription_events.groupby("user_id")
247+
.agg(
248+
start_date=("event_date", "min"),
249+
last_event_date=("event_date", "max"),
250+
max_historical_amount=("monthly_amount", "max"),
251+
downgrade_count=("event_type", lambda x: (x == "downgrade").sum()),
252+
)
253+
.reset_index()
254+
)
255+
256+
merged = pd.merge(agg_df, last_events, on="user_id", how="inner")
257+
merged["days_as_subscriber"] = (
258+
merged["last_event_date"] - merged["start_date"]
259+
).dt.days
260+
261+
result = merged[
262+
(merged["last_event_type"] != "cancel")
263+
& (merged["downgrade_count"] >= 1)
264+
& (merged["current_monthly_amount"] < 0.5 * merged["max_historical_amount"])
265+
& (merged["days_as_subscriber"] >= 60)
266+
][
267+
[
268+
"user_id",
269+
"current_plan",
270+
"current_monthly_amount",
271+
"max_historical_amount",
272+
"days_as_subscriber",
273+
]
274+
]
275+
276+
result = result.sort_values(
277+
["days_as_subscriber", "user_id"], ascending=[False, True]
278+
).reset_index(drop=True)
279+
return result
168280
```
169281

170282
<!-- tabs:end -->

0 commit comments

Comments
 (0)