-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrss2.xml
More file actions
238 lines (132 loc) · 55.6 KB
/
rss2.xml
File metadata and controls
238 lines (132 loc) · 55.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
<title>Dynamic_Pigeon</title>
<link>https://dynamic-pigeon.github.io/</link>
<atom:link href="/rss2.xml" rel="self" type="application/rss+xml"/>
<description>好少年光芒万丈</description>
<pubDate>Thu, 20 Nov 2025 06:56:37 GMT</pubDate>
<generator>http://hexo.io/</generator>
<item>
<title>偶然发现的 rust stdout 的一些东西</title>
<link>https://dynamic-pigeon.github.io/2025/11/20/%E5%81%B6%E7%84%B6%E5%8F%91%E7%8E%B0%E7%9A%84-rust-stdout-%E7%9A%84%E4%B8%80%E4%BA%9B%E4%B8%9C%E8%A5%BF/</link>
<guid>https://dynamic-pigeon.github.io/2025/11/20/%E5%81%B6%E7%84%B6%E5%8F%91%E7%8E%B0%E7%9A%84-rust-stdout-%E7%9A%84%E4%B8%80%E4%BA%9B%E4%B8%9C%E8%A5%BF/</guid>
<pubDate>Thu, 20 Nov 2025 06:56:37 GMT</pubDate>
<description>
<h2 id="起因"><a href="#起因" class="headerlink" title="起因"></a>起因</h2><p>我今天在牛客用 rust 写每日一题,按理来说 rust 是和 c 一个速度的,所以应该跑的很快,但是我却超时了,这让我不是很能理解。我想过
</description>
<content:encoded><![CDATA[<h2 id="起因"><a href="#起因" class="headerlink" title="起因"></a>起因</h2><p>我今天在牛客用 rust 写每日一题,按理来说 rust 是和 c 一个速度的,所以应该跑的很快,但是我却超时了,这让我不是很能理解。我想过是因为 <code>println</code> 每次调用都会去获取 stdout 的锁导致的超时,但是就算我提前锁上了,我依然超时了,最后我用 <code>BufWriter</code> 包了一层才通过,而且速度极大的提高了。</p><h2 id="排查"><a href="#排查" class="headerlink" title="排查"></a>排查</h2><p>然后我就去查看了 stdout 的 <code>write_all</code> 函数,在层层包裹下,我看见了下面的代码</p><pre><code class="rust">fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { match memchr::memrchr(b'\n', buf) { // If there are no new newlines (that is, if this write is less than // one line), just do a regular buffered write (which may flush if // we exceed the inner buffer's size) None => { self.flush_if_completed_line()?; self.buffer.write_all(buf) } Some(newline_idx) => { let (lines, tail) = buf.split_at(newline_idx + 1); if self.buffered().is_empty() { self.inner_mut().write_all(lines)?; } else { // If there is any buffered data, we add the incoming lines // to that buffer before flushing, which saves us at least // one write call. We can't really do this with `write`, // since we can't do this *and* not suppress errors *and* // report a consistent state to the caller in a return // value, but here in write_all it's fine. self.buffer.write_all(lines)?; self.buffer.flush_buf()?; } self.buffer.write_all(tail) } }}</code></pre><p>对于有换行的输出,stdout 会把最后一个换行前的全部 flush,而我写的这题有 1e6 次输出而且每次都换行,频繁的 flush 导致了超时。</p>]]></content:encoded>
<comments>https://dynamic-pigeon.github.io/2025/11/20/%E5%81%B6%E7%84%B6%E5%8F%91%E7%8E%B0%E7%9A%84-rust-stdout-%E7%9A%84%E4%B8%80%E4%BA%9B%E4%B8%9C%E8%A5%BF/#disqus_thread</comments>
</item>
<item>
<title>记一个在群内看见的小东西</title>
<link>https://dynamic-pigeon.github.io/2025/05/21/%E8%AE%B0%E4%B8%80%E4%B8%AA%E5%9C%A8%E7%BE%A4%E5%86%85%E7%9C%8B%E8%A7%81%E7%9A%84%E5%B0%8F%E4%B8%9C%E8%A5%BF/</link>
<guid>https://dynamic-pigeon.github.io/2025/05/21/%E8%AE%B0%E4%B8%80%E4%B8%AA%E5%9C%A8%E7%BE%A4%E5%86%85%E7%9C%8B%E8%A7%81%E7%9A%84%E5%B0%8F%E4%B8%9C%E8%A5%BF/</guid>
<pubDate>Wed, 21 May 2025 08:45:20 GMT</pubDate>
<description>
<p>在 rust 交流群看见一个人希望写一个类似 cpp mutextable 的东西,然后我叫他用 <code>Mutex&lt;()&gt;</code> 代替 <code>std::mutex</code> 写一下试试。</p>
<p>然后他反手写出类似下面的代码:</p
</description>
<content:encoded><![CDATA[<p>在 rust 交流群看见一个人希望写一个类似 cpp mutextable 的东西,然后我叫他用 <code>Mutex<()></code> 代替 <code>std::mutex</code> 写一下试试。</p><p>然后他反手写出类似下面的代码:</p><pre><code class="rust">struct MutexTable { table: DashMap<String, Mutex<()>>,}impl MutexTable { async fn lock(&self, key: &str) -> Result<MutexGuard<'_, ()> >{ let lock = self.table .entry(key.to_string()) .or_insert_with(|| Mutex::new(())); let guard = lock.lock().await; Ok(guard) }}</code></pre><p>然后报错,报错信息是:</p><pre><code class="bash">error[E0515]: cannot return value referencing local variable `lock` --> src/main.rs:22:9 |21 | let guard = lock.lock().await; | ---- `lock` is borrowed here22 | Ok(guard) | ^^^^^^^^^ returns a value referencing data owned by the current function</code></pre><p>当时我挺纳闷的,于是我用 <code>HashMap</code> 写了一个:</p><pre><code class="rust">struct MutexTable { table: HashMap<String, Mutex<()>>,}impl MutexTable { async fn lock(&mut self, key: &str) -> Result<MutexGuard<'_, ()> >{ let mutex = self.table.entry(key.to_string()).or_insert_with(|| Mutex::new(())); Ok(mutex.lock().await) }}</code></pre><p>出乎意料的,这个编译通过了(?</p><hr><p>但是我不理解啊,于是我看了一下返回值类型,<code>HashMap</code> 返回了一个 <code>&mut Mutex<()></code> 而 <code>DashMap</code> 返回了一个 <code>RefMut<'_, String, Mutex<()>></code>。</p><p>看不出任何东西。</p><p>进行生命周期手动标注</p><pre><code class="rust">struct MutexTable { table: HashMap<String, Mutex<()>>,}impl MutexTable { async fn lock<'a>(&'a mut self, key: &str) -> Result<MutexGuard<'a, ()> >{ let mutex: &'a mut Mutex<()> = self.table.entry(key.to_string()).or_insert_with(|| Mutex::new(())); Ok(mutex.lock().await) }}</code></pre><p>通过编译</p><pre><code class="rust">struct MutexTable { table: DashMap<String, Mutex<()>>,}impl MutexTable { async fn lock<'a>(&'a self, key: &str) -> Result<MutexGuard<'a, ()> >{ let lock: dashmap::mapref::one::RefMut<'a, String, Mutex<()>> = self.table .entry(key.to_string()) .or_insert_with(|| Mutex::new(())); let guard: MutexGuard<'a, ()> = lock.lock().await; Ok(guard) }}</code></pre><p>报错信息:</p><pre><code class="bash">error[E0597]: `lock` does not live long enough --> src/main.rs:21:41 |17 | async fn lock<'a>(&'a self, key: &str) -> Result<MutexGuard<'a, ()> >{ | -- lifetime `'a` defined here18 | let lock: dashmap::mapref::one::RefMut<'a, String, Mutex<()>> = self.table | ---- binding `lock` declared here...21 | let guard: MutexGuard<'a, ()> = lock.lock().await; | ------------------ ^^^^ borrowed value does not live long enough | | | type annotation requires that `lock` is borrowed for `'a`22 | Ok(guard)23 | } | - `lock` dropped here while still borrowed</code></pre><p>这下可以确定了,这就是因为 <code>RefMut</code> 包了一层的缘故,因为 <code>RefMut</code> 不等同与引用,所以生命周期推导规则不同,导致了不能返回</p>]]></content:encoded>
<comments>https://dynamic-pigeon.github.io/2025/05/21/%E8%AE%B0%E4%B8%80%E4%B8%AA%E5%9C%A8%E7%BE%A4%E5%86%85%E7%9C%8B%E8%A7%81%E7%9A%84%E5%B0%8F%E4%B8%9C%E8%A5%BF/#disqus_thread</comments>
</item>
<item>
<title>从源码编译安装gcc</title>
<link>https://dynamic-pigeon.github.io/2025/01/27/%E4%BB%8E%E6%BA%90%E7%A0%81%E7%BC%96%E8%AF%91%E5%AE%89%E8%A3%85gcc/</link>
<guid>https://dynamic-pigeon.github.io/2025/01/27/%E4%BB%8E%E6%BA%90%E7%A0%81%E7%BC%96%E8%AF%91%E5%AE%89%E8%A3%85gcc/</guid>
<pubDate>Mon, 27 Jan 2025 10:42:23 GMT</pubDate>
<description>
<p>这是一个个人编译安装 gcc 的存档。</p>
<p>本人操作系统 Debian-12</p>
<h2 id="下载源码以及依赖"><a href="#下载源码以及依赖" class="headerlink" title="下载源码以及依赖"></a>下载源码以及依赖</h
</description>
<content:encoded><![CDATA[<p>这是一个个人编译安装 gcc 的存档。</p><p>本人操作系统 Debian-12</p><h2 id="下载源码以及依赖"><a href="#下载源码以及依赖" class="headerlink" title="下载源码以及依赖"></a>下载源码以及依赖</h2><pre><code class="text">http://gcc.gnu.org/install/ # 官方下载源http://mirrors.nju.edu.cn/gnu/gcc/ # 南京大学源https://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/ # 清华源</code></pre><p>可以选择自己需要的版本进行下载,这里我下载的是 <code>gcc-14.2</code>。</p><pre><code class="bash">wget https://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-14.2.0/gcc-14.2.0.tar.xztar xf gcc-14.2.0.tar.xzcd gcc-14.2.0# 这一步可能需要给执行权限 sudo chmod a+x ./contrib/download_prerequisites# 或者 bash ./contrib/download_prerequisites ./contrib/download_prerequisites </code></pre><h2 id="配置并编译"><a href="#配置并编译" class="headerlink" title="配置并编译"></a>配置并编译</h2><pre><code class="bash">mkdir buildcd build# 安装到默认位置的话不需要使用 --prefix,需要其他语言自行添加../configure --enable-checking=release \ --enable-languages=c,c++ \ --disable-multilib \ --enable-bootstrap \ --prefix=<your-path></code></pre><p>执行完毕后或有成功的提示,如果报错,请根据提示操作。</p><pre><code class="bash"># 这里看你 cpu 有多少核心,我是 13900 所以给了 24 核# 过程中可能会提示两个文件没有执行权限,给权限就行了# 只查看 warning 和 error 使用 make -j 24 > /dev/nullmake -j 24</code></pre><p>大概等待 1~2 小时后就会编译完(看电脑性能)。</p><pre><code class="bash">make install</code></pre><h2 id="配置路径"><a href="#配置路径" class="headerlink" title="配置路径"></a>配置路径</h2><p>使用默认路径的应该可以跳过</p><pre><code class="bash"># 在 ~/.bashrc 中# 替换 <your-path> 为你设置的路径export PATH="<your-path>/gcc-14.2/bin:$PATH"export LD_LIBRARY_PATH="<your-path>/gcc-14.2/lib64:<your-path>/gcc-14.2/lib:$LD_LIBRARY_PATH"</code></pre><p>当然也可以直接换掉原本的 gcc 编译器。</p><pre><code class="bash"># 替换之前最好备份一下本来的编译器# 也可以直接 sudo apt remove g++ gccln -sf <your-path>/bin/gcc /usr/bin/gccln -sf <your-path>/bin/g++ /usr/bin/g++</code></pre><p>现在查看一下版本,应该是 gcc-14.2 了。</p><p>然后你大概会发现 gdb 查看 stl 的功能爆炸了,这时候你可能还需要重新编译一下最新的 gdb(偷笑</p>]]></content:encoded>
<comments>https://dynamic-pigeon.github.io/2025/01/27/%E4%BB%8E%E6%BA%90%E7%A0%81%E7%BC%96%E8%AF%91%E5%AE%89%E8%A3%85gcc/#disqus_thread</comments>
</item>
<item>
<title>Rust 开源操作系统训练营结营报告</title>
<link>https://dynamic-pigeon.github.io/2024/12/22/Rust-%E5%BC%80%E6%BA%90%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E8%AE%AD%E7%BB%83%E8%90%A5%E7%BB%93%E8%90%A5%E6%8A%A5%E5%91%8A/</link>
<guid>https://dynamic-pigeon.github.io/2024/12/22/Rust-%E5%BC%80%E6%BA%90%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E8%AE%AD%E7%BB%83%E8%90%A5%E7%BB%93%E8%90%A5%E6%8A%A5%E5%91%8A/</guid>
<pubDate>Sun, 22 Dec 2024 14:28:34 GMT</pubDate>
<description>
<p>仓库链接:<a href="https://github.com/chy669086/futex">https://github.com/chy669086/futex</a></p>
<p>参与方向:宏内核,posix 接口相关。</p>
<p>我在四阶段中编写的是 fu
</description>
<content:encoded><![CDATA[<p>仓库链接:<a href="https://github.com/chy669086/futex">https://github.com/chy669086/futex</a></p><p>参与方向:宏内核,posix 接口相关。</p><p>我在四阶段中编写的是 futex 有关的代码。</p><h2 id="设计思路"><a href="#设计思路" class="headerlink" title="设计思路"></a>设计思路</h2><p>暂时请求了五个 os 需要实现的接口,分别是</p><ul><li><code>sched_yield</code> 退出当前的任务,不放回调度队列。</li><li><code>translate_vaddr</code> 将当前任务的虚拟地址转换成操作系统可以访问的地址</li><li><code>current_task</code> 取得当前任务</li><li><code>current_prosess_id</code> 取得进程 id</li><li><code>wake</code> 传入一个 <code>FutexQ</code> 类型,唤醒任务(提供了 <code>get_task</code> 函数取得任务)</li></ul><p><code>FutexQ</code> 是存放任务的重要类型,内有 <code>key</code> <code>bitset</code> <code>task</code> 三个字段,其中 <code>key</code> 和 <code>bitset</code> 是用来唤醒任务的重要字段。</p><p><code>FutexKey</code> 是一个枚举,现在只实现了一个 <code>Private</code>,<code>Shared</code> 暂时没有开发的思路。</p><p>任务等待队列存储在 <code>FutexQueues</code> 中,通过一个 <code>futex</code> 的唯一 key 通过哈希变换后放入或唤醒。</p><p>现在实现的调用有:<code>FUTEX_WAIT</code> <code>FUTEX_WAKE</code> <code>FUTEX_REQUEUE</code> <code>FUTEX_CMP_REQUEUE</code> <code>FUTEX_WAKE_OP</code> 以及对应的 bitset 版本</p><p>因为三阶段提供的宏内核中没有合适的线程实现,二阶短的项目不知道什么原因不能编译 <code>link-me</code> 的代码,所以我直接把整个模块删除 <code>linkme</code> 后移植到了阶段二的仓库,并编写测试通过。</p><h2 id="收获"><a href="#收获" class="headerlink" title="收获"></a>收获</h2><p>说实话还是不是很擅长编写 no_std 的代码,所以我还是依赖了很多外部库。</p><p>虽然没有通过最初的设想去适配到任何一个系统里去(直接移植还是太不松耦合了),但是我也花了很多时间去尝试适配,其中阶段二的项目仓库是最接近完成的一个,结果编译错误了,经过测试发现把 <code>futex::syscall::sys_futex</code> 函数调用去掉就可以通过编译,一时间不知道从何改起。转到 <code>arceos</code> 适配的时候,在被迫阅读了大量源码之后,发现提供的宏内核示例压根没有创建线程的系统调用,自己写了半天并没有写出来,所以又放弃了。</p><p>虽然写的挺差的,而且最近也到学校的期末周了,确实有没有太多时间写这个项目了,但是通过这次 posix 接口的编写,我还是学会了不少东西。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>从训练营开始到现在也过去 12 周了,看着自己从对操作系统毫无概念一步步到现在还是很感慨的。感谢老师的辛勤付出,感谢训练营能给我一个这样的平台。</p>]]></content:encoded>
<comments>https://dynamic-pigeon.github.io/2024/12/22/Rust-%E5%BC%80%E6%BA%90%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E8%AE%AD%E7%BB%83%E8%90%A5%E7%BB%93%E8%90%A5%E6%8A%A5%E5%91%8A/#disqus_thread</comments>
</item>
<item>
<title>C++ ADL 简介</title>
<link>https://dynamic-pigeon.github.io/2024/11/20/C-ADL-%E7%AE%80%E4%BB%8B/</link>
<guid>https://dynamic-pigeon.github.io/2024/11/20/C-ADL-%E7%AE%80%E4%BB%8B/</guid>
<pubDate>Wed, 20 Nov 2024 07:49:21 GMT</pubDate>
<description>
<p>前两天水群水到的,突然感觉开发了新大陆。</p>
<h2 id="ADL-简介"><a href="#ADL-简介" class="headerlink" title="ADL 简介"></a>ADL 简介</h2><p>相信很多人应该都写过类似这样的代码</p>
<pre
</description>
<content:encoded><![CDATA[<p>前两天水群水到的,突然感觉开发了新大陆。</p><h2 id="ADL-简介"><a href="#ADL-简介" class="headerlink" title="ADL 简介"></a>ADL 简介</h2><p>相信很多人应该都写过类似这样的代码</p><pre><code class="C++">#include <algorithm>#include <vector>int main() { std::vector<int> a{1, 2, 3, 4}; sort(a.begin(), a.end()); return 0;}</code></pre><p>这里并没有 <code>using namespace std</code> 却可以不用显示指定 <code>std::sort</code> 来调用,应该不止我一个人在第一次写出这个并意识到的时候感到惊讶。</p><p>更重要的是,以下代码会 <code>CE</code></p><pre><code class="C++">#include <algorithm>int main() { int a[10]{}; sort(a, a + 10); return 0;}</code></pre><p><strong>难道是 <code>std::vector</code> 的问题?</strong></p><p>没错,就是因为 <code>std::vector</code>!</p><p><strong>接下来让我来正式介绍一下 ADL(argument-dependent lookup)!</strong></p><p>实参依赖查找(ADL),又称 Koenig 查找,是一组对函数调用表达式(包括对重载运算符的隐式函数调用)中的无限定的函数名进行查找的规则。在通常无限定名字查找所考虑的作用域和命名空间之外,还会在它的各个实参的命名空间中查找这些函数。<a href="#reference1">[1]</a></p><p>简单来说就是,在进行函数调用的时候,先会在当前作用域(名称空间)寻找定义,找不到就会在实参的命名空间中寻找定义,还找不到就会报错。</p><p>在这些调用中,包含类自身,基类,外围类和最内层类的外层命名空间(当然,对于基础类型,如int之类其关联集为空);如果类模板已经特化实参,则还增加对实参类的命名空间及以其为成员的类。</p><div class="admonition note"><p class="admonition-title">注解</p><p>对于更多的细节,请查阅 <a href="https://zh.cppreference.com/w/cpp/language/adl">cppreference.com</a></p></div><p>然后我们就可以写出这样的代码:</p><pre><code class="C++">#include <algorithm>#include <vector>class vec : public std::vector<int> { public: vec() : std::vector<int>() {} vec(std::initializer_list<int> il) : std::vector<int>(il) {}};int main() { vec a{1, 2, 3, 4}; sort(a.begin(), a.end()); return 0;}</code></pre><p><code>sort</code> 调用的时候,发现当前没有定义,然后到实参定义域中找,没有找到,又去他的基类找,基类是 <code>std::vector<int></code>,在 <code>std</code> 名称空间中,于是找到了 <code>std::sort</code> 进行调用。</p><h2 id="ADL-的用途"><a href="#ADL-的用途" class="headerlink" title="ADL 的用途"></a>ADL 的用途</h2><p>通过前面的例子来看,这个东西好像除了增加阅读难度(完全不知道哪里来的函数)之外就没有用了,那么接下来举一个例子:</p><pre><code class="C++">#include <algorithm>#include <iostream>#include <vector> namespace test {class Demo {public: Demo(int a) : a(a) {} int get() const { return a; } friend std::ostream &operator<<(std::ostream &os, const Demo &d) { os << d.a; return os; }private: int a;};} int main() { test::Demo d(114514); std::cout << d << '\n'; return 0;}</code></pre><p><code>std::cout <<</code> 调用的时候通过 ADL 找到了位于 <code>test</code> 名称空间的 <code>std::ostream &operator<<(std::ostream &os, const Demo &d)</code>,大大的简化了代码。</p><p>ADL 允许函数模板在不同名称空间定义,而不需要显示的指定作用域或者全局空间限定。</p><p>再来看一个例子:</p><pre><code class="C++">namespace N1 {struct X {};void f(X);}namespace N2 {void f(N1::X);}void g() { N1::X x; f(x); // 调用 N1::f N2::f(x); // 调用 N2::f using N2::f; f(x); // 错误,Call to 'f' is ambiguous,同时存在了 N1::f 和 N2::f}</code></pre><p>可以发现 <code>using</code> 的函数并不被当作当前作用域,而被当作了与 N1 同级的函数。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>ADL 是 C++ 的一个重要语法概念,不过带来便利的同时也带来的一些不确定性(万一调用调飞了就全毁了),不过这个和允许函数重载是一样的,好用与否,还是在于使用的人。</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><p><span id="reference1"></span><br>[1] <a href="https://zh.cppreference.com/w/cpp/language/adl">https://zh.cppreference.com/w/cpp/language/adl</a> </p>]]></content:encoded>
<comments>https://dynamic-pigeon.github.io/2024/11/20/C-ADL-%E7%AE%80%E4%BB%8B/#disqus_thread</comments>
</item>
<item>
<title>.bashrc 向 .zshrc 迁移的小妙招</title>
<link>https://dynamic-pigeon.github.io/2024/10/11/bashrc-%E5%90%91-zshrc-%E8%BF%81%E7%A7%BB%E7%9A%84%E5%B0%8F%E5%A6%99%E6%8B%9B/</link>
<guid>https://dynamic-pigeon.github.io/2024/10/11/bashrc-%E5%90%91-zshrc-%E8%BF%81%E7%A7%BB%E7%9A%84%E5%B0%8F%E5%A6%99%E6%8B%9B/</guid>
<pubDate>Fri, 11 Oct 2024 09:45:00 GMT</pubDate>
<description>
<p>昨天兴致突发安装了 zsh 并使用了 powerlevel10k 主题。有一说一 zsh 使用体验比 bash 好太多了,但是今天我发现了一个重大的问题:之前的所有配置都在 .bashrc,这些不会自动继承到 .zshrc,这可咋办?而且 zsh 和 bash 命令并不兼容
</description>
<content:encoded><![CDATA[<p>昨天兴致突发安装了 zsh 并使用了 powerlevel10k 主题。有一说一 zsh 使用体验比 bash 好太多了,但是今天我发现了一个重大的问题:之前的所有配置都在 .bashrc,这些不会自动继承到 .zshrc,这可咋办?而且 zsh 和 bash 命令并不兼容,不能简单在 .zshrc 加一句 <code>source $HOME/.bashrc</code> 来解决。</p><p>直接 <code>source $HOME/.bashrc</code> 会出现以下报错</p><pre><code class="shell">/usr/share/bash-completion/bash_completion:45: command not found: shopt/usr/share/bash-completion/bash_completion:1596: parse error near `|'</code></pre><p>一种方法是把所有配置文件都重新迁移到 .zshrc,<del>但是我懒</del>,所以我找到了些小妙招。</p><p>发现在 bash 直接运行 zsh 会让 zsh 继承 bash 的所有环境变量,所以只要想办法让 zsh 启动的时候启动 bash 再用 bash 启动 zsh 就好了。</p><h2 id="解决方法"><a href="#解决方法" class="headerlink" title="解决方法"></a>解决方法</h2><h3 id="手动解决"><a href="#手动解决" class="headerlink" title="手动解决"></a>手动解决</h3><p>一种方法是手动解决</p><pre><code class="shell"># 在 zsh 中执行exec bash# 在 bash 中执行exec zsh</code></pre><h3 id="改写配置文件"><a href="#改写配置文件" class="headerlink" title="改写配置文件"></a>改写配置文件</h3><p>另一种方法是改写 .zshrc 和 .bashrc。</p><p>首先,我们在 .zshrc <strong>头部</strong>加入</p><pre><code class="shell">if [[ -v __USE_ZSH ]]; then unset __USE_ZSHelse echo "ENTER BASH!" export __USE_ZSH=1 exec bashfi</code></pre><p>然后在 .bashrc <strong>底部</strong>加入</p><pre><code class="shell">if [[ $__USE_ZSH -eq 1 ]]; then echo "ENTER ZSH!" exec zshfi</code></pre><p>这两个命令大概就是:进入 zsh 先判断有没有 <code>__USE_ZSH</code> 这变量,有,就 unset 掉,否则创建 <code>__USE_ZSH=1</code> 然后进入 bash,bash 中如果发现 <code>__USE_ZSH==1</code>,就会进入 zsh,就很妙的把 bash 的环境变量偷过来了。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><a href="https://blog.csdn.net/qq_49030008/article/details/137921205">https://blog.csdn.net/qq_49030008/article/details/137921205</a></p>]]></content:encoded>
<comments>https://dynamic-pigeon.github.io/2024/10/11/bashrc-%E5%90%91-zshrc-%E8%BF%81%E7%A7%BB%E7%9A%84%E5%B0%8F%E5%A6%99%E6%8B%9B/#disqus_thread</comments>
</item>
<item>
<title>2024 秋冬季开源操作系统训练营学习笔记</title>
<link>https://dynamic-pigeon.github.io/2024/10/08/2024-%E7%A7%8B%E5%86%AC%E5%AD%A3%E5%BC%80%E6%BA%90%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E8%AE%AD%E7%BB%83%E8%90%A5%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</link>
<guid>https://dynamic-pigeon.github.io/2024/10/08/2024-%E7%A7%8B%E5%86%AC%E5%AD%A3%E5%BC%80%E6%BA%90%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E8%AE%AD%E7%BB%83%E8%90%A5%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</guid>
<pubDate>Tue, 08 Oct 2024 13:07:47 GMT</pubDate>
<description>
个人学习笔记
</description>
<content:encoded><![CDATA[<p>个人 Rust 开源训练营学习笔记(感觉更像日记),大概会隔一段时间更新一次</p><h2 id="2024-11-23"><a href="#2024-11-23" class="headerlink" title="2024-11-23"></a>2024-11-23</h2><div class='collapse'> <div class='collapse-ctrl collapse-cardtitle' onclick='collapseToggle(this)'><div class='collapse-hint-ico'>></div><span class='collapse-hint-show'>展开</span><span class='collapse-hint-hide'>隐藏</span></div> <div class='collapse-content'><div class='content-flex collapse-cardborder'><div style='width:100%'> <p><del>快一个月没有更新了</del></p><p>今天又回去看了地址空间的知识。之前还是学的不扎实,昨天写阶段三的练习的时候又在想操作系统是怎么管理虚拟地址和物理地址的映射的。现在搞懂了,记录一下。</p><p>risc-v 在开启 mmu 之后所有地址访问都是虚拟地址,操作系统为了管理所有的内存,在开启 mmu 之前,需要提前建立一个虚拟地址到物理地址的映射。在 arceos 中是建立了一个零偏移的的虚拟地址到物理地址的映射(才知道地址可以被重复映射),初始化映射表之后,就可以进入 mmu 模式,之后就可以启动应用程序进入用户态了。</p><p>arceos 内核和应用用了两张页表,也就说每次 trap 的时候都需要刷一下 TLB 来保证地址访问正确。</p><p>每个进程申请内存的时候,操作系统都会从剩余物理空间中找一些空间来映射,虚拟地址的连续不代表物理地址的连续,也就是跨页的数据在物理上不一定是连续的(感觉好色)。</p><p>操作系统访问用户地址只需要找到用户的页表(前面整个内存都已经被操作系统映射了,所以操作系统可以访问所有的内存),并根据虚拟页表找到对应的物理页表,就可以直接访问(arceos 因为建立了零偏移的映射,所以不需要下一步转换,如果不是零偏移的转换需要再进行转换)。</p></div></div> </div> </div><h2 id="2024-10-27"><a href="#2024-10-27" class="headerlink" title="2024-10-27"></a>2024-10-27</h2><div class='collapse'> <div class='collapse-ctrl collapse-cardtitle' onclick='collapseToggle(this)'><div class='collapse-hint-ico'>></div><span class='collapse-hint-show'>展开</span><span class='collapse-hint-hide'>隐藏</span></div> <div class='collapse-content'><div class='content-flex collapse-cardborder'><div style='width:100%'> <p>ch8 在 10-21 已经写完了,昨天又花了一些时间重构了一下,这个训练营无疑大大提高了我的编码能力和阅读能力。</p><p>在和群友的热烈讨论下,我也学习到了很多之前不怎么清楚的东西,但是还有很多不太懂的东西。</p><p>很期待三阶段的学习!</p></div></div> </div> </div><h2 id="2024-10-18"><a href="#2024-10-18" class="headerlink" title="2024-10-18"></a>2024-10-18</h2><div class='collapse'> <div class='collapse-ctrl collapse-cardtitle' onclick='collapseToggle(this)'><div class='collapse-hint-ico'>></div><span class='collapse-hint-show'>展开</span><span class='collapse-hint-hide'>隐藏</span></div> <div class='collapse-content'><div class='content-flex collapse-cardborder'><div style='width:100%'> <p><em><strong>ch6 简直噩梦</strong></em></p><p>感觉写的东西问题很大,也没有完全搞懂文件系统和内存系统,就是照葫芦画瓢,我的文件系统是有严重的泄露问题的,<del>但是不会改</del></p><p>今天一口气写了很多垃圾代码,然后还包括一个死锁(同志们调函数的时候一定要注意这个函数要不要持有锁)。</p><p>写好一个项目真是一门艺术。我 ch3 的代码在 ch4 ch5 ch6 分别重构了一遍,现在是我比较满意的一版。但是其他的我感觉写的就很怪,谁家结构体数据全部 pub 啊()</p><p>最近都没有去训练,都去写 os camp 了,现在只剩下最后一个要写代码的实验了,感觉晋级在望!感觉在这个训练营学到的真的很多,本来对操作系统只是一知半解,现在经过浅层的学习,了解到了很多以前只知道名词的概念,也对操作系统的数据组织形式有了更深的了解,同时也解开了以前对汇编的疑问。以前在想为什么汇编要有操作系统作为链接,一直以为汇编是和机器绑定的,是机器码。学习了之后才知道,原来系统中断是操作系统提供的,而且寻址也是被操作系统调整过的,操作系统调度了所有程序的运行,所有程序最终都是回归到操作系统中去的。</p></div></div> </div> </div><h2 id="2024-10-12"><a href="#2024-10-12" class="headerlink" title="2024-10-12"></a>2024-10-12</h2><div class='collapse'> <div class='collapse-ctrl collapse-cardtitle' onclick='collapseToggle(this)'><div class='collapse-hint-ico'>></div><span class='collapse-hint-show'>展开</span><span class='collapse-hint-hide'>隐藏</span></div> <div class='collapse-content'><div class='content-flex collapse-cardborder'><div style='width:100%'> <p>ch3 在昨天看了一天才看懂要干什么<del>我好菜啊</del></p><p>ch3 写完后本地单独测试都能过,但是一起测试就会起飞,debug 后发现 <code>get_time()</code> 函数一直输出的是 0。群内老哥说应该是 sbi 的问题,我也不懂我也不知道啊(x</p><p>然后我就重构了一下,我然后就写了一个很<del>弱智</del>的代码,重现一下大概是这个感觉:</p><pre><code class="rust">use std::cell::RefCell;fn main() { let task = Task::new(1); println!("Task id: {}", task.get_id());}struct TaskInner { arr: [u32; 10], id: usize,}struct Task { inner: RefCell<TaskInner>,}impl Task { fn get_id(&self) -> u32 { self.inner.borrow_mut().arr[self.inner.borrow_mut().id] } fn new(id: u32) -> Task { Task { inner: RefCell::new(TaskInner { arr: [id; 10], id: 0, }), } }}</code></pre><p><code>self.inner.borrow_mut().arr[self.inner.borrow_mut().id]</code> 直接借用两次直接起飞(</p><p><strong>警钟撅烂</strong></p><p>ch3 我的实现十分的垃圾,感觉性能完全不行(</p><p>ch4 直接告诉我要重写(</p><p><del>杀了我算了</del></p></div></div> </div> </div><h2 id="2024-10-9"><a href="#2024-10-9" class="headerlink" title="2024-10-9"></a>2024-10-9</h2><div class='collapse'> <div class='collapse-ctrl collapse-cardtitle' onclick='collapseToggle(this)'><div class='collapse-hint-ico'>></div><span class='collapse-hint-show'>展开</span><span class='collapse-hint-hide'>隐藏</span></div> <div class='collapse-content'><div class='content-flex collapse-cardborder'><div style='width:100%'> <p>一上来第一件事就是抛开了 rust 标准库。我应该能想到的,毕竟标准库需要依赖系统,但是 OS Camp 要开发一个系统。这下轮子都要自己造了。</p><p><del>我不会 RISV-V 汇编啊!</del></p><p>训练营用的是 Qemu7.0 模拟器模拟环境。rust 编译使用 <code>riscv64gc-unknown-none-elf</code> 编译环境</p><div class="admonition note"><p class="admonition-title">注解</p><p>riscv64gc-unknown-none-elf 的 CPU 架构是 riscv64gc,厂商是 unknown,操作系统是 none, elf 表示没有标准的运行时库。没有任何系统调用的封装支持,但可以生成 ELF 格式的执行程序。 我们不选择有 linux-gnu 支持的 riscv64gc-unknown-linux-gnu,是因为我们的目标是开发操作系统内核,而非在 linux 系统上运行的应用程序。</p></div><p>在加入编译命令 <code>riscv64gc-unknown-none-elf</code> 后再在 <code>main.rs</code> 加入 <code>#![no_std]</code> 后,如果在 vsc 使用 rust-analyzer 的时候会出现 <code>Can't find crete test</code> 的报错,但是程序又是没有问题的,这是一个 bug,官方 <a href="https://github.com/rust-lang/vscode-rust/issues/729">issue</a>。</p><p>只要在 vsc 的 settings.json 加入以下内容就行了</p><pre><code class="json">{ "rust-analyzer.checkOnSave.allTargets": false, "rust-analyzer.checkOnSave.extraArgs": [ "--target", "riscv64gc-unknown-none-elf" ]}</code></pre><p>以及训练营给出的解答:</p><pre><code class="json">{ // Prevent "can't find crate for `test`" error on no_std // Ref: https://github.com/rust-lang/vscode-rust/issues/729 // For vscode-rust plugin users: "rust.target": "riscv64gc-unknown-none-elf", "rust.all_targets": false, // For Rust Analyzer plugin users: "rust-analyzer.cargo.target": "riscv64gc-unknown-none-elf", "rust-analyzer.checkOnSave.allTargets": false, // "rust-analyzer.cargo.features": [ // "board_qemu" // ]}</code></pre><p>实验是在裸机平台上进行的(这不是放屁吗,os 还能在哪里跑)。</p><p>看见那些中断和汇编调用,我已经要去世了</p><p><strong>api 与 abi 的区别</strong></p><p>说真的之前都不知道有 abi 这个东西。</p><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>API</td><td>Application Programming Interface</td></tr><tr><td>ABI</td><td>Application Binary Interface</td></tr></tbody></table><p>简单来说,ABI 就是二进制接口,来自系统底层,所有程序都可以通过遵守 ABI 来调用;API 一般局限在一个编程语言,定义了源码级别的操作。</p><div class="admonition info"><p class="admonition-title">训练营给的解释</p><p><strong>API 与 ABI 的区别</strong>应用程序二进制接口 ABI 是不同二进制代码片段的连接纽带。ABI 定义了二进制机器代码级别的规则,主要包括基本数据类型、通用寄存器的使用、参数的传递规则、以及堆栈的使用等等。ABI 与处理器和内存地址等硬件架构相关,是用来约束链接器 (Linker) 和汇编器 (Assembler) 的。在同一处理器下,基于不同高级语言编写的应用程序、库和操作系统,如果遵循同样的 ABI 定义,那么它们就能正确链接和执行。应用程序编程接口 API 是不同源代码片段的连接纽带。API 定义了一个源码级(如 C 语言)函数的参数,参数的类型,函数的返回值等。因此 API 是用来约束编译器 (Compiler) 的:一个 API 是给编译器的一些指令,它规定了源代码可以做以及不可以做哪些事。API 与编程语言相关,如 libc 是基于 C 语言编写的标准库,那么基于 C 的应用程序就可以通过编译器建立与 libc 的联系,并能在运行中正确访问 libc 中的函数。<a href="https://rcore-os.cn/rCore-Tutorial-Book-v3/chapter0/2os-interface.html#apiabi">link</a></p></div></div></div> </div> </div><h2 id="2024-10-8"><a href="#2024-10-8" class="headerlink" title="2024-10-8"></a>2024-10-8</h2><div class='collapse'> <div class='collapse-ctrl collapse-cardtitle' onclick='collapseToggle(this)'><div class='collapse-hint-ico'>></div><span class='collapse-hint-show'>展开</span><span class='collapse-hint-hide'>隐藏</span></div> <div class='collapse-content'><div class='content-flex collapse-cardborder'><div style='width:100%'> <p>上学期第一次了解到 rust 语言。第一次听到 rust 是来自他的外号“语言神”(原神启动!),以及了解到 rust 的最大特色:<strong>编译错误</strong>。</p><p>学习了一段时间后,感觉 rust 是真**的优雅,所有权、生命周期、借用规则让我醍醐灌顶。也让我把这套规则移动到了其他语言使用,也是大大加速了我的编写速度。</p><p>学习阶段去写了 <a href="https://github.com/rust-lang/rustlings">rustling</a>、<a href="https://github.com/skyzh/mini-lsm">mini-lsm</a> 开源学习项目(mini-lsm week3 开头给我干爆炸了就没有继续写下去)。</p><p>感觉还是不是很深得 rust 开发的精髓。写起来 C 里 C 气。感觉取悦编译器也是一门技术活(最经典的 rust 序列访问会生成 assert,所以可以提前 assert 减少 assert 次数)。</p><p><del>cargo 是世界上最好的包管理器!</del></p><p>感谢 <a href="https://course.rs/about-book.html">Rust语言圣经(Rust Course)</a>、<a href="https://www.rustwiki.org.cn/zh-CN/book/">Rust 程序设计语言</a> 和其他网络上的作者贡献的大量的学习资源。</p><p>现开源训练营一阶段晋级。(牛魔的 unsafe 链表真是依托)</p><p><del>顺便吐槽一下 C++ 那依托的 tuple</del></p></div></div> </div> </div>]]></content:encoded>
<comments>https://dynamic-pigeon.github.io/2024/10/08/2024-%E7%A7%8B%E5%86%AC%E5%AD%A3%E5%BC%80%E6%BA%90%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E8%AE%AD%E7%BB%83%E8%90%A5%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/#disqus_thread</comments>
</item>
<item>
<title>C++ CRTP 简介</title>
<link>https://dynamic-pigeon.github.io/2024/10/05/C-CRTP-%E7%AE%80%E4%BB%8B/</link>
<guid>https://dynamic-pigeon.github.io/2024/10/05/C-CRTP-%E7%AE%80%E4%BB%8B/</guid>
<pubDate>Sat, 05 Oct 2024 14:08:24 GMT</pubDate>
<description>
<p>最近看群友讨论到了“虚函数就是历史遗留问题,现在完全可以用 CRTP 代替”,好奇,就去搜索了什么是 CRTP,结果发现这么早的一个技术我现在才知道,真是落后时代 10 余年。</p>
<h2 id="虚函数与动态链接"><a href="#虚函数与动态链接" class=
</description>
<content:encoded><![CDATA[<p>最近看群友讨论到了“虚函数就是历史遗留问题,现在完全可以用 CRTP 代替”,好奇,就去搜索了什么是 CRTP,结果发现这么早的一个技术我现在才知道,真是落后时代 10 余年。</p><h2 id="虚函数与动态链接"><a href="#虚函数与动态链接" class="headerlink" title="虚函数与动态链接"></a>虚函数与动态链接</h2><p>C++ 的多态是通过虚函数实现的,虚函数的实质是在对象中开辟了一个空间来存储函数指针(虚函数表 <code>vtable</code>),调用这个函数是通过这个指针去访问,而不能在编译期确定。</p><p>比如:</p><pre><code class="c++">class Animal {public: virtual void jump() = 0;};class Cat : public Animal {public: void jump() override { std::cout << "cat jump\n"; }};int main() { Animal *p = new Cat; p->jump(); return 0;}</code></pre><p>调用 <code>p->jump()</code> 的时候,程序会去虚函数表寻找那个指针,在这个程序中最终会调用 <code>Cat::jump()</code>。</p><p>但是查表终究是有代价的,有没有兼顾多态和效率的做法呢,于是,CRTP(Curiously Recurring Template Pattern) 应运而生。</p><h2 id="通过模板实现静态绑定"><a href="#通过模板实现静态绑定" class="headerlink" title="通过模板实现静态绑定"></a>通过模板实现静态绑定</h2><p>首先复习一个概念,C++ 的模板是编译期多态,是<strong>零成本抽象</strong>。我们在子类和父类中定义同一个函数,但是不声明为虚函数,通过编译期多态来实现绑定。</p><pre><code class="c++">template <typename T>class Base {public: void show() const { static_cast<const T*>(this)->show(); }};class Driver : public Base<Driver> {public: void show() const { std::cout << "This is class Driver." << std::endl; }};int main() { Base<Driver> *p = new Driver; p->show(); return 0;}</code></pre><p>这是一个简单的例子,有下列特点</p><ul><li>基类是模板类,用来接收子类的名字,因此继承类似于 <code>ClassName : public Base<ClassName></code>。</li><li>基类的函数中,用 <code>static_cast<></code> 将基类的指针转换成子类的指针实现绑定。</li></ul><p>运行上述程序,得到结果 <code>This is class Driver.</code> 说明我们成功进行了子类与父类的绑定,实打实的调用了子类的 <code>show()</code> 函数。</p><h2 id="有什么用,怎么用"><a href="#有什么用,怎么用" class="headerlink" title="有什么用,怎么用"></a>有什么用,怎么用</h2><p>众所周知,多态一般需要类似 <code>std::vector<Base*></code> 的形式,但是由于我们的基类是模板类,不能通过这种方式多态,不如说,没有多态,因为 <code>Animal<Cat>*</code> 和 <code>Animal<Dog>*</code> 是两个完全不同的指针。</p><p><em><strong>那怎么办?</strong></em></p><p>考虑到一次查表的开销不是特别大,虚函数带来的大量开销主要是多层继承带来的巨大链条带来的查询开销,继承链很短的话开销其实是在预期内的。</p><p>那我们用一个模板基类继承一个普通基类,这个普通基类使用虚函数,但是模板基类使用静态绑定,派生类去继承这个模板基类,那么不论如何,我们都只需要查一次表就可以定位到对应的函数,大大降低了多次继承带来的开销。</p><pre><code class="c++">class Animal {public: virtual void say() const = 0; virtual ~Animal() {}};template <typename T>class Animal_CRTP : public Animal {public: void say() const override { static_cast<const T*>(this)->say(); }};class Cat : public Animal_CRTP<Cat> {public: void say() const { std::cout << "I'm a cat.\n"; }};class Dog : public Animal_CRTP<Dog> {public: void say() const { std::cout << "I'm a dog.\n"; }};int main() { std::vector<Animal*> v; v.push_back(new Dog); v.push_back(new Cat); for (auto x : v) { x->say(); } return 0;}</code></pre><p>输出结果</p><pre><code class="test">I'm a dog.I'm a cat.</code></pre><p>不过写起来确实很麻烦,而且没有 override 带来的部分编译提示(字打错了跑哪里哭去),但是这样写我们同时赢得了多态和效率(同时代码量极致膨胀)。</p><p><del>为什么有人还在用继承啊</del></p><h2 id="2024-11-27-ADD"><a href="#2024-11-27-ADD" class="headerlink" title="2024-11-27 ADD"></a>2024-11-27 ADD</h2><p>C++23 带来了一个神奇的 <code>this auto &&self</code> 语法, CRTP 于是有了一个神奇的写法</p><pre><code class="c++">#include <cstdio>struct Base { void name(this auto &&self) { self.impl(); } };struct D1 : public Base { void impl() { std::puts("D1::impl()"); } };struct D2 : public Base { void impl() { std::puts("D2::impl()"); } }; int main(){ D1 d1; d1.name(); D2 d2; d2.name();}</code></pre><p>C++ 标准有点过于色琴了</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><p>[1] <a href="https://en.cppreference.com/w/cpp/language/crtp">https://en.cppreference.com/w/cpp/language/crtp</a></p>]]></content:encoded>
<comments>https://dynamic-pigeon.github.io/2024/10/05/C-CRTP-%E7%AE%80%E4%BB%8B/#disqus_thread</comments>
</item>
<item>
<title>2022 ICPC 杭州区域赛 K 题题解</title>
<link>https://dynamic-pigeon.github.io/2024/10/02/2022-ICPC-%E6%9D%AD%E5%B7%9E%E5%8C%BA%E5%9F%9F%E8%B5%9B-K-%E9%A2%98%E9%A2%98%E8%A7%A3/</link>
<guid>https://dynamic-pigeon.github.io/2024/10/02/2022-ICPC-%E6%9D%AD%E5%B7%9E%E5%8C%BA%E5%9F%9F%E8%B5%9B-K-%E9%A2%98%E9%A2%98%E8%A7%A3/</guid>
<pubDate>Wed, 02 Oct 2024 09:01:08 GMT</pubDate>
<description>
<h2 id="题目大意"><a href="#题目大意" class="headerlink" title="题目大意"></a>题目大意</h2><p>给你 $n$ 个字符串,然后有 $q$ 次询问,每次询问给出一个字典序,请你输出在这个字典序下的逆序对个数。</p>
<h2
</description>
<content:encoded><![CDATA[<h2 id="题目大意"><a href="#题目大意" class="headerlink" title="题目大意"></a>题目大意</h2><p>给你 $n$ 个字符串,然后有 $q$ 次询问,每次询问给出一个字典序,请你输出在这个字典序下的逆序对个数。</p><h2 id="题解"><a href="#题解" class="headerlink" title="题解"></a>题解</h2><p>首先考虑暴力,每次询问可以 $O(|S|\log(n))$ 处理,显然会超时。</p><p>考虑优化,注意到两个字符串比较出结果一定是在某个位置发生的(称这个比出大小的比较为<strong>决定性比较</strong>),这两个字符串一定有一个相同的前缀(长度可能是 0)。那么可以把问题转换成判断某个字符和其他字符发生了几次<strong>决定性</strong>的的比较,那么对于每次询问我们 $O(26 \times 26)$ 处理。</p><p>如何找到发生了几次决定性的比较呢,注意前面的一个性质,决定性比较之前的两个字符串一定是同前缀,这正好撞上了字典树的性质:相同前缀共用一条链。</p><p>我们只要统计每个节点的后继节点字符总数,就可以对每一个新插入的字符串进行决定性比较的统计。对于 <code>a</code> 和 <code>aa</code> 这种串,我们可以简单在每个串后面加一个字符,并人为将这个字符的字典序设置为最小</p><pre><code class="c++">// 字段树节点struct node { // 后继节点位置 std::array<int, 27> nxt; // 后继节点个数统计 std::array<int, 27> sum; node() { std::fill(all(nxt), -1); std::fill(all(sum), 0); }};// 决定性比较次数统计int cnt[27][27];struct Trie { std::vector<node> nodes; int root = 0; Trie() { nodes.emplace_back(); } void insert(const std::string &s) { int cur = root; for (char c : s) { if (nodes[cur].nxt[c - 'a'] == -1) { nodes[cur].nxt[c - 'a'] = nodes.size(); nodes.emplace_back(); } // 统计这个串的这个位置决定性比较次数 for (int i = 0; i < 27; i++) { cnt[c - 'a'][i] += nodes[cur].sum[i]; } // 后继节点计数加一 nodes[cur].sum[c - 'a']++; cur = nodes[cur].nxt[c - 'a']; } }};</code></pre><p>全部代码</p><pre><code class="c++">#include <algorithm>#include <array>#include <cassert>#include <cctype>#include <cmath>#include <cstdint>#include <cstdio>#include <cstring>#include <iostream>#include <ostream>#include <string>#include <vector>#ifdef DYNAMIC_PIGEON#include "algo/debug.h"#else#define debug(...) 114514#endif#define Dynamic_Pigeon 0using i64 = std::int64_t;using u64 = std::uint64_t;using u32 = std::uint32_t;constexpr i64 MOD = i64(1e9) + 7;constexpr int INF = 1e9;constexpr i64 INF_LONG_LONG = 1e18;#define all(a) a.begin(), a.end()#define rall(a) a.rbegin(), a.rend()#define int i64struct node { std::array<int, 27> nxt; std::array<int, 27> sum; node() { std::fill(all(nxt), -1); std::fill(all(sum), 0); }};int cnt[27][27];struct Trie { std::vector<node> nodes; int root = 0; Trie() { nodes.emplace_back(); } void insert(const std::string &s) { int cur = root; for (char c : s) { if (nodes[cur].nxt[c - 'a'] == -1) { nodes[cur].nxt[c - 'a'] = nodes.size(); nodes.emplace_back(); } for (int i = 0; i < 27; i++) { cnt[c - 'a'][i] += nodes[cur].sum[i]; } nodes[cur].sum[c - 'a']++; cur = nodes[cur].nxt[c - 'a']; } }};void tizzytyt_SuKi() { int n, q; std::cin >> n >> q; Trie trie; for (int i = 0; i < n; i++) { std::string s; std::cin >> s; s.push_back('a' + 26); trie.insert(s); } while (q--) { std::string s; std::cin >> s; s = (char)('a' + 26) + s; i64 ans = 0; for (int i = 0; i < 26; i++) { for (int j = i + 1; j < 27; j++) { ans += cnt[s[i] - 'a'][s[j] - 'a']; } } std::cout << ans << '\n'; }}signed main() {#ifdef DYNAMIC_PIGEON freopen("input.in", "r", stdin);#else std::cin.tie(0)->sync_with_stdio(0);#endif tizzytyt_SuKi(); return Dynamic_Pigeon;}</code></pre>]]></content:encoded>
<comments>https://dynamic-pigeon.github.io/2024/10/02/2022-ICPC-%E6%9D%AD%E5%B7%9E%E5%8C%BA%E5%9F%9F%E8%B5%9B-K-%E9%A2%98%E9%A2%98%E8%A7%A3/#disqus_thread</comments>
</item>
<item>
<title>C++ search 函数</title>
<link>https://dynamic-pigeon.github.io/2024/10/01/C-search-%E5%87%BD%E6%95%B0/</link>
<guid>https://dynamic-pigeon.github.io/2024/10/01/C-search-%E5%87%BD%E6%95%B0/</guid>
<pubDate>Tue, 01 Oct 2024 12:34:15 GMT</pubDate>
<description>
<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>std::search 函数在 C++ 很早的版本就开始存在了,但是在 C++17 中添加了一些重载和辅助类,这大大提高了这个函数的使用价值
</description>
<content:encoded><![CDATA[<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>std::search 函数在 C++ 很早的版本就开始存在了,但是在 C++17 中添加了一些重载和辅助类,这大大提高了这个函数的使用价值。这篇文章主要来介绍下面这个重载。</p><pre><code class="c++">template< class ForwardIt, class Searcher >ForwardIt search( ForwardIt first, ForwardIt last, const Searcher& searcher );</code></pre><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><strong>元素名称</strong></td><td><strong>元素意义</strong></td></tr><tr><td>first, last</td><td>要检验的元素范围 —— 一般是迭代器</td></tr><tr><td>searcher</td><td>封装搜索算法和搜索模式的搜索器</td></tr></tbody></table><p>这是从 C++17 开始给 std::search 添加的函数重载,Searcher 是搜索器,接下来就是这个函数最有用的地方了,C++ std 提供了三种搜索器:</p><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>default_searcher (C++17)</td><td>标准 C++ 库搜索算法实现</td></tr><tr><td>boyer_moore_searcher (C++17)</td><td>Boyer-Moore 搜索算法实现</td></tr><tr><td>boyer_moore_horspool_searcher (C++17)</td><td>Boyer-Moore-Horspool 搜索算法实现</td></tr></tbody></table><p>注意到标准库提供了 BM 算法的搜索器,BM 算法一直被称为最快的字符串搜索算法,在实际表现中一般比 KMP 快 2~3 倍(暴力算法在大部分时候其实表现也比 KMP 好)。但是由于 BM 算法的最坏时间复杂度是 $O(nm)$,所以不建议在算法竞赛中使用,但是在生产环境中,BM 算法无疑是最佳选择。</p><h2 id="使用举例"><a href="#使用举例" class="headerlink" title="使用举例"></a>使用举例</h2><pre><code class="c++">#include <functional>#include <iostream>#include <string>int main() { std::string s = "never"; // 用 s 创建一个搜索器 std::boyer_moore_searcher searcher(s.begin(), s.end()); std::string text = "never say never"; // 在 text 中搜索 s auto result = std::search(text.begin(), text.end(), searcher); if (result != text.end()) { std::cout << "The text contains the substring \"never\" at position " << result - text.begin() << '\n'; } else { std::cout << "The text does not contain the substring \"never\"\n"; } // 在另一个文本中搜索 s text = "always say always"; result = std::search(text.begin(), text.end(), searcher); if (result != text.end()) { std::cout << "The text contains the substring \"never\" at position " << result - text.begin() << '\n'; } else { std::cout << "The text does not contain the substring \"never\"\n"; } return 0;}</code></pre><p>输出结果</p><pre><code class="text">The text contains the substring "never" at position 0The text does not contain the substring "never"</code></pre><h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><p><a href="https://zh.cppreference.com/w/cpp/algorithm/search">cppreferrence.com</a></p>]]></content:encoded>
<comments>https://dynamic-pigeon.github.io/2024/10/01/C-search-%E5%87%BD%E6%95%B0/#disqus_thread</comments>
</item>
</channel>
</rss>