Skip to content

Commit 7da6398

Browse files
committed
update: 添加问题“2840.判断通过操作能否让字符串相等II”的代码和题解 (#1474)
2840: AC.cpp (#1473) cpp - string - AC,26.79%,66.07% cpp - hash - AC,98.21%,100.00% Signed-off-by: LetMeFly666 <Tisfy@qq.com>
1 parent 27c9a22 commit 7da6398

9 files changed

Lines changed: 392 additions & 24 deletions

.commitmsg

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
2839: AC.cpp (#1471) - AC,100.00%,100.00%
1+
2840: AC.cpp (#1473)
22

3-
docs: VsCode ssh remote下 Git 行内变动标记不显示
4-
docs: refactor - 类似话题放到一起
3+
cpp - string - AC,26.79%,66.07%
4+
cpp - hash - AC,98.21%,100.00%
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* @Author: LetMeFly
3+
* @Date: 2026-03-30 09:25:57
4+
* @LastEditors: LetMeFly.xyz
5+
* @LastEditTime: 2026-03-30 09:28:44
6+
*/
7+
#ifdef _DEBUG
8+
#include "_[1,2]toVector.h"
9+
#endif
10+
11+
class Solution {
12+
public:
13+
bool checkStrings(const string& s1, const string& s2) {
14+
int n = s1.size();
15+
string odd1, odd2, even1, even2;
16+
odd1.reserve(n / 2);
17+
odd2.reserve(n / 2);
18+
even1.reserve(n / 2);
19+
even2.reserve(n / 2);
20+
for (int i = 0; i < n; i++) {
21+
if (i % 2) {
22+
odd1.push_back(s1[i]);
23+
odd2.push_back(s2[i]);
24+
} else {
25+
even1.push_back(s1[i]);
26+
even2.push_back(s2[i]);
27+
}
28+
}
29+
sort(odd1.begin(), odd1.end());
30+
sort(odd2.begin(), odd2.end());
31+
sort(even1.begin(), even1.end());
32+
sort(even2.begin(), even2.end());
33+
return odd1 == odd2 && even1 == even2;
34+
}
35+
};
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* @Author: LetMeFly
3+
* @Date: 2026-03-30 09:25:57
4+
* @LastEditors: LetMeFly.xyz
5+
* @LastEditTime: 2026-03-30 09:32:52
6+
*/
7+
#ifdef _DEBUG
8+
#include "_[1,2]toVector.h"
9+
#endif
10+
11+
class Solution {
12+
public:
13+
bool checkStrings(const string& s1, const string& s2) {
14+
int n = s1.size();
15+
int cnt[2][26] = {0};
16+
for (int i = 0; i < n; i++) {
17+
cnt[i % 2][s1[i] - 'a']++;
18+
cnt[i % 2][s2[i] - 'a']--;
19+
}
20+
21+
for (int i = 0; i < 26; i++) {
22+
if (cnt[0][i] || cnt[1][i]) {
23+
return false;
24+
}
25+
}
26+
return true;
27+
}
28+
};

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -964,6 +964,7 @@
964964
|2829.k-avoiding数组的最小总和|中等|<a href="https://leetcode.cn/problems/determine-the-minimum-sum-of-a-k-avoiding-array/" target="_blank">题目地址</a>|<a href="https://blog.letmefly.xyz/2025/03/26/LeetCode%202829.k-avoiding%E6%95%B0%E7%BB%84%E7%9A%84%E6%9C%80%E5%B0%8F%E6%80%BB%E5%92%8C/" target="_blank">题解地址</a>|<a href="https://letmefly.blog.csdn.net/article/details/146528057" target="_blank">CSDN题解</a>|<a href="https://leetcode.cn/problems/determine-the-minimum-sum-of-a-k-avoiding-array/solutions/3628637/letmefly-2829k-avoiding-shu-zu-de-zui-xi-sja5/" target="_blank">LeetCode题解</a>|
965965
|2834.找出美丽数组的最小和|中等|<a href="https://leetcode.cn/problems/find-the-minimum-possible-sum-of-a-beautiful-array/" target="_blank">题目地址</a>|<a href="https://blog.letmefly.xyz/2024/03/08/LeetCode%202834.%E6%89%BE%E5%87%BA%E7%BE%8E%E4%B8%BD%E6%95%B0%E7%BB%84%E7%9A%84%E6%9C%80%E5%B0%8F%E5%92%8C/" target="_blank">题解地址</a>|<a href="https://letmefly.blog.csdn.net/article/details/136565723" target="_blank">CSDN题解</a>|<a href="https://leetcode.cn/problems/find-the-minimum-possible-sum-of-a-beautiful-array/solutions/2675178/letmefly-2834zhao-chu-mei-li-shu-zu-de-z-qfzj/" target="_blank">LeetCode题解</a>|
966966
|2839.判断通过操作能否让字符串相等I|简单|<a href="https://leetcode.cn/problems/check-if-strings-can-be-made-equal-with-operations-i/" target="_blank">题目地址</a>|<a href="https://blog.letmefly.xyz/2026/03/29/LeetCode%202839.%E5%88%A4%E6%96%AD%E9%80%9A%E8%BF%87%E6%93%8D%E4%BD%9C%E8%83%BD%E5%90%A6%E8%AE%A9%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9B%B8%E7%AD%89I/" target="_blank">题解地址</a>|<a href="https://letmefly.blog.csdn.net/article/details/159616700" target="_blank">CSDN题解</a>|<a href="https://leetcode.cn/problems/check-if-strings-can-be-made-equal-with-operations-i/solutions/3939958/letmefly-2839pan-duan-tong-guo-cao-zuo-n-wo78/" target="_blank">LeetCode题解</a>|
967+
|2840.判断通过操作能否让字符串相等II|中等|<a href="https://leetcode.cn/problems/check-if-strings-can-be-made-equal-with-operations-ii/" target="_blank">题目地址</a>|<a href="https://blog.letmefly.xyz/2026/03/30/LeetCode%202840.%E5%88%A4%E6%96%AD%E9%80%9A%E8%BF%87%E6%93%8D%E4%BD%9C%E8%83%BD%E5%90%A6%E8%AE%A9%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9B%B8%E7%AD%89II/" target="_blank">题解地址</a>|<a href="https://letmefly.blog.csdn.net/article/details/159634187" target="_blank">CSDN题解</a>|<a href="https://leetcode.cn/problems/check-if-strings-can-be-made-equal-with-operations-ii/solutions/3940240/letmefly-2840pan-duan-tong-guo-cao-zuo-n-i591/" target="_blank">LeetCode题解</a>|
967968
|2843.统计对称整数的数目|简单|<a href="https://leetcode.cn/problems/count-symmetric-integers/" target="_blank">题目地址</a>|<a href="https://blog.letmefly.xyz/2025/04/11/LeetCode%202843.%E7%BB%9F%E8%AE%A1%E5%AF%B9%E7%A7%B0%E6%95%B4%E6%95%B0%E7%9A%84%E6%95%B0%E7%9B%AE/" target="_blank">题解地址</a>|<a href="https://letmefly.blog.csdn.net/article/details/147154718" target="_blank">CSDN题解</a>|<a href="https://leetcode.cn/problems/count-symmetric-integers/solutions/3648385/letmefly-2843tong-ji-dui-cheng-zheng-shu-tf02/" target="_blank">LeetCode题解</a>|
968969
|2844.生成特殊数字的最少操作|中等|<a href="https://leetcode.cn/problems/minimum-operations-to-make-a-special-number/" target="_blank">题目地址</a>|<a href="https://blog.letmefly.xyz/2024/07/25/LeetCode%202844.%E7%94%9F%E6%88%90%E7%89%B9%E6%AE%8A%E6%95%B0%E5%AD%97%E7%9A%84%E6%9C%80%E5%B0%91%E6%93%8D%E4%BD%9C/" target="_blank">题解地址</a>|<a href="https://letmefly.blog.csdn.net/article/details/140695211" target="_blank">CSDN题解</a>|<a href="https://leetcode.cn/problems/minimum-operations-to-make-a-special-number/solutions/2857083/letmefly-2844sheng-cheng-te-shu-shu-zi-d-ds9b/" target="_blank">LeetCode题解</a>|
969970
|2859.计算K置位下标对应元素的和|简单|<a href="https://leetcode.cn/problems/sum-of-values-at-indices-with-k-set-bits/" target="_blank">题目地址</a>|<a href="https://blog.letmefly.xyz/2024/01/25/LeetCode%202859.%E8%AE%A1%E7%AE%97K%E7%BD%AE%E4%BD%8D%E4%B8%8B%E6%A0%87%E5%AF%B9%E5%BA%94%E5%85%83%E7%B4%A0%E7%9A%84%E5%92%8C/" target="_blank">题解地址</a>|<a href="https://letmefly.blog.csdn.net/article/details/135836080" target="_blank">CSDN题解</a>|<a href="https://leetcode.cn/problems/sum-of-values-at-indices-with-k-set-bits/solutions/2618937/letmefly-2859ji-suan-k-zhi-wei-xia-biao-usyx1/" target="_blank">LeetCode题解</a>|
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
---
2+
title: 2840.判断通过操作能否让字符串相等 II:字符串排序或哈希表
3+
date: 2026-03-30 09:33:41
4+
tags: [题解, LeetCode, 中等, 哈希表, 字符串, 排序]
5+
categories: [题解, LeetCode]
6+
---
7+
8+
# 【LetMeFly】2840.判断通过操作能否让字符串相等 II:字符串排序或哈希表
9+
10+
力扣题目链接:[https://leetcode.cn/problems/check-if-strings-can-be-made-equal-with-operations-ii/](https://leetcode.cn/problems/check-if-strings-can-be-made-equal-with-operations-ii/)
11+
12+
<p>给你两个字符串&nbsp;<code>s1</code>&nbsp;&nbsp;<code>s2</code>&nbsp;,两个字符串长度都为&nbsp;<code>n</code>&nbsp;,且只包含&nbsp;<strong>小写&nbsp;</strong>英文字母。</p>
13+
14+
<p>你可以对两个字符串中的 <strong>任意一个</strong>&nbsp;执行以下操作 <strong>任意</strong>&nbsp;次:</p>
15+
16+
<ul>
17+
<li>选择两个下标&nbsp;<code>i</code> 和&nbsp;<code>j</code>&nbsp;,满足 <code>i &lt; j</code>&nbsp;且 <code>j - i</code>&nbsp;是 <strong>偶数</strong>,然后 <strong>交换</strong> 这个字符串中两个下标对应的字符。</li>
18+
</ul>
19+
20+
<p>&nbsp;</p>
21+
22+
<p>如果你可以让字符串<em>&nbsp;</em><code>s1</code><em> </em>和<em>&nbsp;</em><code>s2</code>&nbsp;相等,那么返回 <code>true</code>&nbsp;,否则返回 <code>false</code>&nbsp;。</p>
23+
24+
<p>&nbsp;</p>
25+
26+
<p>&nbsp;</p>
27+
28+
<p><strong class="example">示例 1:</strong></p>
29+
30+
<pre>
31+
<b>输入:</b>s1 = "abcdba", s2 = "cabdab"
32+
<b>输出:</b>true
33+
<b>解释:</b>我们可以对 s1 执行以下操作:
34+
- 选择下标 i = 0 ,j = 2 ,得到字符串 s1 = "cbadba" 。
35+
- 选择下标 i = 2 ,j = 4 ,得到字符串 s1 = "cbbdaa" 。
36+
- 选择下标 i = 1 ,j = 5 ,得到字符串 s1 = "cabdab" = s2 。
37+
</pre>
38+
39+
<p><strong class="example">示例 2:</strong></p>
40+
41+
<pre>
42+
<b>输入:</b>s1 = "abe", s2 = "bea"
43+
<b>输出:</b>false
44+
<b>解释:</b>无法让两个字符串相等。
45+
</pre>
46+
47+
<p>&nbsp;</p>
48+
49+
<p><strong>提示:</strong></p>
50+
51+
<ul>
52+
<li><code>n == s1.length == s2.length</code></li>
53+
<li><code>1 &lt;= n &lt;= 10<sup>5</sup></code></li>
54+
<li><code>s1</code> 和&nbsp;<code>s2</code>&nbsp;只包含小写英文字母。</li>
55+
</ul>
56+
57+
58+
# 解题思路:奇偶分别判
59+
60+
奇数下标元素可以任意交换,偶数下标也可以任意交换,所以只需要分别看看在所有奇数位置组成的字符串和偶数位置组成的字符串是否等价就好了。
61+
62+
## 方法一:字符串排序
63+
64+
将奇数位置和偶数位置字符分别摘出来组成临时字符串,排序,看是否相等。
65+
66+
+ 时间复杂度$O(n\log n)$
67+
+ 空间复杂度$O(n)$
68+
69+
### AC代码
70+
71+
#### C++
72+
73+
```cpp
74+
/*
75+
* @LastEditTime: 2026-03-30 09:28:44
76+
*/
77+
class Solution {
78+
public:
79+
bool checkStrings(const string& s1, const string& s2) {
80+
int n = s1.size();
81+
string odd1, odd2, even1, even2;
82+
odd1.reserve(n / 2);
83+
odd2.reserve(n / 2);
84+
even1.reserve(n / 2);
85+
even2.reserve(n / 2);
86+
for (int i = 0; i < n; i++) {
87+
if (i % 2) {
88+
odd1.push_back(s1[i]);
89+
odd2.push_back(s2[i]);
90+
} else {
91+
even1.push_back(s1[i]);
92+
even2.push_back(s2[i]);
93+
}
94+
}
95+
sort(odd1.begin(), odd1.end());
96+
sort(odd2.begin(), odd2.end());
97+
sort(even1.begin(), even1.end());
98+
sort(even2.begin(), even2.end());
99+
return odd1 == odd2 && even1 == even2;
100+
}
101+
};
102+
```
103+
104+
## 方法二:哈希表
105+
106+
哈希表统计两个字符串在奇数位置和偶数位置出现字符次数的diff,若最终哈希表中元素全部为$0$则返回true。
107+
108+
+ 时间复杂度$O(n + C)$,其中$C=26$
109+
+ 空间复杂度$O(C)$
110+
111+
### AC代码
112+
113+
#### C++
114+
115+
```cpp
116+
/*
117+
* @LastEditTime: 2026-03-30 09:32:52
118+
*/
119+
class Solution {
120+
public:
121+
bool checkStrings(const string& s1, const string& s2) {
122+
int n = s1.size();
123+
int cnt[2][26] = {0};
124+
for (int i = 0; i < n; i++) {
125+
cnt[i % 2][s1[i] - 'a']++;
126+
cnt[i % 2][s2[i] - 'a']--;
127+
}
128+
129+
for (int i = 0; i < 26; i++) {
130+
if (cnt[0][i] || cnt[1][i]) {
131+
return false;
132+
}
133+
}
134+
return true;
135+
}
136+
};
137+
```
138+
139+
AC,98.21%,100.00%
140+
141+
> 同步发文于[CSDN](https://letmefly.blog.csdn.net/article/details/159634187)和我的[个人博客](https://blog.letmefly.xyz/),原创不易,转载经作者同意后请附上[原文链接](https://blog.letmefly.xyz/2026/03/30/LeetCode%202840.%E5%88%A4%E6%96%AD%E9%80%9A%E8%BF%87%E6%93%8D%E4%BD%9C%E8%83%BD%E5%90%A6%E8%AE%A9%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9B%B8%E7%AD%89II/)哦~
142+
>
143+
> 千篇源码题解[已开源](https://github.com/LetMeFly666/LeetCode)

diff.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
ベ

lib-watcher.py

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#!/usr/bin/env python3
2+
"""
3+
北邮图书馆《埃隆·马斯克传》西土城校区在架可借状态监控脚本
4+
每15分钟检查一次,如有西土城在架可借则通过Bark通知
5+
6+
其实多match了一个,检测结果是4本,但是无所谓
7+
"""
8+
from __future__ import annotations
9+
10+
import re
11+
import sys
12+
import time
13+
import logging
14+
import urllib.request
15+
import urllib.error
16+
import urllib.parse
17+
from datetime import datetime
18+
19+
# ============ 配置 ============
20+
BOOK_URL = "http://opac.bupt.edu.cn:8080//bookInfo_01h0483291.html"
21+
BARK_URL = "http://localhost:xxxx/xxxxxxxx"
22+
CHECK_INTERVAL = 15 * 60 # 15分钟(秒)
23+
TIMEOUT = 15 # HTTP请求超时(秒)
24+
25+
logging.basicConfig(
26+
level=logging.INFO,
27+
format="%(asctime)s [%(levelname)s] %(message)s",
28+
datefmt="%Y-%m-%d %H:%M:%S",
29+
)
30+
log = logging.getLogger(__name__)
31+
32+
33+
def fetch_page(url):
34+
"""获取页面HTML内容"""
35+
req = urllib.request.Request(
36+
url,
37+
headers={
38+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
39+
"(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
40+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
41+
},
42+
)
43+
with urllib.request.urlopen(req, timeout=TIMEOUT) as resp:
44+
charset = resp.headers.get_content_charset() or "utf-8"
45+
return resp.read().decode(charset)
46+
47+
48+
def parse_collection(html):
49+
"""
50+
用正则解析馆藏表格。
51+
匹配每个<tr>块中同时包含条码号和馆藏部门/状态的行。
52+
"""
53+
results = []
54+
barcode_pattern = re.compile(r"2111\d{10}")
55+
dept_pattern = re.compile(r"((?:西土城|沙河)[::][^<\n]+)")
56+
status_keywords = ["在架可借", "本馆借出", "阅览", "预约", "装订中", "编目中"]
57+
58+
tr_blocks = re.findall(r"<tr[^>]*>(.*?)</tr>", html, re.DOTALL | re.IGNORECASE)
59+
for block in tr_blocks:
60+
found_barcode = barcode_pattern.search(block)
61+
if not found_barcode:
62+
continue
63+
barcode = found_barcode.group()
64+
65+
# 提取馆藏部门
66+
dept_match = dept_pattern.search(block)
67+
dept = dept_match.group(1).strip() if dept_match else ""
68+
dept = re.sub(r"<[^>]+>", "", dept).strip()
69+
70+
# 提取状态
71+
status = ""
72+
for kw in status_keywords:
73+
if kw in block:
74+
status = kw
75+
break
76+
77+
if dept or status:
78+
results.append({
79+
"department": dept,
80+
"barcode": barcode,
81+
"status": status,
82+
})
83+
84+
return results
85+
86+
87+
def send_bark_notification(title, body):
88+
"""通过Bark发送通知"""
89+
encoded_title = urllib.parse.quote(title, safe="")
90+
encoded_body = urllib.parse.quote(body, safe="")
91+
url = "{}/{}/{}".format(BARK_URL, encoded_title, encoded_body)
92+
try:
93+
req = urllib.request.Request(url)
94+
with urllib.request.urlopen(req, timeout=TIMEOUT) as resp:
95+
log.info("Bark通知发送成功,响应码: %d", resp.status)
96+
return True
97+
except Exception as e:
98+
log.error("Bark通知发送失败: %s", e)
99+
return False
100+
101+
102+
def check_once():
103+
"""执行一次检查,返回西土城在架可借的记录列表"""
104+
log.info("开始检查馆藏状态...")
105+
try:
106+
html = fetch_page(BOOK_URL)
107+
except Exception as e:
108+
log.error("获取页面失败: %s", e)
109+
return []
110+
111+
all_items = parse_collection(html)
112+
log.info("共解析到 %d 条馆藏记录", len(all_items))
113+
for item in all_items:
114+
log.info(" %s | %s | %s", item["department"], item["barcode"], item["status"])
115+
116+
# 筛选西土城 + 在架可借
117+
available = [
118+
item for item in all_items
119+
if "西土城" in item.get("department", "") and "在架可借" in item.get("status", "")
120+
]
121+
# available = [
122+
# {"department": "西土城", "status": "在架可借", "barcode": "mock123"}
123+
# ]
124+
return available
125+
126+
127+
def main():
128+
"""主循环"""
129+
run_once = "--once" in sys.argv
130+
131+
while True:
132+
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
133+
available = check_once()
134+
135+
if available:
136+
log.info("发现 %d 本西土城在架可借!", len(available))
137+
details = "\n".join(
138+
"{} 条码:{}".format(r["department"], r["barcode"])
139+
for r in available
140+
)
141+
title = "北邮图书馆-马斯克传有书可借"
142+
body = "西土城校区发现{}本在架可借\n{}\n检查时间:{}".format(
143+
len(available), details, now
144+
)
145+
send_bark_notification(title, body)
146+
break
147+
else:
148+
log.info("暂无西土城在架可借,%d分钟后再查...", CHECK_INTERVAL // 60)
149+
150+
if run_once:
151+
break
152+
153+
time.sleep(CHECK_INTERVAL)
154+
155+
156+
if __name__ == "__main__":
157+
main()

0 commit comments

Comments
 (0)