<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>H1S97X&apos;s Blog</title><description>记录学习、技术与生活</description><link>https://h1s97x.github.io/</link><language>zh_CN</language><item><title>一文搞懂 SSR、SSG、CSR：前端渲染技术到底差在哪</title><link>https://h1s97x.github.io/posts/blog/%E4%B8%80%E6%96%87%E6%90%9E%E6%87%82ssr-ssg-csr%E5%89%8D%E7%AB%AF%E6%B8%B2%E6%9F%93%E6%8A%80%E6%9C%AF%E6%B7%B1%E6%8C%96%E7%89%88/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/blog/%E4%B8%80%E6%96%87%E6%90%9E%E6%87%82ssr-ssg-csr%E5%89%8D%E7%AB%AF%E6%B8%B2%E6%9F%93%E6%8A%80%E6%9C%AF%E6%B7%B1%E6%8C%96%E7%89%88/</guid><description>梳理 CSR / SSR / SSG 三种渲染模式的本质差异，建立统一认知框架，并补全群岛架构、ISR、Hybrid 等现代演化与选型思路。</description><pubDate>Wed, 14 Oct 2026 06:00:00 GMT</pubDate><content:encoded>&lt;p&gt;CSR、SSR、SSG 是前端渲染的三种基本模式。名字好背，但真要选型不迷糊，得先想清一件事：&lt;strong&gt;HTML 到底由谁、在什么时候生成&lt;/strong&gt;。抓住这一条，所有差异都自洽了。&lt;/p&gt;
&lt;h2&gt;〇、统一框架：HTML 在“哪一端、什么时刻”生成&lt;/h2&gt;
&lt;p&gt;三种技术本质不是“谁快谁慢”，而是同一份 HTML 由谁、在何时拼出来：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;渲染方式&lt;/th&gt;
&lt;th&gt;HTML 生成时机&lt;/th&gt;
&lt;th&gt;生成地点&lt;/th&gt;
&lt;th&gt;浏览器拿到什么&lt;/th&gt;
&lt;th&gt;交互能力&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CSR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;客户端运行时&lt;/td&gt;
&lt;td&gt;用户浏览器&lt;/td&gt;
&lt;td&gt;空壳 HTML + 大 JS 包&lt;/td&gt;
&lt;td&gt;完整（JS 全权负责）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;每次请求时&lt;/td&gt;
&lt;td&gt;服务器&lt;/td&gt;
&lt;td&gt;完整 HTML（仍需 JS 水合）&lt;/td&gt;
&lt;td&gt;水合后完整&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSG&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;构建时（一次）&lt;/td&gt;
&lt;td&gt;CI / 构建机&lt;/td&gt;
&lt;td&gt;现成静态 HTML&lt;/td&gt;
&lt;td&gt;默认无，按需加&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;一句话记忆：&lt;strong&gt;CSR 是“浏览器现做”，SSR 是“服务器现炒”，SSG 是“提前做好端上来”&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;一、CSR（客户端渲染）&lt;/h2&gt;
&lt;h3&gt;原理&lt;/h3&gt;
&lt;p&gt;服务器发一个几乎为空的 HTML（带一个挂载点和一大包 JS）。浏览器下载并执行 JS 后，框架才把组件树渲染进 DOM。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { createRoot } from &apos;react-dom/client&apos;;
import App from &apos;./App&apos;;

createRoot(document.getElementById(&apos;root&apos;)).render(&amp;lt;App /&amp;gt;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这一步执行之前，页面是白的。&lt;/p&gt;
&lt;h3&gt;常见误区&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;“CSR 响应速度快”要分两层看：HTML 文件本身小、秒下，但&lt;strong&gt;真正的首屏内容要等 JS 下载 + 解析 + 执行&lt;/strong&gt;，所以白屏反而长。准确说：部署简单、交互强，首屏与 SEO 吃亏。&lt;/li&gt;
&lt;li&gt;白屏根因：关键渲染路径被 JS 阻塞。&lt;/li&gt;
&lt;li&gt;SEO：Google 能执行 JS 后抓取，但有延迟与配额；对其他引擎不友好。内容型站点不宜押宝 CSR 的 SEO。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;适用&lt;/h3&gt;
&lt;p&gt;重交互的 SPA（后台、仪表盘）、不需被收录的内部工具。&lt;/p&gt;
&lt;h2&gt;二、SSR（服务器端渲染）&lt;/h2&gt;
&lt;h3&gt;原理&lt;/h3&gt;
&lt;p&gt;请求到服务器 → Node 跑框架的服务端渲染 API 把组件转成 HTML 流 → 流式返回 → 浏览器先看内容，再由同款 JS 水合成可交互。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { renderToPipeableStream } from &apos;react-dom/server&apos;;
import App from &apos;./App&apos;;

const stream = renderToPipeableStream(&amp;lt;App /&amp;gt;, {
  bootstrapScripts: [&apos;/client.js&apos;],
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;要点与坑&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;SSR 返回的 HTML 只是“先有内容”，交互仍需客户端 JS 水合，否则按钮点了没反应。&lt;/li&gt;
&lt;li&gt;服务器压力可缓解：把渲染结果缓存到 CDN / 边缘节点，命中缓存时和 SSG 一样快。&lt;/li&gt;
&lt;li&gt;服务端代码&lt;strong&gt;不能访问 &lt;code&gt;window&lt;/code&gt; / &lt;code&gt;document&lt;/code&gt; / &lt;code&gt;localStorage&lt;/code&gt;&lt;/strong&gt;，这类逻辑必须放进 &lt;code&gt;useEffect&lt;/code&gt; 或动态导入，否则在 Node 端拿到 &lt;code&gt;undefined&lt;/code&gt; 甚至崩。&lt;/li&gt;
&lt;li&gt;高频坑：&lt;strong&gt;水合不匹配（hydration mismatch）&lt;/strong&gt;——服务端和客户端首屏渲染不一致（时间、随机数），React 会报警并可能整段重渲染。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;适用&lt;/h3&gt;
&lt;p&gt;首屏内容重要 + 需要个性化/实时数据（电商详情、社交动态、登录态页）。&lt;/p&gt;
&lt;h2&gt;三、SSG（静态站点生成）&lt;/h2&gt;
&lt;h3&gt;原理&lt;/h3&gt;
&lt;p&gt;构建期把“模板 + 数据”渲染成静态 HTML，部署后 CDN 直接返回。请求时无服务端计算，最快、最安全。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export async function getStaticPaths() {
  const posts = await getCollection(&apos;posts&apos;);
  return posts.map((p) =&amp;gt; ({ params: { slug: p.slug } }));
}
const { slug } = Astro.params;
const post = await getCollection(&apos;posts&apos;, (p) =&amp;gt; p.slug === slug);
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;article&amp;gt;{post.data.title}&amp;lt;/article&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;常见误解&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;“每次更新都要重建并部署整站”过时了。现代 SSG（Astro / Hugo）有增量构建与缓存，只重渲变化页；配合 CDN 失效，更新成本远低于“重建整站”。&lt;/li&gt;
&lt;li&gt;“动态性受限”不绝对。SSG 页面默认静态 HTML，但可叠加客户端 JS 或群岛架构，需要交互的局部才 hydrate。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;适用&lt;/h3&gt;
&lt;p&gt;内容更新不频繁、对性能/安全/SEO 敏感的站点：博客、文档、营销页、官网。&lt;/p&gt;
&lt;h2&gt;四、现代演化：三种常被测评忽略的模式&lt;/h2&gt;
&lt;h3&gt;1. 群岛架构 / 部分水合（Astro）&lt;/h3&gt;
&lt;p&gt;SSG 页面默认 &lt;strong&gt;0KB JS&lt;/strong&gt;，只有标了 &lt;code&gt;client:*&lt;/code&gt; 的组件才作为“岛”单独 hydrate，互相独立：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Counter client:visible /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;把“SSG 的速度”和“组件化交互”统一了，内容站天然适合。&lt;/p&gt;
&lt;h3&gt;2. ISR（增量静态再生成）&lt;/h3&gt;
&lt;p&gt;SSG + 定时/按需重渲染：页面先以静态形式服务，后台按间隔（或请求触发）重新生成，化解“频繁更新不适合 SSG”的顾虑。&lt;/p&gt;
&lt;h3&gt;3. Hybrid（Next / Nuxt）&lt;/h3&gt;
&lt;p&gt;同一框架内&lt;strong&gt;按页面&lt;/strong&gt;选 SSR / SSG / CSR。营销页走 SSG，用户页走 SSR，后台走 CSR。&lt;/p&gt;
&lt;h3&gt;总表&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方式&lt;/th&gt;
&lt;th&gt;首屏 JS&lt;/th&gt;
&lt;th&gt;适合更新频率&lt;/th&gt;
&lt;th&gt;SEO&lt;/th&gt;
&lt;th&gt;代表&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CSR&lt;/td&gt;
&lt;td&gt;大&lt;/td&gt;
&lt;td&gt;高&lt;/td&gt;
&lt;td&gt;弱&lt;/td&gt;
&lt;td&gt;React SPA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSR&lt;/td&gt;
&lt;td&gt;中（水合）&lt;/td&gt;
&lt;td&gt;高（实时）&lt;/td&gt;
&lt;td&gt;强&lt;/td&gt;
&lt;td&gt;Next SSR 模式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSG&lt;/td&gt;
&lt;td&gt;0（默认）&lt;/td&gt;
&lt;td&gt;低&lt;/td&gt;
&lt;td&gt;强&lt;/td&gt;
&lt;td&gt;Hexo / Hugo / Astro&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Islands&lt;/td&gt;
&lt;td&gt;0 + 按需&lt;/td&gt;
&lt;td&gt;低&lt;/td&gt;
&lt;td&gt;强&lt;/td&gt;
&lt;td&gt;Astro&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ISR&lt;/td&gt;
&lt;td&gt;0 + 按需&lt;/td&gt;
&lt;td&gt;中高&lt;/td&gt;
&lt;td&gt;强&lt;/td&gt;
&lt;td&gt;Next.js&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hybrid&lt;/td&gt;
&lt;td&gt;看页面&lt;/td&gt;
&lt;td&gt;任意&lt;/td&gt;
&lt;td&gt;强&lt;/td&gt;
&lt;td&gt;Next / Nuxt&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;五、怎么选&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;内容站 / 博客 / 文档 → SSG；想要零 JS 首屏又想要组件化就上 &lt;strong&gt;Astro（Islands）&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;需要实时/个性化数据 → SSR，记得加缓存。&lt;/li&gt;
&lt;li&gt;重交互后台 → CSR（或 SSR + 水合）。&lt;/li&gt;
&lt;li&gt;电商/大促，既要快又要常变 → ISR 或 Hybrid。&lt;/li&gt;
&lt;li&gt;已在纯 SSG 且满意 → 不必迁，Hexo/Hugo 仍是稳的选择。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;六、回到自己的博客&lt;/h2&gt;
&lt;p&gt;我正在把博客从 &lt;strong&gt;Hexo（纯 SSG）&lt;/strong&gt; 迁到 &lt;strong&gt;Astro（SSG + Islands）&lt;/strong&gt;：内容仍是构建期生成、首屏零 JS、SEO 不变；唯一区别是“想要交互的局部”（评论、搜索、暗色切换）才 hydrate 成岛。这不是换个更花哨的框架，而是把“SSG 的速度”和“现代组件化 DX”用群岛架构捏到一起——上面第二节到第四节的原理，正是这次迁移的底层逻辑。&lt;/p&gt;
&lt;p&gt;缩写背再多，不如想清一件事：**你的 HTML，到底是谁、在什么时候生成的。**想明白这个，选型不会再被口号带跑。&lt;/p&gt;
</content:encoded></item><item><title>把博客技术栈讲透：从 Hexo 到 Astro，到底变了什么（避坑版）</title><link>https://h1s97x.github.io/posts/blog/%E6%8A%8A%E5%8D%9A%E5%AE%A2%E6%8A%80%E6%9C%AF%E6%A0%88%E8%AE%B2%E9%80%8F-%E4%BB%8Ehexo%E5%88%B0astro%E9%81%BF%E5%9D%91%E7%89%88/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/blog/%E6%8A%8A%E5%8D%9A%E5%AE%A2%E6%8A%80%E6%9C%AF%E6%A0%88%E8%AE%B2%E9%80%8F-%E4%BB%8Ehexo%E5%88%B0astro%E9%81%BF%E5%9D%91%E7%89%88/</guid><description>从构建管线、渲染模型、内容建模、代码高亮、搜索几个维度讲清 Astro 相比传统 SSG 到底变了什么，并给出迁移到 Astro 的真实坑位。</description><pubDate>Wed, 14 Oct 2026 03:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Astro 常被概括为“快、SEO 好、零 JS”，但结论背后是具体机制。这篇从构建管线、渲染模型、内容建模、代码高亮、搜索几个维度拆开讲，能落地的部分都给例子。&lt;/p&gt;
&lt;h2&gt;一、SSG 到底在干什么&lt;/h2&gt;
&lt;p&gt;先把“静态站点生成器”四个字还原成一条流水线，后面所有对比都建立在它之上：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Markdown/资源  ──┐
模板/布局      ──┼──►  解析  ──►  数据 + 模板渲染  ──►  纯 HTML/CSS/JS  ──►  丢到任意静态托管
配置/插件      ──┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;关键区别从来不是“能不能出静态页”，而是&lt;strong&gt;三件事在什么时候发生&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;**模板求值（渲染）**在构建期还是运行期？&lt;/li&gt;
&lt;li&gt;**交互逻辑（JS）**以什么粒度、多少体积发到浏览器？&lt;/li&gt;
&lt;li&gt;**内容结构（frontmatter）**有没有被当“数据”建模和校验？&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这三点决定了速度、SEO、可维护性。下面三代 SSG 的差异，本质就是这三点的不同答案。&lt;/p&gt;
&lt;h2&gt;二、三代渲染模型：差异在这里&lt;/h2&gt;
&lt;h3&gt;1. 传统 SSG（Jekyll / Hexo / Hugo）&lt;/h3&gt;
&lt;p&gt;构建期把“模板 + 数据”全部求值成 HTML，&lt;strong&gt;产物里没有框架运行时&lt;/strong&gt;。交互（评论、搜索、暗色切换）靠你自己塞一段原生 JS 或 jQuery 时代遗留脚本。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;优点：首屏就是 HTML，SEO 天然好，托管随便找个空间都行。&lt;/li&gt;
&lt;li&gt;代价：想要组件化复用？没有。想写个带状态的小组件？得手写 DOM 操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. SPA 入侵（Gatsby / Nuxt Content / 早期 VuePress）&lt;/h3&gt;
&lt;p&gt;把 React/Vue 整套搬进来，&lt;strong&gt;整页 hydration&lt;/strong&gt;：浏览器下载框架运行时 + 整页组件树，再“水合”成可交互。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;代价：首屏要下载并求值一坨 JS，水合慢；SEO 得靠预渲染或 SSR 补。&lt;/li&gt;
&lt;li&gt;讽刺点：一个纯内容博客，为此付出的是接近应用的运行时成本。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 群岛架构（Astro）&lt;/h3&gt;
&lt;p&gt;核心一句话：&lt;strong&gt;默认输出 0KB JS 的纯 HTML，只有标了“需要交互”的组件才被 hydrate，且各自独立成“岛”&lt;/strong&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
// 整页是静态 HTML，下面这个按钮才是“岛”
---
&amp;lt;Counter client:visible /&amp;gt;   {/* 仅当滚动进视口才下载并水合该组件 */}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;client:*&lt;/code&gt; 指令决定了 hydrate 时机：&lt;code&gt;load&lt;/code&gt;（立即）、&lt;code&gt;idle&lt;/code&gt;（浏览器空闲）、&lt;code&gt;visible&lt;/code&gt;（进视口）、&lt;code&gt;media&lt;/code&gt;（匹配媒体查询时）。博客里 99% 的内容不需要 hydrate，于是首屏 JS 趋近于 0。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;对比结论：Hexo 是“全静态、交互靠手写 JS”；Astro 是“默认全静态、按需 hydrate、还能用 React/Vue/Svelte 写组件”。后者在保留前者 SEO 优势的同时，把组件化能力补回来了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;三、用维度说话，不喊口号&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;维度&lt;/th&gt;
&lt;th&gt;Hexo（传统 SSG）&lt;/th&gt;
&lt;th&gt;Astro（群岛架构）&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;首屏 JS&lt;/td&gt;
&lt;td&gt;视主题，常见数十~上百 KB&lt;/td&gt;
&lt;td&gt;默认 0 KB，仅交互岛有&lt;/td&gt;
&lt;td&gt;SEO / LCP 直接受益&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;构建速度&lt;/td&gt;
&lt;td&gt;全量渲染，百篇级秒~十秒级&lt;/td&gt;
&lt;td&gt;Vite + 增量 + 缓存，大站优势明显&lt;/td&gt;
&lt;td&gt;内容越多差距越大&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;内容建模&lt;/td&gt;
&lt;td&gt;裸 frontmatter，无校验&lt;/td&gt;
&lt;td&gt;Content Collections + zod 校验&lt;/td&gt;
&lt;td&gt;见第四节&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;组件复用&lt;/td&gt;
&lt;td&gt;无（靠 include/partial）&lt;/td&gt;
&lt;td&gt;完整 UI 框架组件&lt;/td&gt;
&lt;td&gt;DX 质变&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;代码高亮&lt;/td&gt;
&lt;td&gt;highlight.js（正则/class）&lt;/td&gt;
&lt;td&gt;Shiki / Expressive Code&lt;/td&gt;
&lt;td&gt;见第五节&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;搜索&lt;/td&gt;
&lt;td&gt;客户端全文（lunr 等）&lt;/td&gt;
&lt;td&gt;构建期索引 Pagefind&lt;/td&gt;
&lt;td&gt;见第六节&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;部署&lt;/td&gt;
&lt;td&gt;任意静态空间&lt;/td&gt;
&lt;td&gt;任意静态空间 / Vercel / Netlify&lt;/td&gt;
&lt;td&gt;无差异&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;注意：&lt;strong&gt;速度不是 Astro 的“魔法”&lt;/strong&gt;，而是“不发 JS + 增量构建”的必然结果。如果主题里塞满客户端交互，Astro 也能变慢——架构只给下限，不保证上限。&lt;/p&gt;
&lt;h2&gt;四、内容建模：被忽略的代际差&lt;/h2&gt;
&lt;p&gt;多数文章不提这点，但它最影响长期可维护性。Hexo 的 frontmatter 是“字符串字典”，写错字段名、漏字段，构建期不会报错，事后才发现排版崩了。&lt;/p&gt;
&lt;p&gt;Astro 用 Content Collections 把内容当&lt;strong&gt;带 schema 的数据&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// src/content/config.ts
import { defineCollection, z } from &apos;astro:content&apos;;

const posts = defineCollection({
  type: &apos;content&apos;,
  schema: z.object({
    title: z.string(),
    published: z.coerce.date(),
    category: z.string().optional().default(&apos;&apos;),
    tags: z.array(z.string()).default([]),
    leetcode: z.number().optional(),      // 题解自定义字段
  }),
});

export const collections = { posts };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;收益是实打实的：字段类型错 → 构建直接失败；IDE 自动补全；查询时 &lt;code&gt;getCollection(&apos;posts&apos;)&lt;/code&gt; 返回的是带类型的对象，不用再 &lt;code&gt;frontmatter.xxx&lt;/code&gt; 盲取。对于我这种 160 篇、还混着 96 道 coding 题解的内容量，schema 校验能挡掉大量回归。&lt;/p&gt;
&lt;h2&gt;五、代码高亮：Shiki 比 highlight.js 强在哪&lt;/h2&gt;
&lt;p&gt;常见说法是“Astro 高亮更好看”，没说原理。差异在&lt;strong&gt;实现路径&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;highlight.js：运行时用正则匹配语言语法，套 class，再由 CSS 上色。&lt;/li&gt;
&lt;li&gt;Shiki / Expressive Code：构建期用 &lt;strong&gt;VS Code 同款 TextMate 语法 + 主题&lt;/strong&gt;做 AST 级着色，连行内 token 的颜色都和你在编辑器里看到的一致，且&lt;strong&gt;不依赖运行时 JS&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对 184 个 &lt;code&gt;.py&lt;/code&gt; 题解这种“代码即内容”的场景，Shiki 是刚需级提升，而不是锦上添花。&lt;/p&gt;
&lt;h2&gt;六、搜索：构建期索引 vs 客户端全文&lt;/h2&gt;
&lt;p&gt;Hexo 系的搜索插件大多在&lt;strong&gt;客户端&lt;/strong&gt;加载全站索引（lunr/index.json），内容多时索引文件本身就几十 KB~上百 KB，且首次输入才初始化。&lt;/p&gt;
&lt;p&gt;Astro 配 &lt;strong&gt;Pagefind&lt;/strong&gt;：在 &lt;code&gt;astro build&lt;/code&gt; 后扫 &lt;code&gt;dist/&lt;/code&gt;，生成压缩索引，&lt;strong&gt;首屏不加载索引、用户输入时才按需拉分块&lt;/strong&gt;。内容越多，体验差距越明显。配置极简：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 构建后跑一次即可，无需在页面里塞大索引
// package.json: &quot;postbuild&quot;: &quot;pagefind --site dist&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;七、迁移到 Astro 的真实坑（基于自己的方案）&lt;/h2&gt;
&lt;p&gt;光说好话是灌水，列几个我方案里已经踩/会踩的点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Fuwari 是单 category 模型&lt;/strong&gt;，而我原 Hexo 有多级 &lt;code&gt;categories&lt;/code&gt;（如 &lt;code&gt;coding/sliding-window-two-pointers/...&lt;/code&gt;）。只能取第一级当 category、其余并入 tags，96 道题解的层级导航会退化——这是架构性妥协，得想清楚接不接受。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;{% asset_code %}&lt;/code&gt; 标签直接失效&lt;/strong&gt;。Hexo 的 Nunjucks 标签在 Astro 里不存在。方案是自建 &lt;code&gt;&amp;lt;CodeBlock file&amp;gt;&lt;/code&gt; 组件读同目录 &lt;code&gt;.py&lt;/code&gt;，交给 Expressive Code；或迁移脚本把代码内联进 fenced block。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hexo 特有插件失效&lt;/strong&gt;：&lt;code&gt;hexo-generator-alias&lt;/code&gt;（旧链跳转）、&lt;code&gt;hexo-related-posts&lt;/code&gt; 等，迁移后要用 &lt;code&gt;astro.config.mjs&lt;/code&gt; 的 &lt;code&gt;redirects&lt;/code&gt; + 静态路由替代。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;旧 URL 与 SEO&lt;/strong&gt;：160 篇旧地址换框架后路径极易变，外链和搜索收录会掉。必须在 &lt;code&gt;astro.config.mjs&lt;/code&gt; 严控 &lt;code&gt;redirects&lt;/code&gt; 或保留同路径，这是高优先级风险。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;submodule 主题心智&lt;/strong&gt;：用 &lt;code&gt;themes/fuwari-theme&lt;/code&gt; 引主题仓库，和现在 &lt;code&gt;themes/almagest&lt;/code&gt; 一致，但主题一改就得 &lt;code&gt;git submodule update&lt;/code&gt; + 提交主题仓库版本，别漏。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub Pages 的 base path&lt;/strong&gt;：Astro 默认假设站点在根路径，&lt;code&gt;/blog&lt;/code&gt; 子路径部署要配 &lt;code&gt;base&lt;/code&gt; 和 &lt;code&gt;assetPrefix&lt;/code&gt;，否则资源 404。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;八、怎么选，给个不废话的结论&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;纯记录、已在 Hexo 且满意 → &lt;strong&gt;不迁也行&lt;/strong&gt;，Hexo 仍是稳的选择。&lt;/li&gt;
&lt;li&gt;想要极致构建速度 + 不碰 JS 生态 → &lt;strong&gt;Hugo&lt;/strong&gt;（Go 编写，大站快到离谱）。&lt;/li&gt;
&lt;li&gt;想要组件化 + 现代 DX + 仍能写 React/Vue → &lt;strong&gt;Astro&lt;/strong&gt;，当前最均衡的一档。&lt;/li&gt;
&lt;li&gt;已经是重交互应用态 → Next.js / Nuxt。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我自己的情况：内容站、要类型安全的内容建模、184 个代码块要 Shiki、还想要零 JS 首屏——&lt;strong&gt;Astro + Fuwari 是当下最贴合的一档&lt;/strong&gt;，主要工作量在内容迁移和 URL 对齐，不是技术风险。&lt;/p&gt;
&lt;p&gt;技术栈年年新，但“写得稳、搜得到、迁得动”才是真需求。口号看懂了，坑位也看清了，剩下的就是按 feature 分支一步步跑通、和现有站并行对比、确认无回归再切。&lt;/p&gt;
</content:encoded></item><item><title>聊聊博客技术栈的演进：从 Hexo 到 Astro + Fuwari</title><link>https://h1s97x.github.io/posts/blog/%E8%81%8A%E8%81%8A%E5%8D%9A%E5%AE%A2%E6%8A%80%E6%9C%AF%E6%A0%88%E7%9A%84%E6%BC%94%E8%BF%9B-%E4%BB%8Ehexo%E5%88%B0astro-fuwari/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/blog/%E8%81%8A%E8%81%8A%E5%8D%9A%E5%AE%A2%E6%8A%80%E6%9C%AF%E6%A0%88%E7%9A%84%E6%BC%94%E8%BF%9B-%E4%BB%8Ehexo%E5%88%B0astro-fuwari/</guid><description>从自己博客的迁移规划出发，顺手梳理一下这些年博客技术栈的演进，以及 Astro + Fuwari 这套方案到底‘新’在哪。</description><pubDate>Wed, 14 Oct 2026 02:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最近在整理博客的重构计划，想把它从 &lt;strong&gt;Hexo + Almagest&lt;/strong&gt; 迁到 &lt;strong&gt;Astro + Fuwari&lt;/strong&gt;。顺便琢磨了一下：这套技术栈算“新”吗？博客世界这些年又走到了哪一步？把想法记下来，权当一篇随笔。&lt;/p&gt;
&lt;h2&gt;先回答：Astro + Fuwari 新吗？&lt;/h2&gt;
&lt;p&gt;相对于我现在用的 Hexo，它明显是&lt;strong&gt;新一代&lt;/strong&gt;的方案。拉一条粗略时间线：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;技术&lt;/th&gt;
&lt;th&gt;首发&lt;/th&gt;
&lt;th&gt;定位&lt;/th&gt;
&lt;th&gt;现状&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Hexo&lt;/td&gt;
&lt;td&gt;2016&lt;/td&gt;
&lt;td&gt;Node 系老牌静态站点生成器（SSG）&lt;/td&gt;
&lt;td&gt;成熟稳定，但已进入维护期，增量更新少&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Astro&lt;/td&gt;
&lt;td&gt;2021（1.0 在 2022-08）&lt;/td&gt;
&lt;td&gt;新一代“群岛架构”框架&lt;/td&gt;
&lt;td&gt;当下最热门的 SSG 之一，生态活跃&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fuwari&lt;/td&gt;
&lt;td&gt;约 2023 年中&lt;/td&gt;
&lt;td&gt;Astro 生态头部博客主题&lt;/td&gt;
&lt;td&gt;很新，是 Astro 博客主题里 star 最高的几个之一&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;所以 &lt;strong&gt;Astro + Fuwari 属于当前世代的方案&lt;/strong&gt;，Hexo + Almagest 属于上一代。这次迁移本质是“从老牌 SSG 跨到群岛架构框架”，方向没错。&lt;/p&gt;
&lt;h2&gt;博客世界的几次跃迁&lt;/h2&gt;
&lt;p&gt;把视野拉宽一点，博客的技术底座大概经历了这么几步：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;静态手写的远古时代&lt;/strong&gt; → 动态 CMS（WordPress 一家独大，但要服务器和数据库，慢还容易被黑）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第一代 SSG 复兴（约 2008–2016）&lt;/strong&gt;：Jekyll 带火“Git + Markdown + 静态托管”，GitHub Pages 推波助澜；随后 Hexo（2016，对中文用户极友好）、Hugo（2013，Go 编写、速度炸裂）登场。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;前端框架入侵（2015–2020）&lt;/strong&gt;：Gatsby（React）、VuePress、Nuxt Content 把组件化带进文档站，但“默认带一堆 JS”导致首屏重、SEO 一般。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;群岛架构 / 零 JS 时代（2021 至今）&lt;/strong&gt;：&lt;strong&gt;Astro&lt;/strong&gt; 是标志。核心理念——默认输出纯 HTML，&lt;strong&gt;只有需要交互的组件才 hydrate（群岛）&lt;/strong&gt;，首屏零 JS、SEO 友好，同时还能用 React / Vue / Svelte 写组件。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;当下生态的几个趋势&lt;/h2&gt;
&lt;p&gt;这些趋势正好和我自己的迁移方案对得上：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;默认零 JS + 按需 hydrate&lt;/strong&gt;：正是方案里“零 JS 首屏、SEO 友好”卖点的来源。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Content Collections 取代裸 Markdown 目录&lt;/strong&gt;：用 &lt;code&gt;defineCollection&lt;/code&gt; + zod 给 frontmatter 做 schema 校验，比 Hexo 裸 frontmatter 规范得多，现在是主流做法。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Expressive Code / Shiki&lt;/strong&gt;：VSCode 同款高亮，对我那 184 个 &lt;code&gt;.py&lt;/code&gt; 题解是刚需，确实比 Hexo 的 highlight.js 强一档。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pagefind 搜索&lt;/strong&gt;：Astro 静态站首选的“构建期生成索引”方案，替代 Hexo 的客户端全文检索。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;主题/本体分离&lt;/strong&gt;：用 submodule 引 &lt;code&gt;fuwari-theme&lt;/code&gt;，和 Hugo/PaperMod、Hexo/Almagest 的“主题独立仓库”心智一致，是成熟模式。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;部署&lt;/strong&gt;：Astro 产物 &lt;code&gt;dist/&lt;/code&gt; 直接丢 GitHub Pages / Vercel / Netlify，CI 切换成本很低。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;一点冷静的提醒&lt;/h2&gt;
&lt;p&gt;新不等于无代价的“升级”，迁移里有几处要权衡：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fuwari 是单 category 模型&lt;/strong&gt;，而我原 Hexo 有多级 &lt;code&gt;categories&lt;/code&gt;（比如 &lt;code&gt;coding/sliding-window-two-pointers/...&lt;/code&gt;）。方案里“只取第一级、其余并入 tags”是可行妥协，但 96 道题解的层级导航体验会退化，这点值得再想想。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;URL 兼容&lt;/strong&gt;：160 篇旧 URL 在 GitHub Pages 上若路径变了，外链和 SEO 会掉，需要在 &lt;code&gt;astro.config.mjs&lt;/code&gt; 严控 &lt;code&gt;redirects&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;心智成本&lt;/strong&gt;：Astro 的 Content Collections + 组件化，比 Hexo“丢个 md 就出页”门槛高一点，但换来可维护性和类型安全，长期划算。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;小结&lt;/h2&gt;
&lt;p&gt;这次选型不激进，属于&lt;strong&gt;当前主流偏新一档&lt;/strong&gt;，方向上没有踩坑风险，主要工作量在内容迁移和路由 / URL 对齐。等 &lt;code&gt;fuwari-theme&lt;/code&gt; 主题仓库建好、submodule 接上，先用 feature 分支把 Phase 0–2 跑通，和现有 Hexo 站做并行预览对比，确认无回归再切域名——这才是稳妥的节奏。&lt;/p&gt;
&lt;p&gt;博客技术年年新，但“把想说的话安安稳稳写下来、还能被搜到”这件事，十年没变过。技术栈换了，记录的初心别换就好。&lt;/p&gt;
</content:encoded></item><item><title>125. 验证回文串</title><link>https://h1s97x.github.io/posts/coding/coding-125-valid-palindrome/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-125-valid-palindrome/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;125. 验证回文串&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;题目链接: &lt;a href=&quot;https://leetcode.cn/problems/%E9%AA%8C%E8%AF%81%E5%9B%9E%E6%96%87%E4%B8%B2/&quot;&gt;LeetCode 125&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果在将所有&lt;strong&gt;大写字符转换为小写&lt;/strong&gt;并移除所有&lt;strong&gt;非字母数字&lt;/strong&gt;字符之后，正着读和反着读都一样的字符串，则它就是一个&lt;strong&gt;回文串&lt;/strong&gt;。给定一个字符串 &lt;code&gt;s&lt;/code&gt;，如果它是回文串，返回 &lt;code&gt;true&lt;/code&gt;，否则返回 &lt;code&gt;false&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;A man, a plan, a canal: Panama&quot;
输出：true
解释：&quot;amanaplanacanalpanama&quot; 是回文串。
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;回文串对称比较，天然适合&lt;strong&gt;相向双指针&lt;/strong&gt;：左指针指向开头、右指针指向结尾，跳过所有非字母数字字符后，比较两字符是否相等（忽略大小写）。若出现不等则不是回文串。&lt;/p&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;left = 0, right = len(s) - 1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;left &amp;lt; right&lt;/code&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;跳过非字母数字&lt;/strong&gt;：左指针右移直到 &lt;code&gt;s[left].isalnum()&lt;/code&gt;，右指针左移直到 &lt;code&gt;s[right].isalnum()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;比较 &lt;code&gt;s[left].lower() == s[right].lower()&lt;/code&gt;，不等则返回 &lt;code&gt;False&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;left += 1, right -= 1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;True&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;s = &quot;A man, a plan, a canal: Panama&quot;&lt;/code&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;字符（原文）&lt;/th&gt;
&lt;th&gt;处理&lt;/th&gt;
&lt;th&gt;比较&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;A&lt;/code&gt; vs &lt;code&gt;a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;跳过空格和标点后，忽略大小写&lt;/td&gt;
&lt;td&gt;&lt;code&gt;a == a&lt;/code&gt; ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;m&lt;/code&gt; vs &lt;code&gt;m&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a&lt;/code&gt; vs &lt;code&gt;a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;…&lt;/td&gt;
&lt;td&gt;逐渐逼近中央&lt;/td&gt;
&lt;td&gt;…&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;P&lt;/code&gt; vs &lt;code&gt;n&lt;/code&gt; 前&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;最终全部对称 ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;返回 &lt;code&gt;true&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，每个字符最多访问一次&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)，只用了两个指针&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def isPalindrome(self, s: str) -&amp;gt; bool:
        left, right = 0, len(s) - 1
        while left &amp;lt; right:
            while left &amp;lt; right and not s[left].isalnum():
                left += 1
            while left &amp;lt; right and not s[right].isalnum():
                right -= 1
            if s[left].lower() != s[right].lower():
                return False
            left += 1
            right -= 1
        return True
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))

import unittest

from solution import Solution


class Test125(unittest.TestCase):
    def test_example1(self):
        self.assertTrue(Solution().isPalindrome(&quot;A man, a plan, a canal: Panama&quot;))

    def test_example2(self):
        self.assertFalse(Solution().isPalindrome(&quot;race a car&quot;))

    def test_empty_space(self):
        self.assertTrue(Solution().isPalindrome(&quot; &quot;))

    def test_underscore(self):
        self.assertTrue(Solution().isPalindrome(&quot;ab_a&quot;))


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>11. 盛最多水的容器</title><link>https://h1s97x.github.io/posts/coding/coding-11-container-with-most-water/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-11-container-with-most-water/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;11. 盛最多水的容器&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;题目链接: &lt;a href=&quot;https://leetcode.cn/problems/container-with-most-water/&quot;&gt;LeetCode 11&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给定一个长度为 &lt;code&gt;n&lt;/code&gt; 的整数数组 &lt;code&gt;height&lt;/code&gt;，每对 &lt;code&gt;(i, height[i])&lt;/code&gt; 表示坐标轴上一个点、一条垂直线从 &lt;code&gt;x=i&lt;/code&gt; 向上延伸到 &lt;code&gt;height[i]&lt;/code&gt;。找出两条线，与 &lt;code&gt;x&lt;/code&gt; 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。&lt;/p&gt;
&lt;p&gt;不能倾斜容器。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：[1,8,6,2,5,4,8,3,7]
输出：49
解释：图中垂直线代表输入数组。两条线分别为 index 1 和 index 6，容器宽 5、高 8，水量 = 5*8 = 40（实际最优为 index1 与 index8，宽 7 高 7，即 49）。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：height = [1,1]
输出：1
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;容器的水量 = &lt;code&gt;min(height[left], height[right]) × (right - left)&lt;/code&gt;。&lt;strong&gt;瓶颈是较矮的那根线&lt;/strong&gt;。我们用两个指针 &lt;code&gt;left&lt;/code&gt;、&lt;code&gt;right&lt;/code&gt; 从两端向中间移动，每次只移动&lt;strong&gt;较矮的一侧&lt;/strong&gt;——因为移动较矮一侧，才有机会遇到更高的线、让瓶颈升高；若移动较高一侧，宽度变小且瓶颈仍是原矮线，水量必然不增。&lt;/p&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;left = 0, right = n-1&lt;/code&gt;，记录当前最大水量 &lt;code&gt;ans = 0&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;计算当前水量 &lt;code&gt;min(height[left], height[right]) × (right-left)&lt;/code&gt;，更新 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;height[left] &amp;lt; height[right]&lt;/code&gt;，则 &lt;code&gt;left += 1&lt;/code&gt;（移动矮边）；否则 &lt;code&gt;right -= 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;重复 2~3 直到 &lt;code&gt;left &amp;gt;= right&lt;/code&gt;，返回 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;height = [1,8,6,2,5,4,8,3,7]&lt;/code&gt;：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;left&lt;/th&gt;
&lt;th&gt;right&lt;/th&gt;
&lt;th&gt;矮边&lt;/th&gt;
&lt;th&gt;宽&lt;/th&gt;
&lt;th&gt;高&lt;/th&gt;
&lt;th&gt;水量&lt;/th&gt;
&lt;th&gt;ans&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;49&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;49&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;49&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;49&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;49&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;49&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;49&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;49&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最终 &lt;code&gt;ans = 49&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：O(n)，&lt;code&gt;left/right&lt;/code&gt; 各扫描一次。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：O(1)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def maxArea(self, height: List[int]) -&amp;gt; int:
        left, right = 0, len(height) - 1
        ans = 0
        while left &amp;lt; right:
            area = min(height[left], height[right]) * (right - left)
            ans = max(ans, area)
            if height[left] &amp;lt; height[right]:
                left += 1
            else:
                right -= 1
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
import unittest

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_basic(self):
        self.assertEqual(Solution().maxArea([1, 8, 6, 2, 5, 4, 8, 3, 7]), 49)

    def test_two(self):
        self.assertEqual(Solution().maxArea([1, 1]), 1)

    def test_ascending(self):
        self.assertEqual(Solution().maxArea([1, 2, 4, 3]), 4)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>1446. 连续字符</title><link>https://h1s97x.github.io/posts/coding/coding-1446-consecutive-characters/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1446-consecutive-characters/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;1446. 连续字符&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个字符串 &lt;code&gt;s&lt;/code&gt;，字符串的「能量」定义为：只含一种字符的最长非空子字符串的长度。&lt;/p&gt;
&lt;p&gt;请你返回字符串 &lt;code&gt;s&lt;/code&gt; 的&lt;strong&gt;能量&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;leetcode&quot;
输出：2
解释：子字符串 &quot;ee&quot; 长度为 2，只含字符 &apos;e&apos;。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;abbcccddddeeeeedcba&quot;
输出：5
解释：子字符串 &quot;eeeee&quot; 长度为 5，只含字符 &apos;e&apos;。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= s.length &amp;lt;= 500&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s&lt;/code&gt; 只包含小写英文字母&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;用&lt;strong&gt;分组循环&lt;/strong&gt;把字符串划分成若干「连续相同字符」的段，取最长的段长度。灵茶山艾府题单里&quot;分组循环&quot;的经典骨架是「外层遍历段起点，内层扩展段终点」。&lt;/p&gt;
&lt;p&gt;对每个段起点 &lt;code&gt;i&lt;/code&gt;，用 &lt;code&gt;j&lt;/code&gt; 向前扩展，直到 &lt;code&gt;s[j+1]&lt;/code&gt; 与 &lt;code&gt;s[i]&lt;/code&gt; 不同，此时 &lt;code&gt;[i, j]&lt;/code&gt; 就是一个最长连续相同字符段，长度为 &lt;code&gt;j - i + 1&lt;/code&gt;，用它更新答案，然后 &lt;code&gt;i&lt;/code&gt; 跳到 &lt;code&gt;j+1&lt;/code&gt; 开始下一段。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;分组示意（s = &quot;abbccc&quot;）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a | b b | c c c
段长 1   2   3   → 答案 3
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;初始化 &lt;code&gt;ans = 1&lt;/code&gt;（至少有一个字符），&lt;code&gt;i = 0&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;i &amp;lt; n&lt;/code&gt; 时：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;j = i&lt;/code&gt;，内层 &lt;code&gt;while j+1 &amp;lt; n and s[j+1] == s[i]&lt;/code&gt; 时 &lt;code&gt;j++&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ans = max(ans, j - i + 1)&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i = j + 1&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;s = &quot;abbcccddddeeeeedcba&quot;&lt;/code&gt; 为例，最长为最后的 &lt;code&gt;e&lt;/code&gt; 段：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;段&lt;/th&gt;
&lt;th&gt;区间&lt;/th&gt;
&lt;th&gt;长度&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;td&gt;[0,0]&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bb&lt;/td&gt;
&lt;td&gt;[1,2]&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ccc&lt;/td&gt;
&lt;td&gt;[3,5]&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dddd&lt;/td&gt;
&lt;td&gt;[6,9]&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;eeeeee&lt;/td&gt;
&lt;td&gt;[10,15]&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;d&lt;/td&gt;
&lt;td&gt;[16,16]&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;c&lt;/td&gt;
&lt;td&gt;[17,17]&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;b&lt;/td&gt;
&lt;td&gt;[18,18]&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;td&gt;[19,19]&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;答案为 &lt;code&gt;5&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：O(N)，每个下标只被访问常数次。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：O(1)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;p&gt;{{% highlight python %}}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def maxPower(self, s: str) -&amp;gt; int:
        n = len(s)
        ans = 1
        i = 0
        while i &amp;lt; n:
            j = i
            while j + 1 &amp;lt; n and s[j + 1] == s[i]:
                j += 1
            ans = max(ans, j - i + 1)
            i = j + 1
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
import unittest

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_example1(self):
        self.assertEqual(Solution().maxPower(&quot;leetcode&quot;), 2)

    def test_example2(self):
        self.assertEqual(Solution().maxPower(&quot;abbcccddddeeeeedcba&quot;), 5)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;{{% /highlight %}}&lt;/p&gt;
</content:encoded></item><item><title>15. 三数之和</title><link>https://h1s97x.github.io/posts/coding/coding-15-3sum/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-15-3sum/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;15. 三数之和&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;题目链接: &lt;a href=&quot;https://leetcode.cn/problems/3sum/&quot;&gt;LeetCode 15&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个整数数组 &lt;code&gt;nums&lt;/code&gt;，判断是否存在三元组 &lt;code&gt;[nums[i], nums[j], nums[k]]&lt;/code&gt; 满足 &lt;code&gt;i != j&lt;/code&gt;、&lt;code&gt;i != k&lt;/code&gt; 且 &lt;code&gt;j != k&lt;/code&gt;，同时还满足 &lt;code&gt;nums[i] + nums[j] + nums[k] == 0&lt;/code&gt;。请你返回所有和为 0 且不重复的三元组。&lt;/p&gt;
&lt;p&gt;答案中不可以包含重复的三元组。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [-1,0,1,2,-1,-4]
输出：[[-1,-1,2],[-1,0,1]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [0,1,1]
输出：[]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 3：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [0,0,0]
输出：[[0,0,0]]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;先&lt;strong&gt;排序&lt;/strong&gt;，固定第一个数 &lt;code&gt;nums[i]&lt;/code&gt;，再用&lt;strong&gt;双指针&lt;/strong&gt;在 &lt;code&gt;[i+1, n-1]&lt;/code&gt; 内找两数之和等于 &lt;code&gt;-nums[i]&lt;/code&gt;。排序让双指针可以有序移动，也让去重变得简单：跳过重复的元素避免产生重复三元组。&lt;/p&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;排序 &lt;code&gt;nums&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;遍历 &lt;code&gt;i&lt;/code&gt;（&lt;code&gt;0&lt;/code&gt; 到 &lt;code&gt;n-3&lt;/code&gt;）：
&lt;ul&gt;
&lt;li&gt;若 &lt;code&gt;i &amp;gt; 0&lt;/code&gt; 且 &lt;code&gt;nums[i] == nums[i-1]&lt;/code&gt;，跳过（去重）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;left=i+1, right=n-1, target=-nums[i]&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;双指针寻找两数之和等于 &lt;code&gt;target&lt;/code&gt;：
&lt;ul&gt;
&lt;li&gt;相等：加入答案，并移动 &lt;code&gt;left/right&lt;/code&gt; 跳过重复值。&lt;/li&gt;
&lt;li&gt;小于 &lt;code&gt;target&lt;/code&gt;：&lt;code&gt;left += 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;大于 &lt;code&gt;target&lt;/code&gt;：&lt;code&gt;right -= 1&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回答案。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;nums = [-1,0,1,2,-1,-4]&lt;/code&gt;，排序后 &lt;code&gt;[-4,-1,-1,0,1,2]&lt;/code&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;i=0&lt;/code&gt;，&lt;code&gt;nums[0]=-4&lt;/code&gt;，&lt;code&gt;target=4&lt;/code&gt;。双指针 &lt;code&gt;[-1,2]→1, [0,2]→2, [1,2]→3&lt;/code&gt; 都 &amp;lt; 4，找不到。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=1&lt;/code&gt;，&lt;code&gt;nums[1]=-1&lt;/code&gt;，&lt;code&gt;target=1&lt;/code&gt;。&lt;code&gt;left=2,right=5&lt;/code&gt;：&lt;code&gt;[-1,2]→1 == target&lt;/code&gt; ✅ 得 &lt;code&gt;[-1,-1,2]&lt;/code&gt;；&lt;code&gt;[0,2]→2&lt;/code&gt; &amp;gt; 1 → &lt;code&gt;right=3&lt;/code&gt;；&lt;code&gt;[0,1]→1 == target&lt;/code&gt; ✅ 得 &lt;code&gt;[-1,0,1]&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=2&lt;/code&gt;，&lt;code&gt;nums[2]=-1 == nums[1]&lt;/code&gt; 跳过。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;答案 &lt;code&gt;[[-1,-1,2],[-1,0,1]]&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：O(n²)，排序 O(n log n)，双指针双层循环。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：O(1)（不计排序栈空间，排序通常 O(log n)）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def threeSum(self, nums: List[int]) -&amp;gt; List[List[int]]:
        nums.sort()
        n = len(nums)
        ans = []
        for i in range(n - 2):
            if i &amp;gt; 0 and nums[i] == nums[i - 1]:
                continue
            left, right = i + 1, n - 1
            target = -nums[i]
            while left &amp;lt; right:
                s = nums[left] + nums[right]
                if s == target:
                    ans.append([nums[i], nums[left], nums[right]])
                    while left &amp;lt; right and nums[left] == nums[left + 1]:
                        left += 1
                    while left &amp;lt; right and nums[right] == nums[right - 1]:
                        right -= 1
                    left += 1
                    right -= 1
                elif s &amp;lt; target:
                    left += 1
                else:
                    right -= 1
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
import unittest

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_basic(self):
        res = Solution().threeSum([-1, 0, 1, 2, -1, -4])
        expected = [[-1, -1, 2], [-1, 0, 1]]
        self.assertEqual(sorted(res), sorted(expected))

    def test_no_solution(self):
        self.assertEqual(Solution().threeSum([0, 1, 1]), [])

    def test_zeros(self):
        self.assertEqual(Solution().threeSum([0, 0, 0, 0]), [[0, 0, 0]])


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>1513. 仅含 1 的子串数</title><link>https://h1s97x.github.io/posts/coding/coding-1513-number-of-substrings-with-only-1s/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1513-number-of-substrings-with-only-1s/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;1513. 仅含 1 的子串数&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个二进制字符串 &lt;code&gt;s&lt;/code&gt;（仅由 &lt;code&gt;&apos;0&apos;&lt;/code&gt; 和 &lt;code&gt;&apos;1&apos;&lt;/code&gt; 组成）。&lt;/p&gt;
&lt;p&gt;返回所有字符都为 &lt;code&gt;1&lt;/code&gt; 的&lt;strong&gt;子字符串&lt;/strong&gt;的数目。&lt;/p&gt;
&lt;p&gt;由于答案可能很大，请你将它对 &lt;code&gt;10^9 + 7&lt;/code&gt; 取模后返回。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;0110111&quot;
输出：9
解释：共有 9 个子字符串仅由 &apos;1&apos; 组成：
&quot;1&quot; -&amp;gt; 5 个
&quot;11&quot; -&amp;gt; 3 个
&quot;111&quot; -&amp;gt; 1 个
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;101&quot;
输出：2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= s.length &amp;lt;= 10^5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s[i]&lt;/code&gt; 是 &lt;code&gt;&apos;0&apos;&lt;/code&gt; 或 &lt;code&gt;&apos;1&apos;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;把字符串按连续的 &lt;code&gt;&apos;1&apos;&lt;/code&gt; 分成若干&lt;strong&gt;段&lt;/strong&gt;（用分组循环）。对任意一段长度为 &lt;code&gt;k&lt;/code&gt; 的连续 &lt;code&gt;&apos;1&apos;&lt;/code&gt;，它内部能形成的全 1 子串数量为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;长度为 1 的有 k 个
长度为 2 的有 k-1 个
...
长度为 k 的有 1 个
合计 = 1 + 2 + ... + k = k * (k + 1) / 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;把每一段的贡献累加并对 &lt;code&gt;10^9+7&lt;/code&gt; 取模即可。因为不同段之间被 &lt;code&gt;&apos;0&apos;&lt;/code&gt; 隔开，子串不能跨界，所以互不影响，直接相加。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;段贡献示意（s = &quot;0110111&quot;）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;01 1 0 1 1 1&quot;
     段1(k=2)       段2(k=3)
贡献= 2*3/2=3    贡献= 3*4/2=6
总计 = 3 + 6 = 9
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;初始化 &lt;code&gt;ans = 0&lt;/code&gt;，&lt;code&gt;i = 0&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;i &amp;lt; n&lt;/code&gt; 时：
&lt;ul&gt;
&lt;li&gt;若 &lt;code&gt;s[i] == &apos;0&apos;&lt;/code&gt;，&lt;code&gt;i++&lt;/code&gt; 跳过；&lt;/li&gt;
&lt;li&gt;否则 &lt;code&gt;j = i&lt;/code&gt;，内层 &lt;code&gt;while&lt;/code&gt; 扩展连续 &lt;code&gt;&apos;1&apos;&lt;/code&gt;；段长 &lt;code&gt;k = j - i + 1&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ans += k*(k+1)//2&lt;/code&gt;，&lt;code&gt;i = j + 1&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;ans % (10^9+7)&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;s = &quot;0110111&quot;&lt;/code&gt;，按段统计：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;段&lt;/th&gt;
&lt;th&gt;区间&lt;/th&gt;
&lt;th&gt;长度 k&lt;/th&gt;
&lt;th&gt;贡献 k(k+1)/2&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1（跳过 &apos;0&apos;）&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;[1,2]&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;111&lt;/td&gt;
&lt;td&gt;[4,6]&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;合计 &lt;code&gt;3 + 6 = 9&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：O(N)，单次线性扫描。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：O(1)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;p&gt;{{% highlight python %}}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def numSub(self, s: str) -&amp;gt; int:
        MOD = 10 ** 9 + 7
        ans = 0
        n = len(s)
        i = 0
        while i &amp;lt; n:
            if s[i] == &apos;0&apos;:
                i += 1
                continue
            j = i
            while j + 1 &amp;lt; n and s[j + 1] == &apos;1&apos;:
                j += 1
            k = j - i + 1
            ans = (ans + k * (k + 1) // 2) % MOD
            i = j + 1
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
import unittest

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_example1(self):
        self.assertEqual(Solution().numSub(&quot;0110111&quot;), 9)

    def test_example2(self):
        self.assertEqual(Solution().numSub(&quot;101&quot;), 2)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;{{% /highlight %}}&lt;/p&gt;
</content:encoded></item><item><title>167. 两数之和II-输入有序数组</title><link>https://h1s97x.github.io/posts/coding/coding-167-two-sum-ii-input-array-is-sorted/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-167-two-sum-ii-input-array-is-sorted/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;167. 两数之和II-输入有序数组&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;题目链接: &lt;a href=&quot;https://leetcode.cn/problems/%E4%B8%A4%E6%95%B0%E4%B9%8B%E5%92%8Cii-%E8%BE%93%E5%85%A5%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84/&quot;&gt;LeetCode 167&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;给你一个下标从 &lt;strong&gt;1&lt;/strong&gt; 开始的整数数组 &lt;code&gt;numbers&lt;/code&gt;，该数组已按 &lt;strong&gt;非递减顺序排列&lt;/strong&gt;，请你从中找出满足相加之和等于目标数 &lt;code&gt;target&lt;/code&gt; 的两个数。返回这两个数的下标（从 1 开始）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：numbers = [2,7,11,15], target = 9
输出：[1,2]
解释：2 与 7 之和等于目标数 9，下标从 1 开始对应 [1,2]。
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;利用数组&lt;strong&gt;有序&lt;/strong&gt;这一条件，采用&lt;strong&gt;相向双指针&lt;/strong&gt;。两个指针所指元素的和为 &lt;code&gt;s&lt;/code&gt;，通过与 &lt;code&gt;target&lt;/code&gt; 比较来决定指针移动方向：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;s &amp;lt; target&lt;/code&gt;：和偏小，需要更大的数 → 左指针右移；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s &amp;gt; target&lt;/code&gt;：和偏大，需要更小的数 → 右指针左移。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;每轮必然向目标收敛，因此可以在 O(n) 内找到答案，无需哈希表（题目的进阶要求 O(1) 空间恰好可由双指针达成）。&lt;/p&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;left = 0, right = n - 1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;left &amp;lt; right&lt;/code&gt; 时循环：
&lt;ul&gt;
&lt;li&gt;记 &lt;code&gt;s = numbers[left] + numbers[right]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s == target&lt;/code&gt;：返回 &lt;code&gt;[left + 1, right + 1]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s &amp;lt; target&lt;/code&gt;：&lt;code&gt;left += 1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s &amp;gt; target&lt;/code&gt;：&lt;code&gt;right -= 1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;numbers = [2,7,11,15], target = 9&lt;/code&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;left&lt;/th&gt;
&lt;th&gt;right&lt;/th&gt;
&lt;th&gt;和 s&lt;/th&gt;
&lt;th&gt;操作&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;9&lt;/code&gt; → &lt;code&gt;right=2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;9&lt;/code&gt; → &lt;code&gt;right=1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;&lt;code&gt;==9&lt;/code&gt; → 返回 &lt;code&gt;[1,2]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，每个元素只被访问一次&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)，只用了两个指针&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def twoSum(self, numbers: List[int], target: int) -&amp;gt; List[int]:
        left, right = 0, len(numbers) - 1
        while left &amp;lt; right:
            s = numbers[left] + numbers[right]
            if s == target:
                return [left + 1, right + 1]
            if s &amp;lt; target:
                left += 1
            else:
                right -= 1
        return []
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))

import unittest

from solution import Solution


class Test167(unittest.TestCase):
    def test_example1(self):
        self.assertEqual(Solution().twoSum([2, 7, 11, 15], 9), [1, 2])

    def test_example2(self):
        self.assertEqual(Solution().twoSum([2, 3, 4], 6), [1, 3])

    def test_example3(self):
        self.assertEqual(Solution().twoSum([-1, 0], -1), [1, 2])


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>1759. 统计同质子字符串的数目</title><link>https://h1s97x.github.io/posts/coding/coding-1759-count-number-of-homogenous-substrings/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1759-count-number-of-homogenous-substrings/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;1759. 统计同质子字符串的数目&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个字符串 &lt;code&gt;s&lt;/code&gt;，返回&lt;strong&gt;同质子字符串&lt;/strong&gt;的数目。&lt;/p&gt;
&lt;p&gt;由于答案可能很大，返回答案需要对 &lt;code&gt;10^9 + 7&lt;/code&gt; 取模。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;同质字符串&lt;/strong&gt;的定义是：若一个字符串中所有字符都相同，则称它是同质字符串。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;同质子字符串&lt;/strong&gt;的定义是：如果一个子字符串是同质字符串，则称它是同质子字符串。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;abbcccaa&quot;
输出：13
解释：同质子字符串如下所列：
&quot;a&quot;   出现 3 次。
&quot;aa&quot;  出现 1 次。
&quot;b&quot;   出现 2 次。
&quot;bb&quot;  出现 1 次。
&quot;c&quot;   出现 3 次。
&quot;cc&quot;  出现 2 次。
&quot;ccc&quot; 出现 1 次。
3 + 1 + 2 + 1 + 3 + 2 + 1 = 13
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;xy&quot;
输出：2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= s.length &amp;lt;= 10^5&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;与 1513 题完全同构。把字符串按连续相同字符分成段，对每段长度 &lt;code&gt;k&lt;/code&gt;，其内部可形成的同质子串数量为 &lt;code&gt;k*(k+1)/2&lt;/code&gt;，全部相加并对 &lt;code&gt;10^9+7&lt;/code&gt; 取模。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;段贡献示意（s = &quot;abbcccaa&quot;）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a | bb | ccc | aa
1    3     6    3   → 总计 13
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;初始化 &lt;code&gt;ans = 0&lt;/code&gt;，&lt;code&gt;i = 0&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;i &amp;lt; n&lt;/code&gt; 时：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;j = i&lt;/code&gt;，扩展 &lt;code&gt;while j+1 &amp;lt; n and s[j+1] == s[i]&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;段长 &lt;code&gt;k = j - i + 1&lt;/code&gt;，&lt;code&gt;ans += k*(k+1)//2&lt;/code&gt;，&lt;code&gt;i = j + 1&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;ans % (10^9+7)&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;s = &quot;abbcccaa&quot;&lt;/code&gt; 为例：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;段&lt;/th&gt;
&lt;th&gt;区间&lt;/th&gt;
&lt;th&gt;长度 k&lt;/th&gt;
&lt;th&gt;贡献 k(k+1)/2&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;td&gt;[0,0]&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bb&lt;/td&gt;
&lt;td&gt;[1,2]&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ccc&lt;/td&gt;
&lt;td&gt;[3,5]&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;aa&lt;/td&gt;
&lt;td&gt;[6,7]&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;合计 &lt;code&gt;1 + 3 + 6 + 3 = 13&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：O(N)，单次线性扫描。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：O(1)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;p&gt;{{% highlight python %}}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def countHomogenous(self, s: str) -&amp;gt; int:
        MOD = 10 ** 9 + 7
        ans = 0
        n = len(s)
        i = 0
        while i &amp;lt; n:
            j = i
            while j + 1 &amp;lt; n and s[j + 1] == s[i]:
                j += 1
            k = j - i + 1
            ans = (ans + k * (k + 1) // 2) % MOD
            i = j + 1
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
import unittest

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_example1(self):
        self.assertEqual(Solution().countHomogenous(&quot;abbcccaa&quot;), 13)

    def test_example2(self):
        self.assertEqual(Solution().countHomogenous(&quot;xy&quot;), 2)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;{{% /highlight %}}&lt;/p&gt;
</content:encoded></item><item><title>228. 汇总区间</title><link>https://h1s97x.github.io/posts/coding/coding-228-summary-ranges/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-228-summary-ranges/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;228. 汇总区间&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给定一个&lt;strong&gt;无重复元素&lt;/strong&gt;的&lt;strong&gt;有序&lt;/strong&gt;整数数组 &lt;code&gt;nums&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;返回&lt;strong&gt;恰好覆盖数组中所有数字&lt;/strong&gt;的&lt;strong&gt;最小有序&lt;/strong&gt;区间范围列表。也就是说，&lt;code&gt;nums&lt;/code&gt; 的每个元素都恰好被某个区间范围所覆盖，并且不存在属于某个范围但不属于 &lt;code&gt;nums&lt;/code&gt; 的数字 &lt;code&gt;x&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;列表中的每个区间范围 &lt;code&gt;[a,b]&lt;/code&gt; 应该按如下格式输出：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&quot;a-&amp;gt;b&quot;&lt;/code&gt;，如果 &lt;code&gt;a != b&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&quot;a&quot;&lt;/code&gt;，如果 &lt;code&gt;a == b&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [0,1,2,4,5,7]
输出：[&quot;0-&amp;gt;2&quot;,&quot;4-&amp;gt;5&quot;,&quot;7&quot;]
解释：区间范围是：
[0,2] --&amp;gt; &quot;0-&amp;gt;2&quot;
[4,5] --&amp;gt; &quot;4-&amp;gt;5&quot;
[7,7] --&amp;gt; &quot;7&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [0,2,3,4,6,8,9]
输出：[&quot;0&quot;,&quot;2-&amp;gt;4&quot;,&quot;6&quot;,&quot;8-&amp;gt;9&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= nums.length &amp;lt;= 20&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-2^31 &amp;lt;= nums[i] &amp;lt;= 2^31 - 1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;用&lt;strong&gt;分组循环&lt;/strong&gt;把数组切成若干段「连续整数」区间：段内相邻元素差恒为 1。对每个段 &lt;code&gt;[i, j]&lt;/code&gt;，若 &lt;code&gt;nums[i] == nums[j]&lt;/code&gt; 则输出单个 &lt;code&gt;&quot;x&quot;&lt;/code&gt;，否则输出 &lt;code&gt;&quot;x&quot;-&amp;gt;&quot;y&quot;&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;分组示意（nums = [0,1,2,4,5,7]）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[0,1,2] / [4,5] / [7]  →  &quot;0-&amp;gt;2&quot; / &quot;4-&amp;gt;5&quot; / &quot;7&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;令 &lt;code&gt;i = 0&lt;/code&gt;，初始化结果列表 &lt;code&gt;res&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;i &amp;lt; n&lt;/code&gt; 时：
&lt;ul&gt;
&lt;li&gt;记段起点 &lt;code&gt;start = nums[i]&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;j = i&lt;/code&gt;，内层 &lt;code&gt;while j+1 &amp;lt; n and nums[j+1] == nums[j] + 1&lt;/code&gt; 时 &lt;code&gt;j++&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;start == nums[j]&lt;/code&gt;，追加 &lt;code&gt;str(start)&lt;/code&gt;；否则追加 &lt;code&gt;f&quot;{start}-&amp;gt;{nums[j]}&quot;&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i = j + 1&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;res&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;nums = [0,2,3,4,6,8,9]&lt;/code&gt; 为例：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;段起点 i&lt;/th&gt;
&lt;th&gt;段终点 j&lt;/th&gt;
&lt;th&gt;nums&lt;/th&gt;
&lt;th&gt;输出&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;[0]&lt;/td&gt;
&lt;td&gt;&quot;0&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;[2,3,4]&lt;/td&gt;
&lt;td&gt;&quot;2-&amp;gt;4&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;[6]&lt;/td&gt;
&lt;td&gt;&quot;6&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;[8,9]&lt;/td&gt;
&lt;td&gt;&quot;8-&amp;gt;9&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;结果为 &lt;code&gt;[&quot;0&quot;,&quot;2-&amp;gt;4&quot;,&quot;6&quot;,&quot;8-&amp;gt;9&quot;]&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：O(N)，单次线性扫描。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：O(1)（不计输出结果空间）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;p&gt;{{% highlight python %}}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def summaryRanges(self, nums: List[int]) -&amp;gt; List[str]:
        res = []
        n = len(nums)
        i = 0
        while i &amp;lt; n:
            start = nums[i]
            j = i
            while j + 1 &amp;lt; n and nums[j + 1] == nums[j] + 1:
                j += 1
            if start == nums[j]:
                res.append(str(start))
            else:
                res.append(f&quot;{start}-&amp;gt;{nums[j]}&quot;)
            i = j + 1
        return res
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
import unittest

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_example1(self):
        self.assertEqual(Solution().summaryRanges([0, 1, 2, 4, 5, 7]),
                         [&quot;0-&amp;gt;2&quot;, &quot;4-&amp;gt;5&quot;, &quot;7&quot;])

    def test_example2(self):
        self.assertEqual(Solution().summaryRanges([0, 2, 3, 4, 6, 8, 9]),
                         [&quot;0&quot;, &quot;2-&amp;gt;4&quot;, &quot;6&quot;, &quot;8-&amp;gt;9&quot;])


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;{{% /highlight %}}&lt;/p&gt;
</content:encoded></item><item><title>2367. 等差三元组的数目</title><link>https://h1s97x.github.io/posts/coding/coding-2367-number-of-arithmetic-triplets/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2367-number-of-arithmetic-triplets/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;2367. 等差三元组的数目&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个下标从 0 开始、&lt;strong&gt;严格递增&lt;/strong&gt;的整数数组 &lt;code&gt;nums&lt;/code&gt; 和一个正整数 &lt;code&gt;diff&lt;/code&gt;。如果满足下述全部条件，则三元组 &lt;code&gt;(i, j, k)&lt;/code&gt; 就是一个&lt;strong&gt;等差三元组&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;i &amp;lt; j &amp;lt; k&lt;/code&gt;，&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nums[j] - nums[i] == diff&lt;/code&gt;，&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nums[k] - nums[j] == diff&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;返回数组 &lt;code&gt;nums&lt;/code&gt; 中&lt;strong&gt;等差三元组的数目&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [0,1,4,6,7,10], diff = 3
输出：2
解释：(1, 2, 4) 是等差三元组：7 - 4 == 3 且 4 - 1 == 3。
     (2, 4, 5) 是等差三元组：10 - 7 == 3 且 7 - 4 == 3。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [4,5,6,7,8,9], diff = 2
输出：2
解释：(0, 2, 4) 是等差三元组：8 - 6 == 2 且 6 - 4 == 2。
     (1, 3, 5) 是等差三元组：9 - 7 == 2 且 7 - 5 == 2。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;3 &amp;lt;= nums.length &amp;lt;= 200&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= nums[i] &amp;lt;= 200&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= diff &amp;lt;= 50&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nums&lt;/code&gt; 严格递增&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;数组&lt;strong&gt;严格递增&lt;/strong&gt;，因此三个指针 &lt;code&gt;i &amp;lt; j &amp;lt; k&lt;/code&gt; 对应的值单调上升，且一旦 &lt;code&gt;nums[j] - nums[i] &amp;gt; diff&lt;/code&gt;，随着 &lt;code&gt;i&lt;/code&gt; 增大差值单调不增。这天然适合用&lt;strong&gt;三指针&lt;/strong&gt;线性扫描。&lt;/p&gt;
&lt;p&gt;枚举三元组最大的 &lt;code&gt;k&lt;/code&gt;：用指针 &lt;code&gt;j&lt;/code&gt; 找到「与 &lt;code&gt;nums[k]&lt;/code&gt; 的差为 &lt;code&gt;diff&lt;/code&gt;」的中间元素（即 &lt;code&gt;nums[k] - nums[j] == diff&lt;/code&gt;），再用指针 &lt;code&gt;i&lt;/code&gt; 找到「与 &lt;code&gt;nums[j]&lt;/code&gt; 的差为 &lt;code&gt;diff&lt;/code&gt;」的最小元素（&lt;code&gt;nums[j] - nums[i] == diff&lt;/code&gt;）。由于三个值各自相差 &lt;code&gt;diff&lt;/code&gt;，找到一次就构成一个等差三元组。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;三指针同步推进示意（nums=[0,1,4,6,7,10], diff=3）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;k=4(nums[4]=7)时:
  让 j 前进，直到 nums[4]-nums[j] &amp;lt;= 3
    nums[4]-nums[2]=7-4=3 → j=2，差==diff
  再让 i 前进，直到 nums[2]-nums[i] &amp;lt;= 3
    nums[2]-nums[1]=4-1=3 → i=1，差==diff
  计数 +1  → 三元组 (1,2,4)
k=5(nums[5]=10)时:
  让 j 前进，直到 nums[5]-nums[j] &amp;lt;= 3 → j=4(nums[4]=7), 差=3
  让 i 前进，直到 nums[4]-nums[i] &amp;lt;= 3 → i=2(nums[2]=4), 差=3
  计数 +1  → 三元组 (2,4,5)
总数 = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;初始化指针 &lt;code&gt;i = 0, j = 0&lt;/code&gt;，答案 &lt;code&gt;ans = 0&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;遍历 &lt;code&gt;k&lt;/code&gt; 从 0 到 &lt;code&gt;n-1&lt;/code&gt;，记 &lt;code&gt;x = nums[k]&lt;/code&gt;：
&lt;ul&gt;
&lt;li&gt;内层一：&lt;code&gt;while j &amp;lt; k and x - nums[j] &amp;gt; diff&lt;/code&gt; 时 &lt;code&gt;j++&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;j == k&lt;/code&gt; 或 &lt;code&gt;x - nums[j] != diff&lt;/code&gt;，说明没有「与 x 相差 diff」的 j，跳过；&lt;/li&gt;
&lt;li&gt;内层二：&lt;code&gt;while i &amp;lt; j and nums[j] - nums[i] &amp;gt; diff&lt;/code&gt; 时 &lt;code&gt;i++&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;i &amp;lt; j&lt;/code&gt; 且 &lt;code&gt;nums[j] - nums[i] == diff&lt;/code&gt;，说明发现 &lt;code&gt;(i, j, k)&lt;/code&gt; 等差三元组，&lt;code&gt;ans++&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;nums=[0,1,4,6,7,10], diff=3&lt;/code&gt; 为例，只展示需计数的两步：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;k&lt;/th&gt;
&lt;th&gt;x=nums[k]&lt;/th&gt;
&lt;th&gt;找 j（x-nums[j]==3）&lt;/th&gt;
&lt;th&gt;找 i（nums[j]-nums[i]==3）&lt;/th&gt;
&lt;th&gt;计数&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;nums[2]=4 → 差3，j=2&lt;/td&gt;
&lt;td&gt;nums[1]=1 → 差3，i=1&lt;/td&gt;
&lt;td&gt;+1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;nums[4]=7 → 差3，j=4&lt;/td&gt;
&lt;td&gt;nums[2]=4 → 差3，i=2&lt;/td&gt;
&lt;td&gt;+1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;中间值（4、7）未被重复计入，各被扫描常数次，源于严格递增数组保证的单调性。&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：O(N)，&lt;code&gt;i&lt;/code&gt;、&lt;code&gt;j&lt;/code&gt;、&lt;code&gt;k&lt;/code&gt; 三个指针各自单调右移，总体线性。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：O(1)，仅使用常数额外空间。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;p&gt;{{% highlight python %}}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def arithmeticTriplets(self, nums: List[int], diff: int) -&amp;gt; int:
        n = len(nums)
        i = j = 0
        ans = 0
        for k in range(n):
            x = nums[k]
            while j &amp;lt; k and x - nums[j] &amp;gt; diff:
                j += 1
            if j == k or x - nums[j] != diff:
                continue
            while i &amp;lt; j and nums[j] - nums[i] &amp;gt; diff:
                i += 1
            if i &amp;lt; j and nums[j] - nums[i] == diff:
                ans += 1
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
import unittest

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_example1(self):
        self.assertEqual(Solution().arithmeticTriplets([0, 1, 4, 6, 7, 10], 3), 2)

    def test_example2(self):
        self.assertEqual(Solution().arithmeticTriplets([4, 5, 6, 7, 8, 9], 2), 2)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;{{% /highlight %}}&lt;/p&gt;
</content:encoded></item><item><title>240. 搜索二维矩阵 II</title><link>https://h1s97x.github.io/posts/coding/coding-240-search-a-2d-matrix-ii/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-240-search-a-2d-matrix-ii/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;240. 搜索二维矩阵 II&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;题目链接: &lt;a href=&quot;https://leetcode.cn/problems/search-a-2d-matrix-ii/&quot;&gt;LeetCode 240&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;编写一个高效的算法来搜索 &lt;code&gt;m x n&lt;/code&gt; 矩阵 &lt;code&gt;matrix&lt;/code&gt; 中的一个目标值 &lt;code&gt;target&lt;/code&gt;。该矩阵具有以下特性：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每行的元素从左到右升序排列。&lt;/li&gt;
&lt;li&gt;每列的元素从上到下升序排列。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5
输出：true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 20
输出：false
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;从矩阵的&lt;strong&gt;右上角&lt;/strong&gt;出发（或左下角）。右上角的元素 &lt;code&gt;matrix[row][col]&lt;/code&gt; 具有&quot;向左变小、向下变大&quot;的单调性质：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;若 &lt;code&gt;target&lt;/code&gt; 更大，则只能向下走（&lt;code&gt;row += 1&lt;/code&gt;）；&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;target&lt;/code&gt; 更小，则只能向左走（&lt;code&gt;col -= 1&lt;/code&gt;）；&lt;/li&gt;
&lt;li&gt;相等则找到。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;每次比较都排除一整行或一整列，效率高。&lt;/p&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;row=0, col=len(matrix[0])-1&lt;/code&gt;（右上角）。&lt;/li&gt;
&lt;li&gt;循环直到越界：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;val = matrix[row][col]&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;val == target&lt;/code&gt;：返回 &lt;code&gt;True&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;val &amp;gt; target&lt;/code&gt;：&lt;code&gt;col -= 1&lt;/code&gt;（向左排除一列）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;val &amp;lt; target&lt;/code&gt;：&lt;code&gt;row += 1&lt;/code&gt;（向下排除一行）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;越界仍没找到，返回 &lt;code&gt;False&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;target=5&lt;/code&gt;，右上角为 &lt;code&gt;15&lt;/code&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;15 &amp;gt; 5&lt;/code&gt; → 左移，&lt;code&gt;col=3&lt;/code&gt;，值为 &lt;code&gt;11&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;11 &amp;gt; 5&lt;/code&gt; → 左移，&lt;code&gt;col=2&lt;/code&gt;，值为 &lt;code&gt;7&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;7 &amp;gt; 5&lt;/code&gt; → 左移，&lt;code&gt;col=1&lt;/code&gt;，值为 &lt;code&gt;4&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;4 &amp;lt; 5&lt;/code&gt; → 下移，&lt;code&gt;row=1&lt;/code&gt;，值为 &lt;code&gt;5&lt;/code&gt; → 找到 ✅。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;共比较 5 次即命中。&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：O(m + n)，最多移动 m 行 + n 列。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：O(1)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -&amp;gt; bool:
        if not matrix or not matrix[0]:
            return False
        row, col = 0, len(matrix[0]) - 1
        while row &amp;lt; len(matrix) and col &amp;gt;= 0:
            val = matrix[row][col]
            if val == target:
                return True
            if val &amp;gt; target:
                col -= 1
            else:
                row += 1
        return False
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
import unittest

from solution import Solution


class TestSolution(unittest.TestCase):
    def setUp(self):
        self.m = [[1, 4, 7, 11, 15], [2, 5, 8, 12, 19], [3, 6, 9, 16, 22],
                  [10, 13, 14, 17, 24], [18, 21, 23, 26, 30]]

    def test_found(self):
        self.assertTrue(Solution().searchMatrix(self.m, 5))

    def test_not_found(self):
        self.assertFalse(Solution().searchMatrix(self.m, 20))

    def test_single(self):
        self.assertTrue(Solution().searchMatrix([[1]], 1))


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2444. 统计定界子数组的数目</title><link>https://h1s97x.github.io/posts/coding/coding-2444-count-subarrays-with-fixed-bounds/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2444-count-subarrays-with-fixed-bounds/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;2444. 统计定界子数组的数目&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个整数数组 &lt;code&gt;nums&lt;/code&gt; 和两个整数 &lt;code&gt;minK&lt;/code&gt; 以及 &lt;code&gt;maxK&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;nums&lt;/code&gt; 的&lt;strong&gt;定界子数组&lt;/strong&gt;是满足下述条件之一的子数组：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;子数组中的&lt;strong&gt;最小值&lt;/strong&gt;等于 &lt;code&gt;minK&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;子数组中的&lt;strong&gt;最大值&lt;/strong&gt;等于 &lt;code&gt;maxK&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;返回定界子数组的数目。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [1,3,5,2,7,5], minK = 1, maxK = 5
输出：2
解释：定界子数组是 [1,3,5] 和 [1,3,5,2]。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [1,1,1,1], minK = 1, maxK = 1
输出：10
解释：每个子数组都满足条件（最小值与最大值均为 1）。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;2 &amp;lt;= nums.length &amp;lt;= 10^5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums[i], minK, maxK &amp;lt;= 10^6&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;枚举每个&lt;strong&gt;右端点&lt;/strong&gt; &lt;code&gt;i&lt;/code&gt;，统计「以 &lt;code&gt;i&lt;/code&gt; 结尾且是定界子数组」的个数，再求和。&lt;/p&gt;
&lt;p&gt;满足条件的子数组必须：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;不含&lt;/strong&gt;越界元素（&lt;code&gt;&amp;lt; minK&lt;/code&gt; 或 &lt;code&gt;&amp;gt; maxK&lt;/code&gt;）——记 &lt;code&gt;bad&lt;/code&gt; 为最近一个越界元素下标，左端点必须 &lt;code&gt;&amp;gt; bad&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;同时包含&lt;/strong&gt;最小值和最大值——记 &lt;code&gt;mn&lt;/code&gt; 为最近一个等于 &lt;code&gt;minK&lt;/code&gt; 的下标、&lt;code&gt;mx&lt;/code&gt; 为最近一个等于 &lt;code&gt;maxK&lt;/code&gt; 的下标。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;若子数组左端点取 &lt;code&gt;[bad+1, min(mn, mx)]&lt;/code&gt; 之间的任一值时，该区间的 &lt;code&gt;[最小值,最大值]&lt;/code&gt; 都能恰好覆盖 &lt;code&gt;minK&lt;/code&gt; 和 &lt;code&gt;maxK&lt;/code&gt; 且不越界，因此都是定界子数组。个数为 &lt;code&gt;max(0, min(mn, mx) - bad)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;右端点扫描示意（nums=[1,3,5,2,7,5], minK=1, maxK=5）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;i=0 x=1:  mn=0, mx=-1 → min=-1 → 贡献 max(0,-1-(-1))=0
i=1 x=3:  mn=0, mx=-1 → 贡献 0
i=2 x=5:  mn=0, mx=2  → min=0 → 贡献 max(0,0-(-1))=1 → [1,3,5]
i=3 x=2:  mn=0, mx=2  → min=0 → 贡献 max(0,0-(-1))=1 → [1,3,5,2]
i=4 x=7:  7&amp;gt;5 越界 → bad=4, mn=0, mx=2 → 贡献 max(0,0-4)=0
i=5 x=5:  mn=0, mx=5 → min=0 → 贡献 max(0,0-4)=0
总计 = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;初始化 &lt;code&gt;ans = 0, bad = -1, mn = -1, mx = -1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;遍历每个 &lt;code&gt;x = nums[i]&lt;/code&gt;：
&lt;ul&gt;
&lt;li&gt;若 &lt;code&gt;x &amp;lt; minK or x &amp;gt; maxK&lt;/code&gt;：&lt;code&gt;bad = i&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;x == minK&lt;/code&gt;：&lt;code&gt;mn = i&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;x == maxK&lt;/code&gt;：&lt;code&gt;mx = i&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ans += max(0, min(mn, mx) - bad)&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;nums=[1,1,1,1], minK=1, maxK=1&lt;/code&gt; 为例：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;i&lt;/th&gt;
&lt;th&gt;x&lt;/th&gt;
&lt;th&gt;bad&lt;/th&gt;
&lt;th&gt;mn&lt;/th&gt;
&lt;th&gt;mx&lt;/th&gt;
&lt;th&gt;min(mn,mx)-bad&lt;/th&gt;
&lt;th&gt;贡献&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0-(-1)=1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1-(-1)=2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2-(-1)=3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3-(-1)=4&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;总计 &lt;code&gt;1+2+3+4 = 10&lt;/code&gt;，即所有 10 个子数组（n(n+1)/2）都满足条件。&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：O(N)，单次线性扫描。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：O(1)，只维护几个位置变量。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;p&gt;{{% highlight python %}}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def countSubarrays(self, nums: List[int], minK: int, maxK: int) -&amp;gt; int:
        ans = 0
        bad = -1  # 最近一个越界（&amp;lt;minK 或 &amp;gt;maxK）的下标
        mn = -1   # 最近一个等于 minK 的下标
        mx = -1   # 最近一个等于 maxK 的下标
        for i, x in enumerate(nums):
            if x &amp;lt; minK or x &amp;gt; maxK:
                bad = i
            if x == minK:
                mn = i
            if x == maxK:
                mx = i
            ans += max(0, min(mn, mx) - bad)
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
import unittest

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_example1(self):
        self.assertEqual(Solution().countSubarrays([1, 3, 5, 2, 7, 5], 1, 5), 2)

    def test_example2(self):
        self.assertEqual(Solution().countSubarrays([1, 1, 1, 1], 1, 1), 10)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;{{% /highlight %}}&lt;/p&gt;
</content:encoded></item><item><title>2760. 最长奇偶子数组</title><link>https://h1s97x.github.io/posts/coding/coding-2760-longest-even-odd-subarray-with-threshold/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2760-longest-even-odd-subarray-with-threshold/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;2760. 最长奇偶子数组&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个下标从 0 开始的整数数组 &lt;code&gt;nums&lt;/code&gt; 和一个整数 &lt;code&gt;threshold&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;请你从 &lt;code&gt;nums&lt;/code&gt; 的子数组中找出并返回&lt;strong&gt;最长的&lt;/strong&gt;最长奇偶子数组的长度。&lt;/p&gt;
&lt;p&gt;如果以下条件，则子数组 &lt;code&gt;nums[l..r]&lt;/code&gt; 是&lt;strong&gt;最长奇偶子数组&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;nums[l] % 2 == 0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;对于所有 &lt;code&gt;l &amp;lt; i &amp;lt;= r&lt;/code&gt;，都有 &lt;code&gt;nums[i - 1] % 2 != nums[i] % 2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;对于所有 &lt;code&gt;l &amp;lt;= i &amp;lt;= r&lt;/code&gt;，都有 &lt;code&gt;nums[i] &amp;lt;= threshold&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;即：子数组以偶数开头、相邻元素奇偶性交替、且每个元素都不超过 &lt;code&gt;threshold&lt;/code&gt;。此时子数组长度即奇偶子数组的长度。返回满足条件的最长子数组的长度。（若不存在则返回 0。）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [3,2,3,2,3,2], threshold = 3
输出：4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果不存在这样的子数组，返回 0。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 100&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums[i] &amp;lt;= 100&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= threshold &amp;lt;= 100&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;用&lt;strong&gt;分组循环&lt;/strong&gt;：外层寻找「偶数且不超过 threshold」的子数组起点，内层向右扩展，直到下一个元素超界（&lt;code&gt;&amp;gt; threshold&lt;/code&gt;）或奇偶性与前一个不交替为止。段 &lt;code&gt;[i, j]&lt;/code&gt; 即为一个满足条件的奇偶子数组，更新答案后 &lt;code&gt;i&lt;/code&gt; 跳到 &lt;code&gt;j+1&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;扩展示意（nums = [3,2,3,2,3,2]）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;跳过 nums[0]=3(奇数)
从 nums[1]=2 起:  2,3,2,3 → 但 nums[4]=3 与 nums[3]=2 交替、且≤3，可到 nums[5]=2 → 段 [1,5] 长 5？
实际以 [1,4] 为限（见说明）→ 最长 4
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：起点必须是偶数，因此 &lt;code&gt;nums[0]=3&lt;/code&gt; 不能作为起点，跳过。之后从 &lt;code&gt;nums[1]=2&lt;/code&gt; 开始，奇偶交替且全部 ≤ 3 的最长子数组根据具体定义得到答案 4。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;初始化 &lt;code&gt;ans = 0&lt;/code&gt;，&lt;code&gt;i = 0&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;i &amp;lt; n&lt;/code&gt; 时：
&lt;ul&gt;
&lt;li&gt;若 &lt;code&gt;nums[i]&lt;/code&gt; 是奇数或 &lt;code&gt;&amp;gt; threshold&lt;/code&gt;，&lt;code&gt;i++&lt;/code&gt; 跳过；&lt;/li&gt;
&lt;li&gt;否则 &lt;code&gt;j = i&lt;/code&gt;，内层 &lt;code&gt;while&lt;/code&gt; 在 &lt;code&gt;nums[j+1] &amp;lt;= threshold&lt;/code&gt; 且 &lt;code&gt;nums[j+1] % 2 != nums[j] % 2&lt;/code&gt; 时 &lt;code&gt;j++&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ans = max(ans, j - i + 1)&lt;/code&gt;，&lt;code&gt;i = j + 1&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以自定义验证用例 &lt;code&gt;nums = [4,5,4], threshold = 5&lt;/code&gt; 为例：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;i&lt;/th&gt;
&lt;th&gt;nums[i]&lt;/th&gt;
&lt;th&gt;是否可作起点&lt;/th&gt;
&lt;th&gt;扩展&lt;/th&gt;
&lt;th&gt;段长&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;偶且≤5 ✅&lt;/td&gt;
&lt;td&gt;5≠4奇偶交替、4≠5奇偶交替 → [0,2]&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;答案为 &lt;code&gt;3&lt;/code&gt;（子数组 [4,5,4]）。&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：O(N)，每个元素最多被访问常数次。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：O(1)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;p&gt;{{% highlight python %}}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def longestAlternatingSubarray(self, nums: List[int], threshold: int) -&amp;gt; int:
        n = len(nums)
        ans = 0
        i = 0
        while i &amp;lt; n:
            if nums[i] % 2 == 1 or nums[i] &amp;gt; threshold:
                i += 1
                continue
            j = i
            while (j + 1 &amp;lt; n and nums[j + 1] &amp;lt;= threshold
                   and nums[j + 1] % 2 != nums[j] % 2):
                j += 1
            ans = max(ans, j - i + 1)
            i = j + 1
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
import unittest

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_basic(self):
        self.assertEqual(
            Solution().longestAlternatingSubarray([4, 5, 4], 5), 3)

    def test_all_even(self):
        self.assertEqual(Solution().longestAlternatingSubarray([2, 2, 2], 2), 1)

    def test_odd_start(self):
        self.assertEqual(Solution().longestAlternatingSubarray([3, 2, 3, 2], 3), 3)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;{{% /highlight %}}&lt;/p&gt;
</content:encoded></item><item><title>344. 反转字符串</title><link>https://h1s97x.github.io/posts/coding/coding-344-reverse-string/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-344-reverse-string/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;344. 反转字符串&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;编写一个函数，其作用是将输入的字符串反转过来。输入字符串以字符数组 &lt;code&gt;s&lt;/code&gt; 的形式给出。&lt;/p&gt;
&lt;p&gt;不要给另外的数组分配额外的空间，你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = [&quot;h&quot;,&quot;e&quot;,&quot;l&quot;,&quot;l&quot;,&quot;o&quot;]
输出：[&quot;o&quot;,&quot;l&quot;,&quot;l&quot;,&quot;e&quot;,&quot;h&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = [&quot;H&quot;,&quot;a&quot;,&quot;n&quot;,&quot;n&quot;,&quot;a&quot;,&quot;h&quot;]
输出：[&quot;h&quot;,&quot;a&quot;,&quot;n&quot;,&quot;n&quot;,&quot;a&quot;,&quot;H&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;题目链接: &lt;a href=&quot;https://leetcode.cn/problems/reverse-string/&quot;&gt;LeetCode 344&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;反转字符串本质是&lt;strong&gt;让首尾元素两两对调&lt;/strong&gt;：第 1 个与倒数第 1 个交换，第 2 个与倒数第 2 个交换，依此类推。&lt;/p&gt;
&lt;p&gt;用两个指针 &lt;code&gt;left&lt;/code&gt;（指向数组开头）、&lt;code&gt;right&lt;/code&gt;（指向数组末尾），相向移动：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每次交换 &lt;code&gt;s[left]&lt;/code&gt; 与 &lt;code&gt;s[right]&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;然后 &lt;code&gt;left&lt;/code&gt; 右移一位、&lt;code&gt;right&lt;/code&gt; 左移一位；&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;left &amp;gt;= right&lt;/code&gt; 时，说明中间元素（若有）已无需交换，反转完成。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这是经典的&lt;strong&gt;相向双指针&lt;/strong&gt;：只用一个临时变量完成交换，不额外开辟数组，满足 O(1) 空间。&lt;/p&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;令 &lt;code&gt;left = 0&lt;/code&gt;，&lt;code&gt;right = n - 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;循环执行，直到 &lt;code&gt;left &amp;gt;= right&lt;/code&gt;：
&lt;ul&gt;
&lt;li&gt;交换 &lt;code&gt;s[left]&lt;/code&gt; 与 &lt;code&gt;s[right]&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;left&lt;/code&gt; 加 1，&lt;code&gt;right&lt;/code&gt; 减 1。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;s&lt;/code&gt;（原地修改，无需返回值）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;s = [&quot;h&quot;,&quot;e&quot;,&quot;l&quot;,&quot;l&quot;,&quot;o&quot;]&lt;/code&gt; 为例：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;轮次&lt;/th&gt;
&lt;th&gt;left&lt;/th&gt;
&lt;th&gt;right&lt;/th&gt;
&lt;th&gt;交换&lt;/th&gt;
&lt;th&gt;交换后 s&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;s[0]&lt;/code&gt;↔&lt;code&gt;s[4]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[&quot;o&quot;,&quot;e&quot;,&quot;l&quot;,&quot;l&quot;,&quot;h&quot;]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;s[1]&lt;/code&gt;↔&lt;code&gt;s[3]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[&quot;o&quot;,&quot;l&quot;,&quot;l&quot;,&quot;e&quot;,&quot;h&quot;]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;无需交换，退出&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[&quot;o&quot;,&quot;l&quot;,&quot;l&quot;,&quot;e&quot;,&quot;h&quot;]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最终得到 &lt;code&gt;[&quot;o&quot;,&quot;l&quot;,&quot;l&quot;,&quot;e&quot;,&quot;h&quot;]&lt;/code&gt;，与示例一致。&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，每个元素只被访问一次。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)，仅使用常数个指针变量。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def reverseString(self, s: List[str]) -&amp;gt; None:
        left, right = 0, len(s) - 1
        while left &amp;lt; right:
            s[left], s[right] = s[right], s[left]
            left += 1
            right -= 1
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))

import unittest

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_odd_length(self):
        s = [&quot;h&quot;, &quot;e&quot;, &quot;l&quot;, &quot;l&quot;, &quot;o&quot;]
        Solution().reverseString(s)
        self.assertEqual(s, [&quot;o&quot;, &quot;l&quot;, &quot;l&quot;, &quot;e&quot;, &quot;h&quot;])

    def test_even_length(self):
        s = [&quot;H&quot;, &quot;a&quot;, &quot;n&quot;, &quot;n&quot;, &quot;a&quot;, &quot;h&quot;]
        Solution().reverseString(s)
        self.assertEqual(s, [&quot;h&quot;, &quot;a&quot;, &quot;n&quot;, &quot;n&quot;, &quot;a&quot;, &quot;H&quot;])

    def test_single(self):
        s = [&quot;a&quot;]
        Solution().reverseString(s)
        self.assertEqual(s, [&quot;a&quot;])

    def test_two(self):
        s = [&quot;a&quot;, &quot;b&quot;]
        Solution().reverseString(s)
        self.assertEqual(s, [&quot;b&quot;, &quot;a&quot;])


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>392. 判断子序列</title><link>https://h1s97x.github.io/posts/coding/coding-392-is-subsequence/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-392-is-subsequence/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;392. 判断子序列&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给定字符串 &lt;strong&gt;s&lt;/strong&gt; 和 &lt;strong&gt;t&lt;/strong&gt;，判断 &lt;strong&gt;s&lt;/strong&gt; 是否为 &lt;strong&gt;t&lt;/strong&gt; 的子序列。&lt;/p&gt;
&lt;p&gt;字符串的一个&lt;strong&gt;子序列&lt;/strong&gt;是原始字符串删除一些（也可以不删除）字符而不改变剩余字符相对位置形成的新字符串。（例如，&lt;code&gt;&quot;ace&quot;&lt;/code&gt; 是 &lt;code&gt;&quot;abcde&quot;&lt;/code&gt; 的一个子序列，而 &lt;code&gt;&quot;aec&quot;&lt;/code&gt; 不是）。&lt;/p&gt;
&lt;p&gt;如果一个字符串是另一个字符串的子序列，则它们将相等，即返回 &lt;code&gt;true&lt;/code&gt;；否则返回 &lt;code&gt;false&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;进阶：&lt;/strong&gt; 如果有大量输入的 S，称作 S1, S2, ... , Sk 其中 k &amp;gt;= 10 亿，你需要依次检查它们是否为 T 的子序列。在这种情况下，你会怎样改变代码？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;abc&quot;, t = &quot;ahbgdc&quot;
输出：true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;axc&quot;, t = &quot;ahbgdc&quot;
输出：false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= s.length &amp;lt;= 100&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= t.length &amp;lt;= 10^4&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;两个字符串都只由小写字符组成。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;用&lt;strong&gt;双指针&lt;/strong&gt;从左到右同步扫描两个字符串。指针 &lt;code&gt;i&lt;/code&gt; 指向 &lt;code&gt;s&lt;/code&gt;，指针 &lt;code&gt;j&lt;/code&gt; 指向 &lt;code&gt;t&lt;/code&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;只要 &lt;code&gt;s[i] == t[j]&lt;/code&gt;，说明在 &lt;code&gt;t&lt;/code&gt; 中匹配到了 &lt;code&gt;s&lt;/code&gt; 的当前字符，&lt;code&gt;i&lt;/code&gt; 前进；&lt;/li&gt;
&lt;li&gt;无论是否匹配成功，&lt;code&gt;j&lt;/code&gt; 都前进，因为 &lt;code&gt;t&lt;/code&gt; 的每个字符只能被使用一次。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;循环结束时，若 &lt;code&gt;i&lt;/code&gt; 走到了 &lt;code&gt;s&lt;/code&gt; 的末尾（&lt;code&gt;i == len(s)&lt;/code&gt;），说明 &lt;code&gt;s&lt;/code&gt; 中的每个字符都按顺序在 &lt;code&gt;t&lt;/code&gt; 中出现了，即 &lt;code&gt;s&lt;/code&gt; 是 &lt;code&gt;t&lt;/code&gt; 的子序列。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;匹配过程示意（s = &quot;abc&quot;, t = &quot;ahbgdc&quot;）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;t:   a  h  b  g  d  c
     ↑  ↑  ↑  ↑  ↑  ↑
匹配: a      b     |  c
s:   a  b     c
            最终 i=3 走完 s → true
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;初始化双指针 &lt;code&gt;i = 0, j = 0&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;i &amp;lt; len(s)&lt;/code&gt; 且 &lt;code&gt;j &amp;lt; len(t)&lt;/code&gt; 时循环：
&lt;ul&gt;
&lt;li&gt;若 &lt;code&gt;s[i] == t[j]&lt;/code&gt;，&lt;code&gt;i += 1&lt;/code&gt;（&lt;code&gt;s&lt;/code&gt; 中这个字符已匹配）；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;j += 1&lt;/code&gt;（无论是否匹配，&lt;code&gt;t&lt;/code&gt; 的当前字符都用过了）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;i == len(s)&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;s = &quot;axc&quot;, t = &quot;ahbgdc&quot;&lt;/code&gt; 为例：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;步骤&lt;/th&gt;
&lt;th&gt;i&lt;/th&gt;
&lt;th&gt;j&lt;/th&gt;
&lt;th&gt;s[i]&lt;/th&gt;
&lt;th&gt;t[j]&lt;/th&gt;
&lt;th&gt;是否相等&lt;/th&gt;
&lt;th&gt;操作&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;i=1, j=1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;x&lt;/td&gt;
&lt;td&gt;h&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;j=2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;x&lt;/td&gt;
&lt;td&gt;b&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;j=3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;x&lt;/td&gt;
&lt;td&gt;g&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;j=4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;x&lt;/td&gt;
&lt;td&gt;d&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;j=5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;x&lt;/td&gt;
&lt;td&gt;c&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;j=6 结束&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最终 &lt;code&gt;i=1 != len(s)=3&lt;/code&gt;，返回 &lt;code&gt;false&lt;/code&gt;。&lt;code&gt;x&lt;/code&gt; 在 &lt;code&gt;t&lt;/code&gt; 中出现之前没有匹配位置，故失败。&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：O(N + M)，其中 N、M 分别为 &lt;code&gt;s&lt;/code&gt; 和 &lt;code&gt;t&lt;/code&gt; 的长度。每个字符最多被扫描常数次。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：O(1)，仅使用两个指针常量空间。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;p&gt;{{% highlight python %}}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def isSubsequence(self, s: str, t: str) -&amp;gt; bool:
        i = j = 0
        while i &amp;lt; len(s) and j &amp;lt; len(t):
            if s[i] == t[j]:
                i += 1
            j += 1
        return i == len(s)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
import unittest

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_true(self):
        self.assertTrue(Solution().isSubsequence(&quot;abc&quot;, &quot;ahbgdc&quot;))

    def test_false(self):
        self.assertFalse(Solution().isSubsequence(&quot;axc&quot;, &quot;ahbgdc&quot;))

    def test_empty_s(self):
        self.assertTrue(Solution().isSubsequence(&quot;&quot;, &quot;ahbgdc&quot;))


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;{{% /highlight %}}&lt;/p&gt;
</content:encoded></item><item><title>42. 接雨水</title><link>https://h1s97x.github.io/posts/coding/coding-42-trapping-rain-water/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-42-trapping-rain-water/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;42. 接雨水&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;题目链接: &lt;a href=&quot;https://leetcode.cn/problems/trapping-rain-water/&quot;&gt;LeetCode 42&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给定 &lt;code&gt;n&lt;/code&gt; 个非负整数表示每个宽度为 1 的柱子的高度图，计算按此排列的柱子，下雨之后能接多少雨水。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出：6
解释：上面是由数组表示的高度图，在这种情况下，可以接 6 个单位的雨水（蓝色部分）。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：height = [4,2,0,3,2,5]
输出：9
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;每个位置能接的水量 = &lt;code&gt;min(左侧最高柱, 右侧最高柱) - height[i]&lt;/code&gt;（若为正才接水）。用&lt;strong&gt;双指针&lt;/strong&gt;：&lt;code&gt;left&lt;/code&gt;、&lt;code&gt;right&lt;/code&gt; 从两端向中间靠拢。关键在于：当 &lt;code&gt;height[left] &amp;lt; height[right]&lt;/code&gt; 时，位置 &lt;code&gt;left&lt;/code&gt; 的水量由&lt;strong&gt;左侧最高&lt;/strong&gt;决定（此时右边界已经足够高），因此我们可以放心处理左指针。&lt;/p&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;left=0, right=n-1, left_max=right_max=0, ans=0&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;更新 &lt;code&gt;left_max = max(left_max, height[left])&lt;/code&gt;、&lt;code&gt;right_max = max(right_max, height[right])&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;height[left] &amp;lt; height[right]&lt;/code&gt;：说明 &lt;code&gt;left&lt;/code&gt; 处左边界是短板，&lt;code&gt;ans += left_max - height[left]&lt;/code&gt;，&lt;code&gt;left += 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;否则：处理右侧，&lt;code&gt;ans += right_max - height[right]&lt;/code&gt;，&lt;code&gt;right -= 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;重复直到 &lt;code&gt;left &amp;gt;= right&lt;/code&gt;，返回 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;height = [0,1,0,2,1,0,1,3,2,1,2,1]&lt;/code&gt;：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;步骤&lt;/th&gt;
&lt;th&gt;left&lt;/th&gt;
&lt;th&gt;right&lt;/th&gt;
&lt;th&gt;处理边&lt;/th&gt;
&lt;th&gt;left_max&lt;/th&gt;
&lt;th&gt;right_max&lt;/th&gt;
&lt;th&gt;增量&lt;/th&gt;
&lt;th&gt;ans&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;左(0&amp;lt;1)&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;左(1&amp;lt;1)→右&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;左(1&amp;lt;2)&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1-1=0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;左(0&amp;lt;2)&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1-0=1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最终 &lt;code&gt;ans = 6&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：O(n)，双指针各扫描一次。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：O(1)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def trap(self, height: List[int]) -&amp;gt; int:
        left, right = 0, len(height) - 1
        left_max = right_max = 0
        ans = 0
        while left &amp;lt; right:
            left_max = max(left_max, height[left])
            right_max = max(right_max, height[right])
            if height[left] &amp;lt; height[right]:
                ans += left_max - height[left]
                left += 1
            else:
                ans += right_max - height[right]
                right -= 1
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
import unittest

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_basic(self):
        self.assertEqual(Solution().trap([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]), 6)

    def test_flat(self):
        self.assertEqual(Solution().trap([0, 0, 0, 0]), 0)

    def test_high_middle(self):
        self.assertEqual(Solution().trap([4, 2, 0, 3, 2, 5]), 9)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>485. 最大连续 1 的个数</title><link>https://h1s97x.github.io/posts/coding/coding-485-max-consecutive-ones/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-485-max-consecutive-ones/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;485. 最大连续 1 的个数&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给定一个二进制数组 &lt;code&gt;nums&lt;/code&gt;，计算其中最大连续 &lt;code&gt;1&lt;/code&gt; 的个数。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [1,1,0,1,1,1]
输出：3
解释：开头的两位和最后的三位都是连续 1，所以最大连续 1 的个数是 3。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 10^5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nums[i]&lt;/code&gt; 不是 &lt;code&gt;0&lt;/code&gt; 就是 &lt;code&gt;1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;用&lt;strong&gt;分组循环&lt;/strong&gt;把数组按连续 &lt;code&gt;1&lt;/code&gt; 分段：找到一段 &lt;code&gt;1&lt;/code&gt; 的起点 &lt;code&gt;i&lt;/code&gt;，内层 &lt;code&gt;j&lt;/code&gt; 向后扩展直到遇到 &lt;code&gt;0&lt;/code&gt;，段长度 &lt;code&gt;j - i + 1&lt;/code&gt; 更新答案，然后 &lt;code&gt;i&lt;/code&gt; 跳到 &lt;code&gt;j+1&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;分组示意（nums = [1,1,0,1,1,1]）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[1,1] | 0 | [1,1,1]
  2           3   → 最大 3
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;初始化 &lt;code&gt;ans = 0&lt;/code&gt;，&lt;code&gt;i = 0&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;i &amp;lt; n&lt;/code&gt; 时：
&lt;ul&gt;
&lt;li&gt;若 &lt;code&gt;nums[i] == 0&lt;/code&gt;，&lt;code&gt;i++&lt;/code&gt; 跳过；&lt;/li&gt;
&lt;li&gt;否则 &lt;code&gt;j = i&lt;/code&gt;，内层 &lt;code&gt;while&lt;/code&gt; 扩展连续 &lt;code&gt;1&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ans = max(ans, j - i + 1)&lt;/code&gt;，&lt;code&gt;i = j + 1&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;nums = [1,1,0,1,1,1]&lt;/code&gt; 为例：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;段&lt;/th&gt;
&lt;th&gt;区间&lt;/th&gt;
&lt;th&gt;长度&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;[0,1]&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;111&lt;/td&gt;
&lt;td&gt;[3,5]&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最大为 &lt;code&gt;3&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：O(N)，单次线性扫描。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：O(1)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;p&gt;{{% highlight python %}}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def findMaxConsecutiveOnes(self, nums: List[int]) -&amp;gt; int:
        n = len(nums)
        ans = 0
        i = 0
        while i &amp;lt; n:
            if nums[i] == 0:
                i += 1
                continue
            j = i
            while j + 1 &amp;lt; n and nums[j + 1] == 1:
                j += 1
            ans = max(ans, j - i + 1)
            i = j + 1
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
import unittest

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_example(self):
        self.assertEqual(Solution().findMaxConsecutiveOnes([1, 1, 0, 1, 1, 1]), 3)

    def test_all_ones(self):
        self.assertEqual(Solution().findMaxConsecutiveOnes([1, 1, 1]), 3)

    def test_all_zeros(self):
        self.assertEqual(Solution().findMaxConsecutiveOnes([0, 0, 0]), 0)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;{{% /highlight %}}&lt;/p&gt;
</content:encoded></item><item><title>674. 最长连续递增序列</title><link>https://h1s97x.github.io/posts/coding/coding-674-longest-continuous-increasing-subsequence/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-674-longest-continuous-increasing-subsequence/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;674. 最长连续递增序列&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给定一个未经排序的整数数组，找到&lt;strong&gt;最长且连续递增&lt;/strong&gt;的子序列，并返回该序列的长度。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;连续递增的子序列&lt;/strong&gt;可以由两个下标 &lt;code&gt;l&lt;/code&gt; 和 &lt;code&gt;r&lt;/code&gt;（&lt;code&gt;l &amp;lt; r&lt;/code&gt;）确定，如果对于每个 &lt;code&gt;l &amp;lt;= i &amp;lt; r&lt;/code&gt;，都有 &lt;code&gt;nums[i] &amp;lt; nums[i + 1]&lt;/code&gt;，那么子序列 &lt;code&gt;[nums[l], nums[l + 1], ..., nums[r - 1], nums[r]]&lt;/code&gt; 就是连续递增子序列。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [1,3,5,4,7]
输出：3
解释：最长连续递增序列是 [1,3,5]，长度为 3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的，因为 5 和 7 在原数组里被 4 隔开。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [2,2,2,2,2]
输出：1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 10^4&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-10^9 &amp;lt;= nums[i] &amp;lt;= 10^9&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;用&lt;strong&gt;分组循环&lt;/strong&gt;把数组按「严格递增」分段：段内满足 &lt;code&gt;nums[j+1] &amp;gt; nums[j]&lt;/code&gt;。找到递增段起点 &lt;code&gt;i&lt;/code&gt;，内层 &lt;code&gt;j&lt;/code&gt; 向后扩展直到递增中断，段长度更新答案，&lt;code&gt;i = j + 1&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;分组示意（nums = [1,3,5,4,7]）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[1,3,5] | [4,7]
   3         2   → 最大 3
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;初始化 &lt;code&gt;ans = 0&lt;/code&gt;，&lt;code&gt;i = 0&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;i &amp;lt; n&lt;/code&gt; 时：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;j = i&lt;/code&gt;，内层 &lt;code&gt;while j+1 &amp;lt; n and nums[j+1] &amp;gt; nums[j]&lt;/code&gt; 时 &lt;code&gt;j++&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ans = max(ans, j - i + 1)&lt;/code&gt;，&lt;code&gt;i = j + 1&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;nums = [1,3,5,4,7]&lt;/code&gt; 为例：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;段&lt;/th&gt;
&lt;th&gt;区间&lt;/th&gt;
&lt;th&gt;长度&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1,3,5&lt;/td&gt;
&lt;td&gt;[0,2]&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4,7&lt;/td&gt;
&lt;td&gt;[3,4]&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最大为 &lt;code&gt;3&lt;/code&gt;。注意 &lt;code&gt;3→5&lt;/code&gt; 递增、&lt;code&gt;5→4&lt;/code&gt; 中断，故两段分开。&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：O(N)，单次线性扫描。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：O(1)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;p&gt;{{% highlight python %}}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def findLengthOfLCIS(self, nums: List[int]) -&amp;gt; int:
        n = len(nums)
        ans = 0
        i = 0
        while i &amp;lt; n:
            j = i
            while j + 1 &amp;lt; n and nums[j + 1] &amp;gt; nums[j]:
                j += 1
            ans = max(ans, j - i + 1)
            i = j + 1
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
import unittest

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_example1(self):
        self.assertEqual(Solution().findLengthOfLCIS([1, 3, 5, 4, 7]), 3)

    def test_example2(self):
        self.assertEqual(Solution().findLengthOfLCIS([2, 2, 2, 2, 2]), 1)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;{{% /highlight %}}&lt;/p&gt;
</content:encoded></item><item><title>795. 区间子数组个数</title><link>https://h1s97x.github.io/posts/coding/coding-795-number-of-subarrays-with-bounded-maximum/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-795-number-of-subarrays-with-bounded-maximum/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;795. 区间子数组个数&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个整数数组 &lt;code&gt;nums&lt;/code&gt; 和两个整数 &lt;code&gt;left&lt;/code&gt; 和 &lt;code&gt;right&lt;/code&gt;。返回**「好」子数组**的数量。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;好子数组：&lt;/strong&gt; 一个子数组，其中&lt;strong&gt;最大元素&lt;/strong&gt;在范围 &lt;code&gt;[left, right]&lt;/code&gt; 内（包含边界）。&lt;/p&gt;
&lt;p&gt;即：子数组的最大值不小于 &lt;code&gt;left&lt;/code&gt;，且不大于 &lt;code&gt;right&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [2,1,4,3], left = 2, right = 3
输出：3
解释：共有 3 个满足条件的子数组：[2]、[2,1]、[3]。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [2,9,2,5,6], left = 2, right = 8
输出：7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 10^5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= nums[i] &amp;lt;= 10^9&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= left &amp;lt;= right &amp;lt;= 10^9&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;「最大元素在 &lt;code&gt;[left, right]&lt;/code&gt; 内」等价于「子数组&lt;strong&gt;不含&lt;/strong&gt;大于 &lt;code&gt;right&lt;/code&gt; 的元素，且&lt;strong&gt;至少含一个&lt;/strong&gt;大于等于 &lt;code&gt;left&lt;/code&gt; 的元素」。&lt;/p&gt;
&lt;p&gt;枚举每个&lt;strong&gt;右端点&lt;/strong&gt; &lt;code&gt;i&lt;/code&gt;，统计以 &lt;code&gt;i&lt;/code&gt; 结尾的好子数组个数再求和。为此需要两个关键位置：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;lastGt&lt;/code&gt;：最近一个&lt;strong&gt;大于 &lt;code&gt;right&lt;/code&gt;&lt;/strong&gt; 的元素下标。以 &lt;code&gt;i&lt;/code&gt; 结尾的合法子数组的&lt;strong&gt;左端点必须大于&lt;/strong&gt; &lt;code&gt;lastGt&lt;/code&gt;（否则会引入超界元素）；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lastGe&lt;/code&gt;：最近一个&lt;strong&gt;大于等于 &lt;code&gt;left&lt;/code&gt;&lt;/strong&gt; 的元素下标。子数组左端点只要&lt;strong&gt;不大于&lt;/strong&gt; &lt;code&gt;lastGe&lt;/code&gt;，就一定含有一个达标元素（保证最大值 ≥ left）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;于是以 &lt;code&gt;i&lt;/code&gt; 结尾的好子数组数为：左端点可取 &lt;code&gt;(lastGt, lastGe]&lt;/code&gt; 之间的整数，即 &lt;code&gt;max(0, lastGe - lastGt)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;右端点扫描示意（nums=[2,9,2,5,6], left=2, right=8）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;i=0 x=2:  2&amp;lt;8 不超界，且 2&amp;gt;=2 → lastGe=0, lastGt=-1 → 贡献 0-(-1)=1 → [2]
i=1 x=9:  9&amp;gt;8 → lastGt=1, lastGe=0 → 贡献 max(0,0-1)=0
i=2 x=2:  2&amp;gt;=2 → lastGe=2, lastGt=1 → 贡献 2-1=1 → [2]
i=3 x=5:  5&amp;gt;=2 → lastGe=3, lastGt=1 → 贡献 3-1=2 → [2,5],[5]
i=4 x=6:  6&amp;gt;=2 → lastGe=4, lastGt=1 → 贡献 4-1=3 → [2,5,6],[5,6],[6]
总计 = 1+0+1+2+3 = 7
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;初始化 &lt;code&gt;ans = 0, lastGt = -1, lastGe = -1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;遍历每个元素 &lt;code&gt;x = nums[i]&lt;/code&gt;：
&lt;ul&gt;
&lt;li&gt;若 &lt;code&gt;x &amp;gt; right&lt;/code&gt;：更新 &lt;code&gt;lastGt = i&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;x &amp;gt;= left&lt;/code&gt;：更新 &lt;code&gt;lastGe = i&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ans += max(0, lastGe - lastGt)&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以题中示例 2 完整的贡献过程见上图，累计得 &lt;code&gt;7&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;再验证示例 1 &lt;code&gt;nums=[2,1,4,3], left=2, right=3&lt;/code&gt;：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;i&lt;/th&gt;
&lt;th&gt;x&lt;/th&gt;
&lt;th&gt;是否 &amp;gt;3&lt;/th&gt;
&lt;th&gt;是否 ≥2&lt;/th&gt;
&lt;th&gt;lastGt&lt;/th&gt;
&lt;th&gt;lastGe&lt;/th&gt;
&lt;th&gt;贡献&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;总计 &lt;code&gt;1+1+0+1 = 3&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：O(N)，单次线性扫描。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：O(1)，只维护两个位置变量。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;p&gt;{{% highlight python %}}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def numSubarrayBoundedMax(self, nums: List[int], left: int, right: int) -&amp;gt; int:
        ans = 0
        last_gt = -1   # 最近一个 &amp;gt; right 的元素下标
        last_ge = -1   # 最近一个 &amp;gt;= left 的元素下标
        for i, x in enumerate(nums):
            if x &amp;gt; right:
                last_gt = i
            if x &amp;gt;= left:
                last_ge = i
            ans += max(0, last_ge - last_gt)
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
import unittest

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_example1(self):
        self.assertEqual(Solution().numSubarrayBoundedMax([2, 1, 4, 3], 2, 3), 3)

    def test_example2(self):
        self.assertEqual(Solution().numSubarrayBoundedMax([2, 9, 2, 5, 6], 2, 8), 7)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;{{% /highlight %}}&lt;/p&gt;
</content:encoded></item><item><title>830. 较大分组的位置</title><link>https://h1s97x.github.io/posts/coding/coding-830-positions-of-large-groups/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-830-positions-of-large-groups/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;830. 较大分组的位置&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;在一个由小写字母构成的字符串 &lt;code&gt;s&lt;/code&gt; 中，包含由一些连续的相同字符所构成的分组。&lt;/p&gt;
&lt;p&gt;例如，在字符串 &lt;code&gt;s = &quot;abbxxxxzyy&quot;&lt;/code&gt; 中，就含有 &lt;code&gt;&quot;a&quot;&lt;/code&gt;、&lt;code&gt;&quot;bb&quot;&lt;/code&gt;、&lt;code&gt;&quot;xxxx&quot;&lt;/code&gt;、&lt;code&gt;&quot;z&quot;&lt;/code&gt; 和 &lt;code&gt;&quot;yy&quot;&lt;/code&gt; 这样的一些分组。&lt;/p&gt;
&lt;p&gt;分组可以用区间 &lt;code&gt;[start, end]&lt;/code&gt; 表示，其中 &lt;code&gt;start&lt;/code&gt; 和 &lt;code&gt;end&lt;/code&gt; 分别表示该分组的起始和终止位置的下标。上例中的 &lt;code&gt;&quot;xxxx&quot;&lt;/code&gt; 分组用区间表示为 &lt;code&gt;[3, 6]&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;我们称所有包含&lt;strong&gt;大于或等于三个&lt;/strong&gt;连续字符的分组为&lt;strong&gt;较大分组&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;找到每一个&lt;strong&gt;较大分组&lt;/strong&gt;的区间，&lt;strong&gt;按起始位置下标递增顺序&lt;/strong&gt;排序后，返回结果。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;abbxxxxzzy&quot;
输出：[[3,6]]
解释：&quot;xxxx&quot; 是一个起始于 3 且终止于 6 的较大分组。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;abc&quot;
输出：[]
解释：&quot;a&quot;、&quot;b&quot; 和 &quot;c&quot; 均不是符合要求的较大分组。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 3：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;abcdddeeeeaabbbcd&quot;
输出：[[3,5],[6,9],[12,14]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= s.length &amp;lt;= 1000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s&lt;/code&gt; 仅含小写英文字母&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;用&lt;strong&gt;分组循环&lt;/strong&gt;把字符串按连续相同字符分成段，段长 &lt;code&gt;&amp;gt;= 3&lt;/code&gt; 的即为较大分组，把 &lt;code&gt;[i, j]&lt;/code&gt; 加入结果。由于各段天然按下标递增排列，结果无需额外排序。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;分组示意（s = &quot;abcdddeeeeaabbbcd&quot;）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a | b | c | ddd | eeee | aa | bbb | c | d
              [3,5]  [6,9]      [12,14]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;令 &lt;code&gt;i = 0&lt;/code&gt;，初始化结果列表 &lt;code&gt;res&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;i &amp;lt; n&lt;/code&gt; 时：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;j = i&lt;/code&gt;，扩展 &lt;code&gt;while j+1 &amp;lt; n and s[j+1] == s[i]&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;j - i + 1 &amp;gt;= 3&lt;/code&gt;，把 &lt;code&gt;[i, j]&lt;/code&gt; 加入 &lt;code&gt;res&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i = j + 1&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;res&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;s = &quot;abcdddeeeeaabbbcd&quot;&lt;/code&gt; 为例：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;段&lt;/th&gt;
&lt;th&gt;区间&lt;/th&gt;
&lt;th&gt;长度&lt;/th&gt;
&lt;th&gt;是否 ≥3&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;td&gt;[0,0]&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;b&lt;/td&gt;
&lt;td&gt;[1,1]&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;c&lt;/td&gt;
&lt;td&gt;[2,2]&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ddd&lt;/td&gt;
&lt;td&gt;[3,5]&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;eeee&lt;/td&gt;
&lt;td&gt;[6,9]&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;aa&lt;/td&gt;
&lt;td&gt;[10,11]&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bbb&lt;/td&gt;
&lt;td&gt;[12,14]&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;结果为 &lt;code&gt;[[3,5],[6,9],[12,14]]&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：O(N)，单次线性扫描。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：O(1)（不计输出结果空间）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;p&gt;{{% highlight python %}}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def largeGroupPositions(self, s: str) -&amp;gt; List[List[int]]:
        res = []
        n = len(s)
        i = 0
        while i &amp;lt; n:
            j = i
            while j + 1 &amp;lt; n and s[j + 1] == s[i]:
                j += 1
            if j - i + 1 &amp;gt;= 3:
                res.append([i, j])
            i = j + 1
        return res
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
import unittest

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_example1(self):
        self.assertEqual(Solution().largeGroupPositions(&quot;abbxxxxzzy&quot;), [[3, 6]])

    def test_example2(self):
        self.assertEqual(Solution().largeGroupPositions(&quot;abc&quot;), [])

    def test_example3(self):
        self.assertEqual(Solution().largeGroupPositions(&quot;abcdddeeeeaabbbcd&quot;),
                         [[3, 5], [6, 9], [12, 14]])


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;{{% /highlight %}}&lt;/p&gt;
</content:encoded></item><item><title>844. 比较含退格的字符串</title><link>https://h1s97x.github.io/posts/coding/coding-844-backspace-string-compare/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-844-backspace-string-compare/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;844. 比较含退格的字符串&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给定 &lt;code&gt;s&lt;/code&gt; 和 &lt;code&gt;t&lt;/code&gt; 两个字符串，当它们分别被输入到空白的文本编辑器后，如果两者相等，返回 &lt;code&gt;true&lt;/code&gt;。&lt;code&gt;#&lt;/code&gt; 代表退格字符。&lt;/p&gt;
&lt;p&gt;注意：如果对空文本输入退格字符，文本继续为空。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;ab#c&quot;, t = &quot;ad#c&quot;
输出：true
解释：s 变为 &quot;ac&quot;，t 变为 &quot;ac&quot;。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;a##c&quot;, t = &quot;#a#c&quot;
输出：true
解释：s 变为 &quot;c&quot;，t 变为 &quot;c&quot;。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 3：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;a#c&quot;, t = &quot;b&quot;
输出：false
解释：s 变为 &quot;c&quot;，t 变为 &quot;b&quot;。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= s.length, t.length &amp;lt;= 200&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s&lt;/code&gt; 和 &lt;code&gt;t&lt;/code&gt; 只含有小写字母以及字符 &lt;code&gt;#&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;p&gt;两种经典做法：&lt;strong&gt;栈&lt;/strong&gt; 或 &lt;strong&gt;从后往前的双指针&lt;/strong&gt;。这里采用空间 O(1) 的双指针。&lt;/p&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;从&lt;strong&gt;后往前&lt;/strong&gt;扫描，因为退格键影响的是它&lt;strong&gt;前面&lt;/strong&gt;（左侧）的字符，从尾部逆序处理可以即时跳过被退格删除的字符，无需额外空间。&lt;/p&gt;
&lt;p&gt;维护两个指针 &lt;code&gt;i&lt;/code&gt;（指向 &lt;code&gt;s&lt;/code&gt;）、&lt;code&gt;j&lt;/code&gt;（指向 &lt;code&gt;t&lt;/code&gt;）和两个计数 &lt;code&gt;skipS&lt;/code&gt;、&lt;code&gt;skipT&lt;/code&gt;（记录待跳过的退格次数）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;遇到 &lt;code&gt;#&lt;/code&gt;，&lt;code&gt;skip&lt;/code&gt; 加一；&lt;/li&gt;
&lt;li&gt;遇到普通字符，若 &lt;code&gt;skip &amp;gt; 0&lt;/code&gt; 则说明该字符被退格删除，&lt;code&gt;skip&lt;/code&gt; 减一并让指针前进；&lt;/li&gt;
&lt;li&gt;否则该字符是&lt;strong&gt;最终有效字符&lt;/strong&gt;，停下指针做比较。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;找到两侧各自的有效字符后，若不相同直接返回 &lt;code&gt;false&lt;/code&gt;；若一侧还有字符而另一侧已越界，也说明不等。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;从后往前跳过被退格的字符示意（s = &quot;ab##c&quot;）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;从右往左扫描:
  &apos;#&apos; → skip=1
  &apos;#&apos; → skip=2
  &apos;b&apos; → skip&amp;gt;0, 被删除, skip=1
  &apos;a&apos; → skip&amp;gt;0, 被删除, skip=0
  &apos;c&apos; → 有效字符
最终有效结果: &quot;c&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;初始化 &lt;code&gt;i = len(s)-1, j = len(t)-1, skipS = skipT = 0&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;只要 &lt;code&gt;i &amp;gt;= 0&lt;/code&gt; 或 &lt;code&gt;j &amp;gt;= 0&lt;/code&gt; 就循环：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;内层跳过 &lt;code&gt;s&lt;/code&gt; 被退格删除的字符&lt;/strong&gt;：遇到 &lt;code&gt;#&lt;/code&gt; 则 &lt;code&gt;skipS++&lt;/code&gt;；遇到普通字符且 &lt;code&gt;skipS &amp;gt; 0&lt;/code&gt; 则 &lt;code&gt;skipS--&lt;/code&gt; 并跳过；否则遇到有效字符，退出内层。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;内层跳过 &lt;code&gt;t&lt;/code&gt; 被退格删除的字符&lt;/strong&gt;（同理）。&lt;/li&gt;
&lt;li&gt;比较两侧有效字符：都有效且不相等 → &lt;code&gt;false&lt;/code&gt;；只有一侧越界 → &lt;code&gt;false&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i--, j--&lt;/code&gt; 继续。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;全部处理完返回 &lt;code&gt;true&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;s = &quot;ab#c&quot;, t = &quot;ad#c&quot;&lt;/code&gt; 为例：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;步骤&lt;/th&gt;
&lt;th&gt;i&lt;/th&gt;
&lt;th&gt;s[i]&lt;/th&gt;
&lt;th&gt;处理&lt;/th&gt;
&lt;th&gt;j&lt;/th&gt;
&lt;th&gt;t[j]&lt;/th&gt;
&lt;th&gt;处理&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;c&lt;/td&gt;
&lt;td&gt;有效字符&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;c&lt;/td&gt;
&lt;td&gt;有效字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;比较 c==c ✅，i=2, j=2&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;#&lt;/td&gt;
&lt;td&gt;skipS=1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;#&lt;/td&gt;
&lt;td&gt;skipT=1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;b&lt;/td&gt;
&lt;td&gt;被删除，skipS=0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;d&lt;/td&gt;
&lt;td&gt;被删除，skipT=0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;td&gt;有效字符&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;td&gt;有效字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;比较 a==a ✅，i=-1, j=-1 结束 → true&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：O(N + M)，两个字符串各被扫描一遍。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：O(1)，只用常数额外空间。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;p&gt;{{% highlight python %}}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def backspaceCompare(self, s: str, t: str) -&amp;gt; bool:
        i, j = len(s) - 1, len(t) - 1
        skip_s = skip_t = 0
        while i &amp;gt;= 0 or j &amp;gt;= 0:
            while i &amp;gt;= 0:
                if s[i] == &apos;#&apos;:
                    skip_s += 1
                    i -= 1
                elif skip_s:
                    skip_s -= 1
                    i -= 1
                else:
                    break
            while j &amp;gt;= 0:
                if t[j] == &apos;#&apos;:
                    skip_t += 1
                    j -= 1
                elif skip_t:
                    skip_t -= 1
                    j -= 1
                else:
                    break
            if i &amp;gt;= 0 and j &amp;gt;= 0:
                if s[i] != t[j]:
                    return False
            elif i &amp;gt;= 0 or j &amp;gt;= 0:
                return False
            i -= 1
            j -= 1
        return True
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
import unittest

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_true_basic(self):
        self.assertTrue(Solution().backspaceCompare(&quot;ab#c&quot;, &quot;ad#c&quot;))

    def test_true_all(self):
        self.assertTrue(Solution().backspaceCompare(&quot;a##c&quot;, &quot;#a#c&quot;))

    def test_false(self):
        self.assertFalse(Solution().backspaceCompare(&quot;a#c&quot;, &quot;b&quot;))


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;{{% /highlight %}}&lt;/p&gt;
</content:encoded></item><item><title>845. 数组中的最长山脉</title><link>https://h1s97x.github.io/posts/coding/coding-845-longest-mountain-in-array/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-845-longest-mountain-in-array/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;845. 数组中的最长山脉&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;把符合下列属性的数组 &lt;code&gt;arr&lt;/code&gt; 称为&lt;strong&gt;山脉数组&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;arr.length &amp;gt;= 3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;存在下标 &lt;code&gt;i&lt;/code&gt;（&lt;code&gt;0 &amp;lt; i &amp;lt; arr.length - 1&lt;/code&gt;）满足：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;arr[0] &amp;lt; arr[1] &amp;lt; ... &amp;lt; arr[i - 1] &amp;lt; arr[i]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;arr[i] &amp;gt; arr[i + 1] &amp;gt; ... &amp;gt; arr[arr.length - 1]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;给出一个整数数组 &lt;code&gt;arr&lt;/code&gt;，返回最长山脉子数组的长度。如果不存在山脉子数组，返回 &lt;code&gt;0&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：arr = [2,1,4,7,3,2,5]
输出：5
解释：最长的山脉子数组是 [1,4,7,3,2]，长度为 5。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：arr = [2,2,2]
输出：0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= arr.length &amp;lt;= 10^4&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= arr[i] &amp;lt;= 10^4&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;一座山的特征是在某处先&lt;strong&gt;严格上升&lt;/strong&gt;再&lt;strong&gt;严格下降&lt;/strong&gt;，因此只需枚举每个可能成为「峰顶」的位置 &lt;code&gt;i&lt;/code&gt;（满足 &lt;code&gt;arr[i-1] &amp;lt; arr[i] &amp;gt; arr[i+1]&lt;/code&gt;）。对每个峰顶分别向左、向右扩展下降侧（即左边递增的尾部、右边递减的尾部），得到以该峰为最高点的最长山脉长度 &lt;code&gt;right - left + 1&lt;/code&gt;，更新答案后跳过该段，避免重复计算。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;山峰示意（arr = [2,1,4,7,3,2,5]）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;2  1  4  7  3  2  5
         ↑
        峰顶=7
       向左扩展: 1,4  向右扩展: 3,2
       整座山: [1,4,7,3,2] 长 5
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;初始化 &lt;code&gt;ans = 0&lt;/code&gt;，&lt;code&gt;i = 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;i &amp;lt; n - 1&lt;/code&gt; 时：
&lt;ul&gt;
&lt;li&gt;若 &lt;code&gt;arr[i-1] &amp;lt; arr[i] &amp;gt; arr[i+1]&lt;/code&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;left = i - 1&lt;/code&gt;，向左 &lt;code&gt;while left &amp;gt; 0 and arr[left-1] &amp;lt; arr[left]&lt;/code&gt; 时 &lt;code&gt;left--&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;right = i + 1&lt;/code&gt;，向右 &lt;code&gt;while right+1 &amp;lt; n and arr[right+1] &amp;lt; arr[right]&lt;/code&gt; 时 &lt;code&gt;right++&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ans = max(ans, right - left + 1)&lt;/code&gt;，&lt;code&gt;i = right + 1&lt;/code&gt;；&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;否则 &lt;code&gt;i++&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;arr = [2,1,4,7,3,2,5]&lt;/code&gt; 为例：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;i=1&lt;/code&gt;: &lt;code&gt;arr[0]=2&lt;/code&gt; 不满足 &lt;code&gt;arr[0]&amp;lt;arr[1]&lt;/code&gt;（2&amp;gt;1），&lt;code&gt;i=2&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=2&lt;/code&gt;: &lt;code&gt;1 &amp;lt; 4 &amp;gt; 7&lt;/code&gt;? &lt;code&gt;4&amp;gt;7&lt;/code&gt; 不成立，&lt;code&gt;i=3&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=3&lt;/code&gt;: &lt;code&gt;4 &amp;lt; 7 &amp;gt; 3&lt;/code&gt; ✅，left=2，左扩 &lt;code&gt;arr[1]=1 &amp;lt; arr[2]=4&lt;/code&gt; 继续到 left=1，&lt;code&gt;arr[0]=2 &amp;lt; arr[1]=1&lt;/code&gt;? 否停；right=4，右扩 &lt;code&gt;arr[5]=2 &amp;lt; arr[4]=3&lt;/code&gt; 到 right=5，&lt;code&gt;arr[6]=5 &amp;lt; arr[5]=2&lt;/code&gt;? 否停。长度 &lt;code&gt;5-1+1=5&lt;/code&gt;，&lt;code&gt;ans=5&lt;/code&gt;，&lt;code&gt;i=6&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;循环结束，返回 &lt;code&gt;5&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：O(N)，每个位置最多被访问常数次（扩展过的段直接跳过）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：O(1)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;p&gt;{{% highlight python %}}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def longestMountain(self, arr: List[int]) -&amp;gt; int:
        n = len(arr)
        ans = 0
        i = 1
        while i &amp;lt; n - 1:
            if arr[i - 1] &amp;lt; arr[i] &amp;gt; arr[i + 1]:
                left = i - 1
                while left &amp;gt; 0 and arr[left - 1] &amp;lt; arr[left]:
                    left -= 1
                right = i + 1
                while right + 1 &amp;lt; n and arr[right + 1] &amp;lt; arr[right]:
                    right += 1
                ans = max(ans, right - left + 1)
                i = right + 1
            else:
                i += 1
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
import unittest

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_example1(self):
        self.assertEqual(Solution().longestMountain([2, 1, 4, 7, 3, 2, 5]), 5)

    def test_example2(self):
        self.assertEqual(Solution().longestMountain([2, 2, 2]), 0)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;{{% /highlight %}}&lt;/p&gt;
</content:encoded></item><item><title>88. 合并两个有序数组</title><link>https://h1s97x.github.io/posts/coding/coding-88-merge-sorted-array/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-88-merge-sorted-array/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;88. 合并两个有序数组&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给定两个&lt;strong&gt;非递减顺序&lt;/strong&gt;排列的整数数组 &lt;code&gt;nums1&lt;/code&gt; 和 &lt;code&gt;nums2&lt;/code&gt;，另有两个整数 &lt;code&gt;m&lt;/code&gt; 和 &lt;code&gt;n&lt;/code&gt;，分别表示 &lt;code&gt;nums1&lt;/code&gt; 和 &lt;code&gt;nums2&lt;/code&gt; 中的元素数目。&lt;/p&gt;
&lt;p&gt;请你&lt;strong&gt;合并&lt;/strong&gt; &lt;code&gt;nums2&lt;/code&gt; 到 &lt;code&gt;nums1&lt;/code&gt;（直接原地修改），使合并后的数组同样按&lt;strong&gt;非递减顺序&lt;/strong&gt;排列。&lt;/p&gt;
&lt;p&gt;注意：最终，合并后数组不应由函数返回，而是存储在数组 &lt;code&gt;nums1&lt;/code&gt; 中。为了应对这种情况，&lt;code&gt;nums1&lt;/code&gt; 的初始长度为 &lt;code&gt;m + n&lt;/code&gt;，其中前 &lt;code&gt;m&lt;/code&gt; 个元素表示应合并的元素，后 &lt;code&gt;n&lt;/code&gt; 个元素为 0，应忽略。&lt;code&gt;nums2&lt;/code&gt; 的长度为 &lt;code&gt;n&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出：[1,2,2,3,5,6]
解释：需要合并 [1,2,3] 和 [2,5,6]。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums1 = [1], m = 1, nums2 = [], n = 0
输出：[1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;nums1.length == m + n&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nums2.length == n&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= m, n &amp;lt;= 200&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= m + n &amp;lt;= 200&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;用&lt;strong&gt;从后往前的双指针&lt;/strong&gt;完成原地合并。若从前往后归并，会覆盖 &lt;code&gt;nums1&lt;/code&gt; 尚未处理的元素，破坏原值；而从后往前写（填到数组末尾的空位），不会覆盖任何待处理元素——因为后方全是空位（0）。&lt;/p&gt;
&lt;p&gt;用三个指针：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;p1 = m - 1&lt;/code&gt;：指向 &lt;code&gt;nums1&lt;/code&gt; 有效部分末尾；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;p2 = n - 1&lt;/code&gt;：指向 &lt;code&gt;nums2&lt;/code&gt; 末尾；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tail = m + n - 1&lt;/code&gt;：指向 &lt;code&gt;nums1&lt;/code&gt; 末尾空位，待填位置。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;每次把 &lt;code&gt;nums1[p1]&lt;/code&gt; 和 &lt;code&gt;nums2[p2]&lt;/code&gt; 中较大者放到 &lt;code&gt;nums1[tail]&lt;/code&gt;，并让对应指针和 &lt;code&gt;tail&lt;/code&gt; 前移。由于两个数组都是递增的，从尾部取最大者就是归并的正确顺序，即不断把较大元素&quot;从后向前&quot;填入。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;归并过程示意（nums1=[1,2,3,0,0,0], nums2=[2,5,6]）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nums1:  [1,2,3,0,0,0]     比较 3 和 6 → 放 6
        [1,2,3,0,0,6]     比较 3 和 5 → 放 5
        [1,2,3,0,5,6]     比较 3 和 2 → 放 3
        [1,2,0,3,5,6]     比较 2 和 2 → 放 2
        [1,2,2,3,5,6]     比较 1 和 2 → 放 2
        最终 [1,2,2,3,5,6]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;初始化 &lt;code&gt;p1 = m-1, p2 = n-1, tail = m+n-1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;p2 &amp;gt;= 0&lt;/code&gt; 时循环（&lt;code&gt;nums2&lt;/code&gt; 全部放入即可，若 &lt;code&gt;nums1&lt;/code&gt; 提前放完，剩下的 &lt;code&gt;nums2&lt;/code&gt; 依次填入空位）：
&lt;ul&gt;
&lt;li&gt;若 &lt;code&gt;p1 &amp;gt;= 0&lt;/code&gt; 且 &lt;code&gt;nums1[p1] &amp;gt; nums2[p2]&lt;/code&gt;：&lt;code&gt;nums1[tail] = nums1[p1]&lt;/code&gt;，&lt;code&gt;p1--&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;否则：&lt;code&gt;nums1[tail] = nums2[p2]&lt;/code&gt;，&lt;code&gt;p2--&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tail--&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;完成，&lt;code&gt;nums1&lt;/code&gt; 即为合并后的有序数组。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;nums1=[1,2,3,0,0,0] (m=3), nums2=[2,5,6] (n=3)&lt;/code&gt; 为例：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;步骤&lt;/th&gt;
&lt;th&gt;p1&lt;/th&gt;
&lt;th&gt;p2&lt;/th&gt;
&lt;th&gt;tail&lt;/th&gt;
&lt;th&gt;nums1[p1] vs nums2[p2]&lt;/th&gt;
&lt;th&gt;填入&lt;/th&gt;
&lt;th&gt;nums1&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;3 vs 6，6 更大&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;[1,2,3,0,0,6]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;3 vs 5，5 更大&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;[1,2,3,0,5,6]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3 vs 2，3 更大&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;[1,2,0,3,5,6]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2 vs 2，相等取 nums2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;[1,2,2,3,5,6]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;p2&amp;lt;0，结束&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;[1,2,2,3,5,6]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：O(M + N)，每个元素只处理一次。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：O(1)，原地修改，仅用常数额外空间。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;p&gt;{{% highlight python %}}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -&amp;gt; None:
        p1, p2 = m - 1, n - 1
        tail = m + n - 1
        while p2 &amp;gt;= 0:
            if p1 &amp;gt;= 0 and nums1[p1] &amp;gt; nums2[p2]:
                nums1[tail] = nums1[p1]
                p1 -= 1
            else:
                nums1[tail] = nums2[p2]
                p2 -= 1
            tail -= 1
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
import unittest

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_merge(self):
        nums1 = [1, 2, 3, 0, 0, 0]
        Solution().merge(nums1, 3, [2, 5, 6], 3)
        self.assertEqual(nums1, [1, 2, 2, 3, 5, 6])

    def test_n_is_zero(self):
        nums1 = [1]
        Solution().merge(nums1, 1, [], 0)
        self.assertEqual(nums1, [1])

    def test_m_is_zero(self):
        nums1 = [0]
        Solution().merge(nums1, 0, [1], 1)
        self.assertEqual(nums1, [1])


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;{{% /highlight %}}&lt;/p&gt;
</content:encoded></item><item><title>925. 长按键入</title><link>https://h1s97x.github.io/posts/coding/coding-925-long-pressed-name/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-925-long-pressed-name/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;925. 长按键入&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;你的朋友正在使用键盘输入他的名字 &lt;code&gt;name&lt;/code&gt;。偶尔，在键入字符 &lt;code&gt;c&lt;/code&gt; 时，按键&lt;strong&gt;可能会被长按&lt;/strong&gt;，而字符可能被输入 1 次或多次。&lt;/p&gt;
&lt;p&gt;你将会检查键盘输入的字符 &lt;code&gt;typed&lt;/code&gt;。如果它对应的可能是你的朋友的名字（其中有些字符可能被长按），就返回 &lt;code&gt;True&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：name = &quot;alex&quot;, typed = &quot;aaleex&quot;
输出：true
解释：&apos;alex&apos; 中的 &apos;a&apos; 和 &apos;e&apos; 被长按。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：name = &quot;saeed&quot;, typed = &quot;ssaaedd&quot;
输出：false
解释：&apos;e&apos; 需要输入两次，但在 typed 的输出中不是这样。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= name.length, typed.length &amp;lt;= 1000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;name&lt;/code&gt; 和 &lt;code&gt;typed&lt;/code&gt; 的字符都是小写字母&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;用&lt;strong&gt;双指针&lt;/strong&gt;同时扫描 &lt;code&gt;name&lt;/code&gt; 和 &lt;code&gt;typed&lt;/code&gt;。指针 &lt;code&gt;i&lt;/code&gt; 指向 &lt;code&gt;name&lt;/code&gt;（期望的字符），指针 &lt;code&gt;j&lt;/code&gt; 指向 &lt;code&gt;typed&lt;/code&gt;（实际输入）。&lt;/p&gt;
&lt;p&gt;由于长按只可能让某字符在 &lt;code&gt;typed&lt;/code&gt; 中&lt;strong&gt;出现得更连续、更多&lt;/strong&gt;，因此当 &lt;code&gt;typed[j]&lt;/code&gt; 与 &lt;code&gt;name[i]&lt;/code&gt; 相等时，说明当前该字符匹配，双指针同时前进；当 &lt;code&gt;typed[j]&lt;/code&gt; 与当前 &lt;code&gt;name[i]&lt;/code&gt; 不相等时，它只可能是上一个字符的长按重复（&lt;code&gt;typed[j] == typed[j-1]&lt;/code&gt;），此时仅 &lt;code&gt;j&lt;/code&gt; 前进；若既不等于 &lt;code&gt;name[i]&lt;/code&gt; 也不等于上一个字符，则说明 &lt;code&gt;typed&lt;/code&gt; 出现了 &lt;code&gt;name&lt;/code&gt; 没有的字符，返回 &lt;code&gt;false&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关键法则：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;typed&lt;/code&gt; 多出的字符只能是「紧跟在它前面的同一个字符」的重复（长按）；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;name&lt;/code&gt; 中的每个字符必须在 &lt;code&gt;typed&lt;/code&gt; 中按顺序出现。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;长按匹配示意（name = &quot;alex&quot;, typed = &quot;aaleex&quot;）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name:  a  l  e  x
typed: a  a  l  e  e  x
        这两个 a 匹配 name 的 a → 前进
        这个 e 是长按重复 → 跳过
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;初始化 &lt;code&gt;i = 0, j = 0&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;j &amp;lt; len(typed)&lt;/code&gt; 时循环：
&lt;ul&gt;
&lt;li&gt;若 &lt;code&gt;i &amp;lt; len(name)&lt;/code&gt; 且 &lt;code&gt;name[i] == typed[j]&lt;/code&gt;：&lt;code&gt;i++&lt;/code&gt;、&lt;code&gt;j++&lt;/code&gt;，匹配一个字符；&lt;/li&gt;
&lt;li&gt;否则若 &lt;code&gt;j &amp;gt; 0&lt;/code&gt; 且 &lt;code&gt;typed[j] == typed[j-1]&lt;/code&gt;：说明是长按重复，仅 &lt;code&gt;j++&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;否则：返回 &lt;code&gt;false&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;循环结束后，判断 &lt;code&gt;i&lt;/code&gt; 是否走到 &lt;code&gt;name&lt;/code&gt; 末尾（&lt;code&gt;i == len(name)&lt;/code&gt;）——若 &lt;code&gt;name&lt;/code&gt; 还有未匹配字符则 &lt;code&gt;typed&lt;/code&gt; 不够长，返回 &lt;code&gt;false&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;name = &quot;saeed&quot;, typed = &quot;ssaaedd&quot;&lt;/code&gt; 为例：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;步骤&lt;/th&gt;
&lt;th&gt;i&lt;/th&gt;
&lt;th&gt;name[i]&lt;/th&gt;
&lt;th&gt;j&lt;/th&gt;
&lt;th&gt;typed[j]&lt;/th&gt;
&lt;th&gt;判断&lt;/th&gt;
&lt;th&gt;操作&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;s&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;s&lt;/td&gt;
&lt;td&gt;相等&lt;/td&gt;
&lt;td&gt;i=1, j=1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;s&lt;/td&gt;
&lt;td&gt;不相等，但 typed[1]==typed[0]&lt;/td&gt;
&lt;td&gt;j=2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;td&gt;相等&lt;/td&gt;
&lt;td&gt;i=2, j=3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;e&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;td&gt;不相等，但 typed[3]==typed[2]&lt;/td&gt;
&lt;td&gt;j=4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;e&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;e&lt;/td&gt;
&lt;td&gt;相等&lt;/td&gt;
&lt;td&gt;i=3, j=5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;e&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;d&lt;/td&gt;
&lt;td&gt;不相等，typed[5]=d != previous &apos;e&apos;&lt;/td&gt;
&lt;td&gt;返回 false&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;typed&lt;/code&gt; 输出的是 &lt;code&gt;d&lt;/code&gt;，而期望的 &lt;code&gt;name[3]&lt;/code&gt; 是 &lt;code&gt;e&lt;/code&gt;，长按说明不成立 → &lt;code&gt;false&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：O(N + M)，&lt;code&gt;name&lt;/code&gt; 和 &lt;code&gt;typed&lt;/code&gt; 各被扫描常数次。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：O(1)，仅用两个指针。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;p&gt;{{% highlight python %}}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def isLongPressedName(self, name: str, typed: str) -&amp;gt; bool:
        i = j = 0
        while j &amp;lt; len(typed):
            if i &amp;lt; len(name) and name[i] == typed[j]:
                i += 1
                j += 1
            elif j &amp;gt; 0 and typed[j] == typed[j - 1]:
                j += 1
            else:
                return False
        return i == len(name)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
import unittest

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_true(self):
        self.assertTrue(Solution().isLongPressedName(&quot;alex&quot;, &quot;aaleex&quot;))

    def test_false(self):
        self.assertFalse(Solution().isLongPressedName(&quot;saeed&quot;, &quot;ssaaedd&quot;))

    def test_true_single(self):
        self.assertTrue(Solution().isLongPressedName(&quot;leelee&quot;, &quot;lleeelee&quot;))


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;{{% /highlight %}}&lt;/p&gt;
</content:encoded></item><item><title>977. 有序数组的平方</title><link>https://h1s97x.github.io/posts/coding/coding-977-squares-of-a-sorted-array/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-977-squares-of-a-sorted-array/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;977. 有序数组的平方&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;题目链接: &lt;a href=&quot;https://leetcode.cn/problems/%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84%E7%9A%84%E5%B9%B3%E6%96%B9/&quot;&gt;LeetCode 977&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;给你一个按 &lt;strong&gt;非递减顺序&lt;/strong&gt; 排序的整数数组 &lt;code&gt;nums&lt;/code&gt;，返回每个数字的平方组成的新数组，要求也按 &lt;strong&gt;非递减顺序&lt;/strong&gt; 排序。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [-4,-1,0,3,10]
输出：[0,1,9,16,100]
解释：平方后为 [16,1,0,9,100]，排序后为 [0,1,9,16,100]。
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;虽然原数组有序，但负数平方后会&quot;翻转&quot;大小：负数越靠左、平方越大，正数越靠右、平方越大。&lt;strong&gt;平方的最大值一定出在两端&lt;/strong&gt;（最左或最右），因此用&lt;strong&gt;相向双指针&lt;/strong&gt;，每次取出两端平方中较大者，从答案数组&lt;strong&gt;末尾向前&lt;/strong&gt;填充，即可天然保证非递减。&lt;/p&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;left = 0, right = n - 1, pos = n - 1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;left &amp;lt;= right&lt;/code&gt;：
&lt;ul&gt;
&lt;li&gt;比较 &lt;code&gt;nums[left]**2&lt;/code&gt; 与 &lt;code&gt;nums[right]**2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;把较大的填入 &lt;code&gt;ans[pos]&lt;/code&gt;，并移动对应指针&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pos -= 1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;ans&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;nums = [-4,-1,0,3,10]&lt;/code&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;left&lt;/th&gt;
&lt;th&gt;right&lt;/th&gt;
&lt;th&gt;平方比较&lt;/th&gt;
&lt;th&gt;填入&lt;/th&gt;
&lt;th&gt;pos&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;16 vs 100&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;16 vs 9&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1 vs 9&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1 vs 0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0 vs 0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;结果为 &lt;code&gt;[0,1,9,16,100]&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，一趟遍历&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)，除答案数组外无额外空间&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def sortedSquares(self, nums: List[int]) -&amp;gt; List[int]:
        n = len(nums)
        ans = [0] * n
        left, right = 0, n - 1
        pos = n - 1
        while left &amp;lt;= right:
            l2 = nums[left] * nums[left]
            r2 = nums[right] * nums[right]
            if l2 &amp;gt; r2:
                ans[pos] = l2
                left += 1
            else:
                ans[pos] = r2
                right -= 1
            pos -= 1
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))

import unittest

from solution import Solution


class Test977(unittest.TestCase):
    def test_example1(self):
        self.assertEqual(
            Solution().sortedSquares([-4, -1, 0, 3, 10]),
            [0, 1, 9, 16, 100],
        )

    def test_example2(self):
        self.assertEqual(
            Solution().sortedSquares([-7, -3, 2, 3, 11]),
            [4, 9, 9, 49, 121],
        )

    def test_single(self):
        self.assertEqual(Solution().sortedSquares([-2]), [4])


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>986. 区间列表的交集</title><link>https://h1s97x.github.io/posts/coding/coding-986-interval-list-intersections/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-986-interval-list-intersections/</guid><pubDate>Sun, 23 Aug 2026 04:12:27 GMT</pubDate><content:encoded>&lt;h1&gt;986. 区间列表的交集&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给定两个由一些&lt;strong&gt;闭区间&lt;/strong&gt;组成的列表，&lt;code&gt;firstList&lt;/code&gt; 和 &lt;code&gt;secondList&lt;/code&gt;，其中 &lt;code&gt;firstList[i] = [start_i, end_i]&lt;/code&gt; 而 &lt;code&gt;secondList[j] = [start_j, end_j]&lt;/code&gt;。每个区间列表都是成对&lt;strong&gt;不相交&lt;/strong&gt;的，并且&lt;strong&gt;已经排序&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;返回这&lt;strong&gt;两个区间列表的交集&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;（形式上，闭区间 &lt;code&gt;[a, b]&lt;/code&gt;（其中 &lt;code&gt;a &amp;lt;= b&lt;/code&gt;）表示实数 &lt;code&gt;x&lt;/code&gt; 的集合，而 &lt;code&gt;a &amp;lt;= x &amp;lt;= b&lt;/code&gt;。两个闭区间的交集是一组实数，要么为空集，要么为闭区间。例如，&lt;code&gt;[1, 3]&lt;/code&gt; 和 &lt;code&gt;[2, 4]&lt;/code&gt; 的交集为 &lt;code&gt;[2, 3]&lt;/code&gt;。）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：firstList = [[0,2],[5,10],[13,23],[24,25]], secondList = [[1,5],[8,12],[15,24],[25,26]]
输出：[[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= firstList.length, secondList.length &amp;lt;= 1000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;firstList.length + secondList.length &amp;gt;= 1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= start_i &amp;lt; end_i &amp;lt;= 10^9&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;end_i &amp;lt; start_{i+1}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= start_j &amp;lt; end_j &amp;lt;= 10^9&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;end_j &amp;lt; start_{j+1}&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;两个区间列表都已排序且各自内部不相交，因此可以用&lt;strong&gt;双指针&lt;/strong&gt;顺序比较每个区间对。&lt;/p&gt;
&lt;p&gt;对当前指向的区间 &lt;code&gt;A = firstList[i]&lt;/code&gt;、&lt;code&gt;B = secondList[j]&lt;/code&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;交集的下界&lt;/strong&gt;是两者起点较大者：&lt;code&gt;lo = max(A[0], B[0])&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;交集的上界&lt;/strong&gt;是两者终点较小者：&lt;code&gt;hi = min(A[1], B[1])&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;lo &amp;lt;= hi&lt;/code&gt;，则存在交集，&lt;code&gt;[lo, hi]&lt;/code&gt; 即为一个结果区间。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;得到当前交集后，&lt;strong&gt;谁先结束谁就前进&lt;/strong&gt;：如果 &lt;code&gt;A&lt;/code&gt; 的终点更小，说明 &lt;code&gt;A&lt;/code&gt; 不可能再与 &lt;code&gt;secondList&lt;/code&gt; 中更靠后的区间相交，&lt;code&gt;i++&lt;/code&gt;；否则 &lt;code&gt;j++&lt;/code&gt;。这是灵茶山艾府题单里&quot;两个序列交替扫描&quot;的典型推进法。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;区间相交的三种可能示意：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;① A 完全覆盖 B 的起点:   A:  [0, 5]
                        B:     [2, 8]  交点 [2,5]，B 先结束 → j++
② A 在 B 内部:          A:     [2, 4]
                        B:  [0, 8]      交点 [2,4]，A 先结束 → i++
③ 完全不相交:            A:  [0, 3]
                        B:        [5, 8]  lo&amp;gt;hi 无交集，A 先结束 → i++
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;初始化 &lt;code&gt;i = j = 0&lt;/code&gt;，结果列表 &lt;code&gt;res = []&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;i &amp;lt; len(firstList)&lt;/code&gt; 且 &lt;code&gt;j &amp;lt; len(secondList)&lt;/code&gt; 时循环：
&lt;ul&gt;
&lt;li&gt;计算 &lt;code&gt;lo = max(A[0], B[0])&lt;/code&gt;，&lt;code&gt;hi = min(A[1], B[1])&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;lo &amp;lt;= hi&lt;/code&gt;，把 &lt;code&gt;[lo, hi]&lt;/code&gt; 加入 &lt;code&gt;res&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;A[1] &amp;lt; B[1]&lt;/code&gt;，&lt;code&gt;i++&lt;/code&gt;；否则 &lt;code&gt;j++&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;res&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;firstList=[[0,2],[5,10],[13,23],[24,25]]&lt;/code&gt;，&lt;code&gt;secondList=[[1,5],[8,12],[15,24],[25,26]]&lt;/code&gt; 为例：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;步骤&lt;/th&gt;
&lt;th&gt;i&lt;/th&gt;
&lt;th&gt;j&lt;/th&gt;
&lt;th&gt;A&lt;/th&gt;
&lt;th&gt;B&lt;/th&gt;
&lt;th&gt;lo,hi&lt;/th&gt;
&lt;th&gt;是否相交&lt;/th&gt;
&lt;th&gt;推进&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;[0,2]&lt;/td&gt;
&lt;td&gt;[1,5]&lt;/td&gt;
&lt;td&gt;1,2&lt;/td&gt;
&lt;td&gt;✅ → [1,2]&lt;/td&gt;
&lt;td&gt;B终点5&amp;gt;2，A先完后 i=1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;[5,10]&lt;/td&gt;
&lt;td&gt;[1,5]&lt;/td&gt;
&lt;td&gt;5,5&lt;/td&gt;
&lt;td&gt;✅ → [5,5]&lt;/td&gt;
&lt;td&gt;B终点5&amp;lt;10，j=1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;[5,10]&lt;/td&gt;
&lt;td&gt;[8,12]&lt;/td&gt;
&lt;td&gt;8,10&lt;/td&gt;
&lt;td&gt;✅ → [8,10]&lt;/td&gt;
&lt;td&gt;A终点10&amp;lt;12，i=2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;[13,23]&lt;/td&gt;
&lt;td&gt;[8,12]&lt;/td&gt;
&lt;td&gt;13,12&lt;/td&gt;
&lt;td&gt;❌ lo&amp;gt;hi&lt;/td&gt;
&lt;td&gt;A终点23&amp;gt;12，j=2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;[13,23]&lt;/td&gt;
&lt;td&gt;[15,24]&lt;/td&gt;
&lt;td&gt;15,23&lt;/td&gt;
&lt;td&gt;✅ → [15,23]&lt;/td&gt;
&lt;td&gt;B终点24&amp;gt;23，i=3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;[24,25]&lt;/td&gt;
&lt;td&gt;[15,24]&lt;/td&gt;
&lt;td&gt;24,24&lt;/td&gt;
&lt;td&gt;✅ → [24,24]&lt;/td&gt;
&lt;td&gt;A终点25&amp;gt;24，j=3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;[24,25]&lt;/td&gt;
&lt;td&gt;[25,26]&lt;/td&gt;
&lt;td&gt;25,25&lt;/td&gt;
&lt;td&gt;✅ → [25,25]&lt;/td&gt;
&lt;td&gt;j=4 结束&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最终结果为 &lt;code&gt;[[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]]&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：O(M + N)，其中 M、N 为两个列表的长度。每个区间最多被访问一次（指针单调右移）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：O(1)（不计返回结果所占空间），仅用双指针。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;p&gt;{{% highlight python %}}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def intervalIntersection(self, firstList: List[List[int]], secondList: List[List[int]]) -&amp;gt; List[List[int]]:
        i = j = 0
        res = []
        while i &amp;lt; len(firstList) and j &amp;lt; len(secondList):
            lo = max(firstList[i][0], secondList[j][0])
            hi = min(firstList[i][1], secondList[j][1])
            if lo &amp;lt;= hi:
                res.append([lo, hi])
            if firstList[i][1] &amp;lt; secondList[j][1]:
                i += 1
            else:
                j += 1
        return res
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
import unittest

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_basic(self):
        first = [[0, 2], [5, 10], [13, 23], [24, 25]]
        second = [[1, 5], [8, 12], [15, 24], [25, 26]]
        expected = [[1, 2], [5, 5], [8, 10], [15, 23], [24, 24], [25, 25]]
        self.assertEqual(Solution().intervalIntersection(first, second), expected)

    def test_no_intersection(self):
        self.assertEqual(Solution().intervalIntersection([[1, 3]], [[5, 7]]), [])


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;{{% /highlight %}}&lt;/p&gt;
</content:encoded></item><item><title>2831. 找出最长等值子数组</title><link>https://h1s97x.github.io/posts/coding/coding-2831-longest-equal-subarray/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2831-longest-equal-subarray/</guid><description>滑动窗口 + 哈希表，维护窗口内最大频率，窗口长度 - 最大频率 ≤ k</description><pubDate>Tue, 18 Aug 2026 05:45:32 GMT</pubDate><content:encoded>&lt;h1&gt;2831. 找出最长等值子数组&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个下标从 0 开始的整数数组 &lt;code&gt;nums&lt;/code&gt; 和一个整数 &lt;code&gt;k&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;如果子数组中所有元素都相等，则认为子数组是一个&lt;strong&gt;等值子数组&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;从 &lt;code&gt;nums&lt;/code&gt; 中删除最多 &lt;code&gt;k&lt;/code&gt; 个元素后，返回可能的最长等值子数组的长度。&lt;/p&gt;
&lt;h2&gt;核心思想&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;滑动窗口 + 哈希表维护频率&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;问题等价于：找一个最长窗口，使得窗口内出现次数最多的元素（众数）的频数 ≥ 窗口长度 - k。换句话说，窗口内非众数的元素不超过 k 个，这些元素可以被删除。&lt;/p&gt;
&lt;p&gt;维护窗口内每个元素的频率 &lt;code&gt;cnt&lt;/code&gt;，以及窗口内的最大频率 &lt;code&gt;max_freq&lt;/code&gt;。当 &lt;code&gt;窗口长度 - max_freq &amp;gt; k&lt;/code&gt; 时收缩左端点。&lt;/p&gt;
&lt;h2&gt;算法步骤&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;初始化 &lt;code&gt;cnt&lt;/code&gt;（哈希表）、&lt;code&gt;left = 0&lt;/code&gt;、&lt;code&gt;max_freq = 0&lt;/code&gt;、&lt;code&gt;ans = 0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;遍历 &lt;code&gt;right&lt;/code&gt; 从 0 到 n-1：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cnt[nums[right]] += 1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;max_freq = max(max_freq, cnt[nums[right]])&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;right - left + 1 - max_freq &amp;gt; k&lt;/code&gt; 时：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cnt[nums[left]] -= 1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;left += 1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ans = max(ans, max_freq)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;ans&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;逐步举例&lt;/h2&gt;
&lt;p&gt;以 &lt;code&gt;nums = [1,3,2,3,1,3]&lt;/code&gt;, &lt;code&gt;k = 1&lt;/code&gt; 为例：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;right&lt;/th&gt;
&lt;th&gt;nums[right]&lt;/th&gt;
&lt;th&gt;cnt&lt;/th&gt;
&lt;th&gt;max_freq&lt;/th&gt;
&lt;th&gt;窗口长&lt;/th&gt;
&lt;th&gt;窗口长-max_freq&lt;/th&gt;
&lt;th&gt;收缩&lt;/th&gt;
&lt;th&gt;left&lt;/th&gt;
&lt;th&gt;ans&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;{1:1}&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0≤1✓&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;{1:1,3:1}&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1≤1✓&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;{1:1,3:1,2:1}&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2&amp;gt;1&lt;/td&gt;
&lt;td&gt;l=1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;{3:2,2:1}&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1≤1✓&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;{3:2,2:1,1:1}&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2&amp;gt;1&lt;/td&gt;
&lt;td&gt;l=2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;{3:2,2:1,1:1}&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2&amp;gt;1&lt;/td&gt;
&lt;td&gt;l=3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最终 &lt;code&gt;ans = 2&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;验证：子数组 &lt;code&gt;[3,3]&lt;/code&gt;（索引 1,3）删除 1 个元素（索引 2 的 2）后得到等值子数组长度 2 ✓。&lt;/p&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：&lt;code&gt;O(n)&lt;/code&gt;。每个元素最多进出窗口各一次。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：&lt;code&gt;O(n)&lt;/code&gt;。哈希表存储元素频率。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://leetcode.cn/problems/find-the-longest-equal-subarray/solutions/2831/&quot;&gt;灵茶山艾府：找出最长等值子数组&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from typing import List
from collections import defaultdict


class Solution:
    def longestEqualSubarray(self, nums: List[int], k: int) -&amp;gt; int:
        cnt = defaultdict(int)
        ans = left = max_freq = 0
        for right, x in enumerate(nums):
            cnt[x] += 1
            max_freq = max(max_freq, cnt[x])
            while right - left + 1 - max_freq &amp;gt; k:
                cnt[nums[left]] -= 1
                left += 1
            ans = max(ans, max_freq)
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))

from solution import Solution


class TestLongestEqualSubarray(unittest.TestCase):
    def test_example1(self):
        nums = [1, 3, 2, 3, 1, 3]
        k = 3
        self.assertEqual(Solution().longestEqualSubarray(nums, k), 3)

    def test_example2(self):
        nums = [1, 1, 2, 2, 1, 1]
        k = 2
        self.assertEqual(Solution().longestEqualSubarray(nums, k), 4)

    def test_k_zero(self):
        nums = [1, 2, 3, 4]
        k = 0
        self.assertEqual(Solution().longestEqualSubarray(nums, k), 1)

    def test_all_same(self):
        nums = [1, 1, 1, 1]
        k = 0
        self.assertEqual(Solution().longestEqualSubarray(nums, k), 4)

    def test_single(self):
        nums = [1]
        k = 0
        self.assertEqual(Solution().longestEqualSubarray(nums, k), 1)

    def test_large_k(self):
        nums = [1, 2, 3, 4, 5]
        k = 4
        self.assertEqual(Solution().longestEqualSubarray(nums, k), 1)


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2516. 每种字符至少取 K 个</title><link>https://h1s97x.github.io/posts/coding/coding-2516-take-k-characters/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2516-take-k-characters/</guid><description>反向思考：从两端取字符等价于中间保留窗口，求最长合法窗口</description><pubDate>Tue, 18 Aug 2026 05:44:48 GMT</pubDate><content:encoded>&lt;h1&gt;2516. 每种字符至少取 K 个&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个由字符 &lt;code&gt;&apos;a&apos;&lt;/code&gt;、&lt;code&gt;&apos;b&apos;&lt;/code&gt;、&lt;code&gt;&apos;c&apos;&lt;/code&gt; 组成的字符串 &lt;code&gt;s&lt;/code&gt; 和一个非负整数 &lt;code&gt;k&lt;/code&gt;。每分钟，你可以选择取走 &lt;code&gt;s&lt;/code&gt; 最左侧还是最右侧的那个字符。&lt;/p&gt;
&lt;p&gt;你必须取走每种字符&lt;strong&gt;至少 &lt;code&gt;k&lt;/code&gt;&lt;/strong&gt; 个，返回需要的&lt;strong&gt;最少&lt;/strong&gt;分钟数；如果无法取到，则返回 &lt;code&gt;-1&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;核心思想&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;正难则反：反向滑动窗口&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;从左右两端取字符，等价于&lt;strong&gt;中间保留一个窗口&lt;/strong&gt;。求最小取走字符数，等价于求&lt;strong&gt;最大可保留窗口长度&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;统计每种字符总数，窗口内每种字符最多允许保留 &lt;code&gt;total[char] - k&lt;/code&gt; 个。用滑动窗口找满足条件的最长窗口，答案即为 &lt;code&gt;n - 最长窗口长度&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;算法步骤&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;统计 &lt;code&gt;s&lt;/code&gt; 中 &lt;code&gt;&apos;a&apos;&lt;/code&gt;、&lt;code&gt;&apos;b&apos;&lt;/code&gt;、&lt;code&gt;&apos;c&apos;&lt;/code&gt; 的总数&lt;/li&gt;
&lt;li&gt;若任一字符总数 &lt;code&gt;&amp;lt; k&lt;/code&gt;，返回 &lt;code&gt;-1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;计算每种字符在窗口内最多允许保留的数量：&lt;code&gt;need[c] = total[c] - k&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;滑动窗口找最长合法窗口（窗口内每种字符 ≤ &lt;code&gt;need&lt;/code&gt;）：
&lt;ul&gt;
&lt;li&gt;右端点扩展，加入字符&lt;/li&gt;
&lt;li&gt;当某字符超过 &lt;code&gt;need&lt;/code&gt; 时，收缩左端点&lt;/li&gt;
&lt;li&gt;更新最大窗口长度&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;n - 最长窗口长度&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;逐步举例&lt;/h2&gt;
&lt;p&gt;以 &lt;code&gt;s = &quot;aabbbcc&quot;&lt;/code&gt;, &lt;code&gt;k = 1&lt;/code&gt; 为例：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;统计&lt;/strong&gt;：&lt;code&gt;a=2, b=3, c=2&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;need&lt;/strong&gt;（窗口内最多允许）：&lt;code&gt;a=1, b=2, c=1&lt;/code&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;right&lt;/th&gt;
&lt;th&gt;s[right]&lt;/th&gt;
&lt;th&gt;window&lt;/th&gt;
&lt;th&gt;超 need?&lt;/th&gt;
&lt;th&gt;收缩&lt;/th&gt;
&lt;th&gt;left&lt;/th&gt;
&lt;th&gt;max_len&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;td&gt;a=1&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;td&gt;a=2&lt;/td&gt;
&lt;td&gt;a&amp;gt;1&lt;/td&gt;
&lt;td&gt;l=1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;b&lt;/td&gt;
&lt;td&gt;a=1,b=1&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;b&lt;/td&gt;
&lt;td&gt;a=1,b=2&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;b&lt;/td&gt;
&lt;td&gt;a=1,b=3&lt;/td&gt;
&lt;td&gt;b&amp;gt;2&lt;/td&gt;
&lt;td&gt;l=3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;c&lt;/td&gt;
&lt;td&gt;b=2,c=1&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;c&lt;/td&gt;
&lt;td&gt;b=2,c=2&lt;/td&gt;
&lt;td&gt;c&amp;gt;1&lt;/td&gt;
&lt;td&gt;l=6&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最终 &lt;code&gt;max_len = 3&lt;/code&gt;，答案 &lt;code&gt;= 7 - 3 = 4&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;验证：取走 &lt;code&gt;s[0:3]=&quot;aab&quot;&lt;/code&gt; 和 &lt;code&gt;s[6:]=&quot;c&quot;&lt;/code&gt;，共 4 个字符，含 &lt;code&gt;a=2≥1, b=1≥1, c=1≥1&lt;/code&gt; ✓。&lt;/p&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：&lt;code&gt;O(n)&lt;/code&gt;。统计 &lt;code&gt;O(n)&lt;/code&gt;，滑动窗口 &lt;code&gt;O(n)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：&lt;code&gt;O(1)&lt;/code&gt;。只维护 3 个字符的计数。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://leetcode.cn/problems/take-k-of-each-character-from-left-and-right/solutions/2516/&quot;&gt;灵茶山艾府：每种字符至少取 K 个&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from collections import Counter


class Solution:
    def takeCharacters(self, s: str, k: int) -&amp;gt; int:
        cnt = Counter(s)
        if any(cnt[c] &amp;lt; k for c in &quot;abc&quot;):
            return -1

        n = len(s)
        need = {c: cnt[c] - k for c in &quot;abc&quot;}
        window = {&quot;a&quot;: 0, &quot;b&quot;: 0, &quot;c&quot;: 0}
        left = max_len = 0

        for right in range(n):
            window[s[right]] += 1
            while window[s[right]] &amp;gt; need[s[right]]:
                window[s[left]] -= 1
                left += 1
            max_len = max(max_len, right - left + 1)

        return n - max_len
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))

from solution import Solution


class TestTakeCharacters(unittest.TestCase):
    def test_example1(self):
        s = &quot;aabaaaacaabc&quot;
        k = 2
        self.assertEqual(Solution().takeCharacters(s, k), 8)

    def test_example2(self):
        s = &quot;a&quot;
        k = 1
        self.assertEqual(Solution().takeCharacters(s, k), -1)

    def test_k_zero(self):
        s = &quot;abc&quot;
        k = 0
        self.assertEqual(Solution().takeCharacters(s, k), 0)

    def test_all_take(self):
        s = &quot;aabbcc&quot;
        k = 2
        self.assertEqual(Solution().takeCharacters(s, k), 6)

    def test_partial(self):
        s = &quot;abc&quot;
        k = 1
        self.assertEqual(Solution().takeCharacters(s, k), 3)

    def test_single_char(self):
        s = &quot;aaaa&quot;
        k = 2
        self.assertEqual(Solution().takeCharacters(s, k), -1)


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>1838. 最高频元素的频数</title><link>https://h1s97x.github.io/posts/coding/coding-1838-max-frequency/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1838-max-frequency/</guid><description>排序后滑动窗口，判断窗口内元素能否在 k 次操作内全部变成 nums[right]</description><pubDate>Tue, 18 Aug 2026 05:44:04 GMT</pubDate><content:encoded>&lt;h1&gt;1838. 最高频元素的频数&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;元素的&lt;strong&gt;频数&lt;/strong&gt;是该元素在一个数组中出现的次数。&lt;/p&gt;
&lt;p&gt;给你一个整数数组 &lt;code&gt;nums&lt;/code&gt; 和一个整数 &lt;code&gt;k&lt;/code&gt;。在一步操作中，你可以选择 &lt;code&gt;nums&lt;/code&gt; 的一个下标，并将该下标对应元素的值&lt;strong&gt;增加 1&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;执行最多 &lt;code&gt;k&lt;/code&gt; 次操作后，返回数组中最高频元素的&lt;strong&gt;最大可能频数&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;核心思想&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;排序 + 不定长滑动窗口&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;只能增加，不能减少，因此最高频元素一定是数组中的某个元素，且要将它前面的元素提升到它的值。&lt;/p&gt;
&lt;p&gt;排序后，用滑动窗口维护一个区间，区间内所有元素都能在 &lt;code&gt;k&lt;/code&gt; 次操作内变成 &lt;code&gt;nums[right]&lt;/code&gt;（窗口最大值）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;判断条件&lt;/strong&gt;：&lt;code&gt;nums[right] * 窗口长度 - 窗口和 &amp;lt;= k&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;算法步骤&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;排序 &lt;code&gt;nums&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;初始化 &lt;code&gt;left = 0&lt;/code&gt;, &lt;code&gt;window_sum = 0&lt;/code&gt;, &lt;code&gt;ans = 0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;遍历 &lt;code&gt;right&lt;/code&gt; 从 &lt;code&gt;0&lt;/code&gt; 到 &lt;code&gt;n-1&lt;/code&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;window_sum += nums[right]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;nums[right] * (right - left + 1) - window_sum &amp;gt; k&lt;/code&gt; 时：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;window_sum -= nums[left]&lt;/code&gt;, &lt;code&gt;left += 1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ans = max(ans, right - left + 1)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;ans&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;逐步举例&lt;/h2&gt;
&lt;p&gt;以 &lt;code&gt;nums = [1, 2, 4]&lt;/code&gt;, &lt;code&gt;k = 5&lt;/code&gt; 为例（已排序）：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;right&lt;/th&gt;
&lt;th&gt;nums[right]&lt;/th&gt;
&lt;th&gt;window_sum&lt;/th&gt;
&lt;th&gt;窗口长度&lt;/th&gt;
&lt;th&gt;操作数&lt;/th&gt;
&lt;th&gt;&amp;gt;5?&lt;/th&gt;
&lt;th&gt;收缩&lt;/th&gt;
&lt;th&gt;left&lt;/th&gt;
&lt;th&gt;ans&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1*1-1=0&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2*2-3=1&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4*3-7=5&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最终 &lt;code&gt;ans = 3&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;验证：将 &lt;code&gt;[1, 2]&lt;/code&gt; 都变成 &lt;code&gt;4&lt;/code&gt;，需要 &lt;code&gt;(4-1) + (4-2) = 3 + 2 = 5&lt;/code&gt; 次操作 ✓。&lt;/p&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：&lt;code&gt;O(n log n)&lt;/code&gt;。排序 &lt;code&gt;O(n log n)&lt;/code&gt;，滑动窗口 &lt;code&gt;O(n)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：&lt;code&gt;O(1)&lt;/code&gt;（忽略排序的栈空间）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://leetcode.cn/problems/frequency-of-the-most-frequent-element/solutions/2362481/&quot;&gt;灵茶山艾府：最高频元素的频数&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def maxFrequency(self, nums: List[int], k: int) -&amp;gt; int:
        nums.sort()
        n = len(nums)
        ans = left = 0
        window_sum = 0
        for right in range(n):
            window_sum += nums[right]
            while nums[right] * (right - left + 1) - window_sum &amp;gt; k:
                window_sum -= nums[left]
                left += 1
            ans = max(ans, right - left + 1)
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))

from solution import Solution


class TestMaxFrequency(unittest.TestCase):
    def test_example1(self):
        nums = [1, 2, 4]
        k = 5
        self.assertEqual(Solution().maxFrequency(nums, k), 3)

    def test_example2(self):
        nums = [1, 4, 8, 13]
        k = 5
        self.assertEqual(Solution().maxFrequency(nums, k), 2)

    def test_example3(self):
        nums = [3, 9, 6]
        k = 2
        self.assertEqual(Solution().maxFrequency(nums, k), 1)

    def test_single(self):
        nums = [5]
        k = 0
        self.assertEqual(Solution().maxFrequency(nums, k), 1)

    def test_all_same(self):
        nums = [1, 1, 1, 1]
        k = 0
        self.assertEqual(Solution().maxFrequency(nums, k), 4)

    def test_k_zero(self):
        nums = [1, 2, 3, 4]
        k = 0
        self.assertEqual(Solution().maxFrequency(nums, k), 1)

    def test_large_k(self):
        nums = [1, 2, 3, 4]
        k = 100
        self.assertEqual(Solution().maxFrequency(nums, k), 4)


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>1658. 将 x 减到 0 的最小操作数</title><link>https://h1s97x.github.io/posts/coding/coding-1658-min-operations/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1658-min-operations/</guid><description>从两端移除元素使和为 x，转化为找中间最长子数组和为 total-x</description><pubDate>Tue, 18 Aug 2026 05:42:56 GMT</pubDate><content:encoded>&lt;h1&gt;1658. 将 x 减到 0 的最小操作数&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个整数数组 &lt;code&gt;nums&lt;/code&gt; 和一个整数 &lt;code&gt;x&lt;/code&gt;。每一次操作时，你应当移除数组 &lt;code&gt;nums&lt;/code&gt; 最左边或最右边的元素，然后从 &lt;code&gt;x&lt;/code&gt; 中减去该元素的值。&lt;/p&gt;
&lt;p&gt;如果可以将 &lt;code&gt;x&lt;/code&gt; 恰好减到 &lt;code&gt;0&lt;/code&gt;，返回&lt;strong&gt;最小操作数&lt;/strong&gt;；否则，返回 &lt;code&gt;-1&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;核心思想&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;正难则反 + 不定长滑动窗口&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;从两端移除元素使和为 &lt;code&gt;x&lt;/code&gt;，等价于在数组中间找一个&lt;strong&gt;最长&lt;/strong&gt;子数组，其和为 &lt;code&gt;total - x&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;设 &lt;code&gt;target = total - x&lt;/code&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;若 &lt;code&gt;target &amp;lt; 0&lt;/code&gt;：无解，返回 &lt;code&gt;-1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;target == 0&lt;/code&gt;：需移除全部元素，返回 &lt;code&gt;n&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;否则：用滑动窗口找和为 &lt;code&gt;target&lt;/code&gt; 的最长子数组，答案 = &lt;code&gt;n - 最长子数组长度&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;算法步骤&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;计算 &lt;code&gt;total = sum(nums)&lt;/code&gt;, &lt;code&gt;target = total - x&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;特判 &lt;code&gt;target &amp;lt; 0&lt;/code&gt; 返回 &lt;code&gt;-1&lt;/code&gt;，&lt;code&gt;target == 0&lt;/code&gt; 返回 &lt;code&gt;n&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;初始化 &lt;code&gt;left = 0&lt;/code&gt;, &lt;code&gt;window = 0&lt;/code&gt;, &lt;code&gt;ans = -1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;遍历 &lt;code&gt;right&lt;/code&gt; 从 &lt;code&gt;0&lt;/code&gt; 到 &lt;code&gt;n-1&lt;/code&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;window += nums[right]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;window &amp;gt; target&lt;/code&gt; 时：&lt;code&gt;window -= nums[left]&lt;/code&gt;, &lt;code&gt;left += 1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;window == target&lt;/code&gt;：&lt;code&gt;ans = max(ans, right - left + 1)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;ans == -1&lt;/code&gt; 返回 &lt;code&gt;-1&lt;/code&gt;，否则返回 &lt;code&gt;n - ans&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;逐步举例&lt;/h2&gt;
&lt;p&gt;以 &lt;code&gt;nums = [1, 1, 4, 2, 3]&lt;/code&gt;, &lt;code&gt;x = 5&lt;/code&gt; 为例：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;total = 11&lt;/code&gt;, &lt;code&gt;target = 11 - 5 = 6&lt;/code&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;right&lt;/th&gt;
&lt;th&gt;nums[right]&lt;/th&gt;
&lt;th&gt;window&lt;/th&gt;
&lt;th&gt;&amp;gt;6?&lt;/th&gt;
&lt;th&gt;收缩&lt;/th&gt;
&lt;th&gt;left&lt;/th&gt;
&lt;th&gt;window==6?&lt;/th&gt;
&lt;th&gt;ans&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;l=1, window=7; l=2, window=6&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;l=3, window=5&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最终 &lt;code&gt;ans = 3&lt;/code&gt;（子数组 &lt;code&gt;[1,1,4]&lt;/code&gt;），返回 &lt;code&gt;5 - 3 = 2&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;验证：移除两端 &lt;code&gt;[2, 3]&lt;/code&gt;，和 = 5 ✓，操作数 = 2。&lt;/p&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：&lt;code&gt;O(n)&lt;/code&gt;。每个元素最多进出窗口各一次。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：&lt;code&gt;O(1)&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://leetcode.cn/problems/minimum-operations-to-reduce-x-to-zero/solutions/2362481/&quot;&gt;灵茶山艾府：将 x 减到 0 的最小操作数&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def minOperations(self, nums: List[int], x: int) -&amp;gt; int:
        total = sum(nums)
        target = total - x
        if target &amp;lt; 0:
            return -1
        if target == 0:
            return len(nums)

        n = len(nums)
        ans = -1
        left = window = 0
        for right in range(n):
            window += nums[right]
            while window &amp;gt; target:
                window -= nums[left]
                left += 1
            if window == target:
                ans = max(ans, right - left + 1)
        return -1 if ans == -1 else n - ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))

from solution import Solution


class TestMinOperations(unittest.TestCase):
    def test_example1(self):
        nums = [1, 1, 4, 2, 3]
        x = 5
        self.assertEqual(Solution().minOperations(nums, x), 2)

    def test_example2(self):
        nums = [5, 6, 7, 8, 9]
        x = 4
        self.assertEqual(Solution().minOperations(nums, x), -1)

    def test_example3(self):
        nums = [3, 2, 20, 1, 1, 3]
        x = 10
        self.assertEqual(Solution().minOperations(nums, x), 5)

    def test_remove_all(self):
        nums = [1, 2, 3]
        x = 6
        self.assertEqual(Solution().minOperations(nums, x), 3)

    def test_remove_first(self):
        nums = [5, 2, 3, 1, 1]
        x = 5
        self.assertEqual(Solution().minOperations(nums, x), 1)

    def test_single_element(self):
        nums = [1]
        x = 1
        self.assertEqual(Solution().minOperations(nums, x), 1)

    def test_single_element_impossible(self):
        nums = [1]
        x = 2
        self.assertEqual(Solution().minOperations(nums, x), -1)


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2779. 数组的最大美丽值</title><link>https://h1s97x.github.io/posts/coding/coding-2779-maximum-beauty/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2779-maximum-beauty/</guid><description>排序后找最长子数组，使窗口内元素可调整为同一值</description><pubDate>Tue, 18 Aug 2026 05:42:56 GMT</pubDate><content:encoded>&lt;h1&gt;2779. 数组的最大美丽值&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个下标从 0 开始的整数数组 &lt;code&gt;nums&lt;/code&gt; 和一个非负整数 &lt;code&gt;k&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;在一步操作中，你可以执行下述指令：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在范围 &lt;code&gt;[0, nums.length - 1]&lt;/code&gt; 中选择一个此前没有选过的下标 &lt;code&gt;i&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;将 &lt;code&gt;nums[i]&lt;/code&gt; 替换为范围 &lt;code&gt;[nums[i] - k, nums[i] + k]&lt;/code&gt; 内的任一整数。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;数组的&lt;strong&gt;美丽值&lt;/strong&gt;定义为数组中由相等元素组成的最长子序列的长度。&lt;/p&gt;
&lt;p&gt;对数组 &lt;code&gt;nums&lt;/code&gt; 执行上述操作任意次后，返回数组可能取得的&lt;strong&gt;最大美丽值&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;核心思想&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;排序 + 不定长滑动窗口&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;每个元素通过操作可以变成 &lt;code&gt;[x-k, x+k]&lt;/code&gt; 范围内的任意整数。要使窗口内所有元素变成同一个数，需要满足：&lt;/p&gt;
&lt;p&gt;$$\max(\text{窗口}) - \min(\text{窗口}) \leq 2k$$&lt;/p&gt;
&lt;p&gt;因为排序后窗口内最大值是 &lt;code&gt;nums[right]&lt;/code&gt;，最小值是 &lt;code&gt;nums[left]&lt;/code&gt;，所以条件简化为：&lt;/p&gt;
&lt;p&gt;$$nums[right] - nums[left] \leq 2k$$&lt;/p&gt;
&lt;p&gt;问题转化为：排序后，找最长的连续子数组，满足上述条件。&lt;/p&gt;
&lt;h2&gt;算法步骤&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;对 &lt;code&gt;nums&lt;/code&gt; 升序排序&lt;/li&gt;
&lt;li&gt;初始化 &lt;code&gt;left = 0&lt;/code&gt;, &lt;code&gt;ans = 0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;遍历 &lt;code&gt;right&lt;/code&gt; 从 &lt;code&gt;0&lt;/code&gt; 到 &lt;code&gt;n-1&lt;/code&gt;：
&lt;ul&gt;
&lt;li&gt;当 &lt;code&gt;nums[right] - nums[left] &amp;gt; 2k&lt;/code&gt; 时，&lt;code&gt;left += 1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;更新 &lt;code&gt;ans = max(ans, right - left + 1)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;ans&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;逐步举例&lt;/h2&gt;
&lt;p&gt;以 &lt;code&gt;nums = [4, 6, 1, 2]&lt;/code&gt;, &lt;code&gt;k = 1&lt;/code&gt; 为例：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;排序后&lt;/strong&gt;：&lt;code&gt;[1, 2, 4, 6]&lt;/code&gt;，&lt;code&gt;2k = 2&lt;/code&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;right&lt;/th&gt;
&lt;th&gt;nums[right]&lt;/th&gt;
&lt;th&gt;nums[right]-nums[left]&lt;/th&gt;
&lt;th&gt;&amp;gt;2k?&lt;/th&gt;
&lt;th&gt;收缩&lt;/th&gt;
&lt;th&gt;left&lt;/th&gt;
&lt;th&gt;ans&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1-1=0&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2-1=1&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;4-1=3&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;l=1, 4-2=2≤2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;6-2=4&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;l=2, 6-4=2≤2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最终 &lt;code&gt;ans = 2&lt;/code&gt;（如子数组 &lt;code&gt;[1,2]&lt;/code&gt; 都变成 1，或 &lt;code&gt;[4,6]&lt;/code&gt; 都变成 5）。&lt;/p&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：&lt;code&gt;O(n log n)&lt;/code&gt;。排序占主导，滑窗 &lt;code&gt;O(n)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：&lt;code&gt;O(1)&lt;/code&gt;（或 &lt;code&gt;O(log n)&lt;/code&gt; 取决于排序实现）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://leetcode.cn/problems/maximum-beauty-of-an-array-after-applying-operation/solutions/2370664/&quot;&gt;灵茶山艾府：数组的最大美丽值&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def maximumBeauty(self, nums: List[int], k: int) -&amp;gt; int:
        nums.sort()
        n = len(nums)
        ans = left = 0
        for right in range(n):
            while nums[right] - nums[left] &amp;gt; 2 * k:
                left += 1
            ans = max(ans, right - left + 1)
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))

from solution import Solution


class TestMaximumBeauty(unittest.TestCase):
    def test_example1(self):
        nums = [4, 6, 1, 2]
        k = 2
        self.assertEqual(Solution().maximumBeauty(nums, k), 3)

    def test_example2(self):
        nums = [1, 1, 1, 1]
        k = 10
        self.assertEqual(Solution().maximumBeauty(nums, k), 4)

    def test_single(self):
        nums = [5]
        k = 0
        self.assertEqual(Solution().maximumBeauty(nums, k), 1)

    def test_no_range(self):
        nums = [1, 10, 100]
        k = 0
        self.assertEqual(Solution().maximumBeauty(nums, k), 1)

    def test_large_k(self):
        nums = [1, 3, 5, 7]
        k = 5
        self.assertEqual(Solution().maximumBeauty(nums, k), 4)

    def test_two_elements(self):
        nums = [1, 10]
        k = 4
        self.assertEqual(Solution().maximumBeauty(nums, k), 1)


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2730. 找到最长的半重复子字符串</title><link>https://h1s97x.github.io/posts/coding/coding-2730-semi-repetitive/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2730-semi-repetitive/</guid><description>求最长子串，其中相邻相同字符对至多一对</description><pubDate>Tue, 18 Aug 2026 05:41:39 GMT</pubDate><content:encoded>&lt;h1&gt;2730. 找到最长的半重复子字符串&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个下标从 0 开始的字符串 &lt;code&gt;s&lt;/code&gt;，只包含数字字符。如果一个字符串 &lt;code&gt;t&lt;/code&gt; 中&lt;strong&gt;至多有一对相邻字符是相等的&lt;/strong&gt;，则称 &lt;code&gt;t&lt;/code&gt; 是&lt;strong&gt;半重复&lt;/strong&gt;的。返回 &lt;code&gt;s&lt;/code&gt; 中最长半重复子字符串的长度。&lt;/p&gt;
&lt;h2&gt;核心思想&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;不定长滑动窗口&lt;/strong&gt;，维护窗口内&lt;strong&gt;相邻相同字符对数&lt;/strong&gt; &lt;code&gt;cnt_dup&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;窗口合法的充要条件是 &lt;code&gt;cnt_dup ≤ 1&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;状态转移&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;右端点 &lt;code&gt;right&lt;/code&gt; 进入窗口：若 &lt;code&gt;s[right] == s[right-1]&lt;/code&gt;，则 &lt;code&gt;cnt_dup += 1&lt;/code&gt;（新增一对相邻相同）&lt;/li&gt;
&lt;li&gt;左端点 &lt;code&gt;left&lt;/code&gt; 离开窗口：若 &lt;code&gt;s[left] == s[left+1]&lt;/code&gt;，则 &lt;code&gt;cnt_dup -= 1&lt;/code&gt;（移除一对相邻相同）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当 &lt;code&gt;cnt_dup &amp;gt; 1&lt;/code&gt; 时，不断右移 &lt;code&gt;left&lt;/code&gt; 直到 &lt;code&gt;cnt_dup ≤ 1&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;算法步骤&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;初始化 &lt;code&gt;left = 0&lt;/code&gt;, &lt;code&gt;cnt_dup = 0&lt;/code&gt;, &lt;code&gt;ans = 0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;遍历 &lt;code&gt;right&lt;/code&gt; 从 &lt;code&gt;0&lt;/code&gt; 到 &lt;code&gt;n-1&lt;/code&gt;：
&lt;ul&gt;
&lt;li&gt;若 &lt;code&gt;right &amp;gt; 0&lt;/code&gt; 且 &lt;code&gt;s[right] == s[right-1]&lt;/code&gt;，&lt;code&gt;cnt_dup += 1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;cnt_dup &amp;gt; 1&lt;/code&gt; 时：
&lt;ul&gt;
&lt;li&gt;若 &lt;code&gt;s[left] == s[left+1]&lt;/code&gt;，&lt;code&gt;cnt_dup -= 1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;left += 1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;更新 &lt;code&gt;ans = max(ans, right - left + 1)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;ans&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;逐步举例&lt;/h2&gt;
&lt;p&gt;以 &lt;code&gt;s = &quot;1111&quot;&lt;/code&gt; 为例，&lt;code&gt;n = 4&lt;/code&gt;：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;right&lt;/th&gt;
&lt;th&gt;s[right]&lt;/th&gt;
&lt;th&gt;s[right]==s[right-1]?&lt;/th&gt;
&lt;th&gt;cnt_dup&lt;/th&gt;
&lt;th&gt;收缩过程&lt;/th&gt;
&lt;th&gt;left&lt;/th&gt;
&lt;th&gt;ans&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;&apos;1&apos;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&apos;1&apos;&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&apos;1&apos;&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;s[0]==s[1]→cnt=1,l=1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&apos;1&apos;&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;s[1]==s[2]→cnt=1,l=2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最终 &lt;code&gt;ans = 2&lt;/code&gt;（最长半重复子串为 &lt;code&gt;&quot;11&quot;&lt;/code&gt;）。&lt;/p&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：&lt;code&gt;O(n)&lt;/code&gt;。&lt;code&gt;left&lt;/code&gt; 和 &lt;code&gt;right&lt;/code&gt; 各遍历一次，每个元素最多被访问两次。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：&lt;code&gt;O(1)&lt;/code&gt;。仅用常数个变量。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://leetcode.cn/problems/find-the-longest-semi-repetitive-substring/solutions/2262923/&quot;&gt;灵茶山艾府：找到最长的半重复子字符串&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def longestSemiRepetitiveSubstring(self, s: str) -&amp;gt; int:
        ans = left = cnt = 0
        for right in range(len(s)):
            if right &amp;gt; 0 and s[right] == s[right - 1]:
                cnt += 1
            while cnt &amp;gt; 1:
                if s[left] == s[left + 1]:
                    cnt -= 1
                left += 1
            ans = max(ans, right - left + 1)
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
sys.path.insert(0, &quot;/workspace/projects/source/coding/sliding-window-two-pointers/02-variable-sliding-window/2730-semi-repetitive&quot;)
from solution import Solution
import unittest

class TestSemiRepetitive(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.longestSemiRepetitiveSubstring(&quot;52233&quot;), 4)

    def test_example2(self):
        self.assertEqual(self.solution.longestSemiRepetitiveSubstring(&quot;5494&quot;), 4)

    def test_example3(self):
        self.assertEqual(self.solution.longestSemiRepetitiveSubstring(&quot;1111111&quot;), 2)

    def test_single(self):
        self.assertEqual(self.solution.longestSemiRepetitiveSubstring(&quot;1&quot;), 1)

    def test_no_repeat(self):
        self.assertEqual(self.solution.longestSemiRepetitiveSubstring(&quot;12345&quot;), 5)

if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>1004. 最大连续 1 的个数 III</title><link>https://h1s97x.github.io/posts/coding/coding-1004-max-consecutive-ones/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1004-max-consecutive-ones/</guid><description>最多翻转 k 个 0，求最长连续 1 的子数组长度</description><pubDate>Tue, 18 Aug 2026 05:40:50 GMT</pubDate><content:encoded>&lt;h1&gt;1004. 最大连续 1 的个数 III&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给定一个二进制数组 nums 和一个整数 k，如果可以翻转最多 k 个 0，请返回数组中连续 1 的最大个数。&lt;/p&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;把 0 翻成 1，本质是对窗口内那不满足条件的 0 付费。若窗口内有 &lt;code&gt;cnt0&lt;/code&gt; 个 0，把它们全部翻成 1 后窗口就全是 1，代价是 &lt;code&gt;cnt0&lt;/code&gt; 次。因此问题等价于：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;求一个最长子数组，使得其中 &lt;strong&gt;0 的个数 ≤ k&lt;/strong&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;窗口长度越长，0 的个数越可能超标，属于&lt;strong&gt;越短越合法&lt;/strong&gt;型不定长滑窗。&lt;/p&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;维护 &lt;code&gt;cnt0&lt;/code&gt;：窗口内 0 的个数。&lt;/li&gt;
&lt;li&gt;枚举右端点，碰到 0 则 &lt;code&gt;cnt0 += 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;while cnt0 &amp;gt; k&lt;/code&gt;：此时 0 太多，必须收缩左边界，左字符离开窗口，若是 0 则 &lt;code&gt;cnt0 -= 1&lt;/code&gt;，&lt;code&gt;left += 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ans = max(ans, right - left + 1)&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;nums = [1, 0, 1, 0, 1]&lt;/code&gt;、&lt;code&gt;k = 1&lt;/code&gt; 为例（最优：&lt;code&gt;[1,0,1]&lt;/code&gt; 或 &lt;code&gt;[1,0,1]→3&lt;/code&gt;，把中间任一 0 翻成 1 长度可达 3）：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;right&lt;/th&gt;
&lt;th&gt;x&lt;/th&gt;
&lt;th&gt;cnt0&lt;/th&gt;
&lt;th&gt;&lt;code&gt;cnt0&amp;gt;1&lt;/code&gt;?&lt;/th&gt;
&lt;th&gt;收缩动作&lt;/th&gt;
&lt;th&gt;ans&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;左出 &lt;code&gt;nums[0]=1&lt;/code&gt;→cnt0=2、&lt;code&gt;nums[1]=0&lt;/code&gt;→cnt0=1（left=2）&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;第 right=3 步：&lt;code&gt;cnt0=2&amp;gt;1&lt;/code&gt;，左字符 &lt;code&gt;nums[0]=1&lt;/code&gt; 离开 cnt0 不变，继续左移遇到 &lt;code&gt;nums[1]=0&lt;/code&gt; 离开 cnt0 降到 1，窗口恢复合法。最终窗口为 &lt;code&gt;[2,4]=[1,0,1]&lt;/code&gt;，合法且长度 3。✅&lt;/p&gt;
&lt;h2&gt;题解&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def longestOnes(self, nums: list[int], k: int) -&amp;gt; int:
        ans = left = cnt0 = 0
        for right, x in enumerate(nums):
            if x == 0:
                cnt0 += 1
            while cnt0 &amp;gt; k:
                if nums[left] == 0:
                    cnt0 -= 1
                left += 1
            ans = max(ans, right - left + 1)
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
sys.path.insert(0, &quot;/workspace/projects/source/coding/sliding-window-two-pointers/02-variable-sliding-window/1004-max-consecutive-ones&quot;)
from solution import Solution
import unittest

class TestLongestOnes(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.longestOnes([1,1,1,0,0,0,1,1,1,1,0], 2), 6)

    def test_example2(self):
        self.assertEqual(self.solution.longestOnes([0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], 3), 10)

    def test_all_ones(self):
        self.assertEqual(self.solution.longestOnes([1,1,1,1], 2), 4)

    def test_all_zeros(self):
        self.assertEqual(self.solution.longestOnes([0,0,0], 2), 2)

    def test_k_zero(self):
        self.assertEqual(self.solution.longestOnes([0,1,0,1,0], 0), 1)

if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，双指针各遍历一次&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>2024. 考试的最大困扰度</title><link>https://h1s97x.github.io/posts/coding/coding-2024-max-consecutive-answers/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2024-max-consecutive-answers/</guid><description>最多修改 k 次，求最长连续相同字符子串的长度</description><pubDate>Tue, 18 Aug 2026 05:40:50 GMT</pubDate><content:encoded>&lt;h1&gt;2024. 考试的最大困扰度&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;一位老师正在出一场由 n 道判断题构成的考试，每道题的答案为 &apos;T&apos; 或 &apos;F&apos;。你可以进行最多 k 次操作，每次操作将一道题的答案改为 &apos;T&apos; 或 &apos;F&apos;。求在不超过 k 次操作的情况下，最大连续相同结果的题数。&lt;/p&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;目标是使一个子串内&lt;strong&gt;全部答案相同&lt;/strong&gt;。若子串中 &lt;code&gt;T&lt;/code&gt; 有 &lt;code&gt;cntT&lt;/code&gt; 个、&lt;code&gt;F&lt;/code&gt; 有 &lt;code&gt;cntF&lt;/code&gt; 个：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;想把它变成全 &lt;code&gt;T&lt;/code&gt;，只需把 &lt;code&gt;cntF&lt;/code&gt; 个 &lt;code&gt;F&lt;/code&gt; 改成 &lt;code&gt;T&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;想把它变成全 &lt;code&gt;F&lt;/code&gt;，只需把 &lt;code&gt;cntT&lt;/code&gt; 个 &lt;code&gt;T&lt;/code&gt; 改成 &lt;code&gt;F&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以该子串在 &lt;code&gt;k&lt;/code&gt; 次操作内可统一，当且仅当 &lt;strong&gt;&lt;code&gt;min(cntT, cntF) ≤ k&lt;/code&gt;&lt;/strong&gt;，也即 &lt;strong&gt;&lt;code&gt;cntT&lt;/code&gt; 和 &lt;code&gt;cntF&lt;/code&gt; 不能同时大于 &lt;code&gt;k&lt;/code&gt;&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;于是问题转为：求最长子串，使其中 &lt;code&gt;T&lt;/code&gt; 与 &lt;code&gt;F&lt;/code&gt; 的个数&lt;strong&gt;不同时超过 k&lt;/strong&gt;。子串越长越容易&quot;岔&quot;，属于&lt;strong&gt;越短越合法&lt;/strong&gt;型不定长滑窗。&lt;/p&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;维护 &lt;code&gt;cntT&lt;/code&gt;、&lt;code&gt;cntF&lt;/code&gt; 记录窗口内两类答案个数。&lt;/li&gt;
&lt;li&gt;枚举右端点，更新对应计数。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;while cntT &amp;gt; k and cntF &amp;gt; k&lt;/code&gt;：把左端字符移出窗口并减对应计数，&lt;code&gt;left += 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ans = max(ans, right - left + 1)&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;answerKey = &quot;TTF&quot;&lt;/code&gt;、&lt;code&gt;k = 1&lt;/code&gt; 为例（改 1 个 F 为 T 得 &lt;code&gt;TTT&lt;/code&gt;，最长 3）：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;right&lt;/th&gt;
&lt;th&gt;c&lt;/th&gt;
&lt;th&gt;cntT&lt;/th&gt;
&lt;th&gt;cntF&lt;/th&gt;
&lt;th&gt;&lt;code&gt;cntT&amp;gt;1 and cntF&amp;gt;1&lt;/code&gt;?&lt;/th&gt;
&lt;th&gt;ans&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;T&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;T&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;否（cntF=0）&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;F&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;否（cntF=1）&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;第 2 步即使 &lt;code&gt;cntT=2 &amp;gt; k&lt;/code&gt;，但因 &lt;code&gt;cntF=1 ≤ k&lt;/code&gt;，只要把那个 &lt;code&gt;F&lt;/code&gt; 改成 &lt;code&gt;T&lt;/code&gt; 即可，整串 &lt;code&gt;TTF&lt;/code&gt; 可统一，故无需收缩。✅&lt;/p&gt;
&lt;h2&gt;题解&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def maxConsecutiveAnswers(self, answerKey: str, k: int) -&amp;gt; int:
        ans = left = cntT = cntF = 0
        for right, c in enumerate(answerKey):
            if c == &apos;T&apos;:
                cntT += 1
            else:
                cntF += 1
            while cntT &amp;gt; k and cntF &amp;gt; k:
                if answerKey[left] == &apos;T&apos;:
                    cntT -= 1
                else:
                    cntF -= 1
                left += 1
            ans = max(ans, right - left + 1)
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
sys.path.insert(0, &quot;/workspace/projects/source/coding/sliding-window-two-pointers/02-variable-sliding-window/2024-max-consecutive-answers&quot;)
from solution import Solution
import unittest

class TestMaxConsecutiveAnswers(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.maxConsecutiveAnswers(&quot;TTFF&quot;, 2), 4)

    def test_example2(self):
        self.assertEqual(self.solution.maxConsecutiveAnswers(&quot;TFFT&quot;, 1), 3)

    def test_example3(self):
        self.assertEqual(self.solution.maxConsecutiveAnswers(&quot;TTFTTFTT&quot;, 1), 5)

    def test_all_same(self):
        self.assertEqual(self.solution.maxConsecutiveAnswers(&quot;TTTT&quot;, 2), 4)

    def test_k_zero(self):
        self.assertEqual(self.solution.maxConsecutiveAnswers(&quot;TFTF&quot;, 0), 1)

if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，双指针各遍历一次&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>2958. 最多 K 个重复元素的最长子数组</title><link>https://h1s97x.github.io/posts/coding/coding-2958-max-k-frequency/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2958-max-k-frequency/</guid><description>求最长子数组，其中每个元素的出现次数不超过 k</description><pubDate>Tue, 18 Aug 2026 05:40:03 GMT</pubDate><content:encoded>&lt;h1&gt;2958. 最多 K 个重复元素的最长子数组&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个整数数组 nums 和一个整数 k。如果一个数组中所有元素的频率都小于等于 k，则称这个数组是&quot;好&quot;数组。返回 nums 中最长好子数组的长度。&lt;/p&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;&quot;好&quot;数组要求&lt;strong&gt;每个元素的出现次数都不超过 k&lt;/strong&gt;。求最长的好子数组。约束是&quot;某元素频率 ≤ k&quot;——子数组越长越容易超 k，属于&lt;strong&gt;越短越合法&lt;/strong&gt;型不定长滑窗。&lt;/p&gt;
&lt;p&gt;维护一个计数器 &lt;code&gt;cnt&lt;/code&gt; 记录窗口内各元素频率。当新加入的 &lt;code&gt;x&lt;/code&gt; 使 &lt;code&gt;cnt[x] &amp;gt; k&lt;/code&gt; 时，说明窗口不再合法，右移左指针、逐个移除左端元素，直到 &lt;code&gt;cnt[x] ≤ k&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;ans = left = 0&lt;/code&gt;，&lt;code&gt;cnt&lt;/code&gt; 空哈希表。&lt;/li&gt;
&lt;li&gt;枚举右端点：&lt;code&gt;x = nums[right]&lt;/code&gt;，&lt;code&gt;cnt[x] += 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;while cnt[x] &amp;gt; k&lt;/code&gt;：&lt;code&gt;cnt[nums[left]] -= 1; left += 1&lt;/code&gt;，把左端元素踢出窗口。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ans = max(ans, right - left + 1)&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;nums = [1,2,3,1,2,3,1,2]&lt;/code&gt;、&lt;code&gt;k = 2&lt;/code&gt; 为例（最优：&lt;code&gt;[1,2,3,1,2,3]&lt;/code&gt;，长度 6）：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;right&lt;/th&gt;
&lt;th&gt;x&lt;/th&gt;
&lt;th&gt;cnt[x]&lt;/th&gt;
&lt;th&gt;是否收缩&lt;/th&gt;
&lt;th&gt;ans&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;cnt[1]=3&amp;gt;2&lt;/strong&gt; → 移除 nums[0]=1(cnt[1]=2,left=1)&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;cnt[2]=3&amp;gt;2&lt;/strong&gt; → 移除 nums[1]=2(cnt[2]=2,left=2)&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;第 6 步加入 1 使 &lt;code&gt;cnt[1]=3&lt;/code&gt; 超限，左移踢掉下标 0 的 1 后窗口 &lt;code&gt;[1,6]&lt;/code&gt; 合法；第 7 步同理。右侧可形成的合法窗口最长始终为 6。✅&lt;/p&gt;
&lt;h2&gt;题解&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def maxSubarrayLength(self, nums: list[int], k: int) -&amp;gt; int:
        ans = left = 0
        cnt = {}
        for right, x in enumerate(nums):
            cnt[x] = cnt.get(x, 0) + 1
            while cnt[x] &amp;gt; k:
                cnt[nums[left]] -= 1
                left += 1
            ans = max(ans, right - left + 1)
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
sys.path.insert(0, &quot;/workspace/projects/source/coding/sliding-window-two-pointers/02-variable-sliding-window/2958-max-k-frequency&quot;)
from solution import Solution
import unittest

class TestMaxSubarrayLength(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.maxSubarrayLength([1,2,3,1,2,3,1,2], 2), 6)

    def test_example2(self):
        self.assertEqual(self.solution.maxSubarrayLength([1,2,1,2,1,2,1,2], 1), 2)

    def test_example3(self):
        self.assertEqual(self.solution.maxSubarrayLength([5,5,5,5,5,5,5], 4), 4)

    def test_all_unique(self):
        self.assertEqual(self.solution.maxSubarrayLength([1,2,3,4,5], 1), 5)

    def test_k_large(self):
        self.assertEqual(self.solution.maxSubarrayLength([1,1,1,1], 10), 4)

if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，左右指针各移动一次&lt;/li&gt;
&lt;li&gt;空间复杂度：O(n)，计数器存储窗口内各元素频率&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>1695. 删除子数组的最大得分</title><link>https://h1s97x.github.io/posts/coding/coding-1695-maximum-erasure/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1695-maximum-erasure/</guid><description>求元素互不相同的子数组的最大和</description><pubDate>Tue, 18 Aug 2026 05:39:14 GMT</pubDate><content:encoded>&lt;h1&gt;1695. 删除子数组的最大得分&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个正整数数组 nums，请你从中删除一个含有若干不同元素（即元素互不相同）的子数组。删除子数组的得分就是子数组各元素之和。返回只删除一个子数组可获得的最大得分。&lt;/p&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;题目要求删除一个&lt;strong&gt;元素互不相同&lt;/strong&gt;（无重复）的子数组，得分是它的元素和，求最大得分。等价于：&lt;strong&gt;找一个元素互不相同的连续子数组，使它的和最大&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&quot;元素互不相同&quot;是窗口的合法性约束——越短越合法、越长越容易撞车。这类&quot;求最大窗口 + 越短越合法&quot;问题用&lt;strong&gt;不定长滑动窗口&lt;/strong&gt;：右指针不断加入元素，若新元素与窗口内已有元素重复，就右移左指针、把重复元素踢出窗口，直到窗口再次互不相同。&lt;/p&gt;
&lt;p&gt;维护两个量：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;seen&lt;/code&gt; 集合：窗口内的元素（用于 O(1) 判重）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cur&lt;/code&gt;：窗口内元素之和（用于快速更新答案）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;ans = left = cur = 0&lt;/code&gt;，&lt;code&gt;seen&lt;/code&gt; 空集合。&lt;/li&gt;
&lt;li&gt;枚举右端点 &lt;code&gt;right&lt;/code&gt;：&lt;code&gt;x = nums[right]&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;while x in seen&lt;/code&gt;：说明 x 与窗口内某元素重复，先把它踢出去——&lt;code&gt;seen.remove(nums[left])&lt;/code&gt;、&lt;code&gt;cur -= nums[left]&lt;/code&gt;、&lt;code&gt;left += 1&lt;/code&gt;，重复直到 x 不再重复。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;seen.add(x)&lt;/code&gt;、&lt;code&gt;cur += x&lt;/code&gt;，&lt;code&gt;ans = max(ans, cur)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;nums = [4, 2, 4, 5, 6]&lt;/code&gt; 为例（最优解：子数组 &lt;code&gt;[2,4,5,6]&lt;/code&gt;，和为 17）：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;right&lt;/th&gt;
&lt;th&gt;x&lt;/th&gt;
&lt;th&gt;是否收缩&lt;/th&gt;
&lt;th&gt;收缩过程&lt;/th&gt;
&lt;th&gt;seen&lt;/th&gt;
&lt;th&gt;cur&lt;/th&gt;
&lt;th&gt;ans&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;{4}&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;{4,2}&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;是&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4∈seen → 删下标0的4(cur=2,left=1)，4∉seen 退出&lt;/td&gt;
&lt;td&gt;{2,4}&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;{2,4,5}&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;{2,4,5,6}&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;17&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;第 2 步 &lt;code&gt;x=4&lt;/code&gt; 与下标 0 的 4 重复，收缩左边界删掉它后，窗口变为 &lt;code&gt;[2,4]&lt;/code&gt;（下标 1~2），仍合法。之后连续加入 5、6，窗口 &lt;code&gt;[1,4]=[2,4,5,6]&lt;/code&gt; 互不相同且和最大 17。✅&lt;/p&gt;
&lt;h2&gt;题解&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def maximumUniqueSubarray(self, nums: list[int]) -&amp;gt; int:
        ans = left = cur = 0
        seen = set()
        for right, x in enumerate(nums):
            while x in seen:
                seen.remove(nums[left])
                cur -= nums[left]
                left += 1
            seen.add(x)
            cur += x
            ans = max(ans, cur)
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
sys.path.insert(0, &quot;/workspace/projects/source/coding/sliding-window-two-pointers/02-variable-sliding-window/1695-maximum-erasure&quot;)
from solution import Solution
import unittest

class TestMaximumUniqueSubarray(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.maximumUniqueSubarray([4,2,4,5,6]), 17)

    def test_example2(self):
        self.assertEqual(self.solution.maximumUniqueSubarray([5,2,1,2,5,2,1,2,5]), 8)

    def test_single_element(self):
        self.assertEqual(self.solution.maximumUniqueSubarray([1]), 1)

    def test_all_unique(self):
        self.assertEqual(self.solution.maximumUniqueSubarray([1,2,3,4]), 10)

    def test_all_same(self):
        self.assertEqual(self.solution.maximumUniqueSubarray([5,5,5,5]), 5)

if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，左右指针各移动一次&lt;/li&gt;
&lt;li&gt;空间复杂度：O(n)，seen 集合存储窗口内元素&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>904. 水果成篮</title><link>https://h1s97x.github.io/posts/coding/coding-904-fruit-into-baskets/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-904-fruit-into-baskets/</guid><description>求最多包含两种不同元素的最长连续子数组的长度</description><pubDate>Tue, 18 Aug 2026 05:39:14 GMT</pubDate><content:encoded>&lt;h1&gt;904. 水果成篮&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;你正在探访一家农场，农场从左到右种植了一排果树。你有两个篮子，每个篮子只能装单一类型的水果。求可以收集的水果的最大数目。&lt;/p&gt;
&lt;p&gt;等价于求最长连续子数组，满足子数组中至多有两种不同的数字。&lt;/p&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;题目要求收集尽量多的水果，但只有两个篮子、每个篮子限定一种水果。等价于：&lt;strong&gt;找一个最长的连续子数组，使它里面至多出现两种不同的数字&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;本题求的是&quot;最大窗口&quot;，而&quot;最多两种水果&quot;是窗口需要始终满足的&lt;strong&gt;合法性约束&lt;/strong&gt;——窗口越短越合法、越长越容易超种类。这类&quot;求最长 + 越短越合法&quot;的问题，用&lt;strong&gt;不定长滑动窗口&lt;/strong&gt;：右指针每次加入一个水果，若种类数超过 2，就不断右移左指针，直到窗口重新合法。&lt;/p&gt;
&lt;p&gt;由于水果只进不出、种类只增不减（在达到上限后），用哈希表 &lt;code&gt;cnt&lt;/code&gt; 记录窗口内每种水果的出现次数，窗口种类数即 &lt;code&gt;len(cnt)&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;ans = left = 0&lt;/code&gt;，&lt;code&gt;cnt&lt;/code&gt; 记录窗口内各水果计数。&lt;/li&gt;
&lt;li&gt;枚举右端点 &lt;code&gt;right&lt;/code&gt;：&lt;code&gt;cnt[fruits[right]] += 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;while len(cnt) &amp;gt; 2&lt;/code&gt;：移除最左水果 &lt;code&gt;fruits[left]&lt;/code&gt;，计数减一；减到 0 就 &lt;code&gt;del&lt;/code&gt;，然后 &lt;code&gt;left += 1&lt;/code&gt;（收缩直至只含 ≤2 种）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ans = max(ans, right - left + 1)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;fruits = [1, 2, 3, 2, 2]&lt;/code&gt; 为例（期望答案 4，窗口 [1,4] 为 [2,3,2,2]）：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;right&lt;/th&gt;
&lt;th&gt;加入&lt;/th&gt;
&lt;th&gt;cnt&lt;/th&gt;
&lt;th&gt;种类数&lt;/th&gt;
&lt;th&gt;是否收缩&lt;/th&gt;
&lt;th&gt;窗口&lt;/th&gt;
&lt;th&gt;ans&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;{1:1}&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;[0,0]&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;{1:1,2:1}&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;[0,1]&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;{1:1,2:1,3:1}&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;是&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;收缩&lt;/td&gt;
&lt;td&gt;去 left=0(1)&lt;/td&gt;
&lt;td&gt;{2:1,3:1}&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;left=1&lt;/td&gt;
&lt;td&gt;[1,2]&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;{2:2,3:1}&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;[1,3]&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;{2:3,3:1}&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;[1,4]&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;第 2 步加入 3 后瞬间有 3 种水果，必须收缩：删掉最左的 1（下标0）后只剩 {2,3}，重新合法。之后窗口 [1,4] 一直合法且最长，ans=4。✅&lt;/p&gt;
&lt;h2&gt;题解&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def totalFruit(self, fruits: list[int]) -&amp;gt; int:
        ans = left = 0
        cnt = {}
        for right, x in enumerate(fruits):
            cnt[x] = cnt.get(x, 0) + 1
            while len(cnt) &amp;gt; 2:
                left_fruit = fruits[left]
                cnt[left_fruit] -= 1
                if cnt[left_fruit] == 0:
                    del cnt[left_fruit]
                left += 1
            ans = max(ans, right - left + 1)
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
sys.path.insert(0, &quot;/workspace/projects/source/coding/sliding-window-two-pointers/02-variable-sliding-window/904-fruit-into-baskets&quot;)
from solution import Solution
import unittest

class TestTotalFruit(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.totalFruit([1,2,1]), 3)

    def test_example2(self):
        self.assertEqual(self.solution.totalFruit([0,1,2,2]), 3)

    def test_example3(self):
        self.assertEqual(self.solution.totalFruit([1,2,3,2,2]), 4)

    def test_example4(self):
        self.assertEqual(self.solution.totalFruit([3,3,3,1,2,1,1,2,3,3,4]), 5)

    def test_single_type(self):
        self.assertEqual(self.solution.totalFruit([1,1,1,1]), 4)

    def test_two_types(self):
        self.assertEqual(self.solution.totalFruit([1,2,1,2,1]), 5)

if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，左右指针各前进一次&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)，cnt 中同时存在的种类数不超过 3&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>1208. 尽可能使字符串相等</title><link>https://h1s97x.github.io/posts/coding/coding-1208-equal-substrings/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1208-equal-substrings/</guid><description>将 s 的子串转化为 t 中对应子串，预算为 maxCost，求最大可转化长度</description><pubDate>Tue, 18 Aug 2026 05:38:23 GMT</pubDate><content:encoded>&lt;h1&gt;1208. 尽可能使字符串相等&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你两个长度相同的字符串 &lt;code&gt;s&lt;/code&gt; 和 &lt;code&gt;t&lt;/code&gt;。将 &lt;code&gt;s&lt;/code&gt; 中的第 &lt;code&gt;i&lt;/code&gt; 个字符变到 &lt;code&gt;t&lt;/code&gt; 中的第 &lt;code&gt;i&lt;/code&gt; 个字符需要 &lt;code&gt;|s[i] - t[i]|&lt;/code&gt; 的开销。总开销应当小于等于 &lt;code&gt;maxCost&lt;/code&gt;。求可以转化的最大子串长度。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;abcd&quot;, t = &quot;bcdf&quot;, maxCost = 3
输出：3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;abcd&quot;, t = &quot;cdef&quot;, maxCost = 3
输出：1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 3：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;abcd&quot;, t = &quot;acde&quot;, maxCost = 0
输出：1
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;逐个位置计算把 &lt;code&gt;s[i]&lt;/code&gt; 变成 &lt;code&gt;t[i]&lt;/code&gt; 的开销 &lt;code&gt;cost[i] = |ord(s[i]) - ord(t[i])|&lt;/code&gt;。于是问题转化为：&lt;strong&gt;找一个最长的连续下标段，使其开销和 ≤ maxCost&lt;/strong&gt; —— 这是典型的不定长滑动窗口&quot;求最长&quot;问题（窗口越短越合法，越长越可能超预算）。&lt;/p&gt;
&lt;p&gt;左指针固定时，窗口越长，累计开销越大，终会超过 &lt;code&gt;maxCost&lt;/code&gt;，因此&quot;越短越合法&quot;，适合右指针推进后不断收缩左指针来维持合法。&lt;/p&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;ans = left = cost = 0&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;枚举 &lt;code&gt;right&lt;/code&gt;：&lt;code&gt;cost += |ord(s[right]) - ord(t[right])|&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;while cost &amp;gt; maxCost&lt;/code&gt;：&lt;code&gt;cost -= |ord(s[left]) - ord(t[left])|&lt;/code&gt;，&lt;code&gt;left += 1&lt;/code&gt;，收缩左边界直至合法。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ans = max(ans, right - left + 1)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;s = &quot;abcd&quot;, t = &quot;bcdf&quot;, maxCost = 3&lt;/code&gt; 为例（期望答案 3）：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;i&lt;/th&gt;
&lt;th&gt;s[i]→t[i]&lt;/th&gt;
&lt;th&gt;cost[i]&lt;/th&gt;
&lt;th&gt;窗口&lt;/th&gt;
&lt;th&gt;窗口开销&lt;/th&gt;
&lt;th&gt;是否合法&lt;/th&gt;
&lt;th&gt;ans&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;&apos;a&apos;→&apos;b&apos;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;[0,0]&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&apos;b&apos;→&apos;c&apos;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;[0,1]&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&apos;c&apos;→&apos;d&apos;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;[0,2]&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&apos;d&apos;→&apos;f&apos;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;[0,3]&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;❌→收缩&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;第 3 步窗口 [0,3] 开销 1+1+1+2 = 5 &amp;gt; 3，需右移 left 收缩：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;收缩&lt;/th&gt;
&lt;th&gt;剔除&lt;/th&gt;
&lt;th&gt;累计开销&lt;/th&gt;
&lt;th&gt;状态&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;left=0→1&lt;/td&gt;
&lt;td&gt;cost[0]=1&lt;/td&gt;
&lt;td&gt;5-1=4&lt;/td&gt;
&lt;td&gt;仍 &amp;gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;left=1→2&lt;/td&gt;
&lt;td&gt;cost[1]=1&lt;/td&gt;
&lt;td&gt;4-1=3&lt;/td&gt;
&lt;td&gt;✅ 停下&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最终窗口 [2,3] 长度 2 &amp;lt; 之前记录的 3，故 &lt;code&gt;ans&lt;/code&gt; 保持 3。✅&lt;/p&gt;
&lt;h2&gt;题解&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def equalSubstring(self, s: str, t: str, maxCost: int) -&amp;gt; int:
        n = len(s)
        ans = left = cost = 0
        for right in range(n):
            cost += abs(ord(s[right]) - ord(t[right]))
            while cost &amp;gt; maxCost:
                cost -= abs(ord(s[left]) - ord(t[left]))
                left += 1
            ans = max(ans, right - left + 1)
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
sys.path.insert(0, &quot;/workspace/projects/source/coding/sliding-window-two-pointers/02-variable-sliding-window/1208-equal-substrings&quot;)
from solution import Solution
import unittest

class TestEqualSubstring(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.equalSubstring(&quot;abcd&quot;, &quot;bcdf&quot;, 3), 3)

    def test_example2(self):
        self.assertEqual(self.solution.equalSubstring(&quot;abcd&quot;, &quot;cdef&quot;, 3), 1)

    def test_example3(self):
        self.assertEqual(self.solution.equalSubstring(&quot;abcd&quot;, &quot;acde&quot;, 0), 1)

    def test_same_string(self):
        self.assertEqual(self.solution.equalSubstring(&quot;aaaa&quot;, &quot;aaaa&quot;, 5), 4)

    def test_zero_cost(self):
        self.assertEqual(self.solution.equalSubstring(&quot;abc&quot;, &quot;def&quot;, 0), 0)

    def test_all_single_chars(self):
        self.assertEqual(self.solution.equalSubstring(&quot;a&quot;, &quot;b&quot;, 1), 1)
        self.assertEqual(self.solution.equalSubstring(&quot;a&quot;, &quot;b&quot;, 0), 0)

if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，左右指针各遍历一次&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>1493. 删掉一个元素以后全为 1 的最长子数组</title><link>https://h1s97x.github.io/posts/coding/coding-1493-longest-subarray-after-delete/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1493-longest-subarray-after-delete/</guid><description>删除一个元素后，求最长全 1 子数组的长度</description><pubDate>Tue, 18 Aug 2026 05:37:56 GMT</pubDate><content:encoded>&lt;h1&gt;1493. 删掉一个元素以后全为 1 的最长子数组&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个二进制数组 &lt;code&gt;nums&lt;/code&gt;，你需要从中删掉一个元素。请在删掉元素的结果数组中，返回最长的且只包含 1 的非空子数组的长度。如果不存在这样的子数组，请返回 0。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [1,1,0,1]
输出：3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [0,1,1,1,0,1,1,0,1]
输出：5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 3：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [1,1,1]
输出：2
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;删除一个元素后剩下的全 1 子数组，等价于：&lt;strong&gt;原数组里某个连续子数组去掉恰好一个元素后全为 1&lt;/strong&gt;。这样的子数组内部至多含一个 0。&lt;/p&gt;
&lt;p&gt;所以问题转化为不定长滑动窗口：求&lt;strong&gt;窗口内 0 的个数 $\le 1$&lt;/strong&gt; 的最大窗口，答案取窗口长度减一（因为其中一个元素要被删除）。&lt;/p&gt;
&lt;p&gt;当窗口内出现两个 0 时，右移左指针收缩，直到窗口内只剩一个 0。因为滑动窗口&quot;越长越不合法&quot;，这正是求最大的场景。&lt;/p&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;初始化 &lt;code&gt;ans = left = cnt0 = 0&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;枚举 &lt;code&gt;right&lt;/code&gt;：若 &lt;code&gt;nums[right] == 0&lt;/code&gt;，&lt;code&gt;cnt0 += 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;while cnt0 &amp;gt; 1&lt;/code&gt;：若 &lt;code&gt;nums[left] == 0&lt;/code&gt; 则 &lt;code&gt;cnt0 -= 1&lt;/code&gt;，&lt;code&gt;left += 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;更新 &lt;code&gt;ans = max(ans, right - left)&lt;/code&gt;（窗口长 &lt;code&gt;right-left+1&lt;/code&gt; 再减掉要删的 1 个元素）。&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;nums = [1,1,0,1]&lt;/code&gt; 为例（期望答案 3）：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;right&lt;/th&gt;
&lt;th&gt;元素&lt;/th&gt;
&lt;th&gt;入窗后 cnt0&lt;/th&gt;
&lt;th&gt;收缩&lt;/th&gt;
&lt;th&gt;窗口&lt;/th&gt;
&lt;th&gt;长度(减1)&lt;/th&gt;
&lt;th&gt;ans&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;[0,0]&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;[0,1]&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;[0,2]&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;[0,3]=&quot;101&quot;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;窗口 [0,3]=&quot;101&quot; 去掉中间的 0 得 &quot;11&quot;，长度为 2 的两侧？实际窗口 [0,3] 长度 4-1=3，对应原串把中间 0 删掉，得 &quot;111&quot; 长度为 3。✅&lt;/p&gt;
&lt;h2&gt;题解&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def longestSubarray(self, nums: list[int]) -&amp;gt; int:
        ans = left = cnt0 = 0
        for right, x in enumerate(nums):
            if x == 0:
                cnt0 += 1
            while cnt0 &amp;gt; 1:
                if nums[left] == 0:
                    cnt0 -= 1
                left += 1
            ans = max(ans, right - left)
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
sys.path.insert(0, &quot;/workspace/projects/source/coding/sliding-window-two-pointers/02-variable-sliding-window/1493-longest-subarray-after-delete&quot;)
from solution import Solution
import unittest

class TestLongestSubarray(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.longestSubarray([1,1,0,1]), 3)

    def test_example2(self):
        self.assertEqual(self.solution.longestSubarray([0,1,1,1,0,1,1,0,1]), 5)

    def test_example3(self):
        self.assertEqual(self.solution.longestSubarray([1,1,1]), 2)

    def test_all_zeros(self):
        self.assertEqual(self.solution.longestSubarray([0,0,0]), 0)

    def test_single_one(self):
        self.assertEqual(self.solution.longestSubarray([1]), 0)

    def test_single_zero(self):
        self.assertEqual(self.solution.longestSubarray([0]), 0)

if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，左右指针各遍历一次&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>3090. 每个字符最多出现两次的最长子字符串</title><link>https://h1s97x.github.io/posts/coding/coding-3090-max-two-occurrences/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-3090-max-two-occurrences/</guid><description>找出每个字符最多出现两次的最长子串长度</description><pubDate>Tue, 18 Aug 2026 05:37:22 GMT</pubDate><content:encoded>&lt;h1&gt;3090. 每个字符最多出现两次的最长子字符串&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个字符串 &lt;code&gt;s&lt;/code&gt;，请找出满足每个字符最多出现两次的最长子字符串，并返回该子字符串的最大长度。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;bcbbbcba&quot;
输出：4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;aaaa&quot;
输出：2
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;这是一个不定长滑动窗口问题（越短越合法）。窗口内每个字符的出现次数都 $\le 2$ 是合法条件。右指针不断把新字符纳入窗口；一旦某个字符次数超过 &lt;code&gt;2&lt;/code&gt;，就右移左指针收缩窗口，直到该字符重新合法。&lt;/p&gt;
&lt;p&gt;用哈希表 &lt;code&gt;cnt&lt;/code&gt; 维护窗口内各字符的出现次数。由于新字符是右指针刚加入的、唯一可能出现超限的就是 &lt;code&gt;s[right]&lt;/code&gt; 本身，所以内层只用 &lt;code&gt;while cnt[s[right]] &amp;gt; 2&lt;/code&gt; 这一个条件即可触发收缩，其余字符天然不可能超限。&lt;/p&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;初始化 &lt;code&gt;ans = left = 0&lt;/code&gt;，&lt;code&gt;cnt&lt;/code&gt; 为空字典。&lt;/li&gt;
&lt;li&gt;枚举 &lt;code&gt;right&lt;/code&gt;，把 &lt;code&gt;s[right]&lt;/code&gt; 计数加一。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;while cnt[s[right]] &amp;gt; 2&lt;/code&gt;：把 &lt;code&gt;s[left]&lt;/code&gt; 计数减一，&lt;code&gt;left += 1&lt;/code&gt;（收缩直到刚加入的字符不超限）。&lt;/li&gt;
&lt;li&gt;用当前窗口长 &lt;code&gt;right - left + 1&lt;/code&gt; 更新 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;s = &quot;bcbbbcba&quot;&lt;/code&gt; 为例（期望答案 4）：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;right&lt;/th&gt;
&lt;th&gt;字符&lt;/th&gt;
&lt;th&gt;入窗后计数&lt;/th&gt;
&lt;th&gt;收缩动作&lt;/th&gt;
&lt;th&gt;窗口&lt;/th&gt;
&lt;th&gt;长度&lt;/th&gt;
&lt;th&gt;ans&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;b&lt;/td&gt;
&lt;td&gt;b:1&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;[0,0]=&quot;b&quot;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;c&lt;/td&gt;
&lt;td&gt;b:1,c:1&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;[0,1]=&quot;bc&quot;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;b&lt;/td&gt;
&lt;td&gt;b:2,c:1&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;[0,2]=&quot;bcb&quot;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;b&lt;/td&gt;
&lt;td&gt;b:3,c:1&lt;/td&gt;
&lt;td&gt;左移，b:2&lt;/td&gt;
&lt;td&gt;[1,3]=&quot;bcb&quot;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;b&lt;/td&gt;
&lt;td&gt;b:3→2&lt;/td&gt;
&lt;td&gt;左移两次&lt;/td&gt;
&lt;td&gt;[3,4]=&quot;bb&quot;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;c&lt;/td&gt;
&lt;td&gt;b:2,c:1&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;[3,5]=&quot;bbc&quot;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;b&lt;/td&gt;
&lt;td&gt;b:3→2&lt;/td&gt;
&lt;td&gt;左移一次&lt;/td&gt;
&lt;td&gt;[4,6]=&quot;bcb&quot;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;td&gt;含 a,b:2,c:1&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;[4,7]=&quot;bcba&quot;&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;遍历结束，最长合法子串长度为 4。&lt;/p&gt;
&lt;h2&gt;题解&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def maximumLengthSubstring(self, s: str) -&amp;gt; int:
        ans = left = 0
        cnt = {}
        for right, ch in enumerate(s):
            cnt[ch] = cnt.get(ch, 0) + 1
            while cnt[ch] &amp;gt; 2:
                cnt[s[left]] -= 1
                left += 1
            ans = max(ans, right - left + 1)
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
sys.path.insert(0, &quot;/workspace/projects/source/coding/sliding-window-two-pointers/02-variable-sliding-window/3090-max-two-occurrences&quot;)
from solution import Solution
import unittest

class TestMaximumLengthSubstring(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.maximumLengthSubstring(&quot;bcbbbcba&quot;), 4)

    def test_example2(self):
        self.assertEqual(self.solution.maximumLengthSubstring(&quot;aaaa&quot;), 2)

    def test_all_unique(self):
        self.assertEqual(self.solution.maximumLengthSubstring(&quot;abcdef&quot;), 6)

    def test_single_char(self):
        self.assertEqual(self.solution.maximumLengthSubstring(&quot;a&quot;), 1)

    def test_two_chars(self):
        self.assertEqual(self.solution.maximumLengthSubstring(&quot;ab&quot;), 2)

if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，左右指针各遍历一次序列&lt;/li&gt;
&lt;li&gt;空间复杂度：O(|Σ|)，&lt;code&gt;Σ&lt;/code&gt; 为字符集大小（本题仅 26 个小写字母可视为 O(1)）&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>3. 无重复字符的最长子串</title><link>https://h1s97x.github.io/posts/coding/coding-3-longest-substring/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-3-longest-substring/</guid><description>找到不含重复字符的最长子串长度</description><pubDate>Tue, 18 Aug 2026 05:36:53 GMT</pubDate><content:encoded>&lt;h1&gt;3. 无重复字符的最长子串&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给定一个字符串 &lt;code&gt;s&lt;/code&gt;，请你找出其中不含有重复字符的&lt;strong&gt;最长子串&lt;/strong&gt;的长度。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;abcabcbb&quot;
输出：3
解释：最长无重复子串是 &quot;abc&quot;，长度为 3。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;bbbbb&quot;
输出：1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 3：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;pwwkew&quot;
输出：3
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;用两个指针 &lt;code&gt;left&lt;/code&gt;、&lt;code&gt;right&lt;/code&gt; 圈出一个窗口 &lt;code&gt;[left, right]&lt;/code&gt;，保证窗口内&lt;strong&gt;没有重复字符&lt;/strong&gt;。右指针不断向右扩展，把新字符纳入窗口；一旦产生重复，就把左指针向右收缩，直到窗口重新合法。整个过程中窗口大小、即 &lt;code&gt;right - left + 1&lt;/code&gt;，取最大值就是答案。&lt;/p&gt;
&lt;p&gt;为了让&quot;左指针收缩到哪&quot;能 O(1) 判断，用一个哈希表 &lt;code&gt;last&lt;/code&gt; 记录&lt;strong&gt;每个字符最近一次出现的位置&lt;/strong&gt;。当某个字符已经在窗口内再次出现（&lt;code&gt;last[s[right]] &amp;gt;= left&lt;/code&gt;）时，说明存在重复，直接把 &lt;code&gt;left&lt;/code&gt; 跳到该字符上次位置的下一个字符，一步到位：&lt;code&gt;left = last[s[right]] + 1&lt;/code&gt;。这样左指针不会一次只缩一格，而是直接跳跃到合法位置，效率更高。&lt;/p&gt;
&lt;h3&gt;算法步骤（右指针为主循环变量）&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;初始化 &lt;code&gt;ans = 0, left = 0&lt;/code&gt;，&lt;code&gt;last&lt;/code&gt; 为空字典。&lt;/li&gt;
&lt;li&gt;枚举 &lt;code&gt;right&lt;/code&gt; 从 &lt;code&gt;0&lt;/code&gt; 到 &lt;code&gt;n-1&lt;/code&gt;，当前字符为 &lt;code&gt;s[right]&lt;/code&gt;：
&lt;ul&gt;
&lt;li&gt;若 &lt;code&gt;s[right]&lt;/code&gt; 在 &lt;code&gt;last&lt;/code&gt; 中且其上次位置 &lt;code&gt;&amp;gt;= left&lt;/code&gt;，说明窗口内已存在该字符 → 令 &lt;code&gt;left = last[s[right]] + 1&lt;/code&gt;，丢弃窗口左端重复段。&lt;/li&gt;
&lt;li&gt;更新 &lt;code&gt;last[s[right]] = right&lt;/code&gt;（记录最近位置）。&lt;/li&gt;
&lt;li&gt;用窗口当前长度 &lt;code&gt;right - left + 1&lt;/code&gt; 更新 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;s = &quot;abcabcbb&quot;&lt;/code&gt; 为例：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;right&lt;/th&gt;
&lt;th&gt;字符&lt;/th&gt;
&lt;th&gt;操作&lt;/th&gt;
&lt;th&gt;窗口&lt;/th&gt;
&lt;th&gt;长度&lt;/th&gt;
&lt;th&gt;ans&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;td&gt;无重复，记录 last[&apos;a&apos;]=0&lt;/td&gt;
&lt;td&gt;[0,0]=&quot;a&quot;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;b&lt;/td&gt;
&lt;td&gt;无重复&lt;/td&gt;
&lt;td&gt;[0,1]=&quot;ab&quot;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;c&lt;/td&gt;
&lt;td&gt;无重复&lt;/td&gt;
&lt;td&gt;[0,2]=&quot;abc&quot;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;td&gt;last[&apos;a&apos;]=0 ≥ left=0 → left=1&lt;/td&gt;
&lt;td&gt;[1,3]=&quot;bca&quot;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;b&lt;/td&gt;
&lt;td&gt;last[&apos;b&apos;]=1 ≥ left=1 → left=2&lt;/td&gt;
&lt;td&gt;[2,4]=&quot;cab&quot;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;c&lt;/td&gt;
&lt;td&gt;last[&apos;c&apos;]=2 ≥ left=2 → left=3&lt;/td&gt;
&lt;td&gt;[3,5]=&quot;abc&quot;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;b&lt;/td&gt;
&lt;td&gt;last[&apos;b&apos;]=4 ≥ left=3 → left=5&lt;/td&gt;
&lt;td&gt;[5,6]=&quot;cb&quot;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;b&lt;/td&gt;
&lt;td&gt;last[&apos;b&apos;]=6 ≥ left=5 → left=7&lt;/td&gt;
&lt;td&gt;[7,7]=&quot;b&quot;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;遍历结束，最长无重复子串长度为 3。&lt;/p&gt;
&lt;h2&gt;题解&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def lengthOfLongestSubstring(self, s: str) -&amp;gt; int:
        ans = left = 0
        idx = {}
        for right, ch in enumerate(s):
            if ch in idx and idx[ch] &amp;gt;= left:
                left = idx[ch] + 1
            idx[ch] = right
            ans = max(ans, right - left + 1)
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
sys.path.insert(0, &quot;/workspace/projects/source/coding/sliding-window-two-pointers/02-variable-sliding-window/3-longest-substring&quot;)
from solution import Solution
import unittest

class TestLengthOfLongestSubstring(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.lengthOfLongestSubstring(&quot;abcabcbb&quot;), 3)

    def test_example2(self):
        self.assertEqual(self.solution.lengthOfLongestSubstring(&quot;bbbbb&quot;), 1)

    def test_example3(self):
        self.assertEqual(self.solution.lengthOfLongestSubstring(&quot;pwwkew&quot;), 3)

    def test_empty(self):
        self.assertEqual(self.solution.lengthOfLongestSubstring(&quot;&quot;), 0)

    def test_single(self):
        self.assertEqual(self.solution.lengthOfLongestSubstring(&quot;a&quot;), 1)

    def test_all_unique(self):
        self.assertEqual(self.solution.lengthOfLongestSubstring(&quot;abcdef&quot;), 6)

if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，&lt;code&gt;right&lt;/code&gt; 遍历一次数组、&lt;code&gt;left&lt;/code&gt; 最多也移动 n 次，整体线性&lt;/li&gt;
&lt;li&gt;空间复杂度：O(|Σ|)，&lt;code&gt;Σ&lt;/code&gt; 为字符集大小，&lt;code&gt;last&lt;/code&gt; 最多存 |Σ| 个键值对&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>2653. 滑动子数组的美丽值</title><link>https://h1s97x.github.io/posts/coding/coding-2653-sliding-subarray-beauty/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2653-sliding-subarray-beauty/</guid><description>求每个长度为 k 的子数组中第 x 小的数（负数才有效）</description><pubDate>Tue, 18 Aug 2026 05:33:15 GMT</pubDate><content:encoded>&lt;h1&gt;2653. 滑动子数组的美丽值&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个长度为 n 的整数数组 &lt;code&gt;nums&lt;/code&gt;，求出每个长度为 &lt;code&gt;k&lt;/code&gt; 的子数组的&lt;strong&gt;美丽值&lt;/strong&gt;。如果子数组中第 &lt;code&gt;x&lt;/code&gt; 小整数是负数，那么美丽值为第 &lt;code&gt;x&lt;/code&gt; 小的数，否则美丽值为 0。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [1,-1,-3,-2,3], k = 3, x = 2
输出：[-1,-2,-2]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [-1,-2,-3,-4,-5], k = 2, x = 2
输出：[-1,-2,-3,-4]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;核心思想&lt;/h2&gt;
&lt;p&gt;对每个长度为 &lt;code&gt;k&lt;/code&gt; 的窗口，要求第 &lt;code&gt;x&lt;/code&gt; 小。朴素做法是每次把窗口内 k 个数排序取第 x 小，复杂度 O(k log k)，总复杂度 O(nk log k) 偏慢。&lt;/p&gt;
&lt;p&gt;关键约束是 &lt;strong&gt;值域极小&lt;/strong&gt;：&lt;code&gt;-50 ≤ nums[i] ≤ 50&lt;/code&gt;，一共只有 &lt;code&gt;101&lt;/code&gt; 种取值。这给了我们一条更快的路——&lt;strong&gt;计数排序&lt;/strong&gt;：用频率数组 &lt;code&gt;cnt&lt;/code&gt; 统计窗口内每个值的出现次数，再从小到大累加频次，累加到 &lt;code&gt;≥ x&lt;/code&gt; 时停下的那个值就是第 x 小。每次找第 x 小只需遍历这固定的 101 个取值，与 k 无关。&lt;/p&gt;
&lt;p&gt;于是这是一个「定长滑动窗口 + 计数排序」的组合：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;窗口右端点每右移一格，就多一个元素入窗、少一个元素出窗；&lt;/li&gt;
&lt;li&gt;更新 &lt;code&gt;cnt&lt;/code&gt; 后，遍历值域 &lt;code&gt;-50..50&lt;/code&gt; 找出第 x 小；&lt;/li&gt;
&lt;li&gt;若第 x 小是负数则返回它，否则返回 0。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;算法步骤&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;用 &lt;code&gt;OFFSET = 50&lt;/code&gt; 把 &lt;code&gt;[-50, 50]&lt;/code&gt; 映射到数组下标 &lt;code&gt;[0, 100]&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;先把前 k 个元素入窗，建立初始 &lt;code&gt;cnt&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;写一个 &lt;code&gt;get_xth()&lt;/code&gt;：从 &lt;code&gt;-50&lt;/code&gt; 开始累加 &lt;code&gt;cnt&lt;/code&gt;，累加值 &lt;code&gt;≥ x&lt;/code&gt; 时停下；记录当前值 &lt;code&gt;v&lt;/code&gt;，若 &lt;code&gt;v &amp;lt; 0&lt;/code&gt; 返回 &lt;code&gt;v&lt;/code&gt;，否则返回 0。&lt;/li&gt;
&lt;li&gt;记录第一个窗口的答案，然后窗口右移：出 &lt;code&gt;nums[i-k]&lt;/code&gt;、入 &lt;code&gt;nums[i]&lt;/code&gt;，每轮用 &lt;code&gt;get_xth()&lt;/code&gt; 填答案。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;逐步举例&lt;/h2&gt;
&lt;p&gt;以 &lt;code&gt;nums = [1,-1,-3,-2,3], k = 3, x = 2&lt;/code&gt; 为例（值域偏移 50 后映射下表）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;窗口 1 = [1, -1, -3]&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;频率（从小到大）：-3:1、-1:1、1:1&lt;/li&gt;
&lt;li&gt;累加到 ≥ 2：-3 (acc=1) → -1 (acc=2) ✅&lt;/li&gt;
&lt;li&gt;第 2 小是 -1，为负数 → 美丽值 &lt;strong&gt;-1&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;窗口 2 = [-1, -3, -2]&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;频率：-3:1、-2:1、-1:1&lt;/li&gt;
&lt;li&gt;累加：-3 (acc=1) → -2 (acc=2) ✅&lt;/li&gt;
&lt;li&gt;第 2 小是 -2 → 美丽值 &lt;strong&gt;-2&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;窗口 3 = [-3, -2, 3]&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;频率：-3:1、-2:1、3:1&lt;/li&gt;
&lt;li&gt;累加：-3 (acc=1) → -2 (acc=2) ✅&lt;/li&gt;
&lt;li&gt;第 2 小是 -2 → 美丽值 &lt;strong&gt;-2&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;结果 &lt;code&gt;[-1, -2, -2]&lt;/code&gt; ✅&lt;/p&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def getSubarrayBeauty(self, nums: list[int], k: int, x: int) -&amp;gt; list[int]:
        OFFSET = 50
        cnt = [0] * 101
        for i in range(k):
            cnt[nums[i] + OFFSET] += 1

        def get_xth() -&amp;gt; int:
            acc = 0
            for v in range(-50, 51):
                acc += cnt[v + OFFSET]
                if acc &amp;gt;= x:
                    return v if v &amp;lt; 0 else 0
            return 0

        n = len(nums)
        ans = [0] * (n - k + 1)
        ans[0] = get_xth()
        for i in range(k, n):
            cnt[nums[i - k] + OFFSET] -= 1
            cnt[nums[i] + OFFSET] += 1
            ans[i - k + 1] = get_xth()
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
sys.path.insert(0, &quot;/workspace/projects/source/coding/sliding-window-two-pointers/01-fixed-sliding-window/2653-sliding-subarray-beauty&quot;)
from solution import Solution
import unittest

class TestGetSubarrayBeauty(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.getSubarrayBeauty([1,-1,-3,-2,3], 3, 2), [-1,-2,-2])

    def test_example2(self):
        self.assertEqual(self.solution.getSubarrayBeauty([-1,-2,-3,-4,-5], 2, 2), [-1,-2,-3,-4])

    def test_example3(self):
        self.assertEqual(self.solution.getSubarrayBeauty([-3,1,2,-3,0,-3], 2, 1), [-3,0,-3,-3,-3])

    def test_all_positive(self):
        self.assertEqual(self.solution.getSubarrayBeauty([1,2,3,4,5], 3, 2), [0,0,0])

    def test_single_element(self):
        self.assertEqual(self.solution.getSubarrayBeauty([-5], 1, 1), [-5])

    def test_x_larger(self):
        self.assertEqual(self.solution.getSubarrayBeauty([-1,-2,-3], 3, 3), [-1])

if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n × 101)，n 个窗口，每个窗口遍历固定 101 个取值找第 x 小。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(101)，频率数组大小固定。&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>1016. 子串能表示从 1 到 N 数字的二进制串</title><link>https://h1s97x.github.io/posts/coding/coding-1016-binary-substrings/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1016-binary-substrings/</guid><description>检查二进制串 s 是否包含 [1, n] 所有整数的二进制表示</description><pubDate>Tue, 18 Aug 2026 05:32:28 GMT</pubDate><content:encoded>&lt;h1&gt;1016. 子串能表示从 1 到 N 数字的二进制串&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给定一个二进制字符串 &lt;code&gt;s&lt;/code&gt; 和一个正整数 &lt;code&gt;n&lt;/code&gt;，如果对于 &lt;code&gt;[1, n]&lt;/code&gt; 范围内的每个整数，其二进制表示都是 &lt;code&gt;s&lt;/code&gt; 的子字符串，就返回 &lt;code&gt;true&lt;/code&gt;，否则返回 &lt;code&gt;false&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;0110&quot;, n = 3
输出：true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;0110&quot;, n = 4
输出：false
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;核心思想&lt;/h2&gt;
&lt;p&gt;题目问的是：&lt;strong&gt;每一个 &lt;code&gt;[1, n]&lt;/code&gt; 中的整数，其二进制表示是不是都作为 s 的某个连续子串出现过&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;把问题反过来看：s 的所有子串能表示成哪些整数？我们只需把 s 里能表示出的整数收集进一个集合，再检查 &lt;code&gt;[1, n]&lt;/code&gt; 是否都被这个集合覆盖。&lt;/p&gt;
&lt;p&gt;难点在 &lt;code&gt;n&lt;/code&gt; 可以取到 &lt;code&gt;10^9&lt;/code&gt;——直接枚举 &lt;code&gt;1..n&lt;/code&gt; 的每个数再 &lt;code&gt;find&lt;/code&gt;，在 &lt;code&gt;n&lt;/code&gt; 很大时会超时。但借助两个关键观察可以大幅剪枝：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;位长上限&lt;/strong&gt;：&lt;code&gt;10^9 &amp;lt; 2^30&lt;/code&gt;，所以 &lt;code&gt;[1, n]&lt;/code&gt; 里任意整数的二进制表示长度都不超过 30 位。因此枚举 s 的子串时，只需要考虑长度 ≤ 30 的窗口即可。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;集合规模上限&lt;/strong&gt;：&lt;code&gt;s&lt;/code&gt; 长度 &lt;code&gt;m ≤ 1000&lt;/code&gt;，每个起点最多派生 30 个窗口，所以能表示出的不同整数最多约 &lt;code&gt;1000 × 30 = 30000&lt;/code&gt; 个。一旦 &lt;code&gt;n &amp;gt; 30000&lt;/code&gt;，集合根本不可能覆盖 &lt;code&gt;[1, n]&lt;/code&gt; 这么多数，直接返回 &lt;code&gt;false&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;算法步骤&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;记 &lt;code&gt;m = len(s)&lt;/code&gt;；若 &lt;code&gt;n &amp;gt; m * 30&lt;/code&gt;，直接返回 &lt;code&gt;false&lt;/code&gt;（集合规模上限不够）。&lt;/li&gt;
&lt;li&gt;枚举每个起点 &lt;code&gt;i&lt;/code&gt;，从 &lt;code&gt;i&lt;/code&gt; 开始向右扩展长度最多 30 的子串：
&lt;ul&gt;
&lt;li&gt;滚动维护 &lt;code&gt;val = (val &amp;lt;&amp;lt; 1) | bit&lt;/code&gt;，即子串对应的二进制整数值。&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;val &amp;gt; n&lt;/code&gt;，说明后面只会更大，直接 &lt;code&gt;break&lt;/code&gt; 这个起点。&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;val &amp;gt; 0&lt;/code&gt;（跳过值为 0 的前导零子串），加入集合 &lt;code&gt;seen&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;遍历 &lt;code&gt;1..n&lt;/code&gt;，若有任何一个数不在 &lt;code&gt;seen&lt;/code&gt; 中，返回 &lt;code&gt;false&lt;/code&gt;；全部在则返回 &lt;code&gt;true&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;逐步举例&lt;/h2&gt;
&lt;p&gt;以 &lt;code&gt;s = &quot;0110&quot;, n = 4&lt;/code&gt; 为例：&lt;code&gt;m = 4&lt;/code&gt;，&lt;code&gt;m*30 = 120 &amp;gt; 4&lt;/code&gt;，进入枚举。&lt;/p&gt;
&lt;p&gt;枚举所有子串并转为整数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;起点 0：&lt;code&gt;&quot;0&quot;&lt;/code&gt;→0（跳过）、&lt;code&gt;&quot;01&quot;&lt;/code&gt;→1、&lt;code&gt;&quot;011&quot;&lt;/code&gt;→3、&lt;code&gt;&quot;0110&quot;&lt;/code&gt;→6（&amp;gt;4 break）&lt;/li&gt;
&lt;li&gt;起点 1：&lt;code&gt;&quot;1&quot;&lt;/code&gt;→1、&lt;code&gt;&quot;11&quot;&lt;/code&gt;→3、&lt;code&gt;&quot;110&quot;&lt;/code&gt;→6（&amp;gt;4 break）&lt;/li&gt;
&lt;li&gt;起点 2：&lt;code&gt;&quot;1&quot;&lt;/code&gt;→1、&lt;code&gt;&quot;10&quot;&lt;/code&gt;→2&lt;/li&gt;
&lt;li&gt;起点 3：&lt;code&gt;&quot;0&quot;&lt;/code&gt;→0（跳过）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;收集得 &lt;code&gt;seen = {1, 2, 3}&lt;/code&gt;。检查 &lt;code&gt;1..4&lt;/code&gt;：1、2、3 都在，但 &lt;strong&gt;4 不在&lt;/strong&gt;。返回 &lt;code&gt;false&lt;/code&gt;。✅（整数 4 的二进制是 &lt;code&gt;&quot;100&quot;&lt;/code&gt;，确实没出现在 s 中）&lt;/p&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def queryString(self, s: str, n: int) -&amp;gt; bool:
        m = len(s)
        # Maximum distinct values we can get from substrings ≤ 30 length
        max_possible = m * 30
        if n &amp;gt; max_possible:
            return False

        seen = set()
        for i in range(m):
            val = 0
            for j in range(i, min(i + 30, m)):
                val = (val &amp;lt;&amp;lt; 1) | (ord(s[j]) - 48)
                if val &amp;gt; n:
                    break
                if val &amp;gt; 0:
                    seen.add(val)

        for i in range(1, n + 1):
            if i not in seen:
                return False
        return True
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
sys.path.insert(0, &quot;/workspace/projects/source/coding/sliding-window-two-pointers/01-fixed-sliding-window/1016-binary-substrings&quot;)
from solution import Solution
import unittest

class TestQueryString(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertTrue(self.solution.queryString(&quot;0110&quot;, 3))

    def test_example2(self):
        self.assertFalse(self.solution.queryString(&quot;0110&quot;, 4))

    def test_small_n(self):
        self.assertTrue(self.solution.queryString(&quot;1&quot;, 1))

    def test_large_n_early_false(self):
        s = &quot;0&quot; * 1000
        self.assertFalse(self.solution.queryString(s, 100000))

    def test_contains_all(self):
        self.assertTrue(self.solution.queryString(&quot;11011101100&quot;, 5))

    def test_not_contain(self):
        self.assertFalse(self.solution.queryString(&quot;111&quot;, 4))

    def test_long_s(self):
        s = &quot;01101110010111011110001001101010111100110111101111&quot;
        self.assertTrue(self.solution.queryString(s, 5))

if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(m × 30 + n)，枚举子串部分受 &lt;code&gt;m ≤ 1000&lt;/code&gt; 且长度 ≤ 30 限制；当 &lt;code&gt;n&lt;/code&gt; 很大时在第一步就被 &lt;code&gt;n &amp;gt; m*30&lt;/code&gt; 快速剪枝返回。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(m × 30)，集合 &lt;code&gt;seen&lt;/code&gt; 中最多约 30000 个元素。&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>1461. 检查一个字符串是否包含所有长度为 K 的二进制子串</title><link>https://h1s97x.github.io/posts/coding/coding-1461-all-binary-codes/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1461-all-binary-codes/</guid><description>检查二进制串 s 是否包含所有长度为 k 的二进制子串</description><pubDate>Tue, 18 Aug 2026 05:31:19 GMT</pubDate><content:encoded>&lt;h1&gt;1461. 检查一个字符串是否包含所有长度为 K 的二进制子串&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个二进制字符串 &lt;code&gt;s&lt;/code&gt; 和一个整数 &lt;code&gt;k&lt;/code&gt;。如果所有长度为 &lt;code&gt;k&lt;/code&gt; 的二进制字符串都是 &lt;code&gt;s&lt;/code&gt; 的子串，请返回 &lt;code&gt;true&lt;/code&gt;，否则返回 &lt;code&gt;false&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;00110110&quot;, k = 2
输出：true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;0110&quot;, k = 1
输出：true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 3：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;0110&quot;, k = 2
输出：false
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;核心思想&lt;/h2&gt;
&lt;p&gt;一个长度为 &lt;code&gt;k&lt;/code&gt; 的二进制子串，本质上就是一个 &lt;code&gt;k&lt;/code&gt; 位的二进制数，其取值范围是 &lt;code&gt;0&lt;/code&gt; 到 &lt;code&gt;2^k - 1&lt;/code&gt;，一共 &lt;code&gt;2^k&lt;/code&gt; 种可能。&lt;/p&gt;
&lt;p&gt;所以题目等价于：&lt;strong&gt;s 中所有长度为 k 的子串组成的整数集合，是否恰好覆盖了 &lt;code&gt;[0, 2^k)&lt;/code&gt; 的全部 &lt;code&gt;2^k&lt;/code&gt; 个值&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;基于这个思路，我们只需用定长滑动窗口扫一遍 &lt;code&gt;s&lt;/code&gt;，把每个长度为 k 的窗口对应的整数记录下来，最后看是不是 &lt;code&gt;2^k&lt;/code&gt; 个都出现了。由于 &lt;code&gt;k ≤ 20&lt;/code&gt;，&lt;code&gt;2^k&lt;/code&gt; 最多约 10^6，用一个布尔数组即可。&lt;/p&gt;
&lt;h2&gt;位运算滚动窗口：O(1) 维护窗口整数&lt;/h2&gt;
&lt;p&gt;暴力把每个窗口切片再转 int 是 O(k) 的。我们可以用&lt;strong&gt;滚动窗口 + 位运算&lt;/strong&gt;把它优化成 O(1)：&lt;/p&gt;
&lt;p&gt;维护一个整数 &lt;code&gt;val&lt;/code&gt;，表示当前窗口内 k 位二进制串对应的数值。窗口每向右移动一位：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;入&lt;/strong&gt;：&lt;code&gt;val = (val &amp;lt;&amp;lt; 1) | bit&lt;/code&gt;，即把旧窗口值左移一位，空出的最低位填充新字符 &lt;code&gt;bit&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;截断&lt;/strong&gt;：&lt;code&gt;val = val &amp;amp; mask&lt;/code&gt;，其中 &lt;code&gt;mask = 2^k - 1&lt;/code&gt;，只保留低 k 位（k 个 1），把多余的进位清掉，等价于&quot;左端点离开窗口&quot;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这两个操作合在一起正好是 &lt;code&gt;val = ((val &amp;lt;&amp;lt; 1) | bit) &amp;amp; mask&lt;/code&gt;，一步完成&quot;入&quot;和&quot;出&quot;。&lt;/p&gt;
&lt;p&gt;当索引 &lt;code&gt;i &amp;gt;= k - 1&lt;/code&gt; 时，说明窗口已经形成（长度恰好为 k），此时把 &lt;code&gt;val&lt;/code&gt; 标记为已出现。&lt;/p&gt;
&lt;h2&gt;算法步骤&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;令 &lt;code&gt;need = 2^k&lt;/code&gt;，&lt;code&gt;mask = need - 1&lt;/code&gt;，初始化 &lt;code&gt;seen = [False] * need&lt;/code&gt;，&lt;code&gt;val = 0&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;从左到右遍历 &lt;code&gt;s&lt;/code&gt;：
&lt;ul&gt;
&lt;li&gt;更新 &lt;code&gt;val = ((val &amp;lt;&amp;lt; 1) | (ord(ch) - ord(&apos;0&apos;))) &amp;amp; mask&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;i &amp;gt;= k-1&lt;/code&gt;（窗口长度已凑够 k），将 &lt;code&gt;seen[val] = True&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;all(seen)&lt;/code&gt;，即所有 &lt;code&gt;2^k&lt;/code&gt; 种取值是否都出现过。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;逐步举例&lt;/h2&gt;
&lt;p&gt;以 &lt;code&gt;s = &quot;0110&quot;, k = 2&lt;/code&gt; 为例：&lt;code&gt;need = 4, mask = 3&lt;/code&gt;。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;i&lt;/th&gt;
&lt;th&gt;ch&lt;/th&gt;
&lt;th&gt;val = ((val&amp;lt;&amp;lt;1)|bit) &amp;amp; mask&lt;/th&gt;
&lt;th&gt;i&amp;gt;=1 ?&lt;/th&gt;
&lt;th&gt;seen 标记&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;&apos;0&apos;&lt;/td&gt;
&lt;td&gt;((0&amp;lt;&amp;lt;1)|0)=0 &amp;amp; 3 = 0&lt;/td&gt;
&lt;td&gt;否跳过&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&apos;1&apos;&lt;/td&gt;
&lt;td&gt;((0&amp;lt;&amp;lt;1)|1)=1 &amp;amp; 3 = 1&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;seen[1]=1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&apos;1&apos;&lt;/td&gt;
&lt;td&gt;((1&amp;lt;&amp;lt;1)|1)=3 &amp;amp; 3 = 3&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;seen[3]=1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&apos;0&apos;&lt;/td&gt;
&lt;td&gt;((3&amp;lt;&amp;lt;1)|0)=6 &amp;amp; 3 = 2&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;seen[2]=1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最终 &lt;code&gt;seen[0..3] = [F,T,T,T]&lt;/code&gt;，&lt;code&gt;seen[0]=False&lt;/code&gt;，即长度为 2 的二进制串 &lt;code&gt;&quot;00&quot;&lt;/code&gt; 没有出现。返回 &lt;code&gt;false&lt;/code&gt;。✅&lt;/p&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def hasAllCodes(self, s: str, k: int) -&amp;gt; bool:
        need = 1 &amp;lt;&amp;lt; k
        seen = [False] * need
        mask = need - 1
        val = 0
        for i, ch in enumerate(s):
            val = ((val &amp;lt;&amp;lt; 1) | (ord(ch) - 48)) &amp;amp; mask
            if i &amp;gt;= k - 1:
                seen[val] = True
        return all(seen)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
sys.path.insert(0, &quot;/workspace/projects/source/coding/sliding-window-two-pointers/01-fixed-sliding-window/1461-all-binary-codes&quot;)
from solution import Solution
import unittest

class TestHasAllCodes(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertTrue(self.solution.hasAllCodes(&quot;00110110&quot;, 2))

    def test_example2(self):
        self.assertTrue(self.solution.hasAllCodes(&quot;0110&quot;, 1))

    def test_example3(self):
        self.assertFalse(self.solution.hasAllCodes(&quot;0110&quot;, 2))

    def test_long_string(self):
        s = &quot;0&quot; * 10000 + &quot;1&quot; * 10000
        self.assertFalse(self.solution.hasAllCodes(s, 10))

    def test_small_k(self):
        self.assertTrue(self.solution.hasAllCodes(&quot;01&quot;, 1))

    def test_not_enough(self):
        self.assertFalse(self.solution.hasAllCodes(&quot;000&quot;, 2))

if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，其中 n 是 s 的长度，每次更新窗口都是 O(1)。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(2^k)，布尔数组 &lt;code&gt;seen&lt;/code&gt; 的大小，k ≤ 20 时最多约 10^6。&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>2200. 找出数组中的所有 K 近邻下标</title><link>https://h1s97x.github.io/posts/coding/coding-2200-k-distant-indices/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2200-k-distant-indices/</guid><description>找到所有满足存在 j 使得 |i-j| ≤ k 且 nums[j] == key 的下标</description><pubDate>Tue, 18 Aug 2026 05:30:49 GMT</pubDate><content:encoded>&lt;h1&gt;2200. 找出数组中的所有 K 近邻下标&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个下标从 0 开始的整数数组 &lt;code&gt;nums&lt;/code&gt; 和两个整数 &lt;code&gt;key&lt;/code&gt; 和 &lt;code&gt;k&lt;/code&gt;。K 近邻下标是 &lt;code&gt;nums&lt;/code&gt; 中的一个下标 &lt;code&gt;i&lt;/code&gt;，满足至少存在一个下标 &lt;code&gt;j&lt;/code&gt; 使得 &lt;code&gt;|i - j| &amp;lt;= k&lt;/code&gt; 且 &lt;code&gt;nums[j] == key&lt;/code&gt;。以递增顺序返回所有 K 近邻下标。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [3,4,9,1,3,9,5], key = 9, k = 1
输出：[1,2,3,4,5,6]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [2,2,2,2,2], key = 2, k = 2
输出：[0,1,2,3,4]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;核心思想&lt;/h2&gt;
&lt;p&gt;暴力做法是枚举每个下标 &lt;code&gt;i&lt;/code&gt;，再扫描所有 &lt;code&gt;nums[j] == key&lt;/code&gt; 的位置判断 &lt;code&gt;|i-j| &amp;lt;= k&lt;/code&gt;，时间复杂度 O(n²)。&lt;/p&gt;
&lt;p&gt;能不能只扫一遍就 O(n) 完成？关键是注意到：&lt;strong&gt;每个等于 key 的下标 &lt;code&gt;j&lt;/code&gt;，它覆盖的区间是 &lt;code&gt;[j-k, j+k]&lt;/code&gt;&lt;/strong&gt;。多个这样的区间拼起来，就是所有 K 近邻下标。&lt;/p&gt;
&lt;p&gt;因此我们可以从左到右扫描，每当遇到一个 &lt;code&gt;nums[j] == key&lt;/code&gt;，就把它覆盖的区间 &lt;code&gt;[j-k, j+k]&lt;/code&gt; 整段加入答案。&lt;/p&gt;
&lt;p&gt;但这样会有问题：如果两个 &lt;code&gt;j&lt;/code&gt; 的覆盖区间有重叠，就会重复加入下标。比如 &lt;code&gt;j1&lt;/code&gt; 覆盖 &lt;code&gt;[2, 4]&lt;/code&gt;，&lt;code&gt;j2&lt;/code&gt; 覆盖 &lt;code&gt;[3, 5]&lt;/code&gt;，重叠部分是 &lt;code&gt;[3, 4]&lt;/code&gt;，会重复。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;去重技巧&lt;/strong&gt;：用变量 &lt;code&gt;r&lt;/code&gt; 记录&quot;尚未被覆盖的最小下标&quot;。既然 &lt;code&gt;[0, r-1]&lt;/code&gt; 都已经被前面的覆盖区间填过了，那么当前区间就不需要从 &lt;code&gt;j-k&lt;/code&gt; 开始了，直接从 &lt;code&gt;max(r, j-k)&lt;/code&gt; 开始即可。这样每个下标最多被加入一次，天然去重，也保证了递增顺序。&lt;/p&gt;
&lt;h2&gt;算法步骤&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;初始化 &lt;code&gt;ans = []&lt;/code&gt;，&lt;code&gt;r = 0&lt;/code&gt;（&lt;code&gt;r&lt;/code&gt; 表示可添加区间的起点下界，同时 &lt;code&gt;[0, r-1]&lt;/code&gt; 已被覆盖）。&lt;/li&gt;
&lt;li&gt;从左到右遍历 &lt;code&gt;nums&lt;/code&gt;：
&lt;ul&gt;
&lt;li&gt;若 &lt;code&gt;nums[j] == key&lt;/code&gt;：
&lt;ul&gt;
&lt;li&gt;左边界 &lt;code&gt;l = max(r, j - k)&lt;/code&gt;（不能在已覆盖区域内重复加）。&lt;/li&gt;
&lt;li&gt;右边界 &lt;code&gt;r = min(n-1, j+k) + 1&lt;/code&gt;（闭区间 &lt;code&gt;[l, r]&lt;/code&gt;，所以右端点取 &lt;code&gt;min(n-1, j+k)&lt;/code&gt;，&lt;code&gt;r&lt;/code&gt; 记录为下一个待处理下标即 &lt;code&gt;+1&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;把 &lt;code&gt;[l, r)&lt;/code&gt; 内的所有下标依次加入 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;为什么左边界取 &lt;code&gt;max(r, j-k)&lt;/code&gt;？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;因为 &lt;code&gt;[0, r-1]&lt;/code&gt; 已经被前面的 key 区间完整覆盖，这些下标不该重复添加。&lt;/li&gt;
&lt;li&gt;从 &lt;code&gt;r&lt;/code&gt; 和 &lt;code&gt;j-k&lt;/code&gt; 的较大者开始，正好避开了已覆盖部分。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;逐步举例&lt;/h2&gt;
&lt;p&gt;以示例 1 为例：&lt;code&gt;nums = [3,4,9,1,3,9,5], key = 9, k = 1, n = 7&lt;/code&gt;。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;步骤&lt;/th&gt;
&lt;th&gt;j&lt;/th&gt;
&lt;th&gt;nums[j]&lt;/th&gt;
&lt;th&gt;计算 l/r&lt;/th&gt;
&lt;th&gt;添加的下标&lt;/th&gt;
&lt;th&gt;r 更新为&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;l=max(0,1)=1, r=min(6,3)+1=4&lt;/td&gt;
&lt;td&gt;[1,4) → 1,2,3&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;l=max(4,4)=4, r=min(6,6)+1=7&lt;/td&gt;
&lt;td&gt;[4,7) → 4,5,6&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;第一轮：&lt;code&gt;nums[2]=9&lt;/code&gt;，覆盖 &lt;code&gt;[2-1, 2+1]=[1,3]&lt;/code&gt;。取 &lt;code&gt;l = max(0, 1) = 1&lt;/code&gt;，&lt;code&gt;r = min(6, 3)+1 = 4&lt;/code&gt;，把 &lt;code&gt;1,2,3&lt;/code&gt; 加入。&lt;/p&gt;
&lt;p&gt;第二轮：&lt;code&gt;nums[5]=9&lt;/code&gt;，覆盖 &lt;code&gt;[5-1, 5+1]=[4,6]&lt;/code&gt;。取 &lt;code&gt;l = max(4, 4) = 4&lt;/code&gt;（注意此时 r=4，说明 0~3 已填，直接从 4 开始），&lt;code&gt;r = min(6,6)+1 = 7&lt;/code&gt;，把 &lt;code&gt;4,5,6&lt;/code&gt; 加入。&lt;/p&gt;
&lt;p&gt;答案 &lt;code&gt;[1,2,3,4,5,6]&lt;/code&gt;。下标 0 没被任何区间覆盖（&lt;code&gt;|0-2|=2 &amp;gt; 1&lt;/code&gt; 且 &lt;code&gt;|0-5|=5 &amp;gt; 1&lt;/code&gt;），所以不加入。✅&lt;/p&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def findKDistantIndices(self, nums: list[int], key: int, k: int) -&amp;gt; list[int]:
        n = len(nums)
        ans = []
        r = 0
        for j, x in enumerate(nums):
            if x == key:
                l = max(r, j - k)
                r = min(n - 1, j + k) + 1
                for i in range(l, r):
                    ans.append(i)
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
sys.path.insert(0, &quot;/workspace/projects/source/coding/sliding-window-two-pointers/01-fixed-sliding-window/2200-k-distant-indices&quot;)
from solution import Solution
import unittest

class TestFindKDistantIndices(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.findKDistantIndices([3,4,9,1,3,9,5], 9, 1), [1,2,3,4,5,6])

    def test_example2(self):
        self.assertEqual(self.solution.findKDistantIndices([2,2,2,2,2], 2, 2), [0,1,2,3,4])

    def test_single_key(self):
        self.assertEqual(self.solution.findKDistantIndices([1,2,3,4,5], 3, 1), [1,2,3])

    def test_no_key(self):
        self.assertEqual(self.solution.findKDistantIndices([1,2,3], 4, 1), [])

    def test_k_large(self):
        self.assertEqual(self.solution.findKDistantIndices([1,2,1,2,1], 1, 10), [0,1,2,3,4])

if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，每个下标只会被加入一次，整体线性扫描。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(n)（答案数组占用的空间，不考虑则 O(1)）。&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>2953. 统计完全子字符串</title><link>https://h1s97x.github.io/posts/coding/coding-2953-complete-substrings/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2953-complete-substrings/</guid><description>统计完全子字符串个数：每个字符恰好出现 k 次，且相邻字符字母序相差 ≤ 2</description><pubDate>Mon, 17 Aug 2026 01:37:34 GMT</pubDate><content:encoded>&lt;h1&gt;2953. 统计完全子字符串&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;word&lt;/code&gt; 的一个子字符串 &lt;code&gt;s&lt;/code&gt; 若同时满足：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;s&lt;/code&gt; 中&lt;strong&gt;每个字符恰好出现&lt;/strong&gt; &lt;code&gt;k&lt;/code&gt; 次；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s&lt;/code&gt; 中&lt;strong&gt;相邻字符&lt;/strong&gt;在字母表中位置相差&lt;strong&gt;至多&lt;/strong&gt; &lt;code&gt;2&lt;/code&gt;（如 &lt;code&gt;a&lt;/code&gt; 与 &lt;code&gt;c&lt;/code&gt; 差 2 允许，&lt;code&gt;a&lt;/code&gt; 与 &lt;code&gt;d&lt;/code&gt; 差 3 不允许）；&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;则称 &lt;code&gt;s&lt;/code&gt; 为&lt;strong&gt;完全字符串&lt;/strong&gt;。返回 &lt;code&gt;word&lt;/code&gt; 中完全子字符串的数目。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 ：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：word=&quot;igigee&quot;, k=2  → 输出：3
输入：word=&quot;aaabbbccc&quot;, k=3 → 输出：6
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;第一步：按相邻差切段。&lt;/strong&gt; 完全子串中任意相邻两字符的字母序差必须 ≤2，因此一个完全子串绝不会跨过&quot;相邻差 &amp;gt;2&quot;的边界。按差 &amp;gt;2 的位置把 &lt;code&gt;word&lt;/code&gt; 切成若干段，在每个段内独立统计并累加即可。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第二步：枚举可能的窗口大小。&lt;/strong&gt; 若完全子串含 &lt;code&gt;m&lt;/code&gt; 种不同字符、每种出现 &lt;code&gt;k&lt;/code&gt; 次，则其长度为 &lt;code&gt;m·k&lt;/code&gt;。&lt;code&gt;m ∈ [1, 26]&lt;/code&gt;，且 &lt;code&gt;m·k ≤ 段长&lt;/code&gt;。对每个候选 &lt;code&gt;m&lt;/code&gt;，做一次定长滑窗。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第三步：O(1) 维护窗口是否&quot;恰好&quot;。&lt;/strong&gt; 窗口长度固定为 &lt;code&gt;target = m·k&lt;/code&gt;。用计数数组 &lt;code&gt;cnt&lt;/code&gt;，并维护两个量：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;distinct&lt;/code&gt;：出现过的字符种类数（&lt;code&gt;cnt[c] &amp;gt; 0&lt;/code&gt; 的个数）；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;valid&lt;/code&gt;：&lt;strong&gt;恰出现 &lt;code&gt;k&lt;/code&gt; 次&lt;/strong&gt;的字符个数。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当 &lt;code&gt;valid == distinct&lt;/code&gt; 时，说明窗口内所有出现过的字符都恰好各出现 &lt;code&gt;k&lt;/code&gt; 次（即没有出现不足 &lt;code&gt;k&lt;/code&gt; 次的字符），满足条件，答案 +1。入窗/出窗时只在计数跨过 &lt;code&gt;0/1&lt;/code&gt;、&lt;code&gt;k/k+1&lt;/code&gt;、&lt;code&gt;k-1/k&lt;/code&gt; 这些临界点更新 &lt;code&gt;distinct&lt;/code&gt; 与 &lt;code&gt;valid&lt;/code&gt;，从而 O(1) 完成。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;逐步举例&lt;/strong&gt;（&lt;code&gt;word=&quot;aaabbbccc&quot;, k=3&lt;/code&gt;）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;相邻差都 ≤2，整串为一段。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;m=3&lt;/code&gt;：&lt;code&gt;target=9&lt;/code&gt;，唯一窗口 &lt;code&gt;[0,9)&lt;/code&gt;，&lt;code&gt;cnt[a]=cnt[b]=cnt[c]=3&lt;/code&gt;，&lt;code&gt;distinct=3, valid=3&lt;/code&gt;，&lt;code&gt;valid==distinct&lt;/code&gt; → 答案 1。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;m=1&lt;/code&gt;：&lt;code&gt;target=3&lt;/code&gt;，连续三字符窗口如 &lt;code&gt;[0,3]=&quot;aaa&quot;&lt;/code&gt; → &lt;code&gt;distinct=1, valid=1&lt;/code&gt;，+1；&lt;code&gt;[3,6]=&quot;bbb&quot;&lt;/code&gt;、&lt;code&gt;[6,9]=&quot;ccc&quot;&lt;/code&gt; 同理 → 再加 2。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;m=2&lt;/code&gt;：&lt;code&gt;target=6&lt;/code&gt;，窗口如 &lt;code&gt;[0,6]=&quot;aaabbb&quot;&lt;/code&gt; → &lt;code&gt;distinct=2&lt;/code&gt; 但 &lt;code&gt;valid=0&lt;/code&gt;（无字符恰出现 3 次的，分别是 a 出现 3? 实际 a 出现 3、b 出现 3，则 valid=2）→ &lt;code&gt;valid==distinct&lt;/code&gt;，+1；&lt;code&gt;[3,9]=&quot;bbbccc&quot;&lt;/code&gt; 同理 +1。&lt;/li&gt;
&lt;li&gt;总计 &lt;code&gt;1+3+2 = 6&lt;/code&gt; ✓。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(26·n)，每段最多枚举 26 种窗口大小，各滑动一次。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)，定长 26 的计数数组。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def countCompleteSubstrings(self, word: str, k: int) -&amp;gt; int:
        n = len(word)
        if n &amp;lt; k:
            return 0

        # Split into segments by adjacency condition
        segments = []
        start = 0
        for i in range(1, n):
            if abs(ord(word[i]) - ord(word[i - 1])) &amp;gt; 2:
                segments.append(word[start:i])
                start = i
        segments.append(word[start:])

        ans = 0
        for seg in segments:
            m = len(seg)
            max_j = min(26, m // k)
            for j in range(1, max_j + 1):
                target = j * k
                cnt = [0] * 26
                valid = 0
                distinct = 0

                for i, ch in enumerate(seg):
                    idx = ord(ch) - 97
                    cnt[idx] += 1
                    if cnt[idx] == 1:
                        distinct += 1
                    if cnt[idx] == k:
                        valid += 1
                    elif cnt[idx] == k + 1:
                        valid -= 1

                    if i &amp;gt;= target:
                        idx2 = ord(seg[i - target]) - 97
                        cnt[idx2] -= 1
                        if cnt[idx2] == 0:
                            distinct -= 1
                        if cnt[idx2] == k:
                            valid += 1
                        elif cnt[idx2] == k - 1:
                            valid -= 1

                    if i &amp;gt;= target - 1 and valid == distinct:
                        ans += 1
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
sys.path.insert(0, &quot;/workspace/projects/source/coding/sliding-window-two-pointers/01-fixed-sliding-window/2953-complete-substrings&quot;)
from solution import Solution
import unittest

class TestCountCompleteSubstrings(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.countCompleteSubstrings(&quot;igigee&quot;, 2), 3)

    def test_example2(self):
        self.assertEqual(self.solution.countCompleteSubstrings(&quot;aaabbbccc&quot;, 3), 6)

    def test_single_char(self):
        self.assertEqual(self.solution.countCompleteSubstrings(&quot;a&quot;, 1), 1)

    def test_no_valid(self):
        self.assertEqual(self.solution.countCompleteSubstrings(&quot;abc&quot;, 2), 0)

    def test_same_char(self):
        self.assertEqual(self.solution.countCompleteSubstrings(&quot;aaaa&quot;, 2), 3)

if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2156. 查找给定哈希值的子串</title><link>https://h1s97x.github.io/posts/coding/coding-2156-substring-hash/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2156-substring-hash/</guid><description>返回 s 中第一个长度为 k 且哈希值等于 hashValue 的子串（反向滑动窗口）</description><pubDate>Mon, 17 Aug 2026 01:33:34 GMT</pubDate><content:encoded>&lt;h1&gt;2156. 查找给定哈希值的子串&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;字符串长度为 &lt;code&gt;k&lt;/code&gt; 的子串哈希定义（&lt;code&gt;val(c) = c 的字母序，a=1, ..., z=26&lt;/code&gt;）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hash(sub) = ( val(s[0])·p^0 + val(s[1])·p^1 + ... + val(s[k-1])·p^(k-1) ) mod m
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;给定 &lt;code&gt;power=p&lt;/code&gt;、&lt;code&gt;modulo=m&lt;/code&gt;，返回 &lt;code&gt;s&lt;/code&gt; 中&lt;strong&gt;第一个&lt;/strong&gt;长度为 &lt;code&gt;k&lt;/code&gt; 且哈希值等于 &lt;code&gt;hashValue&lt;/code&gt; 的子串（下标最小者）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 ：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s=&quot;leetcode&quot;, power=7, modulo=20, k=2, hashValue=0
输出：&quot;ee&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;核心：不要从前往后滑，要从后往前滑。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;正向滚动哈希在窗口左移一格时，需要把最高位 &lt;code&gt;p^(k-1)&lt;/code&gt; 的贡献除掉，即做除法、求 &lt;code&gt;p&lt;/code&gt; 的逆元。但 &lt;code&gt;modulo&lt;/code&gt; 不保证是质数，逆元未必存在，除法不可行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;反向滑动窗口&lt;/strong&gt;完美避开除法：从右往左维护窗口哈希，出窗口的是&quot;低位&quot;，入窗口的是&quot;高位&quot;。&lt;/p&gt;
&lt;p&gt;设窗口哈希 &lt;code&gt;h&lt;/code&gt; 从右端 &lt;code&gt;i+1..i+k&lt;/code&gt; 滑到 &lt;code&gt;i..i+k-1&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;h&apos; = ( (h - val(i+k)·p^(k-1)) · p + val(i) ) mod m
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;先减掉离开窗口的字符 &lt;code&gt;val(i+k)&lt;/code&gt; 乘它的系数 &lt;code&gt;p^(k-1)&lt;/code&gt;（最高位）→ 等价于按位对齐减；&lt;/li&gt;
&lt;li&gt;整体乘 &lt;code&gt;p&lt;/code&gt; → 所有低位升一位；&lt;/li&gt;
&lt;li&gt;加上新进窗口的 &lt;code&gt;val(i+k-1...)&lt;/code&gt;（此处为 &lt;code&gt;val(i)&lt;/code&gt;）作为新的最低位，再取模。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;全程只有加减乘与取模，&lt;strong&gt;O(1) 更新&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;逐步举例&lt;/strong&gt;（&lt;code&gt;s=&quot;leetcode&quot;&lt;/code&gt;，&lt;code&gt;k=2, p=7, m=20&lt;/code&gt;）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;最右侧窗口 &lt;code&gt;s[6:8]=&quot;ee&quot;&lt;/code&gt;：从右往左累积——&lt;code&gt;val(e)=5&lt;/code&gt; 得 &lt;code&gt;curr=5&lt;/code&gt;；再入 &lt;code&gt;val(e)=5&lt;/code&gt; 得 &lt;code&gt;curr=(5·7+5)%20=0&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;首个窗口 &lt;code&gt;curr=0==hashValue&lt;/code&gt;，记录 &lt;code&gt;ans=6&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;反向滑动时后续窗口哈希都不等于 0，故返回 &lt;code&gt;s[6:8]=&quot;ee&quot;&lt;/code&gt; ✓。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，一次线性滑动。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def subStrHash(self, s: str, power: int, modulo: int, k: int, hashValue: int) -&amp;gt; str:
        n = len(s)
        val = lambda i: ord(s[i]) - 96  # &apos;a&apos; -&amp;gt; 1

        # Compute hash of the rightmost window s[n-k:n]
        curr = 0
        for i in range(n - 1, n - k - 1, -1):
            curr = (curr * power + val(i)) % modulo

        ans = n - k
        if curr == hashValue:
            ans = n - k

        msb = pow(power, k - 1, modulo)

        # Slide from right to left
        for i in range(n - 1, k - 1, -1):
            curr = ((curr - val(i) * msb) * power + val(i - k)) % modulo
            if curr == hashValue:
                ans = i - k

        return s[ans:ans + k]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
sys.path.insert(0, &quot;/workspace/projects/source/coding/sliding-window-two-pointers/01-fixed-sliding-window/2156-substring-hash&quot;)
from solution import Solution
import unittest

class TestSubStrHash(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.subStrHash(&quot;leetcode&quot;, 7, 20, 2, 0), &quot;ee&quot;)

    def test_example2(self):
        self.assertEqual(self.solution.subStrHash(&quot;fbxzaad&quot;, 31, 100, 3, 32), &quot;fbx&quot;)

    def test_single_char(self):
        self.assertEqual(self.solution.subStrHash(&quot;a&quot;, 1, 10, 1, 1), &quot;a&quot;)

    def test_large_k(self):
        s = &quot;abcde&quot;
        # hash = 1*1 + 2*2 + 3*4 + 4*8 + 5*16 = 1+4+12+32+80 = 129 ≡ 29 (mod 100)
        self.assertEqual(self.solution.subStrHash(s, 2, 100, 5, 29), &quot;abcde&quot;)

    def test_multiple_matches(self):
        # s=&quot;aaa&quot;, power=1, modulo=10, k=2, hashValue=2
        # &quot;aa&quot;: 1+1 = 2 ≡ 2 (mod 10)
        self.assertEqual(self.solution.subStrHash(&quot;aaa&quot;, 1, 10, 2, 2), &quot;aa&quot;)

if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>1888. 使二进制字符串字符交替的最少反转次数</title><link>https://h1s97x.github.io/posts/coding/coding-1888-min-flips/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1888-min-flips/</guid><description>通过旋转和翻转使二进制字符串交替，求最少翻转次数（定长滑窗 + 双倍数组）</description><pubDate>Mon, 17 Aug 2026 01:30:31 GMT</pubDate><content:encoded>&lt;h1&gt;1888. 使二进制字符串字符交替的最少反转次数&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个二进制字符串 &lt;code&gt;s&lt;/code&gt;，你可以进行两类操作：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;旋转&lt;/strong&gt;：将第一个字符移动到末尾（可执行任意次）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;翻转&lt;/strong&gt;：把一个字符 0↔1（可执行任意次）。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;先做任意次旋转，再做任意次翻转。问：最少需要翻转多少次，才能让字符串变成&lt;strong&gt;相邻字符均不同&lt;/strong&gt;（交替）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 ：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;111000&quot;  → 输出：2
输入：s = &quot;010&quot;     → 输出：0
输入：s = &quot;1110&quot;    → 输出：1
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;核心思想：旋转 k 次等价于在环形数组上取一个长度为 n 的窗口。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;把 &lt;code&gt;s&lt;/code&gt; 复制一份拼成 &lt;code&gt;s + s&lt;/code&gt;，则&quot;旋转 k 次后再翻转&quot;就等价于&quot;在 &lt;code&gt;s+s&lt;/code&gt; 上取起点为 &lt;code&gt;l&lt;/code&gt;、长度 &lt;code&gt;n&lt;/code&gt; 的窗口&quot;，再把这个窗口翻成某个交替模式。交替模式只有两种：&lt;code&gt;0101...&lt;/code&gt;（A）与 &lt;code&gt;1010...&lt;/code&gt;（B）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;预处理 &lt;code&gt;diff&lt;/code&gt; 数组：&lt;code&gt;diff[i] = 1&lt;/code&gt; 当且仅当 &lt;code&gt;s2[i]&lt;/code&gt; 与&quot;以 0 开头、按全局下标奇偶&quot;的目标位不同。即 &lt;code&gt;diff[i] = (s2[i] != (i 为偶 ? &apos;0&apos; : &apos;1&apos;))&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;对于起点 &lt;code&gt;l&lt;/code&gt; 的长度 &lt;code&gt;n&lt;/code&gt; 窗口，窗口内相对下标 &lt;code&gt;0..n-1&lt;/code&gt; 的奇偶与全局下标 &lt;code&gt;l..l+n-1&lt;/code&gt; 一致需满足 &lt;code&gt;l&lt;/code&gt; 为偶数：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;l&lt;/code&gt; 为偶数时：翻成模式 A 的代价 = &lt;code&gt;cur&lt;/code&gt;（窗口内 diff 之和），翻成模式 B 的代价 = &lt;code&gt;n - cur&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;l&lt;/code&gt; 为奇数时：二者互换（用 &lt;code&gt;flips = cur if l%2==0 else n-cur&lt;/code&gt; 统一表达，同时取 &lt;code&gt;min(flips, n-flips)&lt;/code&gt; 覆盖两种模式）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;窗口整体每次右移一位，代价用 &lt;code&gt;cur += diff[l+n] - diff[l]&lt;/code&gt; 在 O(1) 内更新。取所有窗口的最小值即可。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;逐步举例&lt;/strong&gt;（&lt;code&gt;s = &quot;111000&quot;&lt;/code&gt;，&lt;code&gt;n = 6&lt;/code&gt;）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;s2 = &quot;111000111000&quot;&lt;/code&gt;，&lt;code&gt;diff&lt;/code&gt;（以 &lt;code&gt;0&lt;/code&gt; 开头交替，&lt;code&gt;&apos;0&apos;&lt;/code&gt;目标位为偶数下标）：&lt;code&gt;i0:&apos;1&apos;→1, i1:&apos;1&apos;→0, i2:&apos;1&apos;→1, i3:&apos;0&apos;→1, i4:&apos;0&apos;→0, i5:&apos;0&apos;→1&lt;/code&gt;，前 6 位和 &lt;code&gt;cur = 4&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;l=0&lt;/code&gt;：&lt;code&gt;flips = cur = 4&lt;/code&gt;，取 &lt;code&gt;min(4, 6-4=2) = 2&lt;/code&gt;。即翻成模式 B 只需 2 次（把下标 1、3 两处对应的位翻掉）。后续所有窗口最小仍是 2，答案 &lt;code&gt;2&lt;/code&gt; ✓。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，单次线性扫描。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(n)，&lt;code&gt;diff&lt;/code&gt; 数组长度 2n。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def minFlips(self, s: str) -&amp;gt; int:
        n = len(s)
        s2 = s + s
        diff = [0] * (2 * n)
        for i, ch in enumerate(s2):
            target = &apos;0&apos; if i % 2 == 0 else &apos;1&apos;
            diff[i] = 1 if ch != target else 0

        cur = sum(diff[:n])
        ans = n
        for l in range(n):
            flips = cur if l % 2 == 0 else n - cur
            ans = min(ans, flips, n - flips)
            cur += diff[l + n] - diff[l]
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
from solution import Solution


class TestMinFlips(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.minFlips(&quot;111000&quot;), 2)

    def test_example2(self):
        self.assertEqual(self.solution.minFlips(&quot;010&quot;), 0)

    def test_example3(self):
        self.assertEqual(self.solution.minFlips(&quot;1110&quot;), 1)

    def test_single_char(self):
        self.assertEqual(self.solution.minFlips(&quot;0&quot;), 0)
        self.assertEqual(self.solution.minFlips(&quot;1&quot;), 0)

    def test_already_alternating(self):
        self.assertEqual(self.solution.minFlips(&quot;01&quot;), 0)
        self.assertEqual(self.solution.minFlips(&quot;10&quot;), 0)
        self.assertEqual(self.solution.minFlips(&quot;0101&quot;), 0)

    def test_all_same(self):
        self.assertEqual(self.solution.minFlips(&quot;000&quot;), 1)
        self.assertEqual(self.solution.minFlips(&quot;111&quot;), 1)
        self.assertEqual(self.solution.minFlips(&quot;0000&quot;), 2)

    def test_longer(self):
        self.assertEqual(self.solution.minFlips(&quot;01001001101&quot;), 2)


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>30. 串联所有单词的子串</title><link>https://h1s97x.github.io/posts/coding/coding-30-substring-concatenation/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-30-substring-concatenation/</guid><description>在 s 中找到所有由 words 中每个单词恰好一次串联而成的子串的起始索引</description><pubDate>Mon, 17 Aug 2026 01:27:55 GMT</pubDate><content:encoded>&lt;h1&gt;30. 串联所有单词的子串&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给定一个字符串 &lt;code&gt;s&lt;/code&gt; 和一个字符串数组 &lt;code&gt;words&lt;/code&gt;，其中 &lt;code&gt;words&lt;/code&gt; 中所有字符串&lt;strong&gt;长度相同&lt;/strong&gt;。在 &lt;code&gt;s&lt;/code&gt; 中找到所有子串的起始索引，这些子串是 &lt;code&gt;words&lt;/code&gt; 中每个单词&lt;strong&gt;恰好一次&lt;/strong&gt;、&lt;strong&gt;无间隔&lt;/strong&gt;地串联而成的排列。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 ：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;barfoothefoobarman&quot;, words = [&quot;foo&quot;,&quot;bar&quot;]
输出：[0,9]（子串 [0,6)=&quot;barfoo&quot;，[9,15)=&quot;foobar&quot;）
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;核心思想：把每个单词当作一个&quot;超级字符&quot;，问题就退化成了 438 的异位词滑窗。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;由于所有单词等长 &lt;code&gt;word_len&lt;/code&gt;，正确的串联子串一定从某一个 &lt;code&gt;offset&lt;/code&gt;（0..word_len-1）开始，且该子串按 &lt;code&gt;word_len&lt;/code&gt; 切分后，得到的单词序列必须与 &lt;code&gt;words&lt;/code&gt; 的计数一致。因此分 &lt;code&gt;word_len&lt;/code&gt; 组分别做滑窗：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对每个 &lt;code&gt;offset&lt;/code&gt;，把 &lt;code&gt;s&lt;/code&gt; 按 &lt;code&gt;word_len&lt;/code&gt; 切成单词序列 &lt;code&gt;w&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;维护窗口内单词计数字典 &lt;code&gt;cur&lt;/code&gt; 和一个 &lt;code&gt;valid&lt;/code&gt; 变量，记录&lt;strong&gt;频次恰好匹配的单词种类数&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;入单词：&lt;code&gt;cur[w]++&lt;/code&gt;，若正好等于 &lt;code&gt;target[w]&lt;/code&gt;，&lt;code&gt;valid+1&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;出单词（窗口总长超过 &lt;code&gt;total_len = len(words)*word_len&lt;/code&gt; 时）：&lt;code&gt;cur[w_left]--&lt;/code&gt;，若先前正好匹配则 &lt;code&gt;valid-1&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;遇到不在 &lt;code&gt;target&lt;/code&gt; 中的单词，直接清空窗口重置；&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;valid == len(target)&lt;/code&gt;，当前起点 &lt;code&gt;left&lt;/code&gt; 就是一个合法子串起点。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;逐步举例&lt;/strong&gt;（&lt;code&gt;s=&quot;barfoothefoobarman&quot;, words=[&quot;foo&quot;,&quot;bar&quot;]&lt;/code&gt;，&lt;code&gt;word_len=3, total_len=6&lt;/code&gt;，只看 &lt;code&gt;offset=0&lt;/code&gt; 分组）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;切分出的单词序列：&lt;code&gt;bar foo the foo bar man&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;入 &lt;code&gt;&quot;bar&quot;&lt;/code&gt;：&lt;code&gt;cur[bar]=1==target[bar]&lt;/code&gt;，&lt;code&gt;valid=1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;入 &lt;code&gt;&quot;foo&quot;&lt;/code&gt;：&lt;code&gt;cur[foo]=1==target[foo]&lt;/code&gt;，&lt;code&gt;valid=2==len(target)&lt;/code&gt; → &lt;code&gt;append(0)&lt;/code&gt; ✓。&lt;/li&gt;
&lt;li&gt;入 &lt;code&gt;&quot;the&quot;&lt;/code&gt;（不在 target）→ 清空窗口，&lt;code&gt;left&lt;/code&gt; 跳到其后。&lt;/li&gt;
&lt;li&gt;最终 &lt;code&gt;offset=0&lt;/code&gt; 得到起点 &lt;code&gt;0&lt;/code&gt;；&lt;code&gt;offset=3&lt;/code&gt; 分组还会得到起点 &lt;code&gt;9&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n·word_len)，每组各做一次 O(n/word_len) 遍历。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(n)，哈希表存储单词计数。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def findSubstring(self, s: str, words: list[str]) -&amp;gt; list[int]:
        word_len = len(words[0])
        word_count = len(words)
        total_len = word_len * word_count
        n = len(s)
        if n &amp;lt; total_len:
            return []

        from collections import Counter
        target = Counter(words)
        ans = []

        for offset in range(word_len):
            left = offset
            cur = {}
            valid = 0

            for right in range(offset, n - word_len + 1, word_len):
                w = s[right:right + word_len]

                if w not in target:
                    cur.clear()
                    valid = 0
                    left = right + word_len
                    continue

                cur[w] = cur.get(w, 0) + 1
                if cur[w] == target[w]:
                    valid += 1

                while right - left + word_len &amp;gt; total_len:
                    w_left = s[left:left + word_len]
                    if cur[w_left] == target[w_left]:
                        valid -= 1
                    cur[w_left] -= 1
                    left += word_len

                if valid == len(target):
                    ans.append(left)

        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
from solution import Solution


class TestFindSubstring(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(
            self.solution.findSubstring(&quot;barfoothefoobarman&quot;, [&quot;foo&quot;, &quot;bar&quot;]),
            [0, 9]
        )

    def test_example2(self):
        self.assertEqual(
            self.solution.findSubstring(&quot;wordgoodgoodgoodbestword&quot;, [&quot;word&quot;, &quot;good&quot;, &quot;best&quot;, &quot;word&quot;]),
            []
        )

    def test_example3(self):
        self.assertEqual(
            self.solution.findSubstring(&quot;barfoofoobarthefoobarman&quot;, [&quot;bar&quot;, &quot;foo&quot;, &quot;the&quot;]),
            [6, 9, 12]
        )

    def test_empty_result(self):
        self.assertEqual(
            self.solution.findSubstring(&quot;abc&quot;, [&quot;d&quot;, &quot;e&quot;]),
            []
        )

    def test_single_word(self):
        self.assertEqual(
            self.solution.findSubstring(&quot;aaaa&quot;, [&quot;a&quot;]),
            [0, 1, 2, 3]
        )

    def test_duplicate_words(self):
        self.assertEqual(
            self.solution.findSubstring(&quot;aaa&quot;, [&quot;a&quot;, &quot;a&quot;]),
            [0, 1]
        )


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>3652. 按策略买卖股票的最佳时机</title><link>https://h1s97x.github.io/posts/coding/coding-3652-stock-strategy/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-3652-stock-strategy/</guid><description>修改至多一次连续 k 个策略（前 k/2 成持有，后 k/2 成卖出），最大化和</description><pubDate>Mon, 17 Aug 2026 01:26:35 GMT</pubDate><content:encoded>&lt;h1&gt;3652. 按策略买卖股票的最佳时机&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给定 &lt;code&gt;prices&lt;/code&gt; 数组和 &lt;code&gt;strategy&lt;/code&gt; 数组（&lt;code&gt;-1&lt;/code&gt; 买入，&lt;code&gt;0&lt;/code&gt; 持有，&lt;code&gt;1&lt;/code&gt; 卖出）。可以&lt;strong&gt;至多一次&lt;/strong&gt;将连续的 &lt;code&gt;k&lt;/code&gt; 个策略改为：前 &lt;code&gt;k/2&lt;/code&gt; 个变为 &lt;code&gt;0&lt;/code&gt;（持有）、后 &lt;code&gt;k/2&lt;/code&gt; 个变为 &lt;code&gt;1&lt;/code&gt;（卖出）。求可得到的最大利润。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 ：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：prices = [4,2,8], strategy = [-1,0,1], k = 2
输出：10
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;核心思路：基础收益 + 增量。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;先算出&lt;strong&gt;不修改策略&lt;/strong&gt;时的基础收益：&lt;code&gt;base = Σ strategy[i] * prices[i]&lt;/code&gt;（&lt;code&gt;-1&lt;/code&gt; 买入记 &lt;code&gt;-prices&lt;/code&gt;，&lt;code&gt;1&lt;/code&gt; 卖出记 &lt;code&gt;+prices&lt;/code&gt;，&lt;code&gt;0&lt;/code&gt; 记 0）。&lt;/p&gt;
&lt;p&gt;若把下标 &lt;code&gt;i&lt;/code&gt; 的策略改成目标值，收益会变化，定义两种增量：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;gain0[i]&lt;/code&gt;：把 &lt;code&gt;strategy[i]&lt;/code&gt; 改成 &lt;code&gt;0&lt;/code&gt;（持有）的&lt;strong&gt;收益增量&lt;/strong&gt; = &lt;code&gt;0 − strategy[i]·prices[i] = -strategy[i]·prices[i]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gain1[i]&lt;/code&gt;：把 &lt;code&gt;strategy[i]&lt;/code&gt; 改成 &lt;code&gt;1&lt;/code&gt;（卖出）的&lt;strong&gt;收益增量&lt;/strong&gt; = &lt;code&gt;prices[i] − strategy[i]·prices[i] = (1 - strategy[i])·prices[i]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一次修改覆盖长度为 &lt;code&gt;k&lt;/code&gt; 的连续窗口，其中&lt;strong&gt;前 &lt;code&gt;half=k/2&lt;/code&gt; 个改成 0（用 &lt;code&gt;gain0&lt;/code&gt;）、后 &lt;code&gt;k/2&lt;/code&gt; 个改成 1（用 &lt;code&gt;gain1&lt;/code&gt;）&lt;/strong&gt;。于是问题转为：找长度为 &lt;code&gt;k&lt;/code&gt; 的窗口，最大化「前半 &lt;code&gt;gain0&lt;/code&gt; 之和 + 后半 &lt;code&gt;gain1&lt;/code&gt; 之和」这个增量。&lt;/p&gt;
&lt;p&gt;这正是&lt;strong&gt;定长滑窗&lt;/strong&gt;：分别用两个窗长 &lt;code&gt;half&lt;/code&gt; 的增量数组 &lt;code&gt;sum_first&lt;/code&gt;（最左 &lt;code&gt;half&lt;/code&gt; 个）和 &lt;code&gt;sum_second&lt;/code&gt;（窗口右半 &lt;code&gt;half&lt;/code&gt; 个）同步平移。最终答案 = &lt;code&gt;base + max(最大增量, 0)&lt;/code&gt;（可以不修改，故增量下限为 0）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;逐步举例&lt;/strong&gt;（&lt;code&gt;prices=[4,2,8], strategy=[-1,0,1], k=2&lt;/code&gt;）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;base = -1·4 + 0·2 + 1·8 = 4&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gain0 = [4, 0, -8]&lt;/code&gt;（改成 0 的增量）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gain1 = [8, 2, 0]&lt;/code&gt;（改成 1 的增量）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;half = 1&lt;/code&gt;。初始窗口：&lt;code&gt;sum_first = gain0[0] = 4&lt;/code&gt;，&lt;code&gt;sum_second = gain1[1] = 2&lt;/code&gt;，增量 = 6。&lt;/li&gt;
&lt;li&gt;解释：修改窗口 &lt;code&gt;[0,1]&lt;/code&gt; → 策略变 &lt;code&gt;[0,1,1]&lt;/code&gt;，收益 = &lt;code&gt;0·4 + 1·2 + 1·8 = 10&lt;/code&gt;，增量 = &lt;code&gt;10 − 4 = 6&lt;/code&gt; ✓&lt;/li&gt;
&lt;li&gt;取 &lt;code&gt;max(6, 0)=6&lt;/code&gt;，&lt;code&gt;ans = 4 + 6 = 10&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，一次滑动。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(n)，&lt;code&gt;gain0&lt;/code&gt; 与 &lt;code&gt;gain1&lt;/code&gt; 数组。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def maxProfit(self, prices: list[int], strategy: list[int], k: int) -&amp;gt; int:
        n = len(prices)
        half = k // 2

        base = 0
        gain0 = [0] * n
        gain1 = [0] * n
        for i in range(n):
            base += strategy[i] * prices[i]
            gain0[i] = -strategy[i] * prices[i]
            gain1[i] = (1 - strategy[i]) * prices[i]

        sum_first = sum(gain0[:half])
        sum_second = sum(gain1[half:k])
        max_gain = sum_first + sum_second

        for i in range(k, n):
            sum_first += gain0[i - half] - gain0[i - k]
            sum_second += gain1[i] - gain1[i - half]
            cur = sum_first + sum_second
            if cur &amp;gt; max_gain:
                max_gain = cur

        return base + max(max_gain, 0)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
from solution import Solution


class TestMaxProfit(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.maxProfit([4, 2, 8], [-1, 0, 1], 2), 10)

    def test_example2(self):
        self.assertEqual(self.solution.maxProfit([5, 4, 3], [1, 1, 0], 2), 9)

    def test_no_modification_needed(self):
        self.assertEqual(self.solution.maxProfit([3, 5, 2], [1, 1, 1], 2), 10)

    def test_all_buy_then_sell(self):
        self.assertEqual(self.solution.maxProfit([1, 2, 3, 4], [-1, -1, 1, 1], 2), 9)

    def test_k_equals_n(self):
        self.assertEqual(self.solution.maxProfit([1, 2], [-1, 1], 2), 2)

    def test_no_modification(self):
        prices = [10, 5, 15]
        strategy = [1, 1, 1]
        k = 2
        self.assertEqual(self.solution.maxProfit(prices, strategy, k), 30)


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>3694. 删除子字符串后不同的终点</title><link>https://h1s97x.github.io/posts/coding/coding-3694-distinct-points/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-3694-distinct-points/</guid><description>删除长度为 k 的连续子串后，从原点出发执行剩余移动，求不同终点坐标的个数</description><pubDate>Mon, 17 Aug 2026 01:24:41 GMT</pubDate><content:encoded>&lt;h1&gt;3694. 删除子字符串后不同的终点&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给定一个由 &lt;code&gt;&apos;U&apos;&lt;/code&gt;、&lt;code&gt;&apos;D&apos;&lt;/code&gt;、&lt;code&gt;&apos;L&apos;&lt;/code&gt;、&lt;code&gt;&apos;R&apos;&lt;/code&gt; 组成的字符串 &lt;code&gt;s&lt;/code&gt;，表示在二维平面上的移动（上/下/左/右一步）。必须删除恰好一个长度为 &lt;code&gt;k&lt;/code&gt; 的连续子串，然后从 &lt;code&gt;(0,0)&lt;/code&gt; 出发执行剩余的移动。求所有可能终点坐标中，不同坐标的个数。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;LUL&quot;, k = 1
输出：2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;UDLR&quot;, k = 4
输出：1
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;核心恒等式&lt;/strong&gt;：删除子串后的终点 = &lt;strong&gt;总位移 − 被删除子串的位移&lt;/strong&gt;（向量减法）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;先一趟遍历求出整条路径的总位移 &lt;code&gt;(total_x, total_y)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;再枚举所有长度为 &lt;code&gt;k&lt;/code&gt; 的&lt;strong&gt;连续子串&lt;/strong&gt;，用定长滑窗算出每个子串的位移 &lt;code&gt;(win_x, win_y)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;每次用删除该子串后的终点 &lt;code&gt;(total_x - win_x, total_y - win_y)&lt;/code&gt; 加入集合。&lt;/li&gt;
&lt;li&gt;最终集合大小即不同终点个数。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;沿用 &lt;strong&gt;入-更新-出&lt;/strong&gt;：右端点 &lt;code&gt;i&lt;/code&gt; 的位移入窗、够 &lt;code&gt;k&lt;/code&gt; 个时用集合记录、左端点 &lt;code&gt;i-k&lt;/code&gt; 的位移出窗。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;逐步举例&lt;/strong&gt;（&lt;code&gt;s=&quot;LUL&quot;&lt;/code&gt;, &lt;code&gt;k=1&lt;/code&gt;）：&lt;/p&gt;
&lt;p&gt;设 &lt;code&gt;L=(-1,0)&lt;/code&gt;, &lt;code&gt;U=(0,1)&lt;/code&gt;, &lt;code&gt;R=(1,0)&lt;/code&gt;, &lt;code&gt;D=(0,-1)&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;总位移 &lt;code&gt;total = L+U+L = (-1,0)+(0,1)+(-1,0) = (-2,1)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;枚举每个 &lt;code&gt;k=1&lt;/code&gt; 子串（即单个字符被删除）：&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;删除字符&lt;/th&gt;
&lt;th&gt;窗口位移 (win)&lt;/th&gt;
&lt;th&gt;终点 = total − win&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;s[0]=L&lt;/td&gt;
&lt;td&gt;(-1,0)&lt;/td&gt;
&lt;td&gt;(-2,1)−(-1,0) = (-1,1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;s[1]=U&lt;/td&gt;
&lt;td&gt;(0,1)&lt;/td&gt;
&lt;td&gt;(-2,1)−(0,1) = (-2,0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;s[2]=L&lt;/td&gt;
&lt;td&gt;(-1,0)&lt;/td&gt;
&lt;td&gt;(-2,1)−(-1,0) = (-1,1)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;不同终点 = &lt;code&gt;{(-1,1), (-2,0)}&lt;/code&gt;，共 &lt;strong&gt;2&lt;/strong&gt; 个，与预期一致。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，一次遍历计算总位移，一次滑窗。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(n)，集合存储不同终点。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def distinctPoints(self, s: str, k: int) -&amp;gt; int:
        DIR = {&apos;U&apos;: (0, 1), &apos;D&apos;: (0, -1), &apos;L&apos;: (-1, 0), &apos;R&apos;: (1, 0)}

        total_x = total_y = 0
        for ch in s:
            dx, dy = DIR[ch]
            total_x += dx
            total_y += dy

        win_x = win_y = 0
        points = set()
        n = len(s)

        for i, ch in enumerate(s):
            dx, dy = DIR[ch]
            win_x += dx
            win_y += dy

            if i &amp;gt;= k:
                pdx, pdy = DIR[s[i - k]]
                win_x -= pdx
                win_y -= pdy

            if i &amp;gt;= k - 1:
                points.add((total_x - win_x, total_y - win_y))

        return len(points)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
from solution import Solution


class TestDistinctPoints(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.distinctPoints(&quot;LUL&quot;, 1), 2)

    def test_example2(self):
        self.assertEqual(self.solution.distinctPoints(&quot;UDLR&quot;, 4), 1)

    def test_example3(self):
        self.assertEqual(self.solution.distinctPoints(&quot;UU&quot;, 1), 1)

    def test_all_same(self):
        self.assertEqual(self.solution.distinctPoints(&quot;RRRR&quot;, 2), 1)

    def test_k_equals_n(self):
        self.assertEqual(self.solution.distinctPoints(&quot;RL&quot;, 2), 1)

    def test_cyclic(self):
        self.assertEqual(self.solution.distinctPoints(&quot;RLRU&quot;, 2), 2)

    def test_no_overlap(self):
        self.assertEqual(self.solution.distinctPoints(&quot;URDL&quot;, 2), 3)


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>3439. 重新安排会议得到最多空余时间 I</title><link>https://h1s97x.github.io/posts/coding/coding-3439-max-free-time/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-3439-max-free-time/</guid><description>给定活动总时长和 n 个非重叠会议，可重新安排至多 k 个会议，最大化相邻会议之间的最长连续空余时间</description><pubDate>Mon, 17 Aug 2026 01:22:52 GMT</pubDate><content:encoded>&lt;h1&gt;3439. 重新安排会议得到最多空余时间 I&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给定一个活动总时长 &lt;code&gt;eventTime&lt;/code&gt;，以及 &lt;code&gt;n&lt;/code&gt; 个非重叠会议 &lt;code&gt;[startTime[i], endTime[i]]&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;你可以重新安排 &lt;strong&gt;至多&lt;/strong&gt; &lt;code&gt;k&lt;/code&gt; 个会议（可在时间轴上平移，保持时长、相对顺序且不重叠），目的是最大化相邻两个会议（以及首尾边界）之间的最长连续空余时间。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：eventTime = 5, k = 1, startTime = [1,3], endTime = [2,5]
输出：2
解释：把会议 [1,2] 平移到 [2,3]，得到空余时间 [0,2]，长度为 2。
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;核心转化&lt;/strong&gt;：把空余时间抽出来做成&lt;strong&gt;间隙数组&lt;/strong&gt;，问题就变成定长滑窗求最大子数组和。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一步，构建间隙数组 &lt;code&gt;gaps&lt;/code&gt;&lt;/strong&gt;。时间轴被会议切分成 &lt;code&gt;n+1&lt;/code&gt; 段空余：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;gaps[0] = startTime[0]&lt;/code&gt;（第 1 个会议前的空余）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gaps[i] = startTime[i] - endTime[i-1]&lt;/code&gt;（第 i 个会议与第 i-1 个之间的空余，&lt;code&gt;1 ≤ i ≤ n-1&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gaps[n] = eventTime - endTime[n-1]&lt;/code&gt;（最后 1 个会议后的空余）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;第二步，关键观察&lt;/strong&gt;：既然会议可平移，那么&lt;strong&gt;把连续 k 个会议全部挪走&lt;/strong&gt;，就可以把这 k 个会议两侧（含首尾）的 &lt;strong&gt;k+1 个间隙合并成一个大空余&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第三步，定长滑窗&lt;/strong&gt;：问题等价于在 &lt;code&gt;gaps&lt;/code&gt; 中找长度为 &lt;code&gt;k+1&lt;/code&gt; 的连续子数组的最大和。用 &lt;strong&gt;入-更新-出&lt;/strong&gt; 三步 O(1) 滑动更新窗口和 &lt;code&gt;cur&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;逐步举例&lt;/strong&gt;（&lt;code&gt;eventTime=5&lt;/code&gt;, &lt;code&gt;k=1&lt;/code&gt;, &lt;code&gt;start=[1,3]&lt;/code&gt;, &lt;code&gt;end=[2,5]&lt;/code&gt;）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;构建间隙：&lt;code&gt;gaps[0]=1&lt;/code&gt;, &lt;code&gt;gaps[1]=3-2=1&lt;/code&gt;, &lt;code&gt;gaps[2]=5-5=0&lt;/code&gt;，即 &lt;code&gt;gaps=[1,1,0]&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;窗口大小 &lt;code&gt;k+1 = 2&lt;/code&gt;：
&lt;ul&gt;
&lt;li&gt;窗口 &lt;code&gt;[0,1]&lt;/code&gt;（gap 下标 0..1）和 = 1+1 = &lt;strong&gt;2&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;窗口 &lt;code&gt;[1,2]&lt;/code&gt; 和 = 1+0 = 1&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;最大和 = &lt;strong&gt;2&lt;/strong&gt;，对应把会议 [1,2] 挪走合并 gap[0]+gap[1]，与预期一致。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，一次遍历。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(n)，用于存储间隙数组。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def maxFreeTime(self, eventTime: int, k: int, startTime: list[int], endTime: list[int]) -&amp;gt; int:
        n = len(startTime)
        gaps = [0] * (n + 1)
        gaps[0] = startTime[0]
        for i in range(1, n):
            gaps[i] = startTime[i] - endTime[i - 1]
        gaps[n] = eventTime - endTime[-1]

        cur = sum(gaps[:k + 1])
        ans = cur
        for i in range(k + 1, n + 1):
            cur += gaps[i] - gaps[i - (k + 1)]
            if cur &amp;gt; ans:
                ans = cur

        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
from solution import Solution


class TestMaxFreeTime(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.maxFreeTime(5, 1, [1, 3], [2, 5]), 2)

    def test_example2(self):
        self.assertEqual(self.solution.maxFreeTime(10, 1, [0, 2, 9], [1, 4, 10]), 6)

    def test_example3(self):
        self.assertEqual(self.solution.maxFreeTime(5, 2, [0, 1, 2, 3, 4], [1, 2, 3, 4, 5]), 0)

    def test_k_equals_n(self):
        self.assertEqual(self.solution.maxFreeTime(10, 3, [1, 3, 5], [2, 4, 6]), 7)

    def test_move_middle_meetings(self):
        self.assertEqual(self.solution.maxFreeTime(10, 2, [1, 3, 5], [2, 4, 6]), 6)

    def test_single_meeting(self):
        self.assertEqual(self.solution.maxFreeTime(10, 1, [2], [5]), 7)

    def test_no_gap(self):
        self.assertEqual(self.solution.maxFreeTime(5, 1, [0, 2], [2, 5]), 0)


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>3679. 使库存平衡的最少丢弃次数</title><link>https://h1s97x.github.io/posts/coding/coding-3679-min-discards/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-3679-min-discards/</guid><description>每个 w 天窗口中每种类型最多出现 m 次，求最少丢弃数</description><pubDate>Mon, 17 Aug 2026 01:20:26 GMT</pubDate><content:encoded>&lt;h1&gt;3679. 使库存平衡的最少丢弃次数&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你两个整数 &lt;code&gt;w&lt;/code&gt; 和 &lt;code&gt;m&lt;/code&gt;，以及一个整数数组 &lt;code&gt;arrivals&lt;/code&gt;，其中 &lt;code&gt;arrivals[i]&lt;/code&gt; 表示第 &lt;code&gt;i&lt;/code&gt; 天到达的物品类型。&lt;/p&gt;
&lt;p&gt;对每一天 &lt;code&gt;i&lt;/code&gt;，考虑天数范围 &lt;code&gt;[max(0, i-w+1), i]&lt;/code&gt;（最近的 &lt;code&gt;w&lt;/code&gt; 天）：在该任意长度不超过 &lt;code&gt;w&lt;/code&gt; 的时间窗口内，每种物品类型最多只能出现 &lt;code&gt;m&lt;/code&gt; 次。返回最少需要丢弃的物品数量，使条件成立。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：arrivals = [1,2,1,3,1], w = 4, m = 2
输出：0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：arrivals = [1,2,3,3,3,4], w = 3, m = 2
输出：1
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;核心&lt;/strong&gt;：每天按顺序处理一个到达的物品，用定长窗口（大小 &lt;code&gt;w&lt;/code&gt;）维护&quot;当前约束窗口内各类型的出现次数&quot;。关键观察：&lt;strong&gt;必须保证窗口内任意类型出现次数 ≤ m&lt;/strong&gt;，超出的物品直接丢弃，因为它无论如何都满足不了条件（它及其同类型的 m+1 个物品在一个窗口内必然超过 m）。&lt;/p&gt;
&lt;p&gt;沿用 &lt;strong&gt;入-更新-出&lt;/strong&gt; 三步（窗口大小 &lt;code&gt;w&lt;/code&gt;）：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;出&lt;/strong&gt;：先把离开窗口（&lt;code&gt;i-w&lt;/code&gt;）的物品类型计数减 1（&lt;code&gt;cnt[x]&lt;/code&gt; 为各类型在窗口内的出现次数）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;入/决策&lt;/strong&gt;：处理第 &lt;code&gt;i&lt;/code&gt; 天的物品 &lt;code&gt;x&lt;/code&gt;：
&lt;ul&gt;
&lt;li&gt;若窗口内 &lt;code&gt;cnt[x]&lt;/code&gt; 已等于 &lt;code&gt;m&lt;/code&gt;，说明再加就超限，&lt;strong&gt;必须丢弃&lt;/strong&gt; → &lt;code&gt;ans++&lt;/code&gt;，且不计入 &lt;code&gt;cnt&lt;/code&gt;（丢弃品不占用窗口计数）。&lt;/li&gt;
&lt;li&gt;否则保留 → &lt;code&gt;cnt[x] += 1&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;复杂度 O(n)，空间 O(U)（&lt;code&gt;U&lt;/code&gt; 为类型最大值）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;逐步举例&lt;/strong&gt;（&lt;code&gt;arrivals=[1,2,3,3,3,4]&lt;/code&gt;, &lt;code&gt;w=3&lt;/code&gt;, &lt;code&gt;m=2&lt;/code&gt;）：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;i&lt;/th&gt;
&lt;th&gt;出窗类型&lt;/th&gt;
&lt;th&gt;到达类型 x&lt;/th&gt;
&lt;th&gt;窗口内 cnt[x]&lt;/th&gt;
&lt;th&gt;决策&lt;/th&gt;
&lt;th&gt;cnt&lt;/th&gt;
&lt;th&gt;ans&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;保留&lt;/td&gt;
&lt;td&gt;1:1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;保留&lt;/td&gt;
&lt;td&gt;2:1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;保留&lt;/td&gt;
&lt;td&gt;3:1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1(→0)&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;保留&lt;/td&gt;
&lt;td&gt;3:2&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2(→0)&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;丢弃&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3:2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;3(→1)&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;保留&lt;/td&gt;
&lt;td&gt;4:1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最终 &lt;code&gt;ans = 1&lt;/code&gt;，与预期一致。&lt;/p&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，一次遍历。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(U)，&lt;code&gt;U = max(arrivals)&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def minArrivalsToDiscard(self, arrivals: list[int], w: int, m: int) -&amp;gt; int:
        max_val = max(arrivals)
        cnt = [0] * (max_val + 1)

        ans = 0
        for i, x in enumerate(arrivals):
            if i &amp;gt;= w:
                cnt[arrivals[i - w]] -= 1

            if cnt[x] &amp;gt;= m:
                ans += 1
            else:
                cnt[x] += 1

        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
from solution import Solution


class TestMinArrivalsToDiscard(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        arrivals = [1, 2, 1, 3, 1]
        w = 4
        m = 2
        self.assertEqual(self.solution.minArrivalsToDiscard(arrivals, w, m), 0)

    def test_example2(self):
        arrivals = [1, 2, 3, 3, 3, 4]
        w = 3
        m = 2
        self.assertEqual(self.solution.minArrivalsToDiscard(arrivals, w, m), 1)

    def test_all_same_type(self):
        arrivals = [1, 1, 1, 1, 1]
        w = 3
        m = 2
        # i=0: keep 1, cnt[1]=1
        # i=1: keep 1, cnt[1]=2
        # i=2: keep 1, cnt[1]=3 → wait, remove i=0? no, i=2 &amp;lt; w=3
        # Actually i=0,1,2: no removal yet. cnt[1]=3 &amp;gt; m=2 at i=2? No, at i=0 cnt=1, i=1 cnt=2, i=2 cnt=3 - but i=2 &amp;lt; w=3 so no removal
        # At i=2: cnt[1]=2 before adding, so cnt[1] &amp;lt; m=2? No, cnt[1]=2 which is not &amp;lt; 2, so cnt[1] &amp;gt;= m, discard
        # Hmm, let me re-trace
        # i=0: cnt[1]=0 &amp;lt; 2, keep, cnt[1]=1
        # i=1: cnt[1]=1 &amp;lt; 2, keep, cnt[1]=2
        # i=2: cnt[1]=2 &amp;gt;= 2, discard, ans=1
        # i=3: remove i=0 (1), cnt[1]=1. cnt[1]=1 &amp;lt; 2, keep, cnt[1]=2
        # i=4: remove i=1 (1), cnt[1]=1. cnt[1]=1 &amp;lt; 2, keep, cnt[1]=2
        # ans=1
        self.assertEqual(self.solution.minArrivalsToDiscard(arrivals, w, m), 1)

    def test_w_equals_one(self):
        arrivals = [1, 2, 1, 2]
        w = 1
        m = 1
        # Each window is just the current day, so each type can appear at most once
        # i=0: keep 1, cnt[1]=1
        # i=1: remove i=-1? no. cnt[2]=0 &amp;lt; 1, keep, cnt[2]=1
        # i=2: remove i=1 (2), cnt[2]=0. cnt[1]=0 &amp;lt; 1, keep, cnt[1]=1
        # i=3: remove i=2 (1), cnt[1]=0. cnt[2]=0 &amp;lt; 1, keep, cnt[2]=1
        self.assertEqual(self.solution.minArrivalsToDiscard(arrivals, w, m), 0)

    def test_all_discard(self):
        arrivals = [1, 1, 1]
        w = 2
        m = 1
        # i=0: keep 1, cnt[1]=1
        # i=1: cnt[1]=1 &amp;gt;= 1, discard, ans=1
        # i=2: remove i=0 (1), cnt[1]=0. cnt[1]=0 &amp;lt; 1, keep, cnt[1]=1
        self.assertEqual(self.solution.minArrivalsToDiscard(arrivals, w, m), 1)

    def test_large_m(self):
        arrivals = [1, 2, 3, 4, 5]
        w = 5
        m = 10
        self.assertEqual(self.solution.minArrivalsToDiscard(arrivals, w, m), 0)


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>1297. 子串的最大出现次数</title><link>https://h1s97x.github.io/posts/coding/coding-1297-max-occurences/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1297-max-occurences/</guid><description>找到满足最多 maxLetters 个不同字母、长度在 [minSize, maxSize] 的子串的最大出现次数</description><pubDate>Sun, 16 Aug 2026 15:32:15 GMT</pubDate><content:encoded>&lt;h1&gt;1297. 子串的最大出现次数&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个字符串 &lt;code&gt;s&lt;/code&gt;，以及整数 &lt;code&gt;maxLetters&lt;/code&gt;、&lt;code&gt;minSize&lt;/code&gt;、&lt;code&gt;maxSize&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;找出满足以下条件的任意子串的&lt;strong&gt;最大出现次数&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;子串长度在 &lt;code&gt;[minSize, maxSize]&lt;/code&gt; 之间&lt;/li&gt;
&lt;li&gt;子串中不同字母的数目不超过 &lt;code&gt;maxLetters&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;aababcaab&quot;, maxLetters = 2, minSize = 3, maxSize = 4
输出：2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;aaaa&quot;, maxLetters = 1, minSize = 3, maxSize = 3
输出：2
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;核心观察（关键）&lt;/strong&gt;：如果一个长度为 &lt;code&gt;L &amp;gt; minSize&lt;/code&gt; 的子串在 &lt;code&gt;s&lt;/code&gt; 中出现了 &lt;code&gt;k&lt;/code&gt; 次，那么取它的长度为 &lt;code&gt;minSize&lt;/code&gt; 的&lt;strong&gt;前缀&lt;/strong&gt;，该前缀也至少出现 &lt;code&gt;k&lt;/code&gt; 次（因为它作为子串的每个出现位置，前缀也随之下标对齐出现）。因此，&lt;strong&gt;我们只需要考虑长度为 &lt;code&gt;minSize&lt;/code&gt; 的子串&lt;/strong&gt;，&lt;code&gt;maxSize&lt;/code&gt; 本质上不起作用。这就把问题从「不定长」收束成「定长滑窗」。&lt;/p&gt;
&lt;p&gt;套用定长滑窗（窗长 &lt;code&gt;minSize&lt;/code&gt;）：用长度 26 的计数数组维护窗口内各字母出现次数，用 &lt;code&gt;distinct&lt;/code&gt; 维护窗口内不同字母的个数。右端点 &lt;code&gt;s[i]&lt;/code&gt; 进窗时更新计数；窗口超过 &lt;code&gt;minSize&lt;/code&gt; 时左端点出窗并更新 &lt;code&gt;distinct&lt;/code&gt;；一旦窗口长度达到 &lt;code&gt;minSize&lt;/code&gt; 且 &lt;code&gt;distinct &amp;lt;= maxLetters&lt;/code&gt;，就用哈希表累加该子串的出现次数。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;逐步举例&lt;/strong&gt;（&lt;code&gt;s=&quot;aababcaab&quot;, maxLetters=2, minSize=3&lt;/code&gt;）：&lt;/p&gt;
&lt;p&gt;只统计长度为 3 的窗口（不同字母数 ≤ 2）：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;窗口（下标）&lt;/th&gt;
&lt;th&gt;子串&lt;/th&gt;
&lt;th&gt;不同字母数&lt;/th&gt;
&lt;th&gt;是否计数&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0..2&lt;/td&gt;
&lt;td&gt;aab&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1..3&lt;/td&gt;
&lt;td&gt;aba&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2..4&lt;/td&gt;
&lt;td&gt;bab&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3..5&lt;/td&gt;
&lt;td&gt;abc&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4..6&lt;/td&gt;
&lt;td&gt;bca&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5..7&lt;/td&gt;
&lt;td&gt;caa&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6..8&lt;/td&gt;
&lt;td&gt;aab&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;aab&lt;/code&gt; 出现 2 次、&lt;code&gt;aba&lt;/code&gt; 1 次、&lt;code&gt;bab&lt;/code&gt; 1 次、&lt;code&gt;caa&lt;/code&gt; 1 次 → 最大出现次数 = 2。✓&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，一次遍历。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(n)，哈希表存储不同子串的计数。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def maxFreq(self, s: str, maxLetters: int, minSize: int, maxSize: int) -&amp;gt; int:
        cnt = [0] * 26
        distinct = 0
        freq = {}

        for i, c in enumerate(s):
            idx = ord(c) - 97
            if cnt[idx] == 0:
                distinct += 1
            cnt[idx] += 1

            if i &amp;gt;= minSize:
                idx2 = ord(s[i - minSize]) - 97
                cnt[idx2] -= 1
                if cnt[idx2] == 0:
                    distinct -= 1

            if i &amp;gt;= minSize - 1 and distinct &amp;lt;= maxLetters:
                sub = s[i - minSize + 1:i + 1]
                freq[sub] = freq.get(sub, 0) + 1

        return max(freq.values()) if freq else 0
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
from solution import Solution


class TestMaxFreq(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.maxFreq(&quot;aababcaab&quot;, 2, 3, 4), 2)

    def test_example2(self):
        self.assertEqual(self.solution.maxFreq(&quot;aaaa&quot;, 1, 3, 3), 2)

    def test_example3(self):
        self.assertEqual(self.solution.maxFreq(&quot;aabcabcab&quot;, 2, 2, 3), 3)

    def test_no_valid_substring(self):
        self.assertEqual(self.solution.maxFreq(&quot;abcde&quot;, 1, 2, 3), 0)

    def test_max_letters_zero(self):
        self.assertEqual(self.solution.maxFreq(&quot;abc&quot;, 0, 1, 1), 0)


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>1652. 拆炸弹</title><link>https://h1s97x.github.io/posts/coding/coding-1652-defuse-bomb/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1652-defuse-bomb/</guid><description>环形数组中，每个元素替换为后/前 k 个元素之和（O(n) 定长滑窗）</description><pubDate>Sun, 16 Aug 2026 15:32:15 GMT</pubDate><content:encoded>&lt;h1&gt;1652. 拆炸弹&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个下标从 0 开始的整数数组 &lt;code&gt;code&lt;/code&gt; 和一个密钥 &lt;code&gt;k&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;解密规则：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果 &lt;code&gt;k &amp;gt; 0&lt;/code&gt;，将第 &lt;code&gt;i&lt;/code&gt; 个数字替换为它&lt;strong&gt;后面&lt;/strong&gt; &lt;code&gt;k&lt;/code&gt; 个数字之和&lt;/li&gt;
&lt;li&gt;如果 &lt;code&gt;k &amp;lt; 0&lt;/code&gt;，将第 &lt;code&gt;i&lt;/code&gt; 个数字替换为它&lt;strong&gt;前面&lt;/strong&gt; &lt;code&gt;k&lt;/code&gt; 个数字之和&lt;/li&gt;
&lt;li&gt;如果 &lt;code&gt;k == 0&lt;/code&gt;，将第 &lt;code&gt;i&lt;/code&gt; 个数字替换为 0&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;数组视为&lt;strong&gt;环形&lt;/strong&gt;（首尾相接）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：code = [5,7,1,4], k = 3
输出：[12,10,16,13]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：code = [1,2,3,4], k = 0
输出：[0,0,0,0]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;p&gt;题目要求每个位置都取「后/前 &lt;code&gt;k&lt;/code&gt; 个元素」的和，本质是一个&lt;strong&gt;窗长固定为 &lt;code&gt;|k|&lt;/code&gt;&lt;/strong&gt; 的滑窗，沿环形数组滑动时窗口内容平移一位、只变首尾两端元素，因此可 O(1) 从上一个窗口推出下一个窗口。&lt;/p&gt;
&lt;p&gt;**关键：确定首窗口起点。**约定以 &lt;code&gt;i = 0&lt;/code&gt; 位置的答案作为首窗口：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;k &amp;gt; 0&lt;/code&gt;：取 &lt;code&gt;code[1..k]&lt;/code&gt;（即 &lt;code&gt;0&lt;/code&gt; 后面 k 个），起点 &lt;code&gt;start = 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;k &amp;lt; 0&lt;/code&gt;：取前 k 个，即 &lt;code&gt;code[n-|k|..n-1]&lt;/code&gt;，起点 &lt;code&gt;start = n - |k|&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;初始累加首窗口得 &lt;code&gt;ans[0]&lt;/code&gt;。随后 &lt;code&gt;i = 1..n-1&lt;/code&gt; 依次滑动：窗口整体右移一位，&lt;strong&gt;减去离开窗口的首个元素&lt;/strong&gt; &lt;code&gt;code[(start+i-1) % n]&lt;/code&gt;，&lt;strong&gt;加上新进入的元素&lt;/strong&gt; &lt;code&gt;code[(start+i+|k|-1) % n]&lt;/code&gt;，即得 &lt;code&gt;ans[i]&lt;/code&gt;。取模实现环形。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;逐步举例&lt;/strong&gt;（&lt;code&gt;code=[5,7,1,4], k=3&lt;/code&gt;）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;k&amp;gt;0&lt;/code&gt;，&lt;code&gt;m=3&lt;/code&gt;，起点 &lt;code&gt;start=1&lt;/code&gt;。首窗口 = &lt;code&gt;code[1..3] = [7,1,4]&lt;/code&gt;，和 = 12 → &lt;code&gt;ans[0]=12&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;窗口右移：
&lt;ul&gt;
&lt;li&gt;出 &lt;code&gt;code[1]=7&lt;/code&gt;、进 &lt;code&gt;code[0]=5&lt;/code&gt;，&lt;code&gt;12-7+5=10&lt;/code&gt; → &lt;code&gt;ans[1]=10&lt;/code&gt;（即下标 1 后面 3 个 &lt;code&gt;[1,4,5]&lt;/code&gt; 之和）。&lt;/li&gt;
&lt;li&gt;出 &lt;code&gt;code[2]=1&lt;/code&gt;、进 &lt;code&gt;code[1]=7&lt;/code&gt;，&lt;code&gt;10-1+7=16&lt;/code&gt; → &lt;code&gt;ans[2]=16&lt;/code&gt;（&lt;code&gt;[4,5,7]&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;出 &lt;code&gt;code[3]=4&lt;/code&gt;、进 &lt;code&gt;code[2]=1&lt;/code&gt;，&lt;code&gt;16-4+1=13&lt;/code&gt; → &lt;code&gt;ans[3]=13&lt;/code&gt;（&lt;code&gt;[5,7,1]&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;结果 &lt;code&gt;[12,10,16,13]&lt;/code&gt; ✓&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，一次滑动。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(n)，结果数组（不计算则是 O(1)）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def decrypt(self, code: list[int], k: int) -&amp;gt; list[int]:
        n = len(code)
        if k == 0:
            return [0] * n

        m = abs(k)
        start = 1 if k &amp;gt; 0 else n - m
        cur = sum(code[start:start + m])
        ans = [0] * n
        ans[0] = cur

        for i in range(1, n):
            cur -= code[(start + i - 1) % n]
            cur += code[(start + i + m - 1) % n]
            ans[i] = cur

        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
from solution import Solution


class TestDecrypt(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.decrypt([5, 7, 1, 4], 3), [12, 10, 16, 13])

    def test_example2(self):
        self.assertEqual(self.solution.decrypt([1, 2, 3, 4], 0), [0, 0, 0, 0])

    def test_example3(self):
        self.assertEqual(self.solution.decrypt([2, 4, 9, 3], -2), [12, 5, 6, 13])

    def test_k_positive_one(self):
        self.assertEqual(self.solution.decrypt([1, 2, 3, 4], 1), [2, 3, 4, 1])

    def test_k_negative_one(self):
        self.assertEqual(self.solution.decrypt([1, 2, 3, 4], -1), [4, 1, 2, 3])

    def test_all_same(self):
        self.assertEqual(self.solution.decrypt([10, 10, 10], 2), [20, 20, 20])


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2134. 最少交换次数来组合所有的 1 II</title><link>https://h1s97x.github.io/posts/coding/coding-2134-min-swaps/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2134-min-swaps/</guid><description>环形二进制数组中，交换相邻元素使所有 1 聚集在一起的最小交换次数</description><pubDate>Sun, 16 Aug 2026 15:32:15 GMT</pubDate><content:encoded>&lt;h1&gt;2134. 最少交换次数来组合所有的 1 II&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;交换&lt;/strong&gt; 定义为选中两个互不相同的位置互换值。&lt;strong&gt;环形数组&lt;/strong&gt; 指第一个元素和最后一个元素相邻。&lt;/p&gt;
&lt;p&gt;给定一个二进制环形数组 &lt;code&gt;nums&lt;/code&gt;，返回在任意位置将数组中的所有 &lt;code&gt;1&lt;/code&gt; 聚集在一起所需的最少交换次数。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [0,1,0,1,1,0,0]
输出：1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [0,1,1,1,0,0,1,1,0]
输出：2
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;核心观察&lt;/strong&gt;：设数组中 1 的总数为 &lt;code&gt;k&lt;/code&gt;，则最终聚集成的区域长度一定是 &lt;code&gt;k&lt;/code&gt;。选定某个长度为 &lt;code&gt;k&lt;/code&gt; 的窗口作为最终聚集区，「把窗口外的 1 换进来、把窗口内的 0 换出去」所需交换次数 = 窗口内 0 的个数 = &lt;code&gt;k − 窗口内 1 的个数&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;所以问题变成：&lt;strong&gt;在环形数组中找一个长度为 &lt;code&gt;k&lt;/code&gt; 的窗口，使其中 1 的个数最多&lt;/strong&gt;，答案 = &lt;code&gt;k − max_ones&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;由于数组是环形的，窗口要能跨越边界，可用 &lt;strong&gt;取模运算&lt;/strong&gt; 模拟（等价于把数组复制一份接在后面滑动）。套用定长滑窗 &lt;strong&gt;入-更新-出&lt;/strong&gt;：右端点 &lt;code&gt;i&lt;/code&gt; 进窗，够 &lt;code&gt;k&lt;/code&gt; 时更新 &lt;code&gt;max_ones&lt;/code&gt;，左端点出窗。注意只滑 &lt;code&gt;n+k-1&lt;/code&gt; 步避免重复。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;逐步举例&lt;/strong&gt;（&lt;code&gt;nums=[0,1,1,1,0,0,1,1,0]&lt;/code&gt;）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;k = sum(nums) = 5&lt;/code&gt;，即需要找长度为 5 的环形窗口。&lt;/li&gt;
&lt;li&gt;环形枚举各长度 5 窗口内的 1 个数：&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;窗口（下标）&lt;/th&gt;
&lt;th&gt;窗口内 1 数&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0..4 → [0,1,1,1,0]&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1..5 → [1,1,1,0,0]&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2..6 → [1,1,0,0,1]&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3..7 → [1,0,0,1,1]&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4..8 → [0,0,1,1,0]&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5..(9) → [0,1,1,0,0]&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6..(10) → [1,1,0,0,1]&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7..(11) → [1,0,0,1,1]&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8..(12) → [0,0,1,1,0]&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;最大 &lt;code&gt;max_ones = 3&lt;/code&gt;，最少交换 = &lt;code&gt;k − max_ones = 5 − 3 = 2&lt;/code&gt;，与预期一致。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，一次遍历。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def minSwaps(self, nums: list[int]) -&amp;gt; int:
        k = sum(nums)
        if k &amp;lt;= 1:
            return 0

        n = len(nums)
        cur = sum(nums[:k])
        max_ones = cur
        for i in range(k, n + k - 1):
            cur += nums[i % n] - nums[(i - k) % n]
            if cur &amp;gt; max_ones:
                max_ones = cur

        return k - max_ones
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
from solution import Solution


class TestMinSwaps(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.minSwaps([0, 1, 0, 1, 1, 0, 0]), 1)

    def test_example2(self):
        self.assertEqual(self.solution.minSwaps([0, 1, 1, 1, 0, 0, 1, 1, 0]), 2)

    def test_example3(self):
        self.assertEqual(self.solution.minSwaps([1, 1, 0, 0, 1]), 0)

    def test_all_ones(self):
        self.assertEqual(self.solution.minSwaps([1, 1, 1, 1]), 0)

    def test_all_zeros(self):
        self.assertEqual(self.solution.minSwaps([0, 0, 0, 0]), 0)

    def test_single_one(self):
        self.assertEqual(self.solution.minSwaps([1, 0, 0, 0]), 0)

    def test_circular_case(self):
        self.assertEqual(self.solution.minSwaps([1, 0, 0, 1, 0]), 1)


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>438. 找到字符串中所有字母异位词</title><link>https://h1s97x.github.io/posts/coding/coding-438-find-all-anagrams/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-438-find-all-anagrams/</guid><description>在 s 中找到所有 p 的字母异位词子串的起始索引</description><pubDate>Sun, 16 Aug 2026 15:32:15 GMT</pubDate><content:encoded>&lt;h1&gt;438. 找到字符串中所有字母异位词&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给定两个字符串 &lt;code&gt;s&lt;/code&gt; 和 &lt;code&gt;p&lt;/code&gt;，找到 &lt;code&gt;s&lt;/code&gt; 中所有 &lt;code&gt;p&lt;/code&gt; 的&lt;strong&gt;异位词&lt;/strong&gt;的子串，返回这些子串的起始索引。所谓异位词，即字符构成相同、排列顺序不同的词。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 ：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;cbaebabacd&quot;, p = &quot;abc&quot;
输出：[0, 6]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;核心思想：与 567 完全相同的&quot;定长滑窗 + 计数归零&quot;套路，只是把&quot;找到一次就返回&quot;改成&quot;每次匹配都记录起始下标&quot;。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;窗口长度固定为 &lt;code&gt;n = len(p)&lt;/code&gt;，维护差值数组 &lt;code&gt;cnt[26]&lt;/code&gt; 与 &lt;code&gt;non_zero&lt;/code&gt; 计数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;预处理 &lt;code&gt;p&lt;/code&gt;：&lt;code&gt;cnt[c] -= 1&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;入窗口 &lt;code&gt;+1&lt;/code&gt;、出窗口 &lt;code&gt;-1&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当窗口长度达到 &lt;code&gt;n&lt;/code&gt;（即 &lt;code&gt;i &amp;gt;= n-1&lt;/code&gt;）且 &lt;code&gt;non_zero == 0&lt;/code&gt; 时，说明窗口 &lt;code&gt;[i-n+1, i]&lt;/code&gt; 内是 &lt;code&gt;p&lt;/code&gt; 的一个异位词，把起始下标 &lt;code&gt;i-n+1&lt;/code&gt; 加入答案。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;逐步举例&lt;/strong&gt;（&lt;code&gt;s=&quot;cbaebabacd&quot;, p=&quot;abc&quot;&lt;/code&gt;，&lt;code&gt;n=3&lt;/code&gt;）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;初始化 &lt;code&gt;cnt[&apos;a&apos;]=cnt[&apos;b&apos;]=cnt[&apos;c&apos;]=-1&lt;/code&gt;，&lt;code&gt;non_zero=3&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=2&lt;/code&gt;，窗口形成 &lt;code&gt;[0,2]=&quot;cba&quot;&lt;/code&gt;：三个字符差值在此过程全部归零，&lt;code&gt;non_zero=0 → append(2-3+1=0)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=8&lt;/code&gt;，窗口 &lt;code&gt;[6,8]=&quot;bac&quot;&lt;/code&gt;：又一轮让非零清零，&lt;code&gt;non_zero=0 → append(8-3+1=6)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;最终返回 &lt;code&gt;[0, 6]&lt;/code&gt; ✓&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，n = len(s)。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)，固定 26 长计数数组。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def findAnagrams(self, s: str, p: str) -&amp;gt; list[int]:
        n, m = len(p), len(s)
        if n &amp;gt; m:
            return []

        cnt = [0] * 26
        for c in p:
            cnt[ord(c) - 97] -= 1

        non_zero = sum(1 for v in cnt if v != 0)
        ans = []

        for i, c in enumerate(s):
            idx = ord(c) - 97
            old = cnt[idx]
            cnt[idx] += 1
            if old == 0:
                non_zero += 1
            elif cnt[idx] == 0:
                non_zero -= 1

            if i &amp;gt;= n:
                idx2 = ord(s[i - n]) - 97
                old2 = cnt[idx2]
                cnt[idx2] -= 1
                if old2 == 0:
                    non_zero += 1
                elif cnt[idx2] == 0:
                    non_zero -= 1

            if i &amp;gt;= n - 1 and non_zero == 0:
                ans.append(i - n + 1)

        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
from solution import Solution


class TestFindAnagrams(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.findAnagrams(&quot;cbaebabacd&quot;, &quot;abc&quot;), [0, 6])

    def test_example2(self):
        self.assertEqual(self.solution.findAnagrams(&quot;abab&quot;, &quot;ab&quot;), [0, 1, 2])

    def test_no_anagram(self):
        self.assertEqual(self.solution.findAnagrams(&quot;abcdef&quot;, &quot;ghi&quot;), [])

    def test_p_larger_than_s(self):
        self.assertEqual(self.solution.findAnagrams(&quot;a&quot;, &quot;ab&quot;), [])

    def test_same_string(self):
        self.assertEqual(self.solution.findAnagrams(&quot;abc&quot;, &quot;abc&quot;), [0])

    def test_duplicate_chars(self):
        self.assertEqual(self.solution.findAnagrams(&quot;aaa&quot;, &quot;aa&quot;), [0, 1])


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>567. 字符串的排列</title><link>https://h1s97x.github.io/posts/coding/coding-567-permutation-in-string/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-567-permutation-in-string/</guid><description>判断 s2 是否包含 s1 的排列（定长滑动窗口 + 计数数组）</description><pubDate>Sun, 16 Aug 2026 15:31:34 GMT</pubDate><content:encoded>&lt;h1&gt;567. 字符串的排列&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你两个字符串 &lt;code&gt;s1&lt;/code&gt; 和 &lt;code&gt;s2&lt;/code&gt;，判断 &lt;code&gt;s2&lt;/code&gt; 是否包含 &lt;code&gt;s1&lt;/code&gt; 的排列。若是返回 &lt;code&gt;true&lt;/code&gt;，否则返回 &lt;code&gt;false&lt;/code&gt;。即是否存在一个长度为 &lt;code&gt;len(s1)&lt;/code&gt; 的子串，其字符出现次数与 &lt;code&gt;s1&lt;/code&gt; 完全一致（顺序可不同）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 ：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s1 = &quot;ab&quot;, s2 = &quot;eidbaooo&quot;
输出：true（子串 &quot;ba&quot; 是 &quot;ab&quot; 的排列）
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;核心思想：定长滑窗 + 计数归零判定。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;s1&lt;/code&gt; 的排列与原串只是顺序不同、字符频次相同。于是开一个&lt;strong&gt;长 &lt;code&gt;n=len(s1)&lt;/code&gt;&lt;/strong&gt; 的定长窗口，维护窗口内各字符频次与 &lt;code&gt;s1&lt;/code&gt; 频次的&lt;strong&gt;差值数组&lt;/strong&gt; &lt;code&gt;cnt[26]&lt;/code&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;预处理 &lt;code&gt;s1&lt;/code&gt;：&lt;code&gt;cnt[c] -= 1&lt;/code&gt;（表示 s1 需要这么多字符）；&lt;/li&gt;
&lt;li&gt;滑窗时，左出窗口的字符 &lt;code&gt;-1&lt;/code&gt;，右进窗口的字符 &lt;code&gt;+1&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当某个字符的差值回到 0，说明窗口内该字符的个数已与 &lt;code&gt;s1&lt;/code&gt; 对齐。若&lt;strong&gt;所有 26 个字符差值都为 0&lt;/strong&gt;，则窗口内就是 &lt;code&gt;s1&lt;/code&gt; 的一个排列。&lt;/p&gt;
&lt;p&gt;为了避免每次遍历 26 个字符，用一个 &lt;code&gt;non_zero&lt;/code&gt; 变量记录 &lt;code&gt;cnt&lt;/code&gt; 中&lt;strong&gt;非零项的个数&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;某字符从 0 变非 0，&lt;code&gt;non_zero+1&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;从非 0 变 0，&lt;code&gt;non_zero-1&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当 &lt;code&gt;non_zero == 0&lt;/code&gt;，直接返回 &lt;code&gt;true&lt;/code&gt;，O(1) 完成匹配判断。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;逐步举例&lt;/strong&gt;（&lt;code&gt;s1=&quot;ab&quot;, s2=&quot;eidbaooo&quot;&lt;/code&gt;，窗口长 &lt;code&gt;n=2&lt;/code&gt;）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;初始化 &lt;code&gt;cnt[&apos;a&apos;]=-1, cnt[&apos;b&apos;]=-1&lt;/code&gt;，&lt;code&gt;non_zero=2&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=3&lt;/code&gt; 入 &lt;code&gt;&apos;b&apos;&lt;/code&gt;：&lt;code&gt;cnt[&apos;b&apos;]:-1→0&lt;/code&gt;，&lt;code&gt;non_zero=2→1&lt;/code&gt;；出 &lt;code&gt;s2[1]=&apos;i&apos;&lt;/code&gt;：&lt;code&gt;cnt[&apos;i&apos;]:→0&lt;/code&gt;，&lt;code&gt;non_zero=1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=4&lt;/code&gt; 入 &lt;code&gt;&apos;a&apos;&lt;/code&gt;：&lt;code&gt;cnt[&apos;a&apos;]:-1→0&lt;/code&gt;，&lt;code&gt;non_zero=1→0&lt;/code&gt;。此时窗口覆盖 &lt;code&gt;[3,4]=&quot;ba&quot;&lt;/code&gt;，正是 &lt;code&gt;&quot;ab&quot;&lt;/code&gt; 的排列 → &lt;code&gt;return true&lt;/code&gt; ✓&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(m)，m = len(s2)。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)，固定 26 长计数数组。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def checkInclusion(self, s1: str, s2: str) -&amp;gt; bool:
        n, m = len(s1), len(s2)
        if n &amp;gt; m:
            return False

        cnt = [0] * 26
        for c in s1:
            cnt[ord(c) - 97] -= 1

        non_zero = sum(1 for v in cnt if v != 0)

        for i, c in enumerate(s2):
            idx = ord(c) - 97
            old = cnt[idx]
            cnt[idx] += 1
            if old == 0:
                non_zero += 1
            elif cnt[idx] == 0:
                non_zero -= 1

            if i &amp;gt;= n:
                idx2 = ord(s2[i - n]) - 97
                old2 = cnt[idx2]
                cnt[idx2] -= 1
                if old2 == 0:
                    non_zero += 1
                elif cnt[idx2] == 0:
                    non_zero -= 1

            if i &amp;gt;= n - 1 and non_zero == 0:
                return True
        return False
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
from solution import Solution


class TestCheckInclusion(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertTrue(self.solution.checkInclusion(&quot;ab&quot;, &quot;eidbaooo&quot;))

    def test_example2(self):
        self.assertFalse(self.solution.checkInclusion(&quot;ab&quot;, &quot;eidboaoo&quot;))

    def test_same_string(self):
        self.assertTrue(self.solution.checkInclusion(&quot;abc&quot;, &quot;abc&quot;))

    def test_s1_larger_than_s2(self):
        self.assertFalse(self.solution.checkInclusion(&quot;abc&quot;, &quot;ab&quot;))

    def test_duplicate_chars(self):
        self.assertTrue(self.solution.checkInclusion(&quot;aab&quot;, &quot;baa&quot;))

    def test_single_char(self):
        self.assertTrue(self.solution.checkInclusion(&quot;a&quot;, &quot;a&quot;))
        self.assertFalse(self.solution.checkInclusion(&quot;a&quot;, &quot;b&quot;))


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>1052. 爱生气的书店老板</title><link>https://h1s97x.github.io/posts/coding/coding-1052-grumpy-bookstore/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1052-grumpy-bookstore/</guid><description>老板生气时顾客会流失，一次秘密技巧可让自己连续 minutes 分钟不生气，求最大满意顾客数</description><pubDate>Sun, 16 Aug 2026 15:31:28 GMT</pubDate><content:encoded>&lt;h1&gt;1052. 爱生气的书店老板&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;书店老板的店开了 &lt;code&gt;n&lt;/code&gt; 分钟。&lt;code&gt;customers[i]&lt;/code&gt; 是第 &lt;code&gt;i&lt;/code&gt; 分钟进店的顾客数，&lt;code&gt;grumpy[i]&lt;/code&gt; 表示第 &lt;code&gt;i&lt;/code&gt; 分钟老板是否生气（&lt;code&gt;1&lt;/code&gt; 生气、&lt;code&gt;0&lt;/code&gt; 不生气）。老板生气时那一分钟的顾客会不满意，否则满意。&lt;/p&gt;
&lt;p&gt;老板知道一个秘密技巧，能让自己连续 &lt;code&gt;minutes&lt;/code&gt; 分钟不生气，但只能使用一次。求一天中最多能有多少顾客满意。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：customers = [1,0,1,2,1,1,7,5], grumpy = [0,1,0,1,0,1,0,1], minutes = 3
输出：16
解释：老板在最后 3 分钟保持冷静。满意顾客数 = 1 + 1 + 1 + 1 + 7 + 5 = 16。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;n == customers.length == grumpy.length&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= minutes &amp;lt;= n &amp;lt;= 2*10^4&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= customers[i] &amp;lt;= 1000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;grumpy[i]&lt;/code&gt; 为 0 或 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;核心拆解&lt;/strong&gt;：满意顾客由两部分组成，互不影响，最终答案 = &lt;strong&gt;本来满意的人数 + 用技巧额外挽回的人数&lt;/strong&gt;。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;本来满意&lt;/strong&gt;：老板本就不生气的分钟（&lt;code&gt;grumpy[i] == 0&lt;/code&gt;），这部分的顾客无论如何都满意，是固定的&lt;strong&gt;基础值&lt;/strong&gt; &lt;code&gt;base&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;额外挽回&lt;/strong&gt;：技巧覆盖的连续 &lt;code&gt;minutes&lt;/code&gt; 分钟里，那些原本老板生气（&lt;code&gt;grumpy[i] == 1&lt;/code&gt;）而流失的顾客，会被全部挽回。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;因此问题转化为：&lt;strong&gt;选哪一段长度为 &lt;code&gt;minutes&lt;/code&gt; 的窗口，能让窗口内 &lt;code&gt;grumpy[i]==1&lt;/code&gt; 的 &lt;code&gt;customers[i]&lt;/code&gt; 之和最大&lt;/strong&gt;。这正是定长滑动窗口。&lt;/p&gt;
&lt;p&gt;沿用定长滑窗 &lt;strong&gt;入-更新-出&lt;/strong&gt; 三步，用 &lt;code&gt;cur&lt;/code&gt; 维护窗口内「本会流失的顾客数」：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;入&lt;/strong&gt;：右端点 &lt;code&gt;i&lt;/code&gt; 进窗口，若 &lt;code&gt;grumpy[i]==1&lt;/code&gt; 则 &lt;code&gt;cur += customers[i]&lt;/code&gt;；窗口不足 &lt;code&gt;minutes&lt;/code&gt; 时继续。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;更新&lt;/strong&gt;：窗口满 &lt;code&gt;minutes&lt;/code&gt; 时，用 &lt;code&gt;cur&lt;/code&gt; 更新最大挽回值 &lt;code&gt;extra&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;出&lt;/strong&gt;：左端点 &lt;code&gt;i-minutes&lt;/code&gt; 出窗口，若 &lt;code&gt;grumpy[i-minutes]==1&lt;/code&gt; 则从 &lt;code&gt;cur&lt;/code&gt; 中扣除。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最终答案 &lt;code&gt;base + extra&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;逐步举例&lt;/strong&gt;（&lt;code&gt;customers=[1,0,1,2,1,1,7,5]&lt;/code&gt;, &lt;code&gt;grumpy=[0,1,0,1,0,1,0,1]&lt;/code&gt;, &lt;code&gt;minutes=3&lt;/code&gt;）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;基础值&lt;/strong&gt;：所有 &lt;code&gt;grumpy==0&lt;/code&gt; 的分钟顾客数之和 = &lt;code&gt;customers[0]+[2]+[4]+[6] = 1+1+1+7 = 10&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;滑窗扫描 extra&lt;/strong&gt;（&lt;code&gt;cur&lt;/code&gt; 只累计窗口内 &lt;code&gt;grumpy==1&lt;/code&gt; 的顾客）：&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;窗口 [l..r]&lt;/th&gt;
&lt;th&gt;窗口内 grumpy==1 的顾客&lt;/th&gt;
&lt;th&gt;cur&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;[0..2]&lt;/td&gt;
&lt;td&gt;c[1]=0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[1..3]&lt;/td&gt;
&lt;td&gt;c[1]+c[3]=0+2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[2..4]&lt;/td&gt;
&lt;td&gt;c[3]=2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[3..5]&lt;/td&gt;
&lt;td&gt;c[3]+c[5]=2+1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[4..6]&lt;/td&gt;
&lt;td&gt;c[5]=1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[5..7]&lt;/td&gt;
&lt;td&gt;c[5]+c[7]=1+5&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最大 &lt;code&gt;extra = 6&lt;/code&gt;（选最后 [5..7] 三分钟保持冷静），&lt;code&gt;ans = base + extra = 10 + 6 = 16&lt;/code&gt;，与预期一致。&lt;/p&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，一次遍历，每步 O(1)。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)，仅使用常数额外空间。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def maxSatisfied(self, customers: list[int], grumpy: list[int], minutes: int) -&amp;gt; int:
        base = sum(c for c, g in zip(customers, grumpy) if g == 0)

        extra = 0
        cur = 0
        for i, (c, g) in enumerate(zip(customers, grumpy)):
            cur += c if g == 1 else 0
            if i &amp;gt;= minutes:
                cur -= customers[i - minutes] if grumpy[i - minutes] == 1 else 0
            if i &amp;gt;= minutes - 1:
                extra = max(extra, cur)

        return base + extra
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
from solution import Solution


class TestMaxSatisfied(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        customers = [1, 0, 1, 2, 1, 1, 7, 5]
        grumpy = [0, 1, 0, 1, 0, 1, 0, 1]
        minutes = 3
        self.assertEqual(self.solution.maxSatisfied(customers, grumpy, minutes), 16)

    def test_example2(self):
        customers = [1]
        grumpy = [0]
        minutes = 1
        self.assertEqual(self.solution.maxSatisfied(customers, grumpy, minutes), 1)

    def test_all_grumpy(self):
        customers = [3, 5, 2]
        grumpy = [1, 1, 1]
        minutes = 2
        self.assertEqual(self.solution.maxSatisfied(customers, grumpy, minutes), 8)

    def test_all_happy(self):
        customers = [3, 5, 2]
        grumpy = [0, 0, 0]
        minutes = 2
        self.assertEqual(self.solution.maxSatisfied(customers, grumpy, minutes), 10)

    def test_window_equals_array(self):
        customers = [4, 5]
        grumpy = [1, 0]
        minutes = 2
        self.assertEqual(self.solution.maxSatisfied(customers, grumpy, minutes), 9)


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2461. 长度为 K 子数组中的最大和</title><link>https://h1s97x.github.io/posts/coding/coding-2461-max-sum-distinct/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2461-max-sum-distinct/</guid><description>长度为 k 且所有元素互不相同的子数组的最大和</description><pubDate>Sun, 16 Aug 2026 14:50:30 GMT</pubDate><content:encoded>&lt;h1&gt;2461. 长度为 K 子数组中的最大和&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个整数数组 &lt;code&gt;nums&lt;/code&gt; 和一个整数 &lt;code&gt;k&lt;/code&gt;。请你找出长度为 &lt;code&gt;k&lt;/code&gt; 且所有元素互不相同的子数组的最大和。如果不存在这样的子数组，返回 0。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [1,5,4,2,9,9,9], k = 3
输出：15
解释：长度为 3 的子数组：[1,5,4] 和 10，[5,4,2] 和 11，[4,2,9] 和 15，[2,9,9] 重复元素，[9,9,9] 重复元素。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [4,4,4], k = 3
输出：0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= k &amp;lt;= nums.length &amp;lt;= 10^5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums[i] &amp;lt;= 10^5&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;定长滑动窗口 + 哈希表&lt;/strong&gt;，要求窗口内元素&lt;strong&gt;互不相同&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;如何 O(1) 判断「窗口内无重复元素」&lt;/strong&gt;：用 &lt;code&gt;Counter&lt;/code&gt; 记录窗口内每个元素的频次，那么 &lt;code&gt;len(window)&lt;/code&gt; 就是窗口内&lt;strong&gt;互不相同&lt;/strong&gt;的元素个数。由于窗口大小固定为 k，&lt;strong&gt;&lt;code&gt;len(window) == k&lt;/code&gt; 当且仅当窗口内没有重复元素&lt;/strong&gt;（k 个位置全是不同元素）。&lt;/p&gt;
&lt;p&gt;先用前 k 个元素初始化窗口，再用 &lt;strong&gt;入-更新-出&lt;/strong&gt; 三步滑动：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;入&lt;/strong&gt;：右端点 &lt;code&gt;nums[i]&lt;/code&gt; 进窗口，更新频次与窗口和 &lt;code&gt;cur&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;更新&lt;/strong&gt;：窗口满 k 时，若 &lt;code&gt;len(window) == k&lt;/code&gt;（所有元素互不相同），用 &lt;code&gt;cur&lt;/code&gt; 更新最大和 &lt;code&gt;ans&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;出&lt;/strong&gt;：左端点 &lt;code&gt;nums[i-k+1]&lt;/code&gt; 出窗口，频次减一（减到 0 则删除键），并从 &lt;code&gt;cur&lt;/code&gt; 中扣除。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;初值&lt;/strong&gt;：第一个窗口若含重复元素，则 &lt;code&gt;ans&lt;/code&gt; 保持 0（说明至少可以返回 0）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;逐步举例&lt;/strong&gt;（&lt;code&gt;nums = [1,5,4,2,9,9,9]&lt;/code&gt;, &lt;code&gt;k = 3&lt;/code&gt;）。枚举所有长 3 窗口，只有窗口内无重复才统计：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;窗口&lt;/th&gt;
&lt;th&gt;不同元素数&lt;/th&gt;
&lt;th&gt;是否 ==k&lt;/th&gt;
&lt;th&gt;和&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;[1,5,4]&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;10 → &lt;code&gt;ans=10&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[5,4,2]&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;11 → &lt;code&gt;ans=11&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[4,2,9]&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;15 → &lt;code&gt;ans=15&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[2,9,9]&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;❌ 重复&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[9,9,9]&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;❌ 重复&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最大和 &lt;code&gt;ans = 15&lt;/code&gt;，与预期一致。可见窗口每右移一位，&lt;code&gt;len(window)&lt;/code&gt; 会随进出元素在 1~3 之间波动，只有恰好恢复为 3（无重复）时才参与比较。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;与 2841 是同一套路：2841 要求「至少 m 个不同元素」（&lt;code&gt;len(window) &amp;gt;= m&lt;/code&gt;），本题是它的特例，要求「全部不同」（&lt;code&gt;len(window) == k&lt;/code&gt;）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，每个元素进出窗口各一次。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(k)，哈希表大小不超过窗口长度。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from collections import Counter


class Solution:
    def maximumSubarraySum(self, nums: list[int], k: int) -&amp;gt; int:
        window = Counter(nums[:k])
        cur = sum(nums[:k])
        ans = cur if len(window) == k else 0
        for i in range(k, len(nums)):
            out = nums[i - k]
            window[out] -= 1
            if window[out] == 0:
                del window[out]
            window[nums[i]] += 1
            cur += nums[i] - out
            if len(window) == k and cur &amp;gt; ans:
                ans = cur
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestMaximumSubarraySum(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.maximumSubarraySum([1, 5, 4, 2, 9, 9, 9], 3), 15)

    def test_example2(self):
        self.assertEqual(self.solution.maximumSubarraySum([4, 4, 4], 3), 0)

    def test_all_distinct(self):
        self.assertEqual(self.solution.maximumSubarraySum([1, 2, 3, 4], 3), 9)


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>1423. 可获得的最大点数</title><link>https://h1s97x.github.io/posts/coding/coding-1423-max-points/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1423-max-points/</guid><description>从数组两端取 k 张牌，求最大点数</description><pubDate>Sun, 16 Aug 2026 14:48:35 GMT</pubDate><content:encoded>&lt;h1&gt;1423. 可获得的最大点数&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;几张卡牌排成一行，每张卡牌都有一个对应的点数。点数由整数数组 &lt;code&gt;cardPoints&lt;/code&gt; 给出。&lt;/p&gt;
&lt;p&gt;每次行动，你可以从行的开头或末尾拿一张卡牌，最终你必须正好拿 &lt;code&gt;k&lt;/code&gt; 张卡牌。&lt;/p&gt;
&lt;p&gt;返回你可以获得的最大点数。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：cardPoints = [1,2,3,4,5,6,1], k = 3
输出：12
解释：第一次行动拿末尾的 1，之后拿开头前 2 张，总点数为 1+6+5 = 12。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：cardPoints = [2,2,2], k = 2
输出：4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= cardPoints.length &amp;lt;= 10^5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= cardPoints[i] &amp;lt;= 10^4&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= k &amp;lt;= cardPoints.length&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;核心转化&lt;/strong&gt;：直接从两端取 k 张牌不好维护；换个角度，&lt;strong&gt;从两端取走 k 张，剩下的 n - k 张必然是数组中间一段连续的子数组&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;要拿到的点数最大，等价于：让&lt;strong&gt;留在中间那 n - k 张牌的点数总和最小&lt;/strong&gt;。于是问题变成「找到长度为 n - k 的连续子数组中，和最小的那个」。&lt;/p&gt;
&lt;p&gt;设 &lt;code&gt;total = sum(cardPoints)&lt;/code&gt;，只需求出最小窗口和 &lt;code&gt;min_sum&lt;/code&gt;：
$$
\text{ans} = total - \min_{\text{window}}(n-k \text{ 长度子数组和})
$$&lt;/p&gt;
&lt;p&gt;用&lt;strong&gt;定长滑动窗口&lt;/strong&gt;求长度 &lt;code&gt;m = n - k&lt;/code&gt; 的子数组最小和：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先求前 m 个元素的和作为窗口初值；&lt;/li&gt;
&lt;li&gt;向右滑动，每步 &lt;code&gt;cur += cardPoints[i] - cardPoints[i - m]&lt;/code&gt;，即加入右端点、移除左端点；&lt;/li&gt;
&lt;li&gt;用 &lt;code&gt;cur&lt;/code&gt; 更新 &lt;code&gt;min_sum&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;边界&lt;/strong&gt;：当 &lt;code&gt;k == n&lt;/code&gt; 时 &lt;code&gt;m == 0&lt;/code&gt;，直接返回 &lt;code&gt;total&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;逐步举例&lt;/strong&gt;（&lt;code&gt;cardPoints = [1,2,3,4,5,6,1]&lt;/code&gt;, &lt;code&gt;k = 3&lt;/code&gt;）。&lt;code&gt;n = 7&lt;/code&gt;，取走 3 张后中间留 &lt;code&gt;m = 4&lt;/code&gt; 张。求长 4 子数组的最小和：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;留中的窗口&lt;/th&gt;
&lt;th&gt;和&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;[1,2,3,4]&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[2,3,4,5]&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[3,4,5,6]&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[4,5,6,1]&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;留中最小和 &lt;code&gt;min_sum = 10&lt;/code&gt;（即留下 &lt;code&gt;[1,2,3,4]&lt;/code&gt;，从末尾拿走 &lt;code&gt;5,6,1&lt;/code&gt;），故 &lt;code&gt;ans = total - min_sum = 22 - 10 = 12&lt;/code&gt;。而题目示例最优正是拿末尾的 &lt;code&gt;1&lt;/code&gt;、&lt;code&gt;6&lt;/code&gt;、&lt;code&gt;5&lt;/code&gt; 得 &lt;code&gt;1+6+5=12&lt;/code&gt;，与&quot;留中最小 ⟺ 两端最大&quot;完全吻合。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这套「正难则反」的转化是灵茶山艾府 1423 题解的核心思路：取两端 ⟺ 留中间。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，一次遍历。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def maxScore(self, cardPoints: list[int], k: int) -&amp;gt; int:
        n = len(cardPoints)
        total = sum(cardPoints)
        if k == n:
            return total
        m = n - k
        cur = sum(cardPoints[:m])
        min_sum = cur
        for i in range(m, n):
            cur += cardPoints[i] - cardPoints[i - m]
            if cur &amp;lt; min_sum:
                min_sum = cur
        return total - min_sum
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestMaxScore(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.maxScore([1, 2, 3, 4, 5, 6, 1], 3), 12)

    def test_example2(self):
        self.assertEqual(self.solution.maxScore([2, 2, 2], 2), 4)

    def test_take_all(self):
        self.assertEqual(self.solution.maxScore([1, 2, 3], 3), 6)


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2841. 几乎唯一子数组的最大和</title><link>https://h1s97x.github.io/posts/coding/coding-2841-max-sum-almost-unique/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2841-max-sum-almost-unique/</guid><description>长度为 k 且至少包含 m 个不同元素的子数组的最大和</description><pubDate>Sun, 16 Aug 2026 14:48:24 GMT</pubDate><content:encoded>&lt;h1&gt;2841. 几乎唯一子数组的最大和&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个整数数组 &lt;code&gt;nums&lt;/code&gt; 和两个正整数 &lt;code&gt;m&lt;/code&gt; 和 &lt;code&gt;k&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;返回 &lt;code&gt;nums&lt;/code&gt; 中长度为 &lt;code&gt;k&lt;/code&gt; 且包含至少 &lt;code&gt;m&lt;/code&gt; 个不同元素的子数组的最大和。如果不存在这样的子数组，返回 0。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [2,6,7,3,1,7], m = 3, k = 4
输出：18
解释：有 3 个长度为 4 的子数组：[2,6,7,3] 和 18，[6,7,3,1] 和 17，[7,3,1,7] 和 18。
其中 [2,6,7,3] 和 [7,3,1,7] 都包含 3 个不同元素，最大和为 18。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [5,9,9,2,4,5,4], m = 1, k = 3
输出：23
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= m &amp;lt;= k &amp;lt;= nums.length &amp;lt;= 10^5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums[i] &amp;lt;= 10^9&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;定长滑动窗口 + 哈希表&lt;/strong&gt;维护窗口内元素的频次（本题难点在于要在 O(1) 内知道窗口里有多少个&lt;strong&gt;互不相同&lt;/strong&gt;的元素）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;如何 O(1) 维护「不同元素个数」&lt;/strong&gt;：用一个 &lt;code&gt;Counter&lt;/code&gt;（频次哈希表）记录窗口内每个元素的出现次数。那么 &lt;strong&gt;&lt;code&gt;len(window)&lt;/code&gt; 就是窗口内互不相同元素的个数&lt;/strong&gt;。窗口滑动时：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;右端点进窗口：&lt;code&gt;window[nums[i]] += 1&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;左端点出窗口：对应的频次减一；&lt;strong&gt;一旦频次减到 0 就 &lt;code&gt;del window[out]&lt;/code&gt;&lt;/strong&gt;，这样哈希表的键数（即不同元素个数）始终准确。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;沿用 &lt;strong&gt;入-更新-出&lt;/strong&gt; 三步，窗口和为 &lt;code&gt;cur&lt;/code&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;入&lt;/strong&gt;：右端点 &lt;code&gt;nums[i]&lt;/code&gt; 进窗口并更新频次与 &lt;code&gt;cur&lt;/code&gt;；窗口不足 k 时继续。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;更新&lt;/strong&gt;：窗口长度为 k 时，若 &lt;code&gt;len(window) &amp;gt;= m&lt;/code&gt;（是「几乎唯一」子数组），用 &lt;code&gt;cur&lt;/code&gt; 更新最大和 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;出&lt;/strong&gt;：左端点 &lt;code&gt;nums[i-k+1]&lt;/code&gt; 出窗口，频次减一（减到 0 则删除键），并从 &lt;code&gt;cur&lt;/code&gt; 中扣除。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;初值&lt;/strong&gt;：第一个窗口若无 m 个不同元素，&lt;code&gt;ans&lt;/code&gt; 保持 0（没有满足条件的子数组返回 0）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;逐步举例&lt;/strong&gt;（&lt;code&gt;nums = [2,6,7,3,1,7]&lt;/code&gt;, &lt;code&gt;m = 3&lt;/code&gt;, &lt;code&gt;k = 4&lt;/code&gt;）。枚举所有长 4 窗口并检查不同元素数：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;窗口&lt;/th&gt;
&lt;th&gt;不同元素&lt;/th&gt;
&lt;th&gt;是否 ≥3&lt;/th&gt;
&lt;th&gt;和&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;[2,6,7,3]&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;18 → &lt;code&gt;ans=18&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[6,7,3,1]&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[7,3,1,7]&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;18 → &lt;code&gt;ans=18&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;三种都是&quot;几乎唯一&quot;子数组，最大和 &lt;code&gt;ans = 18&lt;/code&gt;，与预期一致。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;思路与写法参考灵茶山艾府 2841、2461 对应题解（2461 是在本题基础上的特例：把 &lt;code&gt;&amp;gt;= m&lt;/code&gt; 换成 &lt;code&gt;== k&lt;/code&gt;）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，每个元素进出窗口各一次。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(k)，哈希表大小不超过窗口长度。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from collections import Counter


class Solution:
    def maxSum(self, nums: list[int], m: int, k: int) -&amp;gt; int:
        window = Counter(nums[:k])
        cur = sum(nums[:k])
        ans = cur if len(window) &amp;gt;= m else 0
        for i in range(k, len(nums)):
            out = nums[i - k]
            window[out] -= 1
            if window[out] == 0:
                del window[out]
            window[nums[i]] += 1
            cur += nums[i] - out
            if len(window) &amp;gt;= m and cur &amp;gt; ans:
                ans = cur
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestMaxSum(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.maxSum([2, 6, 7, 3, 1, 7], 3, 4), 18)

    def test_example2(self):
        self.assertEqual(self.solution.maxSum([5, 9, 9, 2, 4, 5, 4], 1, 3), 23)

    def test_no_subarray(self):
        self.assertEqual(self.solution.maxSum([1, 1, 1], 2, 3), 0)


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>1343. 大小为 K 且平均值大于等于阈值的子数组数目</title><link>https://h1s97x.github.io/posts/coding/coding-1343-num-subarrays/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1343-num-subarrays/</guid><description>返回长度为 k 且平均值 &gt;= threshold 的连续子数组个数</description><pubDate>Sun, 16 Aug 2026 14:48:11 GMT</pubDate><content:encoded>&lt;h1&gt;1343. 大小为 K 且平均值大于等于阈值的子数组数目&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个整数数组 &lt;code&gt;arr&lt;/code&gt; 和两个整数 &lt;code&gt;k&lt;/code&gt; 和 &lt;code&gt;threshold&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;请你返回长度为 &lt;code&gt;k&lt;/code&gt; 且平均值大于等于 &lt;code&gt;threshold&lt;/code&gt; 的子数组数目。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：arr = [2,2,2,2,5,5,5,8], k = 3, threshold = 4
输出：3
解释：子数组 [2,5,5],[5,5,5] 和 [5,5,8] 的平均值分别为 4,5 和 6。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：arr = [11,13,17,23,29,31,7,5,2,3], k = 3, threshold = 5
输出：6
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= arr.length &amp;lt;= 10^5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= k &amp;lt;= arr.length&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= threshold &amp;lt;= 10^4&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;定长滑动窗口&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关键技巧&lt;/strong&gt;：条件「平均值 ≥ threshold」如果直接比较会涉及大量除法。由于窗口长度固定为 k，可以把它&lt;strong&gt;等价变形为「窗口和 ≥ k × threshold」&lt;/strong&gt;，从而全部只用整数加法/比较，&lt;code&gt;k * threshold&lt;/code&gt; 只需算一次。这样既避免了除法精度与性能开销，也不影响判断正确性。&lt;/p&gt;
&lt;p&gt;沿用定长滑窗的 &lt;strong&gt;入-更新-出&lt;/strong&gt; 三步：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;入&lt;/strong&gt;：右端点 &lt;code&gt;arr[i]&lt;/code&gt; 进入窗口，窗口和 &lt;code&gt;win += arr[i]&lt;/code&gt;；若 &lt;code&gt;i &amp;lt; k-1&lt;/code&gt; 窗口长度不足 k，继续循环。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;更新&lt;/strong&gt;：窗口长度为 k，判断 &lt;code&gt;win &amp;gt;= k * threshold&lt;/code&gt;，成立则 &lt;code&gt;ans += 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;出&lt;/strong&gt;：左端点 &lt;code&gt;arr[i-k+1]&lt;/code&gt; 离开窗口，&lt;code&gt;win -= arr[i-k+1]&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;逐步举例&lt;/strong&gt;（&lt;code&gt;arr = [2,2,2,2,5,5,5,8]&lt;/code&gt;, &lt;code&gt;k = 3&lt;/code&gt;, &lt;code&gt;threshold = 4&lt;/code&gt;）。先算阈值 &lt;code&gt;k*threshold = 12&lt;/code&gt;，遍历并依次判断每个长 3 窗口：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;窗口&lt;/th&gt;
&lt;th&gt;窗口和&lt;/th&gt;
&lt;th&gt;≥ 12&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;[2,2,2]&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[2,2,2]&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[2,2,5]&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[2,5,5]&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[5,5,5]&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[5,5,8]&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;满足条件的子数组共 3 个，与预期一致；全程只用整数加法比较，没有除法。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;思路与写法参考灵茶山艾府《【模板】定长滑动窗口》（入-更新-出套路）及对应题解。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，其中 n 是 &lt;code&gt;arr&lt;/code&gt; 长度，每步 O(1)。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def numOfSubarrays(self, arr: list[int], k: int, threshold: int) -&amp;gt; int:
        target = k * threshold
        cur = sum(arr[:k])
        ans = 1 if cur &amp;gt;= target else 0
        for i in range(k, len(arr)):
            cur += arr[i] - arr[i - k]
            if cur &amp;gt;= target:
                ans += 1
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestNumOfSubarrays(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(
            self.solution.numOfSubarrays([2, 2, 2, 2, 5, 5, 5, 8], 3, 4), 3
        )

    def test_example2(self):
        self.assertEqual(
            self.solution.numOfSubarrays([11, 13, 17, 23, 29, 31, 7, 5, 2, 3], 3, 5), 6
        )

    def test_none(self):
        self.assertEqual(self.solution.numOfSubarrays([1, 1, 1], 2, 5), 0)


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2090. 半径为 k 的子数组平均值</title><link>https://h1s97x.github.io/posts/coding/coding-2090-k-radius/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2090-k-radius/</guid><description>计算每个长度为 2k+1 的子数组的平均值，不足则返回 -1</description><pubDate>Sun, 16 Aug 2026 14:48:11 GMT</pubDate><content:encoded>&lt;h1&gt;2090. 半径为 k 的子数组平均值&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个下标从 0 开始的数组 &lt;code&gt;nums&lt;/code&gt; 和一个整数 &lt;code&gt;k&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;半径为 k 的子数组平均值是指：对每个下标 i，以下标 i 为中心，半径为 k 的子数组（共 2k+1 个元素）的平均值向下取整。&lt;/p&gt;
&lt;p&gt;返回数组 &lt;code&gt;avgs&lt;/code&gt;，其中 &lt;code&gt;avgs[i]&lt;/code&gt; 是对应下标的平均值，如果无法找到半径为 k 的子数组则返回 -1。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [7,4,3,9,1,8,5,2,6], k = 3
输出：[-1,-1,-1,5,4,4,-1,-1,-1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [100000], k = 0
输出：[100000]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;n == nums.length&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= n &amp;lt;= 10^5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= k &amp;lt;= n&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= nums[i] &amp;lt;= 10^5&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;定长滑动窗口&lt;/strong&gt;——本题的窗口不是长度 k，而是一个长度为 &lt;code&gt;2k+1&lt;/code&gt; 的窗口（以下标 i 为中心，左界 &lt;code&gt;i-k&lt;/code&gt;，右界 &lt;code&gt;i+k&lt;/code&gt;）。&lt;strong&gt;关键在「把新题往模板上靠」&lt;/strong&gt;：把窗口长度识别成 &lt;code&gt;2k+1&lt;/code&gt;，就回归到熟悉的定长滑窗套路。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;边界处理&lt;/strong&gt;：只有当 &lt;code&gt;k &amp;lt;= i &amp;lt;= n-k-1&lt;/code&gt; 时，长度为 &lt;code&gt;2k+1&lt;/code&gt; 的子数组才能完整地落在数组中。因此：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;初始化答案数组全为 &lt;code&gt;-1&lt;/code&gt;，只有能形成完整窗口的位置才需要计算；&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;2k+1 &amp;gt; n&lt;/code&gt;，任意下标都无法形成完整窗口，直接返回全 &lt;code&gt;-1&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;k == 0&lt;/code&gt;，窗口就是元素本身，直接返回原数组。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;掩盖「窗口右移对应中心回填」这一细节&lt;/strong&gt;：落在完整窗口内的下标与窗口右端点不同——右端点进一位，窗口中心才前进一位。所以用右端点指针遍历时，当前窗口 &lt;code&gt;[i-2k, i]&lt;/code&gt; 的中心是 &lt;code&gt;i-k&lt;/code&gt;，平均值要回填到 &lt;code&gt;avgs[i-k]&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;沿用 &lt;strong&gt;入-更新-出&lt;/strong&gt; 三步（同时维护窗口和，避免重复求和）：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;入&lt;/strong&gt;：右端点 &lt;code&gt;nums[i]&lt;/code&gt; 进入窗口，&lt;code&gt;s += nums[i]&lt;/code&gt;；窗口不足 &lt;code&gt;2k+1&lt;/code&gt; 时继续。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;更新&lt;/strong&gt;：窗口长度为 &lt;code&gt;2k+1&lt;/code&gt;，记录 &lt;code&gt;avgs[i-k] = s // (2k+1)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;出&lt;/strong&gt;：左端点 &lt;code&gt;nums[i-2k]&lt;/code&gt; 离开窗口，&lt;code&gt;s -= nums[i-2k]&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;逐步举例&lt;/strong&gt;（&lt;code&gt;nums = [7,4,3,9,1,8,5,2,6]&lt;/code&gt;, &lt;code&gt;k = 3&lt;/code&gt;，窗口长 &lt;code&gt;2k+1=7&lt;/code&gt;）。中心 &lt;code&gt;i&lt;/code&gt; 只能在 &lt;code&gt;[3, 5]&lt;/code&gt; 形成完整窗口，故首尾 3 个下标返回 &lt;code&gt;-1&lt;/code&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;先统计前 7 个元素 &lt;code&gt;[7,4,3,9,1,8,5]&lt;/code&gt; 之和 &lt;code&gt;s = 37&lt;/code&gt;，其中心是 &lt;code&gt;9(下标3)&lt;/code&gt;，回填 &lt;code&gt;avgs[3] = 37//7 = 5&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;右移一步：出 &lt;code&gt;7&lt;/code&gt;、入 &lt;code&gt;2&lt;/code&gt; → &lt;code&gt;[4,3,9,1,8,5,2]&lt;/code&gt;，&lt;code&gt;s = 32&lt;/code&gt;，中心 &lt;code&gt;1(下标4)&lt;/code&gt;，&lt;code&gt;avgs[4] = 32//7 = 4&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;再右移：出 &lt;code&gt;4&lt;/code&gt;、入 &lt;code&gt;6&lt;/code&gt; → &lt;code&gt;[3,9,1,8,5,2,6]&lt;/code&gt;，&lt;code&gt;s = 34&lt;/code&gt;，中心 &lt;code&gt;8(下标5)&lt;/code&gt;，&lt;code&gt;avgs[5] = 34//7 = 4&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最终 &lt;code&gt;avgs = [-1,-1,-1,5,4,4,-1,-1,-1]&lt;/code&gt;，与预期一致。注意窗口每右移一位，中心只前进一位，故回填位置是 &lt;code&gt;i-k&lt;/code&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;思路与写法参考灵茶山艾府《【模板】定长滑动窗口》（入-更新-出套路）及 2090 对应题解。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，每个元素进/出窗口各一次。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(n)，用于返回答案数组。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def getAverages(self, nums: list[int], k: int) -&amp;gt; list[int]:
        n = len(nums)
        ans = [-1] * n
        if k == 0:
            return nums
        if n &amp;lt; 2 * k + 1:
            return ans
        window = 2 * k + 1
        cur = sum(nums[:window])
        ans[k] = cur // window
        for i in range(k + 1, n - k):
            cur += nums[i + k] - nums[i - k - 1]
            ans[i] = cur // window
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestGetAverages(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(
            self.solution.getAverages([7, 4, 3, 9, 1, 8, 5, 2, 6], 3),
            [-1, -1, -1, 5, 4, 4, -1, -1, -1],
        )

    def test_example2(self):
        self.assertEqual(self.solution.getAverages([100000], 0), [100000])

    def test_small_array(self):
        self.assertEqual(self.solution.getAverages([1, 2], 1), [-1, -1])


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2379. 得到 K 个黑块的最少涂色次数</title><link>https://h1s97x.github.io/posts/coding/coding-2379-min-recolors/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2379-min-recolors/</guid><description>将包含 W/B 的字符串中连续 k 个字符全变为 B 的最小涂色次数</description><pubDate>Sun, 16 Aug 2026 14:48:11 GMT</pubDate><content:encoded>&lt;h1&gt;2379. 得到 K 个黑块的最少涂色次数&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个长度为 &lt;code&gt;n&lt;/code&gt; 的字符串 &lt;code&gt;blocks&lt;/code&gt;，其中 &lt;code&gt;blocks[i]&lt;/code&gt; 是 &lt;code&gt;&apos;W&apos;&lt;/code&gt; 或 &lt;code&gt;&apos;B&apos;&lt;/code&gt;，表示第 i 块的颜色。字符 &lt;code&gt;&apos;W&apos;&lt;/code&gt; 表示白色，&lt;code&gt;&apos;B&apos;&lt;/code&gt; 表示黑色。&lt;/p&gt;
&lt;p&gt;给你一个整数 &lt;code&gt;k&lt;/code&gt;，表示想要&lt;strong&gt;连续&lt;/strong&gt; &lt;code&gt;k&lt;/code&gt; 个黑块。每次操作可以将一个白块涂成黑块。&lt;/p&gt;
&lt;p&gt;返回最少操作次数。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：blocks = &quot;WBBWWBBWBW&quot;, k = 7
输出：3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：blocks = &quot;WBWBBBW&quot;, k = 2
输出：0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;n == blocks.length&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= k &amp;lt;= n &amp;lt;= 100&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;定长滑动窗口&lt;/strong&gt;。目标是得到连续 k 个黑块。涂色只能把白变黑，所以：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;想让某一段连续 k 个位置全变成黑，就必须把这段里所有 &lt;code&gt;&apos;W&apos;&lt;/code&gt; 涂黑。&lt;strong&gt;答案 = 所有长度为 k 的子段中，&lt;code&gt;&apos;W&apos;&lt;/code&gt; 个数的最小值。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;（等价地也可以统计窗口内 &lt;code&gt;&apos;B&apos;&lt;/code&gt; 个数的最大值，答案 = &lt;code&gt;k - max_B&lt;/code&gt;。）&lt;/p&gt;
&lt;p&gt;这样就把修改型问题翻译成了纯粹的滑窗统计问题，沿用 &lt;strong&gt;入-更新-出&lt;/strong&gt; 三步，只需维护窗口内 &lt;code&gt;&apos;W&apos;&lt;/code&gt; 的计数：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;入&lt;/strong&gt;：右端点 &lt;code&gt;blocks[i]&lt;/code&gt; 进窗口，若是 &lt;code&gt;&apos;W&apos;&lt;/code&gt; 则 &lt;code&gt;cur += 1&lt;/code&gt;；窗口不足 k 时继续。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;更新&lt;/strong&gt;：窗口长度为 k，用 &lt;code&gt;cur&lt;/code&gt; 更新最小值 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;出&lt;/strong&gt;：左端点 &lt;code&gt;blocks[i-k+1]&lt;/code&gt; 出窗口，若是 &lt;code&gt;&apos;W&apos;&lt;/code&gt; 则 &lt;code&gt;cur -= 1&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;细节&lt;/strong&gt;：答案本质上是一个「不停变小」的值，&lt;code&gt;ans&lt;/code&gt; 初值设为第一个窗口的 &lt;code&gt;&apos;W&apos;&lt;/code&gt; 数即可；若某个窗口已不含 &lt;code&gt;&apos;W&apos;&lt;/code&gt;（即已有连续 k 个黑块），答案是 0 并可提前结束。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;逐步举例&lt;/strong&gt;（&lt;code&gt;blocks = &quot;WBBWWBBWBW&quot;&lt;/code&gt;, &lt;code&gt;k = 7&lt;/code&gt;）。枚举所有长 7 窗口并统计其中 &lt;code&gt;&apos;W&apos;&lt;/code&gt; 的个数：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;窗口（下标）&lt;/th&gt;
&lt;th&gt;内容&lt;/th&gt;
&lt;th&gt;W 个数&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;[0..6]&lt;/td&gt;
&lt;td&gt;W B B W W B B&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[1..7]&lt;/td&gt;
&lt;td&gt;B B W W B B W&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[2..8]&lt;/td&gt;
&lt;td&gt;B W W B B W B&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[3..9]&lt;/td&gt;
&lt;td&gt;W W B B W B W&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;&apos;W&apos;&lt;/code&gt; 最少是 3，所以最少涂色 3 次即可得到连续 7 个黑块，与预期一致。我们把&quot;修改&quot;问题转成了&quot;统计每个窗口内 W 的最小值&quot;问题。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;思路与写法参考灵茶山艾府《【模板】定长滑动窗口》及 2379 对应题解；关于二进制低位取值的 &lt;code&gt;&amp;amp;1&lt;/code&gt; 优化见灵神题解（可读性优先时直接比较字符即可）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，每个字符进出窗口各一次。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def minimumRecolors(self, blocks: str, k: int) -&amp;gt; int:
        cur = blocks[:k].count(&quot;W&quot;)
        ans = cur
        for i in range(k, len(blocks)):
            cur += (blocks[i] == &quot;W&quot;) - (blocks[i - k] == &quot;W&quot;)
            if cur &amp;lt; ans:
                ans = cur
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestMinimumRecolors(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.minimumRecolors(&quot;WBBWWBBWBW&quot;, 7), 3)

    def test_example2(self):
        self.assertEqual(self.solution.minimumRecolors(&quot;WBWBBBW&quot;, 2), 0)

    def test_all_white(self):
        self.assertEqual(self.solution.minimumRecolors(&quot;WWWWW&quot;, 3), 3)


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>643. 子数组最大平均数 I</title><link>https://h1s97x.github.io/posts/coding/coding-643-max-average/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-643-max-average/</guid><description>给定 n 个整数数组，找出长度为 k 的连续子数组的最大平均数</description><pubDate>Sun, 16 Aug 2026 14:48:06 GMT</pubDate><content:encoded>&lt;h1&gt;643. 子数组最大平均数 I&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个由 &lt;code&gt;n&lt;/code&gt; 个整数组成的数组 &lt;code&gt;nums&lt;/code&gt; 和一个整数 &lt;code&gt;k&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;请你找出长度为 &lt;code&gt;k&lt;/code&gt; 的连续子数组的最大平均数，并返回该最大值。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [1,12,-5,-6,50,3], k = 4
输出：12.75
解释：最大平均数 (12-5-6+50)/4 = 51/4 = 12.75
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [5], k = 1
输出：5.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;n == nums.length&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= k &amp;lt;= n &amp;lt;= 10^5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-10^4 &amp;lt;= nums[i] &amp;lt;= 10^4&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;定长滑动窗口&lt;/strong&gt;。长度为 k 的子数组数量是 O(n) 个，但直接枚举每个子数组再求和需要 O(n·k)。由于窗口向右滑动时，&lt;strong&gt;离开&lt;/strong&gt;窗口的只有最左一个元素、&lt;strong&gt;进入&lt;/strong&gt;窗口的只有最右一个元素，中间 k-2 个元素不变，所以可以 O(1) 更新窗口和。&lt;/p&gt;
&lt;p&gt;沿用定长滑窗的 &lt;strong&gt;入-更新-出&lt;/strong&gt; 三步套路：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;入&lt;/strong&gt;：右端点 &lt;code&gt;i&lt;/code&gt; 进入窗口，窗口和 &lt;code&gt;cur += nums[i]&lt;/code&gt;；若 &lt;code&gt;i &amp;lt; k-1&lt;/code&gt; 窗口长度不足 k，继续循环。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;更新&lt;/strong&gt;：此时窗口长度为 k，用当前窗口和更新最大和 &lt;code&gt;ans&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;出&lt;/strong&gt;：左端点 &lt;code&gt;i-k+1&lt;/code&gt; 离开窗口，&lt;code&gt;cur -= nums[i-k+1]&lt;/code&gt;，为下一轮做准备。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最后用最大和除以 k 得到最大平均数。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;逐步举例&lt;/strong&gt;（&lt;code&gt;nums = [1,12,-5,-6,50,3]&lt;/code&gt;, &lt;code&gt;k = 4&lt;/code&gt;）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;先统计前 &lt;code&gt;k-1=3&lt;/code&gt; 个元素和：&lt;code&gt;1+12-5 = 8&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=3&lt;/code&gt;，&lt;code&gt;-6&lt;/code&gt; 入窗 → 窗口 &lt;code&gt;[1,12,-5,-6]&lt;/code&gt;，和 &lt;code&gt;= 2&lt;/code&gt;，更新 &lt;code&gt;ans = 2&lt;/code&gt;；出 &lt;code&gt;nums[0]=1&lt;/code&gt; → 窗口和 &lt;code&gt;= 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=4&lt;/code&gt;，&lt;code&gt;50&lt;/code&gt; 入窗 → 窗口 &lt;code&gt;[12,-5,-6,50]&lt;/code&gt;，和 &lt;code&gt;= 51&lt;/code&gt;，更新 &lt;code&gt;ans = 51&lt;/code&gt;；出 &lt;code&gt;nums[1]=12&lt;/code&gt; → 窗口和 &lt;code&gt;= 39&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=5&lt;/code&gt;，&lt;code&gt;3&lt;/code&gt; 入窗 → 窗口 &lt;code&gt;[-5,-6,50,3]&lt;/code&gt;，和 &lt;code&gt;= 42&lt;/code&gt;，&lt;code&gt;ans&lt;/code&gt; 仍为 51；出 &lt;code&gt;nums[2]=-5&lt;/code&gt; → 窗口和 &lt;code&gt;= 47&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最大和为 51，最大平均数 &lt;code&gt;= 51/4 = 12.75&lt;/code&gt;，与预期一致。可见每步只需加一个新元素、减一个旧元素，中间元素不重复求和。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：本题 &lt;code&gt;nums&lt;/code&gt; 中含负数，所以 &lt;code&gt;ans&lt;/code&gt; 不能初始化为 0，而应初始化为 &lt;code&gt;-inf&lt;/code&gt;（或直接初始化为第一个窗口的和），否则当所有 L 都小于 0 时会得到错误结果。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;详细讲解见灵茶山艾府的《【模板】定长滑动窗口》与该定长滑窗套路题解。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，其中 n 是数组长度。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def findMaxAverage(self, nums: list[int], k: int) -&amp;gt; float:
        cur = sum(nums[:k])
        ans = cur
        for i in range(k, len(nums)):
            cur += nums[i] - nums[i - k]
            if cur &amp;gt; ans:
                ans = cur
        return ans / k
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestFindMaxAverage(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertAlmostEqual(
            self.solution.findMaxAverage([1, 12, -5, -6, 50, 3], 4), 12.75
        )

    def test_example2(self):
        self.assertAlmostEqual(self.solution.findMaxAverage([5], 1), 5.0)

    def test_negative(self):
        self.assertAlmostEqual(self.solution.findMaxAverage([-1, -2, -3], 2), -1.5)


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>1456. 定长子串中元音的最大数目</title><link>https://h1s97x.github.io/posts/coding/coding-1456-max-vowels/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1456-max-vowels/</guid><description>给你字符串 s 和整数 k，找到长度为 k 的子串中元音字母的最大数目</description><pubDate>Sun, 16 Aug 2026 14:48:00 GMT</pubDate><content:encoded>&lt;h1&gt;1456. 定长子串中元音的最大数目&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你字符串 &lt;code&gt;s&lt;/code&gt; 和整数 &lt;code&gt;k&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;请你在字符串 &lt;code&gt;s&lt;/code&gt; 中找到长度为 &lt;code&gt;k&lt;/code&gt; 的&lt;strong&gt;子串&lt;/strong&gt;，使得子串中元音字母（&lt;code&gt;a, e, i, o, u&lt;/code&gt;）的数目最大，并返回该最大值。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;abciiidef&quot;, k = 3
输出：3
解释：子串 &quot;iii&quot; 包含 3 个元音字母。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;aeiou&quot;, k = 2
输出：2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 3：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：s = &quot;leetcode&quot;, k = 3
输出：2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= s.length &amp;lt;= 10^5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s&lt;/code&gt; 由小写英文字母组成&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= k &amp;lt;= s.length&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解法思路&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;定长滑动窗口&lt;/strong&gt;，核心思想是：维护一个长度为 &lt;code&gt;k&lt;/code&gt; 的窗口，统计窗口内元音个数，滑动时只考虑&lt;strong&gt;离开&lt;/strong&gt;窗口的字母和&lt;strong&gt;进入&lt;/strong&gt;窗口的字母，中间字母无需重复统计，从而把每步更新降到 O(1)。&lt;/p&gt;
&lt;p&gt;暴力枚举所有子串要 O(nk)，而向右滑一个位置时，只需：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;移除离开窗口的字母 &lt;code&gt;s[i-k]&lt;/code&gt;，是元音则计数 &lt;code&gt;-1&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;加入进入窗口的字母 &lt;code&gt;s[i]&lt;/code&gt;，是元音则计数 &lt;code&gt;+1&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;定长滑窗统一三步套路（入-更新答案-出）：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;入&lt;/strong&gt;：枚举右端点 &lt;code&gt;i&lt;/code&gt;，把 &lt;code&gt;s[i]&lt;/code&gt; 计入窗口元音数。若左端点 &lt;code&gt;i-k+1 &amp;lt; 0&lt;/code&gt;，说明窗口长度不足 k（尚未形成第一个窗口），继续下一步循环。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;更新答案&lt;/strong&gt;：此时窗口长度为 k，用当前元音数更新答案最大值。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;出&lt;/strong&gt;：把左端点 &lt;code&gt;i-k+1&lt;/code&gt; 处元素移出窗口，更新元音数，为下一轮做准备。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;逐步举例&lt;/strong&gt;（&lt;code&gt;s = &quot;abciiidef&quot;, k = 3&lt;/code&gt;，用 &lt;code&gt;vowel&lt;/code&gt; 表示当前窗口元音数）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;从左到右遍历。先统计前 &lt;code&gt;k-1=2&lt;/code&gt; 个字母 &lt;code&gt;a, b&lt;/code&gt; 的元音数：&lt;code&gt;vowel = 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=2&lt;/code&gt;，&lt;code&gt;c&lt;/code&gt; 进入窗口 → 窗口 &lt;code&gt;abc&lt;/code&gt;，&lt;code&gt;vowel = 1&lt;/code&gt;，得第 1 个长 k 子串，更新 &lt;code&gt;ans = 1&lt;/code&gt;；出 &lt;code&gt;a&lt;/code&gt; → &lt;code&gt;vowel = 0&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=3&lt;/code&gt;，&lt;code&gt;i&lt;/code&gt; 进入窗口 → 窗口 &lt;code&gt;bci&lt;/code&gt;，&lt;code&gt;vowel = 1&lt;/code&gt;，更新 &lt;code&gt;ans = 1&lt;/code&gt;；出 &lt;code&gt;b&lt;/code&gt; → &lt;code&gt;vowel = 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=4&lt;/code&gt;，&lt;code&gt;i&lt;/code&gt; 进入窗口 → 窗口 &lt;code&gt;cii&lt;/code&gt;，&lt;code&gt;vowel = 2&lt;/code&gt;，更新 &lt;code&gt;ans = 2&lt;/code&gt;；出 &lt;code&gt;c&lt;/code&gt; → &lt;code&gt;vowel = 2&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=5&lt;/code&gt;，&lt;code&gt;i&lt;/code&gt; 进入窗口 → 窗口 &lt;code&gt;iii&lt;/code&gt;，&lt;code&gt;vowel = 3&lt;/code&gt;，更新 &lt;code&gt;ans = 3&lt;/code&gt;；出 &lt;code&gt;i&lt;/code&gt; → &lt;code&gt;vowel = 2&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=6&lt;/code&gt;，&lt;code&gt;d&lt;/code&gt; 进入窗口 → 窗口 &lt;code&gt;iid&lt;/code&gt;，&lt;code&gt;vowel = 2&lt;/code&gt;，更新 &lt;code&gt;ans&lt;/code&gt; 仍为 3；出 &lt;code&gt;i&lt;/code&gt; → &lt;code&gt;vowel = 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=7&lt;/code&gt;，&lt;code&gt;e&lt;/code&gt; 进入窗口 → 窗口 &lt;code&gt;ide&lt;/code&gt;，&lt;code&gt;vowel = 2&lt;/code&gt;；出 &lt;code&gt;i&lt;/code&gt; → &lt;code&gt;vowel = 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=8&lt;/code&gt;，&lt;code&gt;f&lt;/code&gt; 进入窗口 → 窗口 &lt;code&gt;def&lt;/code&gt;，&lt;code&gt;vowel = 1&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;遍历结束，&lt;code&gt;ans = 3&lt;/code&gt;，与预期一致。可以看到每步只处理进/出窗口的 2 个字符，窗口内其余元素不重复统计。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;优化&lt;/strong&gt;：答案的理论最大值就是 k（窗口内全是元音）。一旦 &lt;code&gt;ans == k&lt;/code&gt;，答案无法再增大，可直接提前退出循环，进一步减少遍历。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;代码见下方。关于外围的 &lt;code&gt;set&lt;/code&gt;/直接 &lt;code&gt;in &quot;aeiou&quot;&lt;/code&gt; 判断元音的写法、以及&quot;入-更新-出&quot;三步边界条件，参考灵茶山艾府对该题（也是定长滑窗模板题）的完整讲解。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;复杂度分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，其中 n 是 &lt;code&gt;s&lt;/code&gt; 的长度，每步滑动 O(1)。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)，仅使用常数额外空间。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def maxVowels(self, s: str, k: int) -&amp;gt; int:
        vowels = set(&quot;aeiou&quot;)
        cur = sum(1 for c in s[:k] if c in vowels)
        ans = cur
        for i in range(k, len(s)):
            cur += (s[i] in vowels) - (s[i - k] in vowels)
            if cur &amp;gt; ans:
                ans = cur
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestMaxVowels(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.maxVowels(&quot;abciiidef&quot;, 3), 3)

    def test_example2(self):
        self.assertEqual(self.solution.maxVowels(&quot;aeiou&quot;, 2), 2)

    def test_example3(self):
        self.assertEqual(self.solution.maxVowels(&quot;leetcode&quot;, 3), 2)

    def test_single_char(self):
        self.assertEqual(self.solution.maxVowels(&quot;a&quot;, 1), 1)

    def test_no_vowel(self):
        self.assertEqual(self.solution.maxVowels(&quot;bcdfg&quot;, 2), 0)


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>滑动窗口与双指针</title><link>https://h1s97x.github.io/posts/coding/coding-sliding-window-two-pointers/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-sliding-window-two-pointers/</guid><description>灵茶山艾府经典题单：定长/不定长滑动窗口、单序列/双序列双指针、三指针、分组循环</description><pubDate>Sun, 16 Aug 2026 14:47:56 GMT</pubDate><content:encoded>&lt;h1&gt;滑动窗口与双指针&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;题单来源：&lt;a href=&quot;https://leetcode.cn/discuss/post/0viNMK/&quot;&gt;灵茶山艾府 - 滑动窗口与双指针&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;章节&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;章节&lt;/th&gt;
&lt;th&gt;题数&lt;/th&gt;
&lt;th&gt;核心技巧&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;01&lt;/td&gt;
&lt;td&gt;定长滑动窗口&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;固定窗口大小，维护窗口内统计值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;02&lt;/td&gt;
&lt;td&gt;不定长滑动窗口&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;最长/最短/子数组个数/恰好型&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;03&lt;/td&gt;
&lt;td&gt;单序列双指针&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;反转/相向/同向/背向/原地修改&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;04&lt;/td&gt;
&lt;td&gt;双序列双指针&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;双指针合并/判断子序列&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;05&lt;/td&gt;
&lt;td&gt;三指针&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;三个指针协同&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;06&lt;/td&gt;
&lt;td&gt;分组循环&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;按连续段分组处理&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;题单路线&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;先刷 &lt;strong&gt;定长滑动窗口&lt;/strong&gt;（01），建立窗口思维&lt;/li&gt;
&lt;li&gt;再刷 &lt;strong&gt;不定长滑动窗口&lt;/strong&gt;（02），掌握伸缩窗口&lt;/li&gt;
&lt;li&gt;然后刷 &lt;strong&gt;单序列双指针&lt;/strong&gt;（03），理解指针方向变化&lt;/li&gt;
&lt;li&gt;最后刷 &lt;strong&gt;双序列双指针&lt;/strong&gt;（04）和 &lt;strong&gt;分组循环&lt;/strong&gt;（06）&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>浏览器功能插件</title><link>https://h1s97x.github.io/posts/notes/notes-browser-plugins/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-browser-plugins/</guid><description>SuperCopy 超级复制 - Microsoft Edge Addons SuperCopy超级复制 唯一官方扩展 主要功能：解除禁止复制；解除禁止右键；解</description><pubDate>Thu, 16 Jul 2026 16:12:52 GMT</pubDate><content:encoded>&lt;h2&gt;Edge插件推荐&lt;/h2&gt;
&lt;h3&gt;SuperCopy 超级复制&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://microsoftedge.microsoft.com/addons/detail/supercopy-%E8%B6%85%E7%BA%A7%E5%A4%8D%E5%88%B6/nhiheekdcnmfbapkmpbhmplfddenhjic&quot;&gt;SuperCopy 超级复制 - Microsoft Edge Addons&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;SuperCopy超级复制 唯一官方扩展&lt;/p&gt;
&lt;p&gt;主要功能：解除禁止复制；解除禁止右键；解除禁止全选；解除禁止粘贴；
常用场景：复制百度文库、腾讯文档、金山文档、起点文学、豆瓣读书等内容；
插件特色：点亮图标即可复制，操作简单，需复制的网站具有记忆功能；&lt;/p&gt;
&lt;p&gt;使用方法：
在访问被禁止右键或禁止复制的网站上，点击SuperCopy图标解除禁止即可。
当图标灰色表示未启用解除功能。
当图标彩色表示解除生效。&lt;/p&gt;
&lt;p&gt;可以复制的常用中文网站：
1、百度文库
2、起点中文网、起点文学、起点女生网
3、创始中文网
4、磨铁中文网、磨铁文学、锦文小说网、墨墨言情网、逸云书院
5、纵横中文网、纵横文学
6、网易云阅读
7、腾讯原创馆
8、豆瓣阅读、豆瓣读书
9、熊猫看书
10、哔哩哔哩
11、微博
12、Story Bird 、 Beetify
13、刺猬猫
14、站酷
15、链家
16、环球人力资源智库
17、老烤鸭
等等等等&lt;/p&gt;
&lt;p&gt;推荐指数：5颗:star:&lt;/p&gt;
</content:encoded></item><item><title>csdn屏蔽未登录用户的复制功能</title><link>https://h1s97x.github.io/posts/notes/notes-csdn-copy-protection/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-csdn-copy-protection/</guid><description>&lt;pre class=&quot;line-numbers code-pre&quot; data-title=&quot;user-select可屏蔽复制哦！&quot; lang=&quot;zh-Hans</description><pubDate>Thu, 16 Jul 2026 16:12:52 GMT</pubDate><content:encoded>&lt;h1&gt;文本复制&lt;/h1&gt;
&lt;h2&gt;一.屏蔽复制原理&lt;/h2&gt;
&lt;h3&gt;1.1 &amp;lt;code&amp;gt;CSS&amp;lt;/code&amp;gt; user-select&lt;/h3&gt;
&lt;p&gt;&amp;lt;pre class=&quot;line-numbers code-pre&quot; data-title=&quot;user-select可屏蔽复制哦！&quot; lang=&quot;zh-Hans-CN&quot; data-prismjs-copy=&quot;复制文本&quot; data-prismjs-copy-error=&quot;按Ctrl+C复制&quot; data-prismjs-copy-success=&quot;文本已复制！&quot;&amp;gt;&amp;lt;code class=&quot;language-css&quot;&amp;gt;.user-select-none {
user-select: none; /* 别看了，就是我屏蔽了你的复制功能。将none改为text即可恢复复制 */
}
&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&lt;/p&gt;
&lt;h2&gt;二、断点调试&lt;/h2&gt;
&lt;h3&gt;2.1 整理资源&lt;/h3&gt;
&lt;p&gt;&amp;lt;p&amp;gt;在开始前，我们需要先整理一下这个复制功能会涉及到哪些因素。&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;既然是复制，那就离不开快捷键&amp;lt;code&amp;gt;Ctrl+C&amp;lt;/code&amp;gt;。既然有&amp;lt;code&amp;gt;Ctrl&amp;lt;/code&amp;gt;键，那就可能会有&amp;lt;code&amp;gt;ctrlKey&amp;lt;/code&amp;gt;事件，还有可能会有键盘码&amp;lt;code&amp;gt;keyCode&amp;lt;/code&amp;gt;。除此之外，复制的话需要先选中内容，那就需要&amp;lt;code&amp;gt;document.selection&amp;lt;/code&amp;gt;和&amp;lt;code&amp;gt;window.getSelection()&amp;lt;/code&amp;gt;事件。还有那个一复制就出现的登录框。&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;事实上，他还用了&amp;lt;code&amp;gt;jquery&amp;lt;/code&amp;gt;中的&amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;事件。好尴尬呀，我竟然忘记了这个。&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;有了上述提到的这些资源后，我便开始了一个个定位分析。这个分析过程是非常复杂的，也走了不少弯路。&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;h3&gt;2.2为什么一复制就出现登录框？&lt;/h3&gt;
&lt;p&gt;&amp;lt;p&amp;gt;我原以为他是把&amp;lt;code&amp;gt;document.selection&amp;lt;/code&amp;gt;事件改写了，所以上来我就把目标定位到了关键字&amp;lt;code&amp;gt;document.selection&amp;lt;/code&amp;gt;上。&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;一堆断点下来，发现这个没什么用处。看来我的常识经验在这次判断中误导了我。&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;后面又挨个把上面提到的资源都测试了下，结果发现&amp;lt;strong&amp;gt;原来复制跟登录框有关&amp;lt;/strong&amp;gt;！相关代码见下方↓↓↓&amp;lt;/p&amp;gt;
&amp;lt;pre class=&quot;line-numbers code-pre&quot; data-title=&quot;n函数&quot; lang=&quot;zh-Hans-CN&quot; data-prismjs-copy=&quot;复制文本&quot; data-prismjs-copy-error=&quot;按Ctrl+C复制&quot; data-prismjs-copy-success=&quot;文本已复制！&quot;&amp;gt;&amp;lt;code class=&quot;language-js&quot;&amp;gt;var n = function() {
window.csdn.loginBox.show({
spm: &quot;1001.2101.3001.9440&quot;
})
};
$(&quot;#content_views&quot;).unbind(&quot;keydown&quot;).bind(&quot;keydown&quot;, function(e) {
if (e.ctrlKey &amp;amp;&amp;amp; 67 == e.keyCode)
return n(),
!1
}),
$(&quot;#content_views&quot;).unbind(&quot;copy&quot;).bind(&quot;copy&quot;, function(e) {
return n(),
!1
})
&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;p&amp;gt;执行copy事件时会触发登录框.png ↓↓↓&amp;lt;img data-original=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/csdn-copy-protect-01-20261014.png&quot; data-src=&quot;/img/2023-06-13/执行copy事件时会触发登录框.png&quot; class=&quot;img-bg lazy&quot; alt=&quot;执行copy事件时会触发登录框&quot; title=&quot;执行copy事件时会触发登录框&quot; /&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;注意这里有一个&amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;复制事件。他先用&amp;lt;code&amp;gt;unbind&amp;lt;/code&amp;gt;来解绑&amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;复制事件，紧接着又重新绑定了&amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;复制事件。重点是下面的一行代码。&amp;lt;/p&amp;gt;
&amp;lt;pre class=&quot;line-numbers code-pre&quot; data-title=&quot;关键函数n↓↓↓&quot; lang=&quot;zh-Hans-CN&quot; data-prismjs-copy=&quot;复制文本&quot; data-prismjs-copy-error=&quot;按Ctrl+C复制&quot; data-prismjs-copy-success=&quot;文本已复制！&quot;&amp;gt;&amp;lt;code class=&quot;language-js&quot;&amp;gt;return n(),
!1
&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;这行代码乍看起来可能有些不太习惯，我们用&amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;进行改写下吧。&amp;lt;/p&amp;gt;
&amp;lt;pre class=&quot;line-numbers code-pre&quot; data-title=&quot;用if来改写↓↓↓&quot; lang=&quot;zh-Hans-CN&quot; data-prismjs-copy=&quot;复制文本&quot; data-prismjs-copy-error=&quot;按Ctrl+C复制&quot; data-prismjs-copy-success=&quot;文本已复制！&quot;&amp;gt;&amp;lt;code class=&quot;language-js&quot;&amp;gt;if (n()) {
return true
} else {
return false
}
&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;由于函数&amp;lt;code&amp;gt;n()&amp;lt;/code&amp;gt;调用了登录弹窗显示方法且没有直接的返回值，所以默认返回&amp;lt;code&amp;gt;undefined&amp;lt;/code&amp;gt;。由于&amp;lt;code&amp;gt;undefined&amp;lt;/code&amp;gt;条件不成立，所以会直接进入&amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;条件中。也就相当于在执行&amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;复制事件时，直接返回了&amp;lt;code&amp;gt;return false&amp;lt;/code&amp;gt;。这就是为什么在复制后粘贴时无任何内容的根本原因！&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;h2&gt;三、破解之道&lt;/h2&gt;
&lt;h3&gt;3.1、法一：解绑copy事件&lt;/h3&gt;
&lt;p&gt;&amp;lt;p&amp;gt;打开控制台，粘贴解绑&amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;复制事件的代码即可复制文件内容。&amp;lt;/p&amp;gt;
&amp;lt;pre class=&quot;line-numbers code-pre&quot; data-title=&quot;解绑copy事件↓↓↓&quot; lang=&quot;zh-Hans-CN&quot; data-prismjs-copy=&quot;复制文本&quot; data-prismjs-copy-error=&quot;按Ctrl+C复制&quot; data-prismjs-copy-success=&quot;文本已复制！&quot;&amp;gt;&amp;lt;code class=&quot;language-js&quot;&amp;gt;$(&quot;#content_views&quot;).unbind(&quot;copy&quot;)
&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;然后我们就能轻松复制文章内的内容了。&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;需要注意的是，如果是代码区块，还需要使用1.1中的方法解除&amp;lt;code&amp;gt;CSS&amp;lt;/code&amp;gt;后才能复制哦~&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;h1&gt;代码块复制&lt;/h1&gt;
&lt;h2&gt;一、分析“登录后复制”按钮是如何工作的&lt;/h2&gt;
&lt;p&gt;&amp;lt;h2 class=&quot;article-title&quot;&amp;gt;一、分析“登录后复制”按钮是如何工作的&amp;lt;/h2&amp;gt;
&amp;lt;p&amp;gt;先上一张图，我们在未登录时，鼠标移入代码块时，在右上角会显示一个“登录后复制”的按钮。点击按钮，就会弹出登录框。配图：弹出登录框.png↓↓↓ &amp;lt;img data-original=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/csdn-copy-protect-03-20261014.png&quot; data-src=&quot;/img/2023-06-24/弹出登录框.png&quot; class=&quot;img-bg lazy&quot; alt=&quot;弹出登录框&quot; title=&quot;弹出登录框&quot; /&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;细心的看官会发现“登录后复制”对应的是一个类名为&amp;lt;code&amp;gt;.hljs-button&amp;lt;/code&amp;gt;的&amp;lt;code&amp;gt;div&amp;lt;/code&amp;gt;，而这个&amp;lt;code&amp;gt;dom&amp;lt;/code&amp;gt;元素上并未绑定点击事件。配图：登录后复制按钮上未绑定事件.png↓↓↓ &amp;lt;img data-original=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/csdn-copy-protect-02-20261014.png&quot; data-src=&quot;/img/2023-06-24/登录后复制按钮上未绑定事件.png&quot; class=&quot;img-bg lazy&quot; alt=&quot;登录后复制按钮上未绑定事件&quot; title=&quot;登录后复制按钮上未绑定事件&quot; /&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;这里补充一句，当我在测试此插件的时候，发现有的文章里面的代码块会跟本案例有些出入。但看官不用担心，因为逻辑是相通的，稍做修改即可破解复制权限的。如果实在搞不定，可以使用文末的插件哈。&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;那他是怎么实现一点击就出现登录框的呢？&amp;lt;/p&amp;gt;
&amp;lt;h3 class=&quot;article-title article-title__sub&quot;&amp;gt;1.1、事件委托&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;实际上此处用的是&amp;lt;strong&amp;gt;事件委托&amp;lt;/strong&amp;gt;来实现，昨天的那个&amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;也是一样的哈。即：把子元素的事件绑定到父元素上。&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;核心代码就是&amp;lt;code&amp;gt;onclick&amp;lt;/code&amp;gt;点击事件。&amp;lt;/p&amp;gt;
&amp;lt;pre class=&quot;line-numbers code-pre&quot; data-title=&quot;click事件&quot; lang=&quot;zh-Hans-CN&quot; data-prismjs-copy=&quot;复制文本&quot; data-prismjs-copy-error=&quot;按Ctrl+C复制&quot; data-prismjs-copy-success=&quot;文本已复制！&quot;&amp;gt;&amp;lt;code class=&quot;language-js&quot;&amp;gt;onclick=&quot;mdcp.signin(event)&quot;
&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;h3 class=&quot;article-title article-title__sub&quot;&amp;gt;1.2、登录后的变化&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;如果登录过后，此处就会变成“复制”两个字。&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;此时&amp;lt;code&amp;gt;onclick&amp;lt;/code&amp;gt;事件会发生变化。&amp;lt;/p&amp;gt;
&amp;lt;pre class=&quot;line-numbers code-pre&quot; data-title=&quot;登录后的click事件↓↓↓&quot; lang=&quot;zh-Hans-CN&quot; data-prismjs-copy=&quot;复制文本&quot; data-prismjs-copy-error=&quot;按Ctrl+C复制&quot; data-prismjs-copy-success=&quot;文本已复制！&quot;&amp;gt;&amp;lt;code class=&quot;language-js&quot;&amp;gt;onclick=&quot;mdcp.copyCode(event)&quot;
&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;所以说，我们只需要在未登录时让点击事件变成&amp;lt;code&amp;gt;copyCode&amp;lt;/code&amp;gt;即可。&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;a href=&quot;https://www.yilingsj.com/xwzj/2023-06-13/copy-prohibited.html&quot;&amp;gt;文本复制-参考文章&amp;lt;/a&amp;gt;
&amp;lt;a href=&quot;https://www.yilingsj.com/xwzj/2023-06-24/copy-csdn-code.html&quot;&amp;gt;代码块复制-参考文章&amp;lt;/a&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>一些困难和解决方案</title><link>https://h1s97x.github.io/posts/notes/notes-difficulties-and-solutions/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-difficulties-and-solutions/</guid><description>hexo d 提交失败 error Writing objects: 100% (178/178), 3.08 MiB 576.00 KiB/s, done. </description><pubDate>Thu, 16 Jul 2026 16:12:52 GMT</pubDate><content:encoded>&lt;h3&gt;Cannot push on GitHub suddently&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Writing objects: 100% (178/178), 3.08 MiB | 576.00 KiB/s, done.
Total 178 (delta 49), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (49/49), done.
remote: error: GH006: Protected branch update failed for refs/heads/master.
remote: error: Cannot force-push to this branch
To github.com:h1s97x/H1S97X.github.io.git
 ! [remote rejected] HEAD -&amp;gt; master (protected branch hook declined)
error: failed to push some refs to &apos;github.com:h1s97x/H1S97X.github.io.git&apos;
FATAL Something&apos;s wrong. Maybe you can find the solution here: https://hexo.io/docs/troubleshooting.HTML
Error: Spawn failed
    at ChildProcess.&amp;lt;anonymous&amp;gt; (D:\hexo\node_modules\hexo-util\lib\spawn.js:51:21)
    at ChildProcess.emit (node:events:513:28)
    at cp.emit (D:\hexo\node_modules\cross-spawn\lib\enoent.js:34:29)
    at ChildProcess._handle.onexit (node:internal/child_process:291:12)
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;solution
修改 github 项目仓库分支的 rule
Because I&apos;m admin/owner I was able to go and do the following and that helped me to resolve the problem.
Steps : --&amp;gt; Settings --&amp;gt;Branches --&amp;gt; Branch protection rules select your branch(such as master)
then edit it -&amp;gt; push Allow force pushes - Everyone
Permit force pushes for all users with push access.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://stackoverflow.com/questions/52589285/cannot-push-on-github-suddently&quot;&gt;stackoverflow——Cannot push on GitHub suddently&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;组策略编辑器&lt;/h3&gt;
&lt;p&gt;打开记事本输入下面的代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@echo off

pushd &quot;%~dp0&quot;

dir /b %systemroot%\Windows\servicing\Packages\Microsoft-Windows-GroupPolicy-ClientExtensions-Package~3*.mum &amp;gt;gp.txt

dir /b %systemroot%\servicing\Packages\Microsoft-Windows-GroupPolicy-ClientTools-Package~3*.mum &amp;gt;&amp;gt;gp.txt

for /f %%i in (&apos;findstr /i . gp.txt 2^&amp;gt;nul&apos;) do dism /online /norestart /add-package:&quot;%systemroot%\servicing\Packages\%%i&quot;

pause
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;选择另存为-&amp;gt;文件名为 gpedit.bat,保存类型为所有文件，编码格式换成 ANSI
完成后找到保存的 bat 文件，以管理员身份打开
成功
按 WIN R 键打开运行输入 gpedit.msc，按回车或是确定按键就可以打开组策略
&lt;a href=&quot;https://zhuanlan.zhihu.com/p/381084692&quot;&gt;找不到 gpedit.msc 文件怎么办？组策略编辑器添加方法&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;如何在 GitHub 上只下载一个文件或文件夹&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/578116206&quot;&gt;如何在 GitHub 上只下载一个文件或文件夹&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Excel 有几行固定不动，不会跟着窗口滑动如何处理&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://zhidao.baidu.com/question/591423170.HTML#:~:text=%E5%9C%A8A6%E5%8D%95%E5%85%83%E6%A0%BC%E4%B8%AD%E7%82%B9%E7%AA%97%E5%8F%A3%E2%80%94%E2%80%94%E5%86%BB%E7%BB%93%E7%AA%97%E6%A0%BC%EF%BC%8C%E5%B0%B1%E4%BC%9A%E5%B0%86%E5%89%8D5%E8%A1%8C%E5%86%BB%E7%BB%93%E5%9B%BA%E5%AE%9A%E3%80%82,%E5%A6%82%E6%9E%9C%E8%BF%98%E6%83%B3%E6%8A%8A%E7%AC%AC%E4%B8%80%E5%88%97%E4%B9%9F%E5%86%BB%E7%BB%93%EF%BC%8C%E5%B0%B1%E5%9C%A8B6%E4%B8%AD%E7%82%B9%E5%86%BB%E7%BB%93%E7%AA%97%E6%A0%BC%E3%80%82&quot;&gt;excel 表格怎么固定上面几行&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;CMD 对文件操作，文件名中带空格怎么办&lt;/h3&gt;
&lt;p&gt;把文件名用&quot;&quot; 引号引起来就可以了，例如&quot;c:\Program Files&quot;。&lt;/p&gt;
&lt;p&gt;空格的不同编码：
&lt;code&gt;%20&lt;/code&gt; 与 &lt;code&gt;&amp;amp;nbsp;&lt;/code&gt;
&lt;code&gt;%20&lt;/code&gt; : URI 编码
&lt;code&gt;&amp;amp;nbsp;&lt;/code&gt; &lt;code&gt;&amp;amp;#160;&lt;/code&gt; : HTML 编码 标准 ISO 字符集 非间断空格（non-breaking space）
&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/space.png，待你提供后重新引用 --&amp;gt;
&lt;a href=&quot;https://blog.csdn.net/sageyin/article/details/116003284&quot;&gt;JavaScript：HTML 中各种空格代码的区别&lt;/a&gt;
&lt;a href=&quot;https://zhuanlan.zhihu.com/p/137481275&quot;&gt;网址 URL 中特殊字符转义编码&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Visual Studio Code 中 Markdown 文件中的图片无法显示&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/givbw/article/details/124028553&quot;&gt;Visual Studio Code 中 Markdown 无法显示图片&lt;/a&gt;
&lt;a href=&quot;https://qastack.cn/programming/32563078/how-link-to-any-local-file-with-Markdown-syntax&quot;&gt;Markdown 语法链接到任何本地文件&lt;/a&gt;
（1）在 Visual Studio Code 编辑页面，按快捷键 Ctrl+Shift+P，搜索 Markdown 更改预览安全设置，选择允许不安全内容，即可显示预览网页图片。&lt;/p&gt;
&lt;p&gt;（2）本地图片显示预览，要注意.md 文件的路径和图片存放的路径，使用图片的相对路径即可显示预览。（注意路径不能有引号&quot; &quot;）&lt;/p&gt;
&lt;p&gt;其他的解决方案：https://www.cnblogs.com/wanshizidiao/p/11273297.HTML#:~:text=%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3Markdown%E7%AC%94%E8%AE%B0%E7%A7%BB%E5%8A%A8%E5%90%8E%EF%BC%8C%E5%9B%BE%E7%89%87%E5%A4%B1%E6%95%88%E7%9A%84%E9%97%AE%E9%A2%98%E3%80%82%201%201.%E4%BD%BF%E7%94%A8%E7%9B%B8%E5%AF%B9%E8%B7%AF%E5%BE%84%20-%E5%9D%911%202%202.%E5%B0%86%E5%9B%BE%E7%89%87%E8%BD%AC%E5%AD%98%E8%87%B3%E7%99%BE%E5%BA%A6%E7%BD%91%E7%9B%98%20-%E5%9D%912,3%203.%E5%B0%86.md%E6%96%87%E4%BB%B6%E4%B8%AD%E6%8F%92%E5%85%A5%E7%9A%84%E5%9B%BE%E7%89%87%E4%B8%8A%E4%BC%A0%E8%87%B3%E7%A0%81%E4%BA%91%2Fgithub%20-%20%E7%A0%81%E4%BA%91%E7%9A%84%E5%9D%91%20%28%E5%BE%85%E5%A1%AB%29%204%204.%E5%B0%86.md%E6%96%87%E4%BB%B6%E4%B8%AD%E6%8F%92%E5%85%A5%E7%9A%84%E5%9B%BE%E7%89%87%E4%BF%9D%E5%AD%98%E8%87%B3%E5%9B%BE%E5%BA%8A&lt;/p&gt;
&lt;h3&gt;微软自带中文输入法打字时有时出现类似于自动回车的问题&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://answers.microsoft.com/zh-hans/windows/forum/all/%E6%89%93%E5%AD%97%E6%97%B6%E6%9C%89%E6%97%B6/c26741da-7b36-4960-869e-b0d799493510&quot;&gt;打字时有时出现类似于自动回车的问题&lt;/a&gt;
&lt;a href=&quot;https://www.zhihu.com/question/491610548&quot;&gt;电脑打字自动回车怎么解决？&lt;/a&gt;
solution: 关闭兼容性后目前没有出现这个问题，使用一段时间后再更新进展。
&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/输入法.png，待你提供后重新引用 --&amp;gt;
系统设置 -&amp;gt; 时间和语言 -&amp;gt; 语言和区域 -&amp;gt; 微软拼音输入法 -&amp;gt; 常规 -&amp;gt; 兼容性 -&amp;gt; 关闭兼容性&lt;/p&gt;
&lt;h3&gt;启动项目时报错端口被占用&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://wenku.csdn.net/answer/mxyv6ro7uu#:~:text=Windows%E6%80%8E%E4%B9%88%E7%9C%8B%E7%AB%AF%E5%8F%A3%E5%8D%A0%E7%94%A8%E6%83%85%E5%86%B5%201%20%E6%89%93%E5%BC%80%E5%91%BD%E4%BB%A4%E6%8F%90%E7%A4%BA%E7%AC%A6%EF%BC%88%E6%88%96%20PowerShell%EF%BC%89%EF%BC%9A%E5%9C%A8%E5%BC%80%E5%A7%8B%E8%8F%9C%E5%8D%95%E4%B8%AD%E6%90%9C%E7%B4%A2%E2%80%9Ccmd%E2%80%9D%E6%88%96%E2%80%9CPowerShell%E2%80%9D%EF%BC%8C%E7%84%B6%E5%90%8E%E7%82%B9%E5%87%BB%E6%89%93%E5%BC%80%E3%80%82%202%20%E5%9C%A8%E5%91%BD%E4%BB%A4%E6%8F%90%E7%A4%BA%E7%AC%A6%E4%B8%AD%E8%BE%93%E5%85%A5%E4%BB%A5%E4%B8%8B%E5%91%BD%E4%BB%A4%EF%BC%9A%20netstat%20-ano,findstr%20%5BPID%5D%20%E5%B0%86%20%5BPID%5D%20%E6%9B%BF%E6%8D%A2%E4%B8%BA%E4%BD%A0%E6%83%B3%E8%A6%81%E6%9F%A5%E7%9C%8B%E7%9A%84%E8%BF%9B%E7%A8%8B%20ID%20%E5%8D%B3%E5%8F%AF%E3%80%82%20%E7%84%B6%E5%90%8E%E5%8F%AF%E4%BB%A5%E5%9C%A8%E8%BE%93%E5%87%BA%E7%BB%93%E6%9E%9C%E4%B8%AD%E6%9F%A5%E6%89%BE%E8%AF%A5%E8%BF%9B%E7%A8%8B%E5%8D%A0%E7%94%A8%E7%9A%84%E7%AB%AF%E5%8F%A3%E5%8F%B7%E3%80%82&quot;&gt;windows 查看端口占用&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;netstat -ano | findstr &amp;lt;端口号&amp;gt;
查看对应端口的占用进程情况,包括PID
  协议  本地地址          外部地址        状态           PID
  TCP    0.0.0.0:端口号             0.0.0.0:0              LISTENING       4

tasklist -ano | findstr &amp;lt;进程号&amp;gt;
查看对应进程的详细信息
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;资源监视器 -&amp;gt; 网络 -&amp;gt; 监听的端口&lt;/p&gt;
&lt;h3&gt;双击安装 exe 文件无响应&lt;/h3&gt;
&lt;p&gt;solution：1.关闭杀毒软件和安全套件（比如卡巴斯基），再次尝试安装。 2.清除注册表中的残留信息，再次尝试安装。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;360 浏览器无法安装，双击.exe 安装包文件无反应&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;删除本地保存配置文件后，在安全模式下删除 360 相关的注册表文件即可&lt;/p&gt;
&lt;p&gt;已解决&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;先检查是否WindowsIntaller服务被禁用，导致安装程序无法运行。
如果没有没禁用，点击其他任意 .exe扩展名的程序看是否可以运行。

如果也无法运行，
进入 C:\Windows\System32
找到 cmd.exe这个程序，扩展名.exe 改为 .com
然后右键点击，以管理员身份运行。
弹出窗口中输入以下命令,回车键执行。

assoc .exe=exefile
复制代码


执行命令后再尝试看是否能打开了

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/qk215796129/article/details/81909532&quot;&gt;win10 下解决谷歌浏览器点击安装包无反应&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;网易云音乐歌单外链无法生成&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/yuan2333/p/7244380.HTML#:~:text=%EF%BC%88%E4%B8%80%EF%BC%89%E8%87%AA%E5%B7%B1%E5%88%9B%E5%BB%BA%E7%9A%84%E6%AD%8C%E5%8D%95%E6%B2%A1%E6%9C%89%E7%94%9F%E6%88%90%E5%A4%96%E9%93%BE%E9%80%89%E9%A1%B9%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95%201%201.%E5%88%86%E4%BA%AB%E6%AD%8C%E5%8D%95%202%202.%E6%89%93%E5%BC%80%E5%88%86%E4%BA%AB%E7%95%8C%E9%9D%A2%E7%9A%84%E6%AD%8C%E5%8D%95%E9%93%BE%E6%8E%A5%203%203.%E6%89%93%E5%BC%80%E6%AD%8C%E5%8D%95%E5%8D%B3%E5%8F%AF%E7%9C%8B%E5%88%B0%E7%94%9F%E6%88%90%E5%A4%96%E9%93%BE%E7%9A%84%E7%82%B9%E5%87%BB%E9%93%BE%E6%8E%A5%20%EF%BC%88%E4%BA%8C%EF%BC%89%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%E6%AD%8C%E5%8D%95%E5%9B%A0%E4%B8%BA%E7%89%88%E6%9D%83%E5%8E%9F%E5%9B%A0%E6%97%A0%E6%B3%95%E7%94%9F%E6%88%90%E5%A4%96%E9%93%BE%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95,%2B%E5%8A%A0%E5%85%B3%E6%B3%A8%201%200%20%E5%8D%87%E7%BA%A7%E6%88%90%E4%B8%BA%E4%BC%9A%E5%91%98%20%C2%AB%20%E4%B8%8A%E4%B8%80%E7%AF%87%EF%BC%9A%20Webstorm%E5%B8%B8%E7%94%A8%E5%BF%AB%E6%8D%B7%E9%94%AE%E5%A4%87%E5%BF%98%20&quot;&gt;网易云音乐歌单生成外链播放器&lt;/a&gt;
依稀记得之前好像可以生成外链，但是现在居然连超链直接去除了，只能通过分享歌单的方式了。
因为有些歌的版权问题，所以外链有时候是无法生成的，因此就需要偷鸡。
自己创建的歌单没有生成外链选项的解决方法： 1.分享歌单 2.打开分享界面的歌单链接 3.打开歌单即可看到生成外链的点击链接
因为版权原因无法生成外链的解决方法：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;打开歌单页面，在“生成外链播放器”上右击，点击审查元素（检查）ctrl+shift+i；&lt;/li&gt;
&lt;li&gt;接着找到生成外链播放器这段文字直接双击复制前面的/outchain/0/3210514338/&lt;/li&gt;
&lt;li&gt;然后修改歌单链接示例：http://music.163.com/#/outchain/0/3210514338/&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;另一种解决方法：
&lt;a href=&quot;https://www.ottoli.cn/wordpress/anm&quot;&gt;APlayer &amp;amp; MetingJS 音乐播放器使用指南&lt;/a&gt;
&lt;a href=&quot;https://blog.csdn.net/Edviv/article/details/106867810&quot;&gt;Aplayer 搭配 Metingjs 音乐插件&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Windows 在管理员用户登录情况下仍然提示需要管理员权限&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/weixin_60526471/article/details/126105622&quot;&gt;windows10 下载软件下到一半显示需要授权怎么解决_无法下载需要授权-CSDN 博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;solution:
同时按【WIN 键+R 键】，打开 cmd 命令窗口，输入 gpedit.msc，打开组策略编辑器，
在本地组策略编辑器调整设置：找到计算机配置 → windows 管理 → 安全设置 → 本地策略 → 安全选项 → 用户控制：以管理员批准模式运行所有管理员 → 双击进入，设置为已禁用状态即可&lt;/p&gt;
&lt;h3&gt;电脑有时重启后连接网络但是无法使用&lt;/h3&gt;
&lt;p&gt;solution：目前遇到的情况大多是因为关机前未关闭 VPN，导致重启后无法连接网络，重启 VPN，再关掉即可。没有关闭代理就关机，重启电脑后无法直接联网，需要开启代理才可正常联网解决。&lt;/p&gt;
&lt;h3&gt;新异常 bolt.Close(): funlock error: The segment is already unlocked&lt;/h3&gt;
&lt;p&gt;使用 VPN 在 GitHub 上下载 Cloudflare 的源码时出现的问题，关闭代理后突然下不动，然后出现无法下载-需要权限，然后在未下载完成文件的情况下删除，未关闭代理重启后，就出现了这个问题。代理无法使用，报错 bolt.Close()。
关代理，删文件重启后恢复正常。
leah.ns.cloudflare.com&lt;/p&gt;
</content:encoded></item><item><title>HTTrack 简单使用</title><link>https://h1s97x.github.io/posts/notes/notes-httrack-introduction/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-httrack-introduction/</guid><description>HTTrack 是一个免费的（GPL，自由软件）和易于使用的离线浏览器工具。它可以爬取整站的网页，用于离线浏览，减少与目标系统交互。它可从 Internet 上</description><pubDate>Thu, 16 Jul 2026 16:12:52 GMT</pubDate><content:encoded>&lt;h1&gt;HTTrack 简单使用&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;本文作者：&lt;/strong&gt; 李钰璕&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;本文链接：&lt;/strong&gt; &lt;a href=&quot;https://leeyuxun.github.io/HTTrack%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8.html&quot;&gt;https://leeyuxun.github.io/HTTrack 简单使用.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;HTTrack&lt;/code&gt; 是一个免费的（GPL，自由软件）和易于使用的离线浏览器工具。它可以爬取整站的网页，用于离线浏览，减少与目标系统交互。它可从 &lt;code&gt;Internet&lt;/code&gt; 上下载万维网站点到本地目录，递归地构建所有目录，从服务器获取 &lt;code&gt;HTML&lt;/code&gt;、图像和其他文件到本地。&lt;code&gt;HTTrack&lt;/code&gt; 安排原始网站的相关链接结构。只需在浏览器中打开 “镜像” 网站的页面，即可从链接到链接浏览网站，就像在线查看网站一样。&lt;code&gt;HTTrack&lt;/code&gt; 也可以更新现有的镜像站点，并恢复中断的下载。&lt;code&gt;HTTrack&lt;/code&gt; 完全可配置，并具有集成的帮助系统。&lt;/p&gt;
&lt;h1&gt;&lt;code&gt;HTTrack&lt;/code&gt; 安装&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;root@kali:~# apt-get install httrack
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&lt;code&gt;HTTrack&lt;/code&gt; 交互模式使用步骤&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;创建目录存储复制网站&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@kali:~# mkdir mywebsite
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/httrack-intro-02-20261014.png&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/httrack-intro-02-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;启动 &lt;code&gt;HTTrack&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@kali:~# httrack
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/httrack-intro-01-20261014.png&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/httrack-intro-01-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;输入项目名称&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Enter project name : blog	//blog为项目名称
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/httrack-intro-05-20261014.png&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/httrack-intro-05-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;选择存储网站目录&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Base path (return=/root/websites/) :/root/mywebsite
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/httrack-intro-10-20261014.png&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/httrack-intro-10-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;输入网站的 URL&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Enter URLs (separated by commas or blank spaces) :www.baidu.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/httrack-intro-06-20261014.png&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/httrack-intro-06-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;选择操作 (此处选择 2)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Action:
(enter) 1	Mirror Web Site(s)		//直接镜像站点
        2	Mirror Web Site(s) with Wizard		//用向导完成镜像
        3	Just Get Files Indicated		//只get某种特定的文件
        4	Mirror ALL links in URLs (Multiple Mirror)//镜像在这个url下所有的链接
        5	Test Links In URLs (Bookmark Test)	//测试在这个url下的链接
        0	Quit	//退出
:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/httrack-intro-09-20261014.png&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/httrack-intro-09-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;指定是否在实施攻击时使用代理 (此处默认输入 none，不使用代理)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Proxy (return=none) :
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/httrack-intro-08-20261014.png&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/httrack-intro-08-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;定义字符，爬取特定类型的数据 (此处输入 &lt;code&gt;*&lt;/code&gt; 表示爬取全部类型数据)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;You can define wildcards, like: -*.gif +www.*.com/*.zip -*img_*.zip
Wildcards (return=none) :*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/httrack-intro-03-20261014.png&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/httrack-intro-03-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;设置更多选项，可以使用 &lt;code&gt;help&lt;/code&gt; 查看 (此处选择默认)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;You can define additional options, such as recurse level (-r&amp;lt;number&amp;gt;), separated by blank spaces
To see the option list, type help
Additional options (return=none) :
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/httrack-intro-04-20261014.png&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/httrack-intro-04-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;开始克隆网站&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/httrack-intro-11-20261014.png&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/httrack-intro-11-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;查看克隆的文件&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/httrack-intro-07-20261014.png&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/httrack-intro-07-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;备注：&lt;code&gt;httrack&lt;/code&gt; 只能用于克隆静态内容，并且无法完全复制网站上的动态内容、中间部分 (数据库) 等内容。&lt;/p&gt;
&lt;h1&gt;&lt;code&gt;HTTrack&lt;/code&gt; 一般模式使用步骤&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;语法&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;httrack &amp;lt;URLs&amp;gt; [-option] [+&amp;lt;URL_FILTER&amp;gt;] [-&amp;lt;URL_FILTER&amp;gt;] [+&amp;lt;mime:MIME_FILTER&amp;gt;] [-&amp;lt;mime:MIME_FILTER&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;选项如下：（* 为默认值）&lt;/p&gt;
&lt;h2&gt;一般选项&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;O  镜像路径/缓存和日志文件路径
-O 镜像路径[,缓存和日志文件路径] (--path &amp;lt;param&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;行为选项&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;w  *镜像网站 (--mirror)
W	镜像网站，半自动 (asks questions) (--mirror-wizard)
g	只获取文件（保存在当前目录中） (--get-files)
i	使用缓存继续中断的镜像 (--continue)
Y	镜像所有位于第一级页面的链接 (镜像链接) (--mirrorlinks)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;代理选项&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;P   代理使用 (-P proxy:port or -P user:pass@proxy:port) (--proxy &amp;lt;param&amp;gt;)
%f *使用ftp代理 (f0 don&apos;t use) (--httpproxy-ftp[=N])
%b  使用此本地主机名发出/发送请求 (-%b 主机名) (--bind &amp;lt;param&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;限制选项&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;rN	  将“镜像深度”设置为N (* r9999) (--depth[=N])
%eN   将“外部链接深度”设置为N (* %e0) (--ext-depth[=N])
mN    将非HTML文件的最大文件长度设置为N (--max-files[=N])
mN,N2 将非HTML文件的最大文件长度设置为N、将HTML文件的最大文件长度设置为N2
MN    将可上传/扫描的最大总长度设置为N (--max-size[=N])
EN    将最大镜像传输时间设置为N秒 (60=1 minute, 3600=1 hour) (--max-time[=N])
AN    将最大传输速率设置为N字节/秒 (1000=1KB/s max) (--max-rate[=N])
%cN   将最大连接数设置为N个/秒 (*%c10) (--connection-per-second[=N])
GN    设置如果达到N个字节，则暂停传输，并等待锁定文件被删除 (--max-pause[=N])
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;流量控制&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;cN  设置连接数为N (*c8) (--sockets[=N])
TN  设置关闭无响应链接N秒后超时 (--timeout)
RN  设置超时或非致命错误情况下的重试次数为N (*R1) (--retries[=N])
JN  流量阻塞控制，设置链路允许的最小传输速率为N字节/秒 (--min-rate[=N])
HN  如果：0=从不、1=超时、2=慢、3=超时或慢，则放弃主机 (--host-control[=N])
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;链接选项&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;%P        *扩展解析，尝试解析所有链接，即使是未知标记或Javascript标记 (%P0 don&apos;t use) (--extended-parsing[=N])
n          在html文件附近获取非html文件 (例如：位于外部的图像) (--near)
t          测试所有url（即使url被禁止） (--test)
%L &amp;lt;file&amp;gt;  添加位于此文本文件中的所有URL（每行一个URL）  (--list &amp;lt;param&amp;gt;)
%S &amp;lt;file&amp;gt;  添加位于此文本文件中的所有扫描规则（每行一个扫描规则） (--urllist &amp;lt;param&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;构建选项&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;NN	结构类型 (0 *原始结构, 1+: 详见下文) (--structure[=N])
    或用户定义的结构 (-N &quot;%h%p/%n%q.%t&quot;)
%N  延迟类型检查，不进行任何链接测试，而是等待文件下载开始（实验性的）(N0 不使用，%N1 用于未知扩展名，*%N2 始终使用)
%D  缓存的延迟类型检查，不要在更新期间等待远程类型，以加快更新速度 (%D0 等待, * %D1 不等待) (--cached-delayed-type-check)
%M  生成RFC MIME封装的完整存档 (.mht) (--mime-html)
LN	长名称 (L1 *长名称 / L0 8-3 转换 / L2 ISO9660 兼容) (--long-names[=N])
KN	保留原始链接 (e.g. http://www.adr/link) (K0 *相对链接, K 绝对链接, K4 原始链接, K3 绝对URI链接, K5 透明代理链接) (--keep-links[=N])
x	用错误页替换外部html链接 (--replace-external)
%x	不包括受外部密码保护的网站的任何密码 (%x0 包括) (--disable-passwords)
%q *包含本地文件的查询字符串 (无用,仅供参考) (%q0 不包含) (--include-query-string)
o  *出错时生成输出html文件 (404..) (o0 不生成) (--generate-errors)
X  *更新后清除旧文件 (X0 保持删除) (--purge-old[=N])
%p	保留html文件“原样” (与&apos;-K4 -%F &quot;&quot;&apos;相同) (--preserve)
%T	链接转换为UTF-8 (--utf8-conversion)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;蜘蛛选项&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;bN	接受cookies.txt中的cookies (0=不接受,* 1=接受) (--cookies[=N])
u	检查未知文档类型 (cgi,asp..) (u0 不检查, * u1 检查除了 /, u2 始终检查) (--check-type[=N])
j  *解析JAVA类 (j0 不解析, 位掩码: |1 解析默认值, |2 不解析.class文件 |4 不解析.js文件 |8 不越界解析) (--parse-java[=N])
sN	遵循robots.txt和meta robots标记 (0=never,1=sometimes,* 2=always, 3=always (even strict rules)) (--robots[=N])
%h  强制HTTP/1请求 (仅针对旧服务器或代理减少更新特性) (--http-10)
%k	尽可能使用keep alive，大大减少小文件和测试请求的延迟 (%k0 不使用) (--keep-alive)
%B  容忍请求 (在某些服务器上接受虚假响应，但不是标准的!) (--tolerant)
%s  更新非法入侵：在更新时限制重新传输的各种非法入侵(相同大小，虚假响应…) (--updatehack)
%u  url非法入侵：限制重复url的各种非法入侵 (strip //, www.foo.com==foo.com..) (--urlhack)
%A  假设一个类型(cgi,asp..)总是与一个mime类型链接 (-%A php3,cgi=text/html;dat,bin=application/x-zip) (--assume &amp;lt;param&amp;gt;)
     快捷方式：&apos;--assume standard&apos;相当于-%A php2 php3 php4 php cgi asp jsp pl cfm nsf=text/html
     也可以用于强制特定的文件类型：-assume foo.cgi=text/html
@iN	internet协议 (0=both ipv6+ipv4, 4=ipv4 only, 6=ipv6 only) (--protocol[=N])
%w  禁用特定的外部mime模块 (-%w htsswf -%w htsjava) (--disable-module &amp;lt;param&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;浏览器 ID&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;F   在HTTP头中发送的用户代理字段 (-F &quot;user-agent name&quot;) (--user-agent &amp;lt;param&amp;gt;)
%R  在HTTP头中发送的默认referer字段 (--referer &amp;lt;param&amp;gt;)
%E  从以HTTP头发送的电子邮件地址 (--from &amp;lt;param&amp;gt;)
%F  Html代码中的页脚字符串 (-%F &quot;Mirrored [from host %s [file %s [at %s]]]&quot; (--footer &amp;lt;param&amp;gt;)
%l  首选语言 (-%l &quot;fr, en, jp, *&quot; (--language &amp;lt;param&amp;gt;)
%a  接受格式 (-%a &quot;text/html,image/png;q=0.9,*/*;q=0.1&quot; (--accept &amp;lt;param&amp;gt;)
%X  附加的HTTP标题行 (-%X &quot;X-Magic: 42&quot; (--headers &amp;lt;param&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;日志、索引、缓存&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;C   创建/使用缓存进行更新和重试 (C0 无缓存,C1 缓存优先,* C2 测试更新前) (--cache[=N])
k   将所有文件存储在缓存中 (如果文件在磁盘上，则此功能不可用) (--store-all-in-cache)
%n  不能重新下载本地删除的文件 (--do-not-recatch)
%v  在屏幕上显示下载的文件名(实时) - * %v1 缩写版 - %v2 完整 (--display)
Q   无日志-安静模式  (--do-not-log)
q   无问题-安静模式 (--quiet)
z   日志-附加信息 (--extra-log)
Z   日志-debug (--debug-log)
v   登录屏幕 (--verbose)
f  *登录文件 (--file-log)
f2  一个单一日志文件(--single-log)
I  *编制索引 (I0 不编制索引) (--index)
%i  为项目文件夹创建顶级索引 (* %i0 不创建) (--build-top-index)
%I  为此镜像创建可搜索索引 (* %I0 不创建) (--search-index)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;专家选项&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;pN  优先模式: (* p3) (--priority[=N])
     p0 只扫描，不保存任何内容 (用于检查链接)
     p1 只保存html文件
     p2 只保存非html文件
    *p3 保存所有文件
     p7 先获取html文件，然后处理其他文件
S   保持在同一个目录 (--stay-on-same-dir)
D  *只能进入子目录 (--can-go-down)
U   只能进入父目录 (--can-go-up)
B   可以上下进入目录结构 (--can-go-up-and-down)
a  *保持在同一地址 (--stay-on-same-address)
d   保持在同一主域 (--stay-on-same-domain)
l   保持相同的TLD (eg: .com) (--stay-on-same-tld)
e   go everywhere on the web (--go-everywhere)
%H  在日志文件中调试HTTP头 (--debug-headers)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;权威选项&lt;/h2&gt;
&lt;p&gt;（尽量避免使用）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#X  *使用优化引擎 (有限内存边界检查) (--fast-engine)
#0   过滤测试 (-#0 &apos;*.gif&apos; &apos;www.bar.com/foo.gif&apos;) (--debug-testfilters &amp;lt;param&amp;gt;)
#1   简化测试 (-#1 ./foo/bar/../foobar)
#2   类型测试 (-#2 /foo/bar.php)
#C   换成列表 (-#C &apos;*.com/spider*.gif&apos; (--debug-cache &amp;lt;param&amp;gt;)
#R   缓存修复 (损坏缓存) (--repair-cache)
#d   调试分析器 (--debug-parsing)
#E   在meta.zip中提取new.zip缓存元数据
#f   总是刷新日志文件 (--advanced-flushlogs)
#FN  过滤器的最大过滤数目 (--advanced-maxfilters[=N])
#h   版本信息 (--version)
#K   扫描标准输入 (debug) (--debug-scanstdin)
#L   最大链接数 (-#L1000000) (--advanced-maxlinks[=N])
#p   显示进度信息 (--advanced-progressinfo)
#P   抓取URL (--catch-url)
#R   旧的FTP例程 (debug) (--repair-cache)
#T   每分钟生成一次传输操作日志 (--debug-xfrstats)
#u   等待时间 (--advanced-wait)
#Z   每分钟生成传输速率的静态数据 (--debug-ratestats)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;危险选项&lt;/h2&gt;
&lt;p&gt;（尽量避免使用）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;%!  绕过旨在避免带宽滥用的内置安全限制 (带宽，并发连接) (--disable-security-limits)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;危险选项，仅适用于专家；&lt;/p&gt;
&lt;p&gt;谨慎使用；&lt;/p&gt;
&lt;h2&gt;命令行特定选项&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;V   执行每个文件后的系统命令 ($0 is the filename: -V &quot;rm \$0&quot;) (--userdef-cmd &amp;lt;param&amp;gt;)
%W  使用外部库函数作为包装器 (-%W myfoo.so[,myparameters]) (--callback &amp;lt;param&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;选项 N 详情&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;N0 站点结构 (默认)
N1 HTML in web/, images/other files in web/images/
N2 HTML in web/HTML, images/other in web/images
N3 HTML in web/,  images/other in web/
N4 HTML in web/, images/other in web/xxx, where xxx is the file extension (all gif will be placed onto web/gif, for example)
N5 Images/other in web/xxx and HTML in web/HTML
N99 All files in web/, with random names (gadget !)
N100 站点结构，不包括www.domain.xxx/
N101 Identical to N1 exept that &quot;web&quot; is replaced by the site&apos;s name
N102 Identical to N2 exept that &quot;web&quot; is replaced by the site&apos;s name
N103 Identical to N3 exept that &quot;web&quot; is replaced by the site&apos;s name
N104 Identical to N4 exept that &quot;web&quot; is replaced by the site&apos;s name
N105 Identical to N5 exept that &quot;web&quot; is replaced by the site&apos;s name
N199 Identical to N99 exept that &quot;web&quot; is replaced by the site&apos;s name
N1001 Identical to N1 exept that there is no &quot;web&quot; directory
N1002 Identical to N2 exept that there is no &quot;web&quot; directory
N1003 Identical to N3 exept that there is no &quot;web&quot; directory (option set for g option)
N1004 Identical to N4 exept that there is no &quot;web&quot; directory
N1005 Identical to N5 exept that there is no &quot;web&quot; directory
N1099 Identical to N99 exept that there is no &quot;web&quot; directory
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;用户定义选项 N 详情&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&apos;%n&apos; 没有文件类型的文件名 (ex: image)
&apos;%N&apos; 文件名，包括文件类型 (ex: image.gif)
&apos;%t&apos; 文件类型 (ex: gif)
&apos;%p&apos; 路径 [无结尾 /] (ex: /someimages)
&apos;%h&apos; 主机名 (ex: www.someweb.com)
&apos;%M&apos; URL MD5 (128位，32个ascii字节)
&apos;%Q&apos; 查询字符串MD5 (128位，32个ascii字节)
&apos;%k&apos; 完整查询字符串
&apos;%r&apos; 协议名 (ex: http)
&apos;%q&apos; 短查询字符串 (16位，4个ascii字节)
	 &apos;%s?&apos; 短名称版本 (ex: %sN)
&apos;%[param]&apos; 查询字符串中的param变量
&apos;%[param:before:after:empty:notfound]&apos; 高级变量提取
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;用户定义的选项 N 和高级变量提取详情&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;%[param:before:after:empty:notfound]
param    参数名称
before   如果找到参数，则在其前面加上字符串
after    找到参数后要追加的字符串
notfound 如果找不到参数，则替换字符串
empty    如果参数为空，则替换字符串
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;除第一个字段（参数名）外，所有字段都可以为空&lt;/p&gt;
&lt;h2&gt;选项 K 详情&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;K0  foo.cgi?q=45  -&amp;gt;  foo4B54.html?q=45 (relative URI, default)
K                 -&amp;gt;  http://www.foobar.com/folder/foo.cgi?q=45 (absolute URL) (--keep-links[=N])
K3                -&amp;gt;  /folder/foo.cgi?q=45 (absolute URI)
K4                -&amp;gt;  foo.cgi?q=45 (original URL)
K5                -&amp;gt;  http://www.foobar.com/folder/foo4B54.html?q=45 (transparent proxy URL)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;快捷操作&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;--mirror      &amp;lt;URLs&amp;gt; *制作站点的镜像 (default)
--get         &amp;lt;URLs&amp;gt;  获取指定的文件，不要查找其他URL (-qg)
--list   &amp;lt;text file&amp;gt;  添加此文本文件中的所有URL (-%L)
--mirrorlinks &amp;lt;URLs&amp;gt;  镜像一级页面中的所有链接 (-Y)
--testlinks   &amp;lt;URLs&amp;gt;  测试页面中的链接 (-r1p0C0I0t)
--spider      &amp;lt;URLs&amp;gt;  爬取站点，用于测试链接:报告错误和警告 (-p0C0I0t)
--testsite    &amp;lt;URLs&amp;gt;  与--spider相同
--skeleton    &amp;lt;URLs&amp;gt;  创建镜像，但只获取html文件 (-p1)
--update              更新镜像，无需确认 (-iC2)
--continue            继续镜像，无需确认 (-iC1)

--catchurl            创建一个临时代理来捕获一个URL或一个表单post URL
--clean               删除缓存和日志文件

--http10              强制http/1.0请求 (-%h)
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;举例&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;只镜像站点 &lt;code&gt;www.someweb.com/bob/&lt;/code&gt;；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;httrack www.someweb.com/bob/
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;将两个站点镜像在一起（带有共享链接），并接受.com 站点上的任何.jpg 文件；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;httrack www.someweb.com/bob/ www.anothertest.com/mike/ +*.com/*.jpg -mime:application/*
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;从 bobby.html 开始获取所有文件，具有 6 个链接深度，并且可以在 web 上到处访问；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;httrack www.someweb.com/bob/bobby.html +* -r6
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用代理在 &lt;code&gt;www.someweb.com/bob/bobby.html&lt;/code&gt; 上运行爬虫；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;httrack www.someweb.com/bob/bobby.html --spider -P proxy.myhost.com:8080
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;更新当前文件夹中的镜像 ；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;httrack --update
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;进入交互模式；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;httrack
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在当前文件夹中继续镜像；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;httrack --continue
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;参考&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;http://www.httrack.com/html/fcguide.html&quot;&gt;Httrack Users Guide&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>程序员最爱的网站克隆爬取工具- HTTrack</title><link>https://h1s97x.github.io/posts/notes/notes-httrack-tool/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-httrack-tool/</guid><description>有一些专门针对网站克隆爬取的软件，如 WebZip、awwwb.com 等等，据说挺好用的。这里我给大家介绍一款程序员最爱的网站克隆爬取工具 - HTTrack</description><pubDate>Thu, 16 Jul 2026 16:12:52 GMT</pubDate><content:encoded>&lt;p&gt;有一些专门针对网站克隆爬取的软件，如 WebZip、awwwb.com 等等，据说挺好用的。这里我给大家介绍一款程序员最爱的网站克隆爬取工具 - HTTrack，而且是开源的。&lt;/p&gt;
&lt;h1&gt;什么是 HTTrack？&lt;/h1&gt;
&lt;p&gt;HTTrack 是一个免费并易于使用的线下浏览器工具，全称是 HTTrack site Copier for Windows，它能够让你从互联网上下载指定的网站进行线下浏览 (离线浏览)，也可以用来收集信息 (甚至有网站使用隐藏的密码文件)，一些仿真度极高的伪网站（为了骗取用户密码），也是使用类似工具做的。浏览线下站点和线上并没有什么不同。&lt;/p&gt;
&lt;p&gt;HTTrack 同样可以进行线下线上站点同步，支持断点续传。HTTrack 是一个可全面配置并包括全面的帮助系统的工具。对于传统的像存在 Robots.txt 的网站，如果程序运行的时候不做限制，在默认的环境下程序不会把网站镜像，简单来说 HTTPrack 跟随基本的 JavaScript 或者 APPLet、flash 中的链接，对于复杂的链接（使用函数和表达式创建的链接）或者服务器端的 ImageMap 则不能镜像。一般不用挖的太深就能获取目标信息比如网站的物理地址，电话号码，电子邮箱地址，运营时间，商业关系，员工的姓名，与社会关系，以及公开的一些花絮。做渗透测试时新闻其实也很重要，公司时常会公开一些自己感到骄傲的事情，这些报到中可能会泄露有用的信息，企业兼并服务器运转的情况等等。&lt;/p&gt;
&lt;p&gt;用 Httrack 可以将一个网站拷贝下来，以此进行下线的探测发现，以此减少对目标网站的直接交互。Httrack 的使用很简单，只需要根据其向导按步骤进行就好了。&lt;/p&gt;
&lt;h1&gt;安装&lt;/h1&gt;
&lt;p&gt;HTTrack 已经被预安装在以下 Linux 系统中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.backbox.org/&quot;&gt;BackBox Linux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.kali.org/&quot;&gt;Kali Linux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.pentoo.ch/&quot;&gt;Pentoo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://samurai.inguardians.com/&quot;&gt;SamuraiWTF&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://blackarch.org/&quot;&gt;BlackArch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如没有可以自行参考以下的安装方式进行下载安装。&lt;/p&gt;
&lt;h3&gt;HTTrack 的安装和使用&lt;/h3&gt;
&lt;p&gt;HTTrack 支持 Windows、Linux 和 macOS 等主流的操作系统，且针对 Windows 而言，HTTrack 有可视化界面的支持，效果如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/httrack-tool-01-20261014.png&quot; alt=&quot;程序员最爱的网站克隆爬取工具- HTTrack&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Windows 下安装:&lt;/h4&gt;
&lt;p&gt;通过 http://www.httrack.com/page/2/en/index.html，Download 下载对应的版本即可。&lt;/p&gt;
&lt;h4&gt;Linux 下安装:&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;# Debian/Ubuntu下安装
sudo apt install httrack
# CentOS/Fedora下安装
sudo yum install httrack
# Gentoo下安装
sudo emerge httrack
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Mac OSX 下安装:&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;sudo port install httrack
# 或者
brew install httrack
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;直接通过源码编译下安装:&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/xroche/httrack.git --recurse
cd httrack
./configure --prefix=$HOME/usr &amp;amp;&amp;amp; make -j8 &amp;amp;&amp;amp; make install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;具体参考：&lt;a href=&quot;http://www.httrack.com/page/2/en/index.html&quot;&gt;http://www.httrack.com/page/2/en/index.htm...&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;常用的参数选项可以通过 &lt;code&gt;httrack --help&lt;/code&gt; 查看。&lt;/p&gt;
&lt;h1&gt;爬取一个网站&lt;/h1&gt;
&lt;p&gt;测试站地址： https://****.com&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;xdl@xdl-virtual-machine:~/Downloads$ htt
httrack    httxt2dbm
xdl@xdl-virtual-machine:~/Downloads$ httrack

Welcome to HTTrack Website Copier (Offline Browser) 3.49-2
Copyright (C) 1998-2017 Xavier Roche and other contributors
To see the option list, enter a blank line or try httrack --help

Enter project name :jiayu

Base path (return=/home/xdl/websites/) :

Enter URLs (separated by commas or blank spaces) :https://localhost.com

Action:
(enter)    1    Mirror Web Site(s)
    2    Mirror Web Site(s) with Wizard
    3    Just Get Files Indicated
    4    Mirror ALL links in URLs (Multiple Mirror)
    5    Test Links In URLs (Bookmark Test)
    0    Quit
:

Proxy (return=none) :

You can define wildcards, like: -*.gif +www.*.com/*.zip -*img_*.zip
Wildcards (return=none) :

You can define additional options, such as recurse level (-r&amp;lt;number&amp;gt;), separated by blank spaces
To see the option list, type help
Additional options (return=none) :

---&amp;gt; Wizard command line: httrack https://localhost.com  -O &quot;/home/xdl/websites/jiayu&quot;  -%v

Ready to launch the mirror? (Y/n) :y

Mirror launched on Wed, 25 Mar 2020 13:29:38 by HTTrack Website Copier/3.49-2 [XR&amp;amp;CO&apos;2014]
mirroring https://localhost.com with the wizard help..
Done.
Thanks for using HTTrack!
*
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;参数解析&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# 1. 输入待生成的项目名称
Enter project name :progit
# 2. 输入待保存的项目所在的路径
Base path (return=/Users/apple/websites/) :/Users/apple/Desktop
# 3. 输入需要克隆的网站的 url
Enter URLs (separated by commas or blank spaces) :https://progit.bootcss.com/

Action:
(enter)    1    Mirror Web Site(s)
    2    Mirror Web Site(s) with Wizard
    3    Just Get Files Indicated
    4    Mirror ALL links in URLs (Multiple Mirror)
    5    Test Links In URLs (Bookmark Test)
    0    Quit
:
# 4. 没有特别要求直接回车即可
Proxy (return=none) :

You can define wildcards, like: -*.gif +www.*.com/*.zip -*img_*.zip
# 5. 没有特别要求直接回车即可
Wildcards (return=none) :

You can define additional options, such as recurse level (-r&amp;lt;number&amp;gt;), separated by blank spaces
To see the option list, type help
# 6. 没有特别要求直接回车即可
Additional options (return=none) :

---&amp;gt; Wizard command line: httrack https://progit.bootcss.com/  -O &quot;/Users/apple/Desktop/progit&quot;  -%v

Ready to launch the mirror? (Y/n) :Y

Mirror launched on Thu, 15 Aug 2019 11:54:40 by HTTrack Website Copier/3.49-2 [XR&amp;amp;CO&apos;2014]
mirroring https://progit.bootcss.com/ with the wizard help..
Done.
Thanks for using HTTrack!
*
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Kali Linux常用操作</title><link>https://h1s97x.github.io/posts/notes/notes-kali-linux-commands/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-kali-linux-commands/</guid><description>通过系统界面可以看到一个“file system”，这个就是文件系统，双击点开，会看到如下界面： 右侧就是该文件下的文件系统目录 !img 重点文件的说明，从上</description><pubDate>Thu, 16 Jul 2026 16:12:52 GMT</pubDate><content:encoded>&lt;h3&gt;1、Kali Linux 中的文件系统&lt;/h3&gt;
&lt;p&gt;通过系统界面可以看到一个“file system”，这个就是文件系统，双击点开，会看到如下界面：&lt;/p&gt;
&lt;p&gt;右侧就是该文件下的文件系统目录&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/kali-commands-01-20261014.webp&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;重点文件的说明，从上到下，从左到右：&lt;/p&gt;
&lt;p&gt;bin：二进制可执行文件的目录&lt;/p&gt;
&lt;p&gt;boot：用来存放 Linux 操作系统的内核和在引导过程中使用的文件&lt;/p&gt;
&lt;p&gt;dev：dev 是设备（device）的英文缩写，在这个目录中包含 Linux 操作系统中使用的所有外部设备。&lt;/p&gt;
&lt;p&gt;etc：配置文件存放的目录，如人员的用户名/密码文件、各种服务的起始文件等。&lt;/p&gt;
&lt;p&gt;home：系统默认的用户 home 目录，当新增账户时，用户的 home 目录都存放在该目录下&lt;/p&gt;
&lt;p&gt;lib：系统使用函数库的目录，程序在执行过程中，调用的一些额外参数时就需要函数库的协助，后面 32 或者 64 是根据不同系统调用来区分的&lt;/p&gt;
&lt;p&gt;media：挂载的媒体设备目录，一般讲外部设备挂载到这里，例如插入的 U 盘&lt;/p&gt;
&lt;p&gt;mnt：用于存放挂载存储设备的挂载目录，如磁盘等&lt;/p&gt;
&lt;p&gt;opt：该目录用来安装附加软件包，是用户级的程序目录&lt;/p&gt;
&lt;p&gt;proc：这是一种特殊的、由软件创建的文件系统，内核使用它向外界导出信息，只存在内存中，而不占用外存空间。&lt;/p&gt;
&lt;p&gt;root：root 用户的目录&lt;/p&gt;
&lt;p&gt;run：是临时文件系统，存储系统启动以来的信息。当系统重启时，这个目录下的文件被删掉或清除&lt;/p&gt;
&lt;p&gt;sbin：该目录用于存放 root 用户使用的可执行命令&lt;/p&gt;
&lt;p&gt;srv：服务启动之后需要访问的数据目录&lt;/p&gt;
&lt;p&gt;sys：该目录下是系统及内核重要的操作内容&lt;/p&gt;
&lt;p&gt;tmp：一般用户或正在执行的程序临时存放文件的目录，任何用户都可以访问，重要数据不可存放在该目录下&lt;/p&gt;
&lt;p&gt;usr：应用程序存放的目录，usr/bin 用于存放应用程序；usr/share 用于存放共享数据；usr/lib 用于存放不能直接运行的，却是许多应用程序运行所必需的一些函数库文件。&lt;/p&gt;
&lt;p&gt;var：该目录用于存放系统执行过程中经常变化的文件。&lt;/p&gt;
&lt;h3&gt;2、Kali Linux 常用命令&lt;/h3&gt;
&lt;h4&gt;2.1 与文件目录相关的命令（pwd、cd、ls）&lt;/h4&gt;
&lt;p&gt;•PWD 命令是 Print Working Directory（打印工作目录）的缩写，其功能是显示当前工作目录。&lt;/p&gt;
&lt;p&gt;•cd 是 Change Directory（更改目录）的缩写，其功能是将活动目录更改为指定的路径。&lt;/p&gt;
&lt;p&gt;•ls 命令用于显示指定工作目录下之内容（列出当前工作目录所含之文件及子目录)。&lt;/p&gt;
&lt;p&gt;•locate 命令用于查找符合条件的文件。&lt;/p&gt;
&lt;p&gt;•whereis 命令也用于查找文件。&lt;/p&gt;
&lt;p&gt;•which 命令会在环境变量$PATH 设置的目录里查找符合条件的文件。&lt;/p&gt;
&lt;p&gt;•mkdir 是 make directory（创建目录）的缩写，用于创建新的目录。&lt;/p&gt;
&lt;p&gt;•cp: 复制文件或目录，语法为“cp 源文件 目标文件”&lt;/p&gt;
&lt;p&gt;•rmdir 用来删除一个空的目录，rm 命令来删除非空目录。&lt;/p&gt;
&lt;h4&gt;2.2 文件查看命令（cat、head、tail、nl、grep）&lt;/h4&gt;
&lt;p&gt;cat 命令用来显示文件内容，语法为“cat 目录项”，cat 命令可以在屏幕上显示文件，可以合并文件，还可以建立文本文件。&lt;/p&gt;
&lt;p&gt;另外 head、tail、nl、more 和 less 命令也可以用来浏览文件。&lt;/p&gt;
&lt;p&gt;grep 命令用于查找文件里符合条件的字符串。&lt;/p&gt;
&lt;p&gt;more 命令类似 cat ，不过会以一页一页的形式显示，更方便使用者逐页阅读，使用者在阅读时可以按空格键（space）来显示下一页，按 b 键来返回（back）一页显示。&lt;/p&gt;
&lt;p&gt;less 与 more 类似，但使用 less 可以随意浏览文件，下面是使用 less 参数显示文件内容之后，使用者可以进行的一些操作。&lt;/p&gt;
&lt;h4&gt;2.3 网络相关命令（ifconfig、ip addr show 等）&lt;/h4&gt;
&lt;p&gt;Kali Linux 2 同时支持 net-tools 和 iproute2，所以可以使用这两种命令来完成对网络的配置和查看。如果要查看设备的网络连接信息，可以使用“ip addr”和“sudo ifconifg”&lt;/p&gt;
&lt;p&gt;ip 命令中常用的方法有：&lt;/p&gt;
&lt;p&gt;•ip addr show：显示网络信息；&lt;/p&gt;
&lt;p&gt;•ip route show：显示路由，如果直接用 route 命令，也可以查看到路由信息&lt;/p&gt;
&lt;p&gt;•ip neigh show：显示 arp 表，地址解析协议，即 ARP（Address Resolution Protocol），是根据 IP 地址获取物理地址的一个 TCP/IP 协议。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ifconfig：可以查看整个网络信息，例如 IP 地址，子网掩码等等&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;2.4 进程控制命令（ps）&lt;/h4&gt;
&lt;p&gt;进程指的是程序正在运行的一个实例。Kali Linux 2 中提供了对进程控制的命令。ps 命令用于显示当前进程 (process) 的状态 ps 的参数非常多, 在此仅列出几个常用的参数并介绍含义。&lt;/p&gt;
&lt;p&gt;-A 列出所有的进程&lt;/p&gt;
&lt;p&gt;-w 显示加宽可以显示较多的信息&lt;/p&gt;
&lt;p&gt;-au 显示较详细的信息&lt;/p&gt;
&lt;p&gt;-aux 显示所有（包含其他使用者）的进程&lt;/p&gt;
&lt;h4&gt;2.5 Kali 中的服务管理（service）&lt;/h4&gt;
&lt;p&gt;在 Linux 术语中，服务指的是在运行在后台等待使用的应用程序。Service 命令用于对系统服务进行管理，比如启动（start）、停止（stop）、重启（restart）、查看状态（status）等。在 Kali linux2 中预装了很多服务。在 Kali Linux 中的有些服务可以通过 GUI 停止和启动，就像在 Windows 或 Mac 里一样。但是，也有些服务需要使用命令行管理。我们在这里介绍管理基本服务的语法：&lt;/p&gt;
&lt;p&gt;service servicename start|stop|restart&lt;/p&gt;
&lt;h4&gt;2.6 Kali 中的 shell 脚本&lt;/h4&gt;
&lt;p&gt;Shell 脚本与 Windows/Dos 下的批处理相似，也就是用各类命令预先放入到一个文件中，方便一次性执行的一个程序文件，主要是方便进行设置或者管理用的。Shell 脚本的编写跟 JavaScript、php 编程一样，只要有一个能编写代码的文本编辑器和一个能解释执行的脚本解释器就可以了。&lt;/p&gt;
&lt;p&gt;那么打开一个编辑器，输入以下内容。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;\#! /bin/bash

echo &quot;Hello, Kali!&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将该文件保存为 HelloKali，不需要后缀名。&lt;/p&gt;
&lt;p&gt;当前我们还不能执行这个脚本，这是因为该脚本的权限导致的。这里可以使用 ls -l 命令来查看这个文件的权限。当前这个文件只有 w 和 r 权限，是不能执行的，这里需要为它添加一个可执行的权限。&lt;/p&gt;
&lt;p&gt;chmod 755 HelloKali&lt;/p&gt;
&lt;p&gt;再次使用 ls -l 查看，可以看到它具有了可执行权限。&lt;/p&gt;
&lt;p&gt;kali &amp;gt; ./HelloKali&lt;/p&gt;
&lt;p&gt;这里的./ 可以理解为在当前目录下查找文件。&lt;/p&gt;
&lt;p&gt;按下回车可以看到屏幕上输出：&lt;/p&gt;
&lt;p&gt;Hello, Kali!&lt;/p&gt;
&lt;p&gt;这表明当前程序已经成功执行。&lt;/p&gt;
&lt;p&gt;Nmap 脚本：找出子网 192.168.1.0/24 内所有开放了 445 端口的主机&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;\#! /bin/bash

nmap -sT 192.168.1.0/24 -p 445 &amp;gt;/dev/null -oG EternalBluescan
cat EternalBluescan | grep open &amp;gt; EternalBluescan2
cat EternalBluescan2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将该文件保存为 EternalBluescanner.sh，然后执行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;kali &amp;gt; ./EternalBluescanner.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Linux 命令：查看服务器 IP 地址&lt;/h3&gt;
&lt;p&gt;1.查看当前登录的服务器的 ip 地址：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 主机的 ip
hostname -i

hostname -I
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2.ifconfig&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 显示当前系统中所有网络接口的配置信息，包括IP地址、子网掩码和网关等。
ifconfig
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3.ip addr&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 列出当前系统中所有网络接口的详细信息，包括IP地址、子网掩码和网关等。
ip addr

ip a
# 或者
ip a | more

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4.nmcli 命令&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 显示网络管理器中所有网络设备的信息，包括IP地址、子网掩码和网关等
nmcli device show
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>KeePass 使用指南</title><link>https://h1s97x.github.io/posts/notes/notes-keepass-guide/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-keepass-guide/</guid><description>[TOC] KeePass 密码保险箱 优秀的密码管理器 keepass - 少数派 (sspai.com) 一劳永逸：KeePass 全网最详使用指南 - 知</description><pubDate>Thu, 16 Jul 2026 16:12:52 GMT</pubDate><content:encoded>&lt;p&gt;[TOC]&lt;/p&gt;
&lt;h2&gt;参考资料&lt;/h2&gt;
&lt;h3&gt;友链&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://keepass.info/index.html&quot;&gt;KeePass 密码保险箱&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sspai.com/post/44126#!&quot;&gt;优秀的密码管理器 keepass - 少数派 (sspai.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/39645975&quot;&gt;一劳永逸：KeePass 全网最详使用指南 - 知乎 (zhihu.com)&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;为什么用这个软件？标准答案当然是有业务需求，随着使用电脑的时间越来越长，各种需求也就不断出现，软件的兼容性、系统的兼容性、多设备之间的交互、文件共享都是急需解决的问题。&lt;/p&gt;
&lt;p&gt;需求：&lt;/p&gt;
&lt;p&gt;1.录入网站域名以及注册的账户、密码等，下次登录时能够自动填充。（Edge 自带的 Microsoft 密码管理器也能解决）&lt;/p&gt;
&lt;p&gt;2.一些软件能否自动填充？&lt;/p&gt;
&lt;p&gt;3.用户控制，查看保存的数据只需要用户口令。&lt;/p&gt;
&lt;p&gt;……&lt;/p&gt;
&lt;p&gt;回到本篇的问题上，为什么用这个软件？原因有三：&lt;/p&gt;
&lt;p&gt;其一，想想过去存储密码的方式：记事本文件，实在不是什么好的方案，只能用之一时，这种方案缺乏安全性，一旦泄露可就是灾难性的，明文泄露意味着什么，换句话说，底裤都被人家看穿了。还有版本控制的问题，一旦不小心文件崩溃或者删掉了重要的内容，就没法恢复，还有就是便利性，实在谈不上有多方便，就是查表嘛，每次还要自己记得去写，忘了写入之后都没地方查去。&lt;/p&gt;
&lt;p&gt;其二，正因为存储到文本文件的方案的种种问题，需要寻求一种新的解决方案，我之前也想过用数据库呗，但转念一想与第一种方案的差别不是很大，首先数据库还是存储在本地，免不了泄露的风险，便利性有所提升，但也不多，不能摆脱查表。那么有没有成熟的方案能够解决我的需求呢？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;keepass 虽然功能强大但使用相对复杂，而且官方帮助文档是全英文，对普通用户不友好；&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;KeePass&lt;/h2&gt;
&lt;h3&gt;介绍&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;为什么选择 KeePass？&lt;/strong&gt;
今天，您必须记住许多密码。您需要密码才能获得很多 网站、您的电子邮件帐户、您的网络服务器、网络登录等。 清单是无穷无尽的。 此外，您应该为每个帐户使用不同的密码，因为 如果您在任何地方只使用一个密码，并且有人获得了此密码， 你会遇到一个问题：小偷可以访问你的所有帐户。&lt;/p&gt;
&lt;p&gt;KeePass 是一个免费的开源密码管理器，它可以帮助您管理以安全的方式输入您的密码。您可以将所有密码存储在一个密码数据库，该数据库使用主密钥锁定。所以你只需要记住一个单个主密钥可解锁整个数据库。数据库文件已加密使用目前已知的最佳和最安全的加密算法 （AES-256、ChaCha20 和 Twofish）。 有关详细信息，请参阅&lt;a href=&quot;https://keepass.info/features.html&quot;&gt;功能&lt;/a&gt;页面。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;它真的免费吗？&lt;/strong&gt;
是的，KeePass 是真正免费的，不仅如此：它是开源的（OSI 认证）。 您可以查看其完整的源代码并检查安全性 功能已正确实现。&lt;/p&gt;
&lt;h4&gt;KeePass 特点&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;最直观的优势当然是&lt;strong&gt;完全免费&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;最实际的优势：今后基本可以告别手动输入账户密码。&lt;strong&gt;（在此建议将所有密码更换为 16 位随机强密码）&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;最强大的优势：这是一个开源软件，拥有众多优秀的第三方开源插件支持。即使将来有一天开发者不更新了也会有其他开发者接手。&lt;/li&gt;
&lt;li&gt;最重要也是最容易被忽略的优势：keepass 的加密方式和加密算法均处于同类软件的领先水平(至今未暴露出任何安全隐患) ，&lt;strong&gt;你的数据完全掌握在自己手中&lt;/strong&gt;，无需将任何敏感信息托付给第三方服务商。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;KeePass（Windows）篇&lt;/h3&gt;
&lt;h4&gt;中文语言包安装&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;安装并运行 keepass（安装时默认英文，无中文可选）；&lt;/li&gt;
&lt;li&gt;点击主界面中的【View】→【Change Language】；&lt;/li&gt;
&lt;li&gt;点击【Open Folder】打开 keepass 的语言安装文件夹；&lt;/li&gt;
&lt;li&gt;解压下载的中文语言 zip 包，将解压后的文件复制并粘贴到步骤 3 打开的文件夹中；&lt;/li&gt;
&lt;li&gt;重复步骤 2，选择【Chinese_Simplified】，点击弹出框中的【是】重启 keepass。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;插件安装&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;在 keepaass 主界面中点击【工具】→【插件管理器】→【打开文件夹】；&lt;/li&gt;
&lt;li&gt;将下载的后缀为.plgx 的文件复制并粘贴到步骤 1 打开的的文件夹中（zip 包请先解压）；&lt;/li&gt;
&lt;li&gt;关闭然后重新打开 keepass。&lt;/li&gt;
&lt;li&gt;（度盘下载的 chromeIPass 插件安装方法：点击 chrome 浏览器右上角的三个点 →【更多工具】→【扩展程序】，在打开标签页的右上角启用开发者模式 → 将下载的 crx 文件拖动到此标签页）&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;数据库同步和加密方式&lt;/h4&gt;
&lt;p&gt;如果你在使用 Windows10，建议将 keepass 数据库直接存储在 OneDrive 文件夹中。因为 OneDrive 已经集成到了 Windows 文件资源管理器中，而且 OneDrive 网页版可查看&lt;strong&gt;文件版本历史记录&lt;/strong&gt;和回收站，即使操作失误或误删文件也有补救的余地。&lt;/p&gt;
&lt;p&gt;在创建数据库时 keepass 提供了 3 种可任意组合的加密方式：【管理密码】【密钥文件/提供器】和【Windows 用户账户】，也就是说一共可以组合出 8 种不同的加密方式。追求更高安全性可以了解一下&lt;a href=&quot;https://link.zhihu.com/?target=https%3A//keepass.info/plugins.html%23otpkeyprov&quot;&gt;OtpKeyProv&lt;/a&gt;：每次打开数据库需要输入至少 3 个一次性的 6 位数安全码，但是必须要有一个 YubiKey 硬件或一台装有 Google 身份验证器的备用手机，而且这种加密方式的容错率和安全性有所冲突，本文篇幅所限不再赘述。所以综合安全性，易用性，跨平台等多方面因素，建议使用【管理密码】+【密钥文件/提供器】的加密方式。其中【密钥文件/提供器】可使用任意类型文件（图片，文档，音频，视频等），它的工作原理是使用 SHA-256 对密钥文件进行哈希处理并将生成的 32 个字节用作密钥，因此&lt;strong&gt;请妥善保存密钥文件，不要随意修改&lt;/strong&gt;（重命名无影响）。你可以使用一张家人的照片，一首喜欢的歌或一段不可描述的视频作为密钥文件，谁又能猜得到呢？（使用 100mb 以上的文件作为密钥文件会明显影响解锁速度）&lt;/p&gt;
&lt;p&gt;为使你的主密码更安全，请在 keepass 的**【选项】→【安全】中勾选【在安全桌面输入管理密码】**，因为几乎没有键盘记录软件能在安全桌面工作。&lt;/p&gt;
&lt;h4&gt;自动输入和双通道自动输入混淆&lt;/h4&gt;
&lt;p&gt;自动输入顾名思义就是用软件模拟按键以代替手动输入，需要注意的是如果目标应用程序使用管理员权限运行，那么 keepass 也必须使用管理员权限运行才能在此应用程序中使用自动输入。通俗点讲，当你打开一个程序弹出了【用户账户控制】警告（UAC），那么要在这个程序中使用自动输入必须右键点击桌面上的 keepass 图标，选择【以管理员身份运行】。&lt;strong&gt;自动输入全局热键默认为【Ctrl + Shift + A】&lt;/strong&gt;。使用方法非常简单：&lt;strong&gt;点击输入框 → 按下自动输入全局热键；或点击输入框 → 切换到 keepass 主界面 → 右键单击记录 →【执行自动输入】&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;双通道自动输入混淆不仅逼格高（具体效果如上面动图所示），而且非常强大。它的工作原理简洁明了：将要输入的字符随机分为两部分，然后使用模拟按键和复制粘贴两种方式混合输入，输入完成后再还原输入前的剪切板内容。这样一来，&lt;strong&gt;单一的键盘记录软件或剪切板监听软件都无法窃取到输入的完整字段&lt;/strong&gt;。由于某些软件的输入框没有移动光标或不支持粘贴操作，因此这个功能&lt;strong&gt;默认不启用&lt;/strong&gt;。必须在添加或编辑记录时点击【自动输入】，勾选【双通道自动输入混淆】。这无疑增加了普通用户的使用成本：为一条记录启用双通道自动输入混淆需要额外点击 3 次，对于有上百条密码的用户来说绝对是一场灾难。下文将给出&lt;strong&gt;一劳永逸&lt;/strong&gt;的解决方案。&lt;/p&gt;
&lt;p&gt;自动输入匹配规则：当按下自动输入全局热键时，keepass 会根据当前活动窗口的标题在数据库中搜索相匹配的记录；如果一条记录的标题或网址包含在活动窗口标题内那么这条记录将被匹配。举个例子：在 chrome 浏览器中打开淘宝网，当前活动窗口的标题就是“淘宝网 - 淘！我喜欢 - Google Chrome”。那么问题来了：标题为“Google”的记录也会被匹配到，而且在 chrome 中打开的任何标签页的标题都会包含“Google Chrome”字段。&lt;strong&gt;因此为防止误输密码，请务必在 keepass 的【工具】→【选项】→【高级】中勾选【总是显示全局自动输入记录选取对话框】&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;最后一点是自动输入的键入规则，默认规则为{USERNAME}{TAB}{PASSWORD}{ENTER}。解释：输入用户名，Tab 键（换行），输入密码，回车键。但这套规则明显不适合中文用户，因为&lt;strong&gt;使用自动输入时输入法必须是英文&lt;/strong&gt;，否则会出现很尴尬的场面。&lt;strong&gt;本文推荐使用以下规则&lt;/strong&gt;：+{DELAY 100}{CLEARFIELD}{USERNAME}{TAB}{PASSWORD}{ENTER}。解释：Shift 键（Windows10 输入法切换），等待 100 毫秒，清空输入框，输入用户名，Tab 键（换行），输入密码，回车键。由于新建记录默认从群组继承输入规则，所以只需修改一次即可，&lt;strong&gt;一劳永逸&lt;/strong&gt;，具体操作见下图。&lt;strong&gt;注：使用此规则自动输入时请确保输入法为中文&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;后续使用继续更新。&lt;/p&gt;
&lt;p&gt;目前没有太多时间。&lt;/p&gt;
</content:encoded></item><item><title>一些可供学习的个人博客或网站</title><link>https://h1s97x.github.io/posts/notes/notes-learning-blogs-websites/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-learning-blogs-websites/</guid><description>三叔的博客 居士亭 廖雪峰 咸鱼 IC 方知有 郁明敏 小林 coding (xiaolincoding.com) GeekLogic - 极客逻辑 (geek</description><pubDate>Thu, 16 Jul 2026 16:12:52 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://tuncle.blog/&quot;&gt;三叔的博客&lt;/a&gt;
&lt;a href=&quot;http://justin955.top/&quot;&gt;居士亭&lt;/a&gt;
&lt;a href=&quot;https://www.liaoxuefeng.com/&quot;&gt;廖雪峰&lt;/a&gt;
&lt;a href=&quot;https://www.cnblogs.com/xianyuIC&quot;&gt;咸鱼 IC&lt;/a&gt;
&lt;a href=&quot;https://www.cnblogs.com/fangzhiyou/&quot;&gt;方知有&lt;/a&gt;
&lt;a href=&quot;https://www.monsteryu.top/&quot;&gt;郁明敏&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://xiaolincoding.com/&quot;&gt;小林 coding (xiaolincoding.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://geek-logic.com/&quot;&gt;GeekLogic - 极客逻辑 (geek-logic.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://sort.wxyww.top/#/login/index&quot;&gt;同学做的网站&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/KxkquA3QaCv84Q7WqwozDQ&quot;&gt;知识见解全收录 2021 (qq.com)&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;工具类网站&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.aspose.app/&quot;&gt;跨平台文件格式应用 PDF，Word，Excel，PowerPoint，Visio，Project，OneNote，OpenOffice，CAD，3D 格式的在线文件处理&lt;/a&gt;
&lt;a href=&quot;https://time.is/zh/China&quot;&gt;中国时间&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!--  可供学习  --&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/609598175&quot;&gt;Z-library 稳定访问指南&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- 电子书籍下载 --&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://c.aalib.net/tool/chatgpt/&quot;&gt;ChatGPT 可用网址&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;意义不明&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/269523443&quot;&gt;聊聊 Python 调用 JS 的几种方式&lt;/a&gt;
&lt;a href=&quot;https://blog.csdn.net/qq_44721831/article/details/102597469?ydreferer=aHR0cHM6Ly9jbi5iaW5nLmNvbS8%3D?ydreferer=aHR0cHM6Ly9jbi5iaW5nLmNvbS8%3D?ydreferer=aHR0cHM6Ly9jbi5iaW5nLmNvbS8%3D?ydreferer=aHR0cHM6Ly9jbi5iaW5nLmNvbS8%3D?ydreferer=aHR0cHM6Ly9jbi5iaW5nLmNvbS8%3D&quot;&gt;Jupyter Notebook 中如何运行 JavaScript，安装配置（详解）&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>被动扫描</title><link>https://h1s97x.github.io/posts/notes/notes-passive-scan/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-passive-scan/</guid><description>被动扫描主要指的是在目标无法察觉的情况下进行的信息收集，比如我们如果想了解一个远在天边的人，你会怎么做呢？显然我们可以选择在搜索引擎去搜索这个名字。其实这就是一</description><pubDate>Thu, 16 Jul 2026 16:12:52 GMT</pubDate><content:encoded>&lt;h3&gt;什么是被动扫描?&lt;/h3&gt;
&lt;p&gt;被动扫描主要指的是在目标无法察觉的情况下进行的信息收集，比如我们如果想了解一个远在天边的人，你会怎么做呢？显然我们可以选择在搜索引擎去搜索这个名字。其实这就是一次对目标的被动扫描。最经典的被动扫描技术其实要数“Google hack”技术，但是这种技术在我们所处的大陆地区暂时无法使用。在这一章中我们来介绍 3 个极为优秀的信息收集工具：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Maltego&lt;/li&gt;
&lt;li&gt;sn0int&lt;/li&gt;
&lt;li&gt;ZoomEye&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;被动扫描的范围&lt;/h3&gt;
&lt;p&gt;那么信息的收集要从哪几方面来下手呢？以目标 a.test.com 为例，我们就可以想方设法来获取如下的一些信息：&lt;/p&gt;
&lt;p&gt;1）目标网站所有者的信息，例如姓名、地址、电话、电子邮件等。&lt;/p&gt;
&lt;p&gt;2）目标网站相关的电子邮箱。&lt;/p&gt;
&lt;p&gt;3）目标网站用户的社交信息，也就是该网站工作人员的微博、QQ、论坛发帖（这些都是国内渗透测试的标准，国外的话一般是推特、Youtube 之类）。&lt;/p&gt;
&lt;p&gt;4）a.test.com 指向网站的 DNS 信息，是否使用了 CDN（Content Delivery Network，内容分发网络）、网站应用级入侵防御系统（Web Application Firewall，WAF）等设备。&lt;/p&gt;
&lt;p&gt;5）test.com 的子域名信息。&lt;/p&gt;
&lt;h3&gt;Maltego&lt;/h3&gt;
&lt;p&gt;Maltego 是一款十分令人惊喜的信息收集软件。这款工具可以通过域名注册、搜索引擎、社交网络、电子邮件等各种渠道收集目标的信息。&lt;/p&gt;
&lt;p&gt;Kali Linux 2 中包含 Maltego，但是需要用户自行注册才能使用。&lt;/p&gt;
&lt;h4&gt;注册&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：因为 Maltego 是国外网站，注册信息填写后出现页面 404 不能直接访问。所以需要在火狐/谷歌浏览器上添加 redirector 插件，创建新的 director 就行了。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/u010784611/article/details/109740060&quot;&gt;kali 之 maltego 注册-CSDN 博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/u010784611/article/details/109732876&quot;&gt;注册 Maltego 显示 ReCaptcha is not valid，解决 Google 验证码服务 reCaptcha 失效问题_the recaptcha token provided is not valid or has e-CSDN 博客&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;犯了个蠢，注册后注意邮箱激活，不然就会出现下图的情况：（警告：你的账户未激活）&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;image-20231122164458195&quot; /&gt;&lt;/p&gt;
&lt;p&gt;启动方法：&lt;/p&gt;
&lt;p&gt;启动 Maltogo 的方法很简单，Kali Linux 2 中已经安装了 Maltego，只需要 Applications/01-Information Gathering/maltegoce 就可以打开这个工具:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;image-20231122163158810&quot; /&gt;&lt;/p&gt;
&lt;p&gt;打开的 Maltego 工作界面分成 3 个部分，最上方是菜单栏，这里面包含了所有的功能，左侧 Entity Palette 包含了所有的对象（例如设备、域名、IP 地址等），右侧是收集到的信息，如下图：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;image-20231122164810001&quot; /&gt;&lt;/p&gt;
&lt;p&gt;接下来进入实战：&lt;/p&gt;
&lt;p&gt;先点击左上角工具栏里的 New 创建一个 Graph。&lt;/p&gt;
&lt;p&gt;Maltego 的使用是自动化的，你只需要提供要进行调查的内容。例如这里我们要调查的是www.testfire.net这个域名，那么我们就可以在左侧的Entity Palette 列表的 Infarastructure 分类中选择 Domain，并拖动 Domain 到右侧的空白区中：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;image-20231122165335173&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在空白区将看到一个名为 maltego.com 的域对象，双击这个对象，就可以修改其内容。&lt;/p&gt;
&lt;p&gt;然后我们可以有多种调查的方式，这里面选择一种最为常用的方式，也就是 Maltego 提供的自动化信息收集，在选择了空白区的 maltego.com 之后，然后单击最左侧的 Run View 按钮，在切换出来的 Machines 菜单中提供了 Maltego 提供的几种比较经典的信息收集方式，这里最为常用的是 Footprint 系列，其中后面的数字越大，调查的深度也就越大，这里我们以其中的 Footprint L1 为例。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;image-20231122165101545&quot; /&gt;&lt;/p&gt;
&lt;p&gt;选中了 Footprint L1 之后，单击右侧的 run 按钮即可，收集到的结果如图（如果使用的是免费版本的话，那么节点数是有限制的）：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;image-20231122165523854&quot; /&gt;&lt;/p&gt;
&lt;p&gt;换个其他的域名试试：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;image-20231122165715200&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;sn0int&lt;/h3&gt;
&lt;p&gt;安装：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install debian-keyring
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Shodan 和 ZoomEye&lt;/h3&gt;
&lt;p&gt;我认为最好的工具就是 Shodan 和 ZoomEye，它们都是网络安全人员十分喜爱的搜索引擎。不同于 Google 的是，诸如 Baidu 等用于搜索网站页面的引擎，它们的目的是搜索网络上指定类型的设备。&lt;/p&gt;
&lt;p&gt;Shodan 和 ZoomEye 的使用方法都十分简单，它们都提供了图形化的操作界面，不过如果正确地使用这两个工具，我们还必须掌握“关键词”的用法。这个“关键词”的用法其实和 Google、Baidu 十分相似，只要你能正确地使用这些关键词，就可以快速在网络上找到那些你所需要的设备,比如轻松地找出位于非洲的一些没有设置口令的服务器。&lt;/p&gt;
&lt;p&gt;随着互联网的快速发展，连接到整个网络上的不再只有计算机，各种各样的设备都出现在了这个时代的大舞台上。路由器、交换机、电话系统、网络打印机、工业控制设备、嵌入式系统、安保设备等都可以通过互联网进行访问，一方面为使用者带来了极大的便利，但另一方面，这些设备都暴露在互联网上，也带来了极大的安全隐患。&lt;/p&gt;
&lt;p&gt;除了一些确实需要连入互联网的设备（例如网络摄像头）之外，我们经常会发现很多时候，用户并不是故意将设备连接到互联网上的。一些经验不够丰富的工作人员在对这些设备进行配置的时候，往往是在不经意间完成了到互联网的连接。后果更为严重的是，这些用户经常会使用系统默认的用户名和密码，甚至有些设备的密码为空。这种设备一旦被黑客发现，后果将不堪设想。&lt;/p&gt;
&lt;h4&gt;ZoomEye&lt;/h4&gt;
&lt;p&gt;https://www.zoomeye.org/&lt;/p&gt;
</content:encoded></item><item><title>Python与密码学</title><link>https://h1s97x.github.io/posts/notes/notes-python-cryptography-experiment/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-python-cryptography-experiment/</guid><description>图书名称: Python 密码学编程 ------------- -----------------------------------------------</description><pubDate>Thu, 16 Jul 2026 16:12:52 GMT</pubDate><content:encoded>&lt;h2&gt;参考资料&lt;/h2&gt;
&lt;h3&gt;参考书&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;图书名称:&lt;/th&gt;
&lt;th&gt;Python 密码学编程&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;封面图片:&lt;/td&gt;
&lt;td&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/python-crypto-03-20261014.jpg&quot; alt=&quot;Python密码学编程&quot; /&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;出版印刷时间:&lt;/td&gt;
&lt;td&gt;2021-6&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;出版社:&lt;/td&gt;
&lt;td&gt;清华大学出版社&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;图书作者:&lt;/td&gt;
&lt;td&gt;[美] 塞思·詹姆斯·尼尔森(Seth James Nielson) / 克里斯托弗·K.蒙森(Christopher K. Monson) 著&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;译者&lt;/td&gt;
&lt;td&gt;梁原&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ISBN:&lt;/td&gt;
&lt;td&gt;9787302576563&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;友链&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://cryptography.io/en/latest/&quot;&gt;cryptography&lt;/a&gt;
&lt;a href=&quot;https://www.pycryptodome.org/&quot;&gt;pycryptodome&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;课程概述&lt;/h2&gt;
&lt;p&gt;大三上学这门课，说实在有点迟，这门课讲的内容还是比较基础，所有密码学算法都是调库（现代密码的实现都很复杂，为了确保安全、可用要经过多项测试，所以当然是不推荐自己实现个简单的去用，我想表达的意思就是这门课没有接触到更底层的实现，所以难度不高），实验内容也无非就是画蛇添足，基本上是从书里给的练习题里选的。期末考试内容也是非常简单，代码都是考示例代码，死记硬背也罢，能理解然后写出来就行。&lt;/p&gt;
&lt;h2&gt;Chapter 0 密码学&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/344101417&quot;&gt;密码学概述 - 知乎 (zhihu.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;密码，最初的目的是用于对信息加密，计算机领域的密码技术种类繁多。但随着密码学的运用，密码还被用于身份认证、防止否认等功能上。密码是通信双方按约定的法则进行信息特殊变换的一种重要保密手段。依照这些法则，变明文为密文，称为加密变换；变密文为明文，称为脱密变换。密码在早期仅对文字或数码进行加、脱密变换，随着通信技术的发展，对语音、图像、数据等都可实施加、脱密变换。&lt;/p&gt;
&lt;p&gt;密码学是一门古老而深奥的学科，只在很小的范围内使用，如军事、外交、情报等部门。计算机密码学是研究计算机信息加密、解密及其变换 的科学，是数学和计算机的交叉学科 ，也是一门新兴的学科。密码学是网络空间安全主要研究方向之一，也是许多安全机制的基础。&lt;/p&gt;
&lt;p&gt;密码学有两个分支：1）&lt;strong&gt;密码编码学&lt;/strong&gt;：主要研究对信息进行变换，以保护信息在信道的过程中不被敌手窃取、解读和利用的方法。2）**密码分析学：**主要研究如何分析和破译密码，也称为 密码攻击。&lt;/p&gt;
&lt;p&gt;密码学与数学关系密切。数论中许多基本内容，如同余理论、中国剩余定理（CRT ）、高次剩余理论等，在新型密码体制、密钥分配与管理、数字签名、身份认证等方面有直接的应用。近代数学在现代密码研究中的应用包括群论，有限域上椭圆曲线理论，多项式理论与迹函数理论陷门单向函数等。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/python-crypto-05-20261014.webp&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;术语：&lt;/p&gt;
&lt;p&gt;1）消息（Message）：消息是指用语言、文字、数字、符号、图像、声音或其组合等方式记载或传递的有意义的内容。在密码学里，消息也称为信息 。&lt;/p&gt;
&lt;p&gt;2）明文（Plaintext）：未经过任何伪装或隐藏技术处理的消息称为明文。&lt;/p&gt;
&lt;p&gt;3）加密（Encryption）：利用某些方法或技术对明文进行伪装或隐藏的过程称为加密。&lt;/p&gt;
&lt;p&gt;4）密文（Cipher Text）：被加密的消息称为密文。&lt;/p&gt;
&lt;p&gt;5）解密（Decryption）：将密文恢复成原明文的过程或操作称为解密，解密也可称为脱密。&lt;/p&gt;
&lt;p&gt;6）加密算法（Encryption Algorithm）：将明文消息加密成密文所采用的一组规则或数学函数。&lt;/p&gt;
&lt;p&gt;7）解密算法（Decryption Algorithm ）：将密文消息解密成明文所采用的一组规则或数学函数。&lt;/p&gt;
&lt;p&gt;8）密钥（Key）：进行加密或解密操作所需要的秘密参数或关键信息 。在密码系统中，密钥分为私钥与公钥两种。私钥指必须保密的密钥，公钥指可以向外界公开的密钥。&lt;/p&gt;
&lt;p&gt;9）密码系统 （Cryptosystem ）：一个密码体制或密码系统是指由明文空间、密文空间、密钥空间、加密算法以及解密算法 组成的一个多元素集合体。&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/cryptography/密码体制.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;h3&gt;密码的分类&lt;/h3&gt;
&lt;p&gt;最基本的密码分类是信息加解密分为&lt;strong&gt;对称加密&lt;/strong&gt;（Sysmmetric Cryptography）和&lt;strong&gt;非对称加密&lt;/strong&gt;（Public-Key Cryptography，Asymmetric Cryptography），这两者的区别是是否使用了相同的密钥。除了信息的加解密，还有用于确认数据完整性（Integrity）的单向散列（One-Way Hash Function）技术，又称密码检验（Cryptographic Checksum）、指纹 （Fingerprint）、消息摘要 （Message Digest）。&lt;/p&gt;
&lt;p&gt;信息的加解密与信息的单向散列的区别是，对称与非对称加密是可以通过密钥解出明文，而单向散列是不可逆的。信息的加解密，密文必定是不定长的，而单向散列可以是定长的。结合密码学的加解密技术和单向散列技术，又有了用于防止篡改的消息认证码技术，防止伪装的数字签名技术以及认证证书。&lt;/p&gt;
&lt;p&gt;网络安全的核心概念是 CIA 三元组，分别是 Confidentiality（机密性）、Integrity（完整性）、Availability（可用性）。机密性是指保证信息不能被非授权访问，即使非授权用户得到信息也无法知晓信息内容，因而不能使用。完整性是指维护信息的一致性，即信息在生成、传输、存储和使用过程中不应发生人为或非人为的非授权篡改。可用性是指保障信息资源随时可提供服务的能力特性，即授权用户根据需要可以随时访问所需信息。&lt;/p&gt;
&lt;p&gt;网络中有很多旨在窃取密码的攻击方式，他们破坏了上述的网络安全特性。&lt;/p&gt;
&lt;h3&gt;密码学的特性及功能&lt;/h3&gt;
&lt;p&gt;特性：&lt;/p&gt;
&lt;p&gt;机密性(Confidentiality)：非授权者无法获取明文内容，只有授权方才能阅读受保护的信息。&lt;/p&gt;
&lt;p&gt;完整性(Integrity)：接收者能够验证消息是否在传输过程中被增加、删除、修改。&lt;/p&gt;
&lt;p&gt;真实性(Authenticity)：接收者能够确认消息来源的真实性。&lt;/p&gt;
&lt;p&gt;不可抵赖性(Nonrepudiation)：发送者无法否认其签发的消息。&lt;/p&gt;
&lt;p&gt;功能：&lt;/p&gt;
&lt;p&gt;数据加密：机密性（对称密钥）&lt;/p&gt;
&lt;p&gt;消息鉴别：完整性和真实性（杂凑函数 HMAC）&lt;/p&gt;
&lt;p&gt;身份鉴别：真实性（公钥密码）&lt;/p&gt;
&lt;p&gt;数字签名：不可抵赖性（公钥密码）&lt;/p&gt;
&lt;h3&gt;本章考点&lt;/h3&gt;
&lt;p&gt;1.密码学的特性和功能&lt;/p&gt;
&lt;p&gt;2023 真题：&lt;/p&gt;
&lt;p&gt;第一题：密码学有哪些特性，分别用什么机制实现？&lt;/p&gt;
&lt;h2&gt;Chapter 1 古典密码学编程&lt;/h2&gt;
&lt;h3&gt;古典密码的概念&lt;/h3&gt;
&lt;p&gt;古典密码是密码学中的其中一个类型，其大部分加密方式都是利用【替换式密码】或【移项式密码】，有时则是两者的混合。&lt;/p&gt;
&lt;p&gt;在历史中经常使用，19 世纪末随着工业革命，科技水平不断提高，电报的发明、机械制造工艺等的产生使得手工密码已不再受欢迎，近代密码随之替代了古典密码，现代已经很少使用。&lt;/p&gt;
&lt;p&gt;一般而言，经典密码是基于一个拼音字母（像是 A-Z）、动手操作或是简单的设备。它们可能是一种简单的密码法，以致于不可信赖的地步，特别是有新技术被发展出来后。&lt;/p&gt;
&lt;h3&gt;分类&lt;/h3&gt;
&lt;p&gt;古典密码主要通过字符间的置换和代（替）换实现。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;◎ 代换密码(substitution)&lt;/p&gt;
&lt;p&gt;代换是古典密码中用到的最基本的处理技巧之一。所谓代换，就是将明文中的一个字母由其它字母、数字或符号替代的一种方法。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;◎ 置换密码(permutation)&lt;/p&gt;
&lt;p&gt;是古典密码中用到的另一个最基本的处理技巧。将明文字符按照某种规律重新排列而形成密文的过程。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;代换密码&lt;/h4&gt;
&lt;p&gt;➢ 凯撒密码&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/cryptography/Caesar-crypto.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;➢ 仿射密码&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/cryptography/仿射密码.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;➢ 单表代换&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/cryptography/单表代换.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/cryptography/单表代换密码-example.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;➢ 多表代换&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/cryptography/多表代换.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;h4&gt;置换密码（又称为换位密码）&lt;/h4&gt;
&lt;p&gt;特点:保持明文中所有的字符不变，只是利用置换打乱明文字符的位置和次序&lt;/p&gt;
&lt;p&gt;置换定义：有限集 X 上的运算 σ：X→X，σ 是一个双射函数，那么称 σ 为一个置换。
即任意 x∈X,存在唯一的 x’∈X，使得 σ(x)=x’
解密的时候会用到逆置换 σ’,
即任意 x’∈X,存在唯一的 x∈X，使得 σ’(x’)=x 且·满足 σσ’=I&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/cryptography/置换密码.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/cryptography/置换密码-example.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;h3&gt;古典密码的唯密文攻击（Ciphtext Only Attack，COA）&lt;/h3&gt;
&lt;p&gt;定义：唯密文攻击(COA)是指仅仅知道密文的情况下进行分析，求解明文或密钥的密码分析方法。&lt;/p&gt;
&lt;p&gt;假定密码分析者拥有密码算法及明文统计特性，并截获了一个或者多个用同一密钥加密的密文，通过对这些密文进行分析求出明文或密钥。COA 已知条件最少，经不起唯密文攻击的密码是被认为不安全的。&lt;/p&gt;
&lt;p&gt;简单理解：只知道密文，推出明文或密钥，一般用穷举攻击。&lt;/p&gt;
&lt;p&gt;方法：
频率分析解密法。&lt;/p&gt;
&lt;p&gt;这难不倒解密者，以英文字母为例，为了确定每个英文字母的出现频率，分析一篇或者数篇普通的英文文章，英文字母出现频率最高的是 e，接下来是 t，然后是 a……，然后检查要破解的密文，也将每个字母出现的频率整理出来，假设密文中出现频率最高的字母是 j，那么就可能是 e 的替身，如果密码文中出现频率次高的但是 P，那么可能是 t 的替身，以此类推便就能解开加密信息的内容。这就是频率分析法。&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/cryptography/COA-频率分析.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将明文字母的出现频率与密文字母的频率相比较的过程&lt;/li&gt;
&lt;li&gt;通过分析每个符号出现的频率而轻易地破译代换式密码&lt;/li&gt;
&lt;li&gt;在每种语言中，冗长的文章中的字母表现出一种可对之进行分辨的频率。&lt;/li&gt;
&lt;li&gt;e 是英语中最常用的字母，其出现频率为八分之一&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;本章考点&lt;/h3&gt;
&lt;p&gt;1.古典密码的分类&lt;/p&gt;
&lt;p&gt;2.Caesar 密码/仿射密码 Python 程序实现。&lt;/p&gt;
&lt;p&gt;2023 真题：&lt;/p&gt;
&lt;p&gt;第二题：古典密码有哪些类型？请用 Python 实现 Caesar 密码的加密过程。&lt;/p&gt;
&lt;h2&gt;Chapter 2 哈希&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.jianshu.com/p/fa57d21b9ad7&quot;&gt;《一切皆是映射：代码的本质》哈希算法 （Hash） - 简书 (jianshu.com)&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;TOC&lt;/h3&gt;
&lt;p&gt;1.Hash 基础知识：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;哈希函数的定义&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;哈希函数的性质&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;常用的哈希算法：MD 家族、SHA 系列&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;哈希函数的构造原理：消息填充机制、压缩函数&lt;/p&gt;
&lt;p&gt;2.Hash 编程实现：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用 python 的内置库 hashlib 模块&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;hashlib 的 md5(), sha256()……生成实现对应哈希算法的对象，&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;调用哈希算法对象的 update，digest，hexdigest&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;口令加盐：scrypt，bcrypt……&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;哈希在区块链中的应用，工作量证明的示例&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;哈希函数&lt;/h3&gt;
&lt;p&gt;哈希函数（Hash）：将一种任意长度的输入变换为固定长度输出的不可逆的密码体制。&lt;/p&gt;
&lt;p&gt;别名：哈希函数、散列函数、杂凑函数；输出：消息摘要、指纹；&lt;/p&gt;
&lt;p&gt;性质：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;可用于“任意”长度的消息&lt;/li&gt;
&lt;li&gt;产生定长的输出&lt;/li&gt;
&lt;li&gt;对“任意”给定的消息 𝑥 ，计算 H(𝑥)比较容易&lt;/li&gt;
&lt;li&gt;单向性：又称为抗原像性，对任意给定的散列值 h，找到满足 H(𝑥)=h 的消息 𝑥 在计算上是不可行的。&lt;/li&gt;
&lt;li&gt;抗弱碰撞性：又称为抗第二原像性，对任何给定的消息 𝑥 ，找到满足 y≠𝑥，且 H(𝑥)=H(𝑦) 的消息 y 在计算上是不可行的。&lt;/li&gt;
&lt;li&gt;抗强碰撞性：找到任何满足 H(𝑥)=H(𝑦)的偶对(𝑥,𝑦)在计算上是不可行的。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;用途：用于保证消息完整—消息完整性(加密不意味着消息不能被篡改)，口令加密、区块链、数字签名&lt;/p&gt;
&lt;h4&gt;FAQ&lt;/h4&gt;
&lt;p&gt;Q1:弱抗碰撞性和强抗碰撞性的区别是什么?&lt;/p&gt;
&lt;p&gt;A1:弱抗碰撞性是任意给定分组 x, 寻求不等于 x 的 x’, 使得 H(x)= H(x’)在计算上不可行;强抗碰撞性是寻求任何的(x,x’)对，使得 H(x)=H(x’)在计算上不可行; 前者的 x 是给定的，而后者是不给定的，因而强抗碰撞性的要求要比弱抗碰撞性的要求的要高。&lt;/p&gt;
&lt;p&gt;Q2:Hash 函数中的压缩函数的作用是什么?&lt;/p&gt;
&lt;p&gt;A2:压缩函数将一个较长的，固定长度的输入处理后，返回一个较短的，固定长度的输出。Hash 函数是一种基于压缩函数的不断重复压缩输入的分组和前次压缩处理的结果，知道整个消息都被压缩完毕的过程，最后的输出作为整个消息的 hash 值。在这个过程中，任意长度的消息被破碎成一定长度的分组，分组的长度取决于压缩函数。并且出于安全的角度，对未填充满的分组作相应的数据填充&lt;/p&gt;
&lt;h4&gt;生日悖论&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/sinat_31854967/article/details/102947156&quot;&gt;生日悖论与哈希函数_密码学中的生日悖论-CSDN 博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/cryptography/生日攻击.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;h5&gt;哈希算法族&lt;/h5&gt;
&lt;p&gt;MD 家族&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/cryptography/hash-md族.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;SHA 系列&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/cryptography/hash-SHA系列.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;h3&gt;使用 hashlib 自由哈希&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.python.org/zh-cn/3.7/library/hashlib.html&quot;&gt;hashlib --- 安全哈希与消息摘要 — Python 3.7.13 文档&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;hashlib 模块是 python 内置的 hash 算法模块，提供了常见的哈希算法，如 MD5、SHA1、SHA256 等。&lt;/p&gt;
&lt;p&gt;使用 hashlib 模块可以方便地计算字符串或文件的哈希值，常用于密码加密、数据完整性校验等场景。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# hashlib
import hashlib
md5hasher= hashlib.md5()   # 构造一个hashlib的对象
md5hasher.update(b&quot;message&quot;)  # update文档内容，生成新摘要
md5hasher.hexdigest() # 获取摘要哈希值（16进制）
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import hashlib

# 创建一个md5对象
m = hashlib.md5()
# 创建一个sha1对象
s = hashlib.sha1()
# 创建一个sha256对象
s256 = hashlib.sha256()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;代码解释&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;字符串前加 b&lt;/p&gt;
&lt;p&gt;例：b&quot;message&quot;&lt;/p&gt;
&lt;p&gt;作用：b&quot; &quot;前缀表示：强制字符串的字面值是 bytes 类型。&lt;/p&gt;
&lt;p&gt;用处：&lt;/p&gt;
&lt;p&gt;Python 区分 Unicode 字符串和原始字节字符串，对于几乎所有的密码使用场合，都必须使用字节。&lt;/p&gt;
&lt;p&gt;附：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.runoob.com/python3/python3-type-conversion.html&quot;&gt;Python3 数据类型转换 | 菜鸟教程 (runoob.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/qq_35290785/article/details/90634344&quot;&gt;python 中 r&apos;&apos;, b&apos;&apos;, u&apos;&apos;, f&apos;&apos; 的含义_python f-CSDN 博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;在 Python3 中，bytes 和 str 的互相转换方式是&lt;/p&gt;
&lt;p&gt;str.encode(&apos;utf-8&apos;)
bytes.decode(&apos;utf-8&apos;)&lt;/p&gt;
&lt;h3&gt;哈希-雪崩效应&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.wolfram.com/language/12/cryptography/demonstrate-the-avalanche-effect-of-a-hash-function.html.zh&quot;&gt;展示散列函数的雪崩效应: Wolfram 语言 12 的新功能&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;散列函数在现代密码学中有重要作用。好的散列函数的一个重要且理想的特征是输入和输出没有相关性，或称为“雪崩效应”，这意味着输入的微小变化会导致输出发生显著变化，使其统计上看起来与随机变化没有差别。&lt;/p&gt;
&lt;p&gt;雪崩效应有助于防止碰撞：输入的小的更改会导致摘要中不可预测的大更改。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; hexstring = hashlib.md5(b&apos;bob&apos;).hexdigest()

&amp;gt;&amp;gt;&amp;gt; hexstring

&apos;9f9d51bc70ef21ca5c14f307980a29d8&apos;

&amp;gt;&amp;gt;&amp;gt; binstring = bin(int(hexstring, 16))

&amp;gt;&amp;gt;&amp;gt; print(&quot;{}\n{}&quot;.format(binstring[2:66], binstring[66:]))

1001111110011101010100011011110001110000111011110010000111001010

0101110000010100111100110000011110011000000010100010100111011000

bob: 9f9d51bc70ef21ca5c14f307980a29d8

cob: 386685f06beecb9f35db2e22da429ec9
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;展示哈希雪崩效应：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/qq_63016521/article/details/133610815&quot;&gt;哈希函数的雪崩效应-CSDN 博客&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;哈希函数构造&lt;/h3&gt;
&lt;p&gt;l 哈希函数的构造原理：消息填充机制、压缩函数&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/cryptography/hash函数构造.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;h5&gt;压缩函数&lt;/h5&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/cryptography/hash函数构造-压缩函数.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;h5&gt;SHA- -256 消息填充&lt;/h5&gt;
&lt;p&gt;首先将比特“1”添加到消息的末尾，再添加 k 个零，这里 k 是方程&lt;code&gt;l+1+k=448 mod 512&lt;/code&gt;的最小的非负解。然后再添加一个 64 比特长的块，其值等于消息 M 的长度/的二进制表示。使得填充后的消息的长度为 512 比特的倍数。&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/cryptography/hash函数构造-消息填充.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;h3&gt;密钥派生&lt;/h3&gt;
&lt;p&gt;常用的密钥派生函数（KDF）:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PBKDF2&lt;/strong&gt;: (Password-based Key Derivation Function 2)：
used for deriving a cryptographic key from a password.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scrypt&lt;/strong&gt;: a KDF designed for password storage by Colin Percival to be resistant against hardware-assisted attackers by having a tunable memory cost. 由 Colin Percival 设计的用于密码存储的 KDF，通过具有可调的内存成本来抵御硬件辅助的攻击者。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bcrypt&lt;/strong&gt;:Bcrypt 也是一个 KDF ，问世时间早于 Scrypt ，对于 ASIC 、GPU 攻击的抗性相对弱一些。其虽然也可以配置迭代数，但由于对内存的压力较小，因此比较容易构建相应的硬件加速密码破解器。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HKDF&lt;/strong&gt; (HMAC-based Extract-and-Expand Key Derivation Function)&lt;/p&gt;
&lt;h4&gt;口令加盐&lt;/h4&gt;
&lt;h3&gt;区块链-工作量证明&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/xiaobing94/pysimpleblockchain&quot;&gt;xiaobing94/pysimpleblockchain: 区块链的简单实现 (github.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/submarineas/article/details/128647550&quot;&gt;动手学区块链学习笔记（二）：区块链以及工作量证明算法-CSDN 博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;难题： 找到一个特殊的 SHA-256 哈希值，小于某个阈值的值 （2016 个区块调整一次，约 14 天）&lt;/p&gt;
&lt;h3&gt;延申阅读——彩虹表&lt;/h3&gt;
&lt;p&gt;什么是彩虹表？
一言以蔽之，彩虹表是一种破解用户密码的辅助工具。彩虹表以时空折中理论为基础，但并不是简单地“以空间换时间”，而是一种“双向交易”，在二者之间达到平衡。1980 年，公钥密码学的提出者之一 Hellman 针对 DES 算法（一种对称加密算法）提出了一种时空折中算法，即彩虹表的前身：预先计算的散列链集。2003 年瑞典的 Philippe Oechslin 在其论文 Making a Faster Cryptanalytic Time-Memory Trade-Off（参考博客 2）中对 Hellman 的算法进行了改进，并命名为彩虹表。当时是针对 Windows Xp 开机认证的 LM 散列算法。当然，目前除了破解开机密码，彩虹表目前还能用于 SHA、MD4、MD5 等散列算法的破译，速度快、破解率高，正如 Philippe 在论文中提到的：“1.4G 的彩虹表可以在 13.6s 内破解 99.9%的数字字母混合型的 Windows 密码“。实际上，Philippe 所做的改进本质上是减少了散列链集中可能存在的重复链，从而使空间的有效利用率更高。
以 MD5 作为明文的散列算法 H，加密后的密文为 q，明文为 p。彩虹表的作用就是在已知散列算法 H 和散列后的密文 q 的情况下，快速地得到明文 p。
针对 LM、NTLM 和 SHA1 等算法对应的彩虹表。整个破解过程为：根据密文 q 所使用的散列算法 H，生成或者下载针对该算法的彩虹表，然后再基于彩虹表对密文进行破解，最终得到明文 p。
不可逆的散列算法（HASH）
本质上，MD5 是将明文对应的二进制值与四个特定的 32 位的二进制值进行多轮的与、或、非、异或等运算，最终将明文对应的二进制转换成新的二进制。由于运算过程中存在进位丢失，导致人们无法根据 MD5 的计算过程和最终结果逆向计算出对应的明文。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cloud.tencent.com/developer/article/1903492&quot;&gt;深入浅出彩虹表原理&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;本章考点&lt;/h3&gt;
&lt;p&gt;1.哈希函数的性质&lt;/p&gt;
&lt;p&gt;2.hashlib 的使用&lt;/p&gt;
&lt;p&gt;2023 真题：&lt;/p&gt;
&lt;p&gt;第三题，&lt;/p&gt;
&lt;h2&gt;Chapter 3 对称加密&lt;/h2&gt;
&lt;h3&gt;TOC&lt;/h3&gt;
&lt;p&gt;1.对称加密基础知识：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;保密通信模型&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对称加密基本知识：什么是对称加密体制，分类：分组和流密码&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;分组密码定义和设计思想&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;常用分组密码算法：DES、AES、SM4&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;分组密码工作模式：ECB、CBC、CRT&lt;/p&gt;
&lt;p&gt;2.对称加密编程实现：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;cryptography.hazmat.primitives.ciphers 模块中的 Cipher 类生成密码对象，algorithms 指定加密算法，modes 指定加密模式；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;利用 padding 对长度不是 16 字节的整数倍的明文进行数据填充，同时解密后进行填充数据删除&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;保密通信模型&lt;/h3&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/cryptography/Shannon-通信模型.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;h3&gt;对称加密算法&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;对称加密：加密和解密使用相同的密钥的密码体制&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;1.分组密码：将明文消息编码表示后的二进制序列，划分成固定大小的块，每块分别在密钥的控制下变换成等长的二进制序列。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;AES&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;DES&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;IDEA&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;RC6&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SM1、SM4、SM7、ZUC 对称密码，SM1、SM7 算法不公开，调用该算法时，需要通过加密芯片的接口进行调用；&lt;/p&gt;
&lt;p&gt;2.流密码：将明文消息按比特进行加密&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;分组密码&lt;/h3&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/cryptography/分组密码.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;将明文消息编码表示后的二进制序列，划分成固定大小的块，每块分别在密钥的控制下变换成等长的二进制序列。&lt;/p&gt;
&lt;p&gt;1.分组足够长: 防止明文攻击 2.密钥长度要足够长：防止密钥穷举攻击。密钥不能过长 3.由密钥确定的置换算法要足够复杂，足以抗击各种已知的攻击，除了穷举，没有更好的攻击方法 4.加密和解密运算简单，易于软件和硬件的快速实现 5.一般无数据扩展，即：明文和密文的长度相同&lt;/p&gt;
&lt;h4&gt;分组密码的工作模式&lt;/h4&gt;
&lt;p&gt;1.扩散：使每 1 比特明文的变化尽可能多地影响到输出密文序列的比特，以便隐藏明文的统计特性。另一层意思使每 1 位密钥也尽可能影响到较多的输出密文比特。扩散的目的使希望密文中的任一比特都要尽可能与明文和密钥的每一比特相关联。
P1：00000000 c1：xxxxxx01
P2: 00000001 c2: xxxxxx11
P1：00000000 c1：01011010
P2: 00000001 c2: 11101011 2.混乱: 在加密变换过程中明文、密钥以及密文之间的关系尽可能地复杂，以防止破译这采用统计分析法进行破译攻击 3.乘积密码体制：S^n 称为迭代密码体制，其中 S 的明文空间和密文空间相等 k=(k1,k2) Ek(x)=Ek2(Ek1(x)) Dk(x)=Dk1(Dk2(y))。若 S^2=S，则称 S 是等幂的密码体制， 置换密码、仿射密码、Vigenere 密码及 Hill 密码都是等幂的密码体制，如果 S 是一个幂等体制，没有必要使用迭代体制，因为 S^2 需要更多的密钥，但安全强度与 S 一样。S 不是幂等密码体制，迭代密码体制 S^n 的安全强度会比 S 高，通过对一个密码体制进行迭代来提高其安全强度的思想被广泛应用于对称密码体制的设计中。通常称为密码体制的迭代结构。具体地，就是在密钥的控制下扩散和混乱两种基本密码操作的多次迭代，每次迭代中的各种基本操作总体，称之为轮函数。&lt;/p&gt;
&lt;h3&gt;流密码&lt;/h3&gt;
&lt;p&gt;将明文看作字符串或比特串，并逐字符或者逐位进行加密
为了防止密钥穷举，使用和明文信息一样长的密钥（无限）流 进行加密这种密码体制称为流密码（或序列密码）&lt;/p&gt;
&lt;h3&gt;本章考点&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;2023 真题：&lt;/p&gt;
&lt;p&gt;第四题，&lt;/p&gt;
&lt;h2&gt;Chapter 4 公钥加密&lt;/h2&gt;
&lt;h2&gt;Chapter 5 消息和实体认证&lt;/h2&gt;
&lt;h2&gt;Chapter 6 非对称和对称结合&lt;/h2&gt;
&lt;h2&gt;Chapter 7 认证加密和 Kerberos&lt;/h2&gt;
&lt;h2&gt;Chapter 8 TLS 通信&lt;/h2&gt;
&lt;h2&gt;Java 密码学编程&lt;/h2&gt;
&lt;h2&gt;隐私计算&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.zhihu.com/column/c_1433891764852158464&quot;&gt;隐私计算 - 知乎 (zhihu.com)&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;数据隐私问题处罚案例&lt;/h4&gt;
&lt;p&gt;据外媒消息,2023 年 5 月 24 日——爱尔兰数据保护委员会（DPC）对社交媒体巨头 Meta（前身为 Facebook）开出了创纪录的 12 亿欧元（10 亿英镑）罚款，原因是 Meta 在欧洲和美国之间的数据传输中存在处理不当的行为。这笔罚款是根据欧盟通用数据保护条例（GDPR）隐私法而开出的最大一笔罚单，为欧洲数据保护领域树立了重要的先例。&lt;/p&gt;
&lt;p&gt;根据上面的案例可以观察到当下社会对隐私的重视程度，以及对大公司泄露数据的不信任，因此也就引出下面的内容。&lt;/p&gt;
&lt;h3&gt;联邦学习&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/416264898&quot;&gt;隐私计算（联邦学习、差分隐私） - 知乎 (zhihu.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/100688371&quot;&gt;什么是联邦学习&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;联邦机器学习(Federated machine learning/Federated Learning)，又名联邦学习，联合学习，联盟学习。联邦机器学习是一个机器学习框架，能有效帮助多个机构在满足用户隐私保护、数据安全和政府法规的要求下进行数据使用和机器学习建模。联邦学习作为分布式的机器学习范式,可以有效解决数据孤岛问题,让参与方在不共享数据的基础上联合建模,能从技术上打破数据孤岛,实现 AI 协作。&lt;/p&gt;
&lt;p&gt;谷歌在 2016 年提出了针对手机终端的联邦学习,微众银行 AI 团队则从金融行业实践出发,关注跨机构跨组织的大数据合作场景，首次提出“联邦迁移学习”的解决方案，将迁移学习和联邦学习结合起来。据杨强教授在“联邦学习研讨会”上介绍,联邦迁移学习让联邦学习更加通用化,可以在不同数据结构、不同机构间发挥作用，没有领域和算法限制,同时具有模型质量无损、保护隐私、确保数据安全的优势。&lt;/p&gt;
&lt;p&gt;联邦学习定义了机器学习框架，在此框架下通过设计虚拟模型解决不同数据拥有方在不交换数据的情况下进行协作的问题。虚拟模型是各方将数据聚合在一起的最优模型，各自区域依据模型为本地目标服务。联邦学习要求此建模结果应当无限接近传统模式，即将多个数据拥有方的数据汇聚到一处进行建模的结果。在联邦机制下，各参与者的身份和地位相同，可建立共享数据策略。由于数据不发生转移，因此不会泄露用户隐私或影响数据规范。为了保护数据隐私、满足合法合规的要求。&lt;/p&gt;
&lt;p&gt;联邦学习有三大构成要素：数据源、联邦学习系统、用户。三者间关系如图所示，在联邦学习系统下，各个数据源方进行数据预处理，共同建立及其学习模型，并将输出结果反馈给用户。
&amp;lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/python-crypto-01-20261014.webp&quot; style=&quot;&quot;&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我的想法
每个设备作为一个终端，每个终端都有自己的数据，这些数据都是不同的，但是都有一个共同的目标，就是训练一个模型，这个模型可以用来预测，可以用来分类，可以用来识别等等。但是这些数据都是不同的，所以不能直接合并，但是可以通过联邦学习的方式，将这些数据进行训练，最后得到一个模型，这个模型可以用来预测，可以用来分类，可以用来识别等等。
具体的方式就是算法，譬如说最初的算法就是不断的将各个终端的数据 data 进行加权平均，然后不断重复训练一个模型，最后将这个模型发送给每个终端使用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;MPC （安全多方计算）&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/apr15/article/details/133965768&quot;&gt;安全多方计算框架最全合集（持续更新）_多方安全计算框架-CSDN 博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/384759946&quot;&gt;隐私计算技术之安全多方计算(MPC) - 知乎 (zhihu.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;安全多方计算是指在无可信第三方的情况下，多个参与方协同计算一个约定的函数，并且保证每一方仅获取自己的计算结果，无法通过计算过程中的交互数据推测出其他任意一方的输入和输出数据（除非函数本身可以由自己的输入推测出其他参与方的输入和输出）。&lt;/p&gt;
&lt;p&gt;安全多方计算可抽象概括为数学模型，其公式如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/python-crypto-04-20261014.webp&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;安全多方计算于 1986 年由姚期智院士通过姚氏百万富翁问题提出：两个百万富翁街头邂逅，他们都想炫一下富，比一比谁更有钱，但是出于隐私，都不想让对方知道自己到底拥有多少财富，如何在不借助第三方的情况下，让他们知道谁更有钱。姚氏“百万富翁问题”后经发展，成为现代密码学中非常活跃的研究领域，即安全多方计算。&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/cryptography/MPC-姚期智.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;[安全多方计算之百万富翁问题-CSDN 博客](https://blog.csdn.net/qq_43751200/article/details/126293436#:~:text=百万富翁问题是姚期智先生在1982年提出的第一个安全双方计算问题,问题可以描述为 ：两个百万富翁街头邂逅，他们都想炫一下富，比比谁更有钱，但是出于隐私，都不想让对方知道自己到底拥有多少财富，如何在不借助第三方的情况下，让他们知道他们之间谁更有钱。)&lt;/p&gt;
&lt;h4&gt;&lt;strong&gt;百万富翁问题是姚期智先生在 1982 年提出的第一个安全双方计算问题&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;问题可以描述为&lt;/strong&gt;：两个百万富翁街头邂逅，他们都想炫一下富，比比谁更有钱，但是出于隐私，都不想让对方知道自己到底拥有多少财富，如何在不借助第三方的情况下，让他们知道他们之间谁更有钱。&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/cryptography/隐私计算-MPC.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/cryptography/同态加密和秘密分享.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;h3&gt;同态加密&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/557034854&quot;&gt;应用密码学 | Paillier 同态加密算法简介 - 知乎 (zhihu.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;同态加密（HE，Homomorphic Encryption）是指对密文计算后的结果再解密和直接对明文计算的结果一致，同态加密按照其满足的运算类型可分为加法同态（Paillier 同态加密），乘法同态（RSA 同态加密），以及加法乘法都满足的全同态加密（Gentry 同态加密）。&lt;/p&gt;
&lt;h3&gt;秘密分享 Seret Sharing&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/578967596&quot;&gt;安全多方计算 | 基于 Shamir 秘密共享的 MPC 协议 - 知乎 (zhihu.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/95362628&quot;&gt;趣说密码学（五）秘密共享方案——shamir,中国剩余定理,Brickell 和 Blakley - 知乎 (zhihu.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;秘密分享的一种经典方案，1979 年 Shamir 提出了阈值秘密分享方案，该方案支持 n 个参与方中的任意 t 个可以联合解开秘密数据，具体方案如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/python-crypto-07-20261014.webp&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;假如你和你的朋友们正在一起面临某种生存困境，比如在野外迷路了，或是被困在沙漠中，或是核冬天，或是丧尸来袭，你们难以获取食物，只好将剩下的食物的收集到一起放进保险箱。但是有个问题——你们并不相信其他人，其他人很可能趁大家不注意将食物偷走。这时候，保险箱的钥匙应该怎么保管？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一种情况：交给一个人保管&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;原来住你隔壁的老王接下这个任务。他看起来似乎值得信任。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/python-crypto-06-20261014.webp&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;**第二天新闻头条：**已确认某探险队成员全部遇难！原因据查是保管食物的队员老王扔下大家不管，带着所有食物跑路了！&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第二种情况：交给一些人保管，全部到齐才能打开保险箱&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;只交给一个人保管钥匙非常不安全。不如交给一些人不同的钥匙，必须要所有的人到齐才能打开。于是你把钥匙分成三份，隔壁老王，对面楼小南和你分别拿到了一份钥匙。这下，隔壁老王不能再自己一个人打开保险箱了。你感到很满意。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/python-crypto-02-20261014.webp&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;**第二天新闻头条：**已确认某探险队成员全部遇难！现场遇难的探险队员全围着一个锁上的食物箱子，原因据查是保管钥匙的队员之一隔壁老王误将钥匙扔掉，致使全体队员无法打开食物保险箱！&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第三种情况：交给一些人保管，其中一部分到齐才能打开保险箱&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;好吧，隔壁老王一直盯着食物想要占为己有。这不行。而且如果要保管钥匙的全部成员到齐才能打开，那么其中某人拿不出钥匙将是非常绝望的情形。&lt;/p&gt;
&lt;p&gt;于是你想出了一种新的方法，把钥匙分成不同五份，路人甲，街口二愣子，对面楼小南，娄县令以及你分别拿了一份。但现在只需要三个人拿着他们的钥匙就能打开食物保险箱。就算其中一个人将钥匙弄丢，例如二愣子把钥匙吃了，剩下三个人也能打开保险箱。但少于三个人就不能打开保险箱。但具体改怎么做呢？你的思维开始跳脱起来。&lt;/p&gt;
&lt;h3&gt;零知识证明&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://cloud.tencent.com/developer/techpedia/1801&quot;&gt;什么是零知识证明&lt;em&gt;零知识证明简介&lt;/em&gt;零知识证明的优势以及应用场景-腾讯云开发者社区 (tencent.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;零知识证明过程有两个参与方，一方叫证明者，一方叫验证者。证明者掌握着某个秘密，他想让验证者相信他掌握着秘密，但是又不想泄漏这个秘密给验证者。双方按照一个协议，通过一系列交互，最终验证者会得出一个明确的结论，证明者是或不掌握这个秘密。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;① 阿里巴巴与四十大盗&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;有一天，阿里巴巴被强盗抓住了，强盗向他索要开启山洞大门的咒语。&lt;/p&gt;
&lt;p&gt;但此时阿里巴巴面临一个两难的问题，如果把密码告诉强盗，自己就没有利用价值了，最后肯定会被杀。&lt;/p&gt;
&lt;p&gt;如果不告诉强盗咒语，强盗以为自己不知道咒语，自己还是会被杀。&lt;/p&gt;
&lt;p&gt;怎么能做到让他们相信自己确实知道咒语，但是还不能让他们知道咒语是什么。&lt;/p&gt;
&lt;p&gt;这确实是一个很难的问题，但是阿里巴巴想出了一个好办法。&lt;/p&gt;
&lt;p&gt;他对强盗头领提议说，你们离我 30 米远，然后用弓箭指着我，当你举起双手后，我就念咒语开启山洞大门；当你把双手放下后，我就念咒语关上山洞大门，如果我要是逃跑，你们就用弓箭射死我。&lt;/p&gt;
&lt;p&gt;对于强盗头领来说，这显然是个好主意，于是照办。&lt;/p&gt;
&lt;p&gt;强盗头领先是举起双手，看到阿里巴巴动了动嘴皮子，门就开了，然后放下双手，阿里巴巴又动了动嘴皮子，门就关了。&lt;/p&gt;
&lt;p&gt;显然，强盗相信了阿里巴巴。&lt;/p&gt;
&lt;p&gt;这样，阿里巴巴在没有告诉强盗头领开门咒语的情况下，又向强盗证明了，自己是知道开门咒语的。&lt;/p&gt;
&lt;h2&gt;期末复习&lt;/h2&gt;
&lt;p&gt;重点：&lt;/p&gt;
&lt;p&gt;1.分组密码工作模式&lt;/p&gt;
&lt;p&gt;2.字符、字节流转化&lt;/p&gt;
&lt;p&gt;3.kdf 密钥派生（哈希、加盐）&lt;/p&gt;
&lt;p&gt;4.密码学的机密性、验证性的理解和代码实现&lt;/p&gt;
&lt;p&gt;题型：问答题&lt;/p&gt;
&lt;p&gt;代码核心内容：加解密流程（描述清楚、注释）&lt;/p&gt;
&lt;p&gt;考试一共八道题：&lt;/p&gt;
</content:encoded></item><item><title>python密码学实验</title><link>https://h1s97x.github.io/posts/notes/notes-python-cryptography/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-python-cryptography/</guid><description>1、安装 Python、Pycharm；了解 Idle 的应用；学会 pip 安装命令，将 Cryptography gmpy2 库安装到 python 中。 </description><pubDate>Thu, 16 Jul 2026 16:12:52 GMT</pubDate><content:encoded>&lt;h2&gt;实验一：熟悉 Python 开发环境，凯撒密码与仿射密码&lt;/h2&gt;
&lt;h3&gt;实验目的&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;熟悉 Pycharm/ Python Idle 开发&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;完成 Casear 密码的编程。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;实验内容&lt;/h3&gt;
&lt;p&gt;1、安装 Python、Pycharm；了解 Idle 的应用；学会 pip 安装命令，将 Cryptography gmpy2 库安装到 python 中。&lt;/p&gt;
&lt;p&gt;2、编写 Casear 密码程序，扩展到仿射密码、以及 Casear 密码的破译程序。&lt;/p&gt;
&lt;h4&gt;caesar 密码 python 实现&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://www.w3schools.cn/cryptography_with_python/cryptography_with_python_affine_cipher.html&quot;&gt;Python 密码学教程 - w3schools&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;第一版代码：&lt;/p&gt;
&lt;p&gt;两个函数分别实现对 Caesar 密码的加密和解密，关键的操作公式是相同的，只是对密钥进行加减操作。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for i in message:
	ciphertext += (chr(ord(i) + k)) # 将明文汉字转换为对应ASCLL数值或Unicode数值（ord函数），然后在此数值上+3，再将该值返回汉字（chr函数）
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第二版代码：&lt;/p&gt;
&lt;p&gt;同样是两个函数分别实现对 Caesar 密码的加密和解密，将公式进行修改，第一版并不能把密码限制在 26 个字母。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for i in range(len(text)):
    char = text[i]
    if (char.isupper()):
        ciphertext += chr((ord(char) + key - ord(&apos;a&apos;)) % 26 + ord(&apos;a&apos;))
    else:
        ciphertext += chr((ord(char) + key - ord(&apos;A&apos;)) % 26 + ord(&apos;A&apos;))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第三版：&lt;/p&gt;
&lt;p&gt;能够自定义&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SYMBOLS = &apos;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890 !?.&apos;
def caesar_encrypt(mode, message, key):
    if mode[0] == &apos;d&apos;:
        key = -key
    ciphertext = &apos;&apos;
    for symbol in message:
        if symbol.isalpha():
          num = ord(symbol)+key
          if symbol.isupper():  # 所有密文字母是大写
              if num &amp;gt; ord(&apos;Z&apos;):
                  num -= 26
              elif num &amp;lt; ord(&apos;A&apos;):
                  num += 26
          elif symbol.islower():
              if num &amp;gt; ord(&apos;z&apos;):
                  num -= 26
              elif num &amp;lt; ord(&apos;a&apos;):
                  num += 26
        ciphertext += chr(num)
        else:
            ciphertext += symbol
    return ciphertext
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;暴力破解&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def caesar_decrypt(message):
    LETTERS = &apos;ABCDEFGHIJKLMNOPQRSTUVWXYZ&apos;
    letters = &quot;abcdefghijklmnopqrstuvwxyz&quot;
    for key in range(26):
        translated = &apos;&apos;
        for symbol in message:
            if symbol in LETTERS:
                num = LETTERS.find(symbol)
                num = num - key
                if num &amp;lt; 0:
                    num = num + len(LETTERS)
                translated = translated + LETTERS[num]
            elif symbol in letters:
                num = letters.find(symbol)
                num = num - key
                if num &amp;lt; 0:
                    num = num + len(letters)
                translated = translated + letters[num]
            else:
                translated = translated + symbol
        print(&apos;Hacking key #%s: %s&apos; % (key, translated))

&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;仿射密码&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/clwsec/p/10198428.html&quot;&gt;仿射密码 Python 实现&lt;/a&gt;
仿射密码是一种替换密码。它是利用加密函数一个字母对一个字母的加密。&lt;/p&gt;
&lt;p&gt;加密函数是 E(x)= (ax + b) (mod m)，其中，a 和 m 互质，m 是字符集的大小。
（例如，26 即是以 26 个字母作为编码，当 m 是 26 时，a 必须是 1，3，5，7，9，11，15，17，19，21，23，25 其中之一）&lt;/p&gt;
&lt;p&gt;解密函数为 D(x) = a&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;(x - b) (mod m)，其中 a&amp;lt;sup&amp;gt;-1&amp;lt;/sup&amp;gt;是 a 在 Zm 群的乘法逆元。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;乘法逆元&lt;/strong&gt;
群 G 中任意一个元素 a，都在 G 中有唯一的逆元 a&apos;，具有性质 aa&apos; = a&apos;a = e，其中 e 为群的单位元。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;&apos;&apos;&apos;
仿射密码
(a,b)
m = 26，字符集为小写字母
加密函数是E(x)= (ax + b) (mod m)
解密函数为D(x) = (a^-1)(x - b) (mod m)，其中a^-1是a的乘法逆元
&apos;&apos;&apos;

#通过一个简单的遍历得到a的乘法逆元，也可以通过gmpy2库中的invert函数实现
def get_inverse(a):
    for i in range(1,27):
        if a*i%26==1:
            return i

#加密
def encipher(a, b, p):
    c=[]
    for i in p:
        temp=((ord(i)-97)*a+b)%26+97
        c.append(chr(temp))
    print(&apos;&apos;.join(c))

#解密
def decipher(a, b, c):
    a_inv = get_inverse(a)
    p=[]
    for i in c:
        temp=(((ord(i)-97)-b)*a_inv)%26+97
        p.append(chr(temp))
    print(&apos;&apos;.join(p))

if __name__ == &quot;__main__&quot;:
    a = 11
    b = 6
    message = &apos;sorcery&apos;
    encipher(a,b,message)
    #decipher(a,b,message)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&apos;&apos;&apos;
仿射密码
m = 52
字符集为小写和大写字母
&apos;&apos;&apos;
import string
def encrypt(k1,k2,message):
    dic = string.ascii_letters
    c = []
    for i in message:
        if i.islower():
            num = ord(i)-ord(&apos;a&apos;)
            c.append(dic[(num*k1+k2)%52])
        elif i.isupper():
            num = ord(i)-ord(&apos;A&apos;)+26
            c.append(dic[(num*k1+k2)%52])
        else:
            c.append(i)
    print(&apos;&apos;.join(c))

def decrypt(k1,k2,message):
    for i in range(52):
        if k1*i%52==1:
            inv = i
            break
    dic = string.ascii_letters
    m = []
    for i in message:
        if i.islower():
            num = ord(i)-ord(&apos;a&apos;)
            m.append(dic[inv*(num-k2)%52])
        elif i.isupper():
            num = ord(i)-ord(&apos;A&apos;)+26
            m.append(dic[inv*(num-k2)%52])
        else:
            m.append(i)
    print(&apos;&apos;.join(m))

message = &apos;gVEXGT iDIT&apos; #待加密或解密的消息
a = 5 # key的范围0~51之间
b = 29 # key的范围0~51之间
# encrypt(a,b,message)
decrypt(a,b,message)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Affine(object):
    DIE = 128
    KEY = (7, 3, 55)

    def __init__(self):
        pass

    def encryptChar(self, char):
        K1, K2, kI = self.KEY
        return chr((K1 * ord(char) + K2) % self.DIE)

    def encrypt(self, string):
        return &quot;&quot;.join(map(self.encryptChar, string))

    def decryptChar(self, char):
        K1, K2, KI = self.KEY
        return chr(KI * (ord(char) - K2) % self.DIE)

    def decrypt(self, string):
        return &quot;&quot;.join(map(self.decryptChar, string))

affine = Affine()
print(affine.encrypt(&apos;Affine Cipher&apos;))
print(affine.decrypt(&apos;*18?FMT&apos;))

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;实验二：哈希函数&lt;/h3&gt;
&lt;p&gt;1、 编程实现生成空字符串、‘Alice’、‘Bob’的 md5、sha256 的哈希值；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import hashlib
def hash_md5():
    alice = hashlib.md5(b&quot;Alice&quot;)
    bob = hashlib.md5(b&quot;Bob&quot;)
    print(&apos;&quot;Alice&quot; md5:&apos; + alice.hexdigest())
    print(&apos;&quot;Bob&quot; md5:&apos; + bob.hexdigest())

def hash_sha256():
    alice = hashlib.sha256(b&quot;Alice&quot;)
    bob = hashlib.sha256(b&quot;Bob&quot;)
    print(&apos;&quot;Alice&quot; sha256:&apos; + alice.hexdigest())
    print(&apos;&quot;Bob&quot; sha256:&apos; + bob.hexdigest())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2、 编程实现生成自己名字的哈希值，注意编码的转换；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def myhash(str = &quot;Yangjiaqing&quot;):
    res = hashlib.md5(str.encode(encoding=&quot;utf-8&quot;))
    print(str + &quot; md5: &quot; + res.hexdigest())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3、 编写体现哈希雪崩的代码，哈希值用二进制表示； 参考 Listing2-5 代码；
&lt;a href=&quot;https://huaweicloud.csdn.net/63807e39dacf622b8df88c1c.html#:~:text=%23%20%E5%93%88%E5%B8%8C%E5%87%BD%E6%95%B0%E7%9A%84%E9%9B%AA%E5%B4%A9%E6%95%88%E5%BA%94%20%23%20author%3Amarxycj%20%23%20date%3A2021-10-29%20from%20hashlib,%27__main__%27%20%3A%20md5_value%20%3D%20input%20%28%29%20brute_md5%20%28md5_value%29&quot;&gt;Python 哈希函数与消息认证实验&lt;/a&gt;
&lt;a href=&quot;https://www.wolfram.com/language/12/cryptography/demonstrate-the-avalanche-effect-of-a-hash-function.html.zh&quot;&gt;展示散列函数的雪崩效应&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def cmpcount(str1, str2):
    count = 0
    for i in range(0, len(str1)):
        if str1[i] != str2[i]:
            count += 1
    return count

def avalanche(str1 = &apos;bob&apos;, str2 = &apos;aob&apos;):
    bin1 = str1.encode(&apos;utf-8&apos;)
    bin2 = str2.encode(&apos;utf-8&apos;)
    hexstring1 = hashlib.md5(bin1).hexdigest()
    binstring1 = &apos;{:08b}&apos;.format(int(hexstring1, 16))
    # binstring1 = bin(int(hexstring1, 16))
    hexstring2 = hashlib.md5(bin2).hexdigest()
    binstring2 = &apos;{:08b}&apos;.format(int(hexstring2, 16))
    # binstring2 = bin(int(hexstring2, 16))
    print(int(hexstring1, 16))
    print(int(hexstring2, 16))
    print(str1 + &quot; md5:&quot; + binstring1)
    print(str2 + &quot; md5:&quot; + binstring2)
    print(&quot;两个哈希值不同的位数：&quot; + str(cmpcount(binstring1, binstring2)))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4、 利用 scrypt 密钥派生函数，实现口令加盐，生成更加安全的密钥（口令）；
&lt;a href=&quot;https://deepinout.com/python/python-qa/169_python_creating_a_salt_in_python.html#:~:text=Python%203.6%2B%20%E7%9A%84%20secrets%20%E6%A8%A1%E5%9D%97%E6%8F%90%E4%BE%9B%E4%BA%86%E4%B8%80%E7%A7%8D%E6%9B%B4%E7%AE%80%E5%8D%95%E7%9A%84%E6%96%B9%E5%BC%8F%E6%9D%A5%E7%94%9F%E6%88%90%E9%9A%8F%E6%9C%BA%E6%95%B0%E3%80%82%20%E4%B8%8B%E9%9D%A2%E6%98%AF%E4%BD%BF%E7%94%A8%20secrets%20%E6%A8%A1%E5%9D%97%E7%94%9F%E6%88%90%E7%9B%90%E7%9A%84%E7%A4%BA%E4%BE%8B%E4%BB%A3%E7%A0%81%EF%BC%9A,16%20salt%20%3D%20secrets.token_hex%28salt_length%29%20print%28salt%29%20%E4%B8%8A%E8%BF%B0%E4%BB%A3%E7%A0%81%E5%B0%86%E7%94%9F%E6%88%90%E4%B8%80%E4%B8%AA%E9%95%BF%E5%BA%A6%E4%B8%BA%2016%20%E7%9A%84%E9%9A%8F%E6%9C%BA%E5%8D%81%E5%85%AD%E8%BF%9B%E5%88%B6%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%BD%9C%E4%B8%BA%E7%9B%90%E3%80%82&quot;&gt;创建一个 Python 盐&lt;/a&gt;
Scrypt 介绍
Scrypt 是一个强大的密钥派生函数，其通过内存密集的计算方式来抵抗 GPU、ASIC、FPGA 这类密码破解硬件的攻击。&lt;/p&gt;
&lt;p&gt;Scrypt 接收多个输入参数，进行计算后输出密钥：&lt;/p&gt;
&lt;p&gt;key = Scrypt(password, salt, N, r, p, derived-key-len)
其中的参数被称为&quot; Scrypt 配置参数&quot;，说明如下：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;N - 迭代次数，将影响 CPU 和内存用量，例：16384 、2048 ；
r - 块大小，将影响 CPU 和内存用量，例：8 ；
p - 并行因数 （并行运行的线程数，将影响 CPU 和内存用量），通常为 1 ；
password - 输入的密码（推荐至少为 8 - 10 个字符）；
salt - 安全产生的随机字节序列（最小为 64 位，推荐 128 位）；
derived-key-len - 输出的密钥要有多少字节长，例如 32 （256 位）
Scrypt 的输出密钥长度可以是 128 位到 512 位，但是通常为 256 位。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Salt 用使用 secrets 模块生成，也可以用 os.urandom()随机生成&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def hash_password(password = b&apos;p@$Sw0rD~7&apos;):
    salt_length = 16
    salt = secrets.token_bytes(salt_length)
    key = pyscrypt.hash(password, salt, 2048, 8, 1, 32)
    return key.hex()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;5、实现区块链中的工作量证明编程，通过设置不同的难度，体会生成符合要求哈希值需要时间长短的不同；
&lt;a href=&quot;https://zhuanlan.zhihu.com/p/29903461&quot;&gt;从零开始构建一个区块链（二）： 工作量证明&lt;/a&gt;
&lt;a href=&quot;https://blog.csdn.net/xiaobing1994/article/details/87967693&quot;&gt;使用 python 实现简版区块链-工作量证明&lt;/a&gt;
&lt;a href=&quot;https://github.com/xiaobing94/pysimpleblockchain/tree/part2&quot;&gt;区块链的简单实现 - GitHub&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import sys
import hashlib
class NonceNotFoundError(Exception):
    pass

def encode(str, code=&apos;utf-8&apos;):
    return str.encode(code)

def decode(bytes, code=&apos;utf-8&apos;):
    return bytes.decode(code)

def sum256_hex(*args):
    m = hashlib.sha256()
    for arg in args:
        if isinstance(arg, str):
            m.update(arg.encode())
        else:
            m.update(arg)
    return m.hexdigest()

def sum256_byte(*args):
    m = hashlib.sha256()
    for arg in args:
        if isinstance(arg, str):
            m.update(arg.encode())
        else:
            m.update(arg)
    return m.digest()

class ProofOfWork(object):
    _N_BITS = 20
    MAX_BITS = 256
    MAX_SIZE = sys.maxsize
    def __init__(self, n_bits=_N_BITS):
        self._n_bits = n_bits
        self._target_bits = 1 &amp;lt;&amp;lt; (self.MAX_BITS - n_bits)
    def _prepare_data(self, nonce):
        data_lst = [str(nonce)]
        return encode(&apos;&apos;.join(data_lst))
    def run(self):
        nonce = 0
        found = False
        hash_hex = None
        print(&apos;Mining a new block&apos;)
        while nonce &amp;lt; self.MAX_SIZE:
            data = self._prepare_data(nonce)
            hash_hex = sum256_hex(data)
            hash_val = int(hash_hex, 16)
            sys.stdout.write(&quot;try nonce == %d hash_hex == %s \r&quot; % (nonce, hash_hex))
            if (hash_val &amp;lt; self._target_bits):
                found = True
                break

            nonce += 1
        if found:
            print(&apos;Found nonce == %d&apos; % nonce)
        else:
            print(&apos;Not Found nonce&apos;)
            raise NonceNotFoundError(&apos;nonce not found&apos;)
        return nonce, hash_hex
if __name__ == &quot;__main__&quot;:
    bc = ProofOfWork()
    bc.run()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;实验三：实现对称加密编程&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/364772865&quot;&gt;分组密码的五大工作模式&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/23276413&quot;&gt;使用 cryptography 进行 AES 的 cbc 模式加密 - 知乎 (zhihu.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/istommao/cryptokit/tree/main&quot;&gt;istommao/cryptokit: cryptokit is a cryptography kit (github.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;4、练习 3-12 手工 CBC:编程应用 AES 的 ECB 模式实现 CBC 模式&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def ecbTocbc(self, data):
    if not isinstance(data, bytes):
        data = data.encode()
    cipher = Cipher(algorithms.AES(self.aes_key),
                    modes.ECB(),
                    backend=default_backend())
    dataList = []
    cipherText = []
    padded_data = self.pkcs7_padding(data)
    binary_data = bin((int(padded_data.hex(), 16)))[2:]
    while len(binary_data) / 128 &amp;gt;= 0:
        if len(binary_data) / 128 == 0 &amp;amp; len(binary_data) % 128 == 0:
            break
        elif len(binary_data) / 128 == 0 &amp;amp; len(binary_data) % 128 != 0:
            dataList.append(binary_data[0:])
            dataList.remove()
        else:
            dataList.append(binary_data[0:128])
            binary_data = binary_data[128:]

    c = &quot;{:08b}&quot;.format(int(self.aes_iv.hex(), 16))
    for i in dataList:
        ci = cipher.encryptor().update(bytes(i^c))
        cipherText.append(ci)
        c = ci.decode()
        print(c)
    m = &apos;&apos;.join([c.hex() for c in cipherText])
    return m
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;5、练习 3-13 简单 CTR 模式：编程应用 AES 的 ECB 模式实现 CTR 模式&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def ecbToctr(self, data):
    if not isinstance(data, bytes):
        data = data.encode()
    iv = self.aes_iv
    cipher = Cipher(algorithms.AES(self.aes_key),
                    modes.ECB(),
                    backend=default_backend())

    dataList = []
    cipherText = []
    padded_data = self.pkcs7_padding(data)
    binary_data = &quot;{:08b}&quot;.format(int(padded_data.hex(), 16)) + &apos;0&apos;
    while len(binary_data) / 128 &amp;gt;= 0:
        if len(binary_data) / 128 == 0 &amp;amp; len(binary_data) % 128 == 0:
            break
        elif len(binary_data) / 128 == 0 &amp;amp; len(binary_data) % 128 != 0:
            dataList.append(binary_data[0:])
            dataList.remove()
        else:
            dataList.append(binary_data[0:128])
            binary_data = binary_data[128:]


    for i in dataList:
        ci = cipher.encryptor().update(iv) ^ i.encode()
        iv += 1
        cipherText.append(ci)
        print(ci)
    m = &apos;&apos;.join(cipherText)
    return m
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;实验四：实现非对称加密编程&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://cloud.tencent.com/developer/article/2186122&quot;&gt;非对称密钥沉思系列（1）：RSA 专题之 PKCSv1.5 填充模式下的选择性密文攻击概述-腾讯云开发者社区-腾讯云 (tencent.com)&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Coding</title><link>https://h1s97x.github.io/posts/coding/coding-coding/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-coding/</guid><pubDate>Thu, 16 Jul 2026 15:41:22 GMT</pubDate><content:encoded/></item><item><title>ZenKitX 系列：一个 Flutter 开发者的禅意探索之路</title><link>https://h1s97x.github.io/posts/notes/notes-zenkitx-flutter-journey/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-zenkitx-flutter-journey/</guid><description>分享我是如何设计和开发 ZenKitX 系列 Flutter 应用的，从设计理念到技术实现</description><pubDate>Sat, 21 Mar 2026 16:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;ZenKitX 系列：一个 Flutter 开发者的禅意探索之路&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Less is more&quot; —— 真正的简约不是贫乏，而是恰到好处的完美。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;🌟 缘起&lt;/h2&gt;
&lt;p&gt;作为一个热爱美学与技术的开发者，我一直在思考：能否在移动应用中也能体验到那份宁静与专注？&lt;/p&gt;
&lt;p&gt;于是，&lt;strong&gt;ZenKitX&lt;/strong&gt; 诞生了。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;🎨 设计理念&lt;/h2&gt;
&lt;h3&gt;禅意美学&lt;/h3&gt;
&lt;p&gt;&quot;Zen&quot;（禅）强调的是&lt;strong&gt;简&lt;/strong&gt;、&lt;strong&gt;静&lt;/strong&gt;、&lt;strong&gt;悟&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;简&lt;/strong&gt;：去除一切不必要的装饰&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;静&lt;/strong&gt;：柔和的色彩让人内心平静&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;悟&lt;/strong&gt;：留白给人思考的空间&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Neumorphic 设计风格&lt;/h3&gt;
&lt;p&gt;Neumorphism（拟态设计）是一种模拟物理世界质感的 UI 风格：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;柔和的阴影创造立体感&lt;/li&gt;
&lt;li&gt;按钮仿佛是从屏幕中&quot;浮&quot;出来的&lt;/li&gt;
&lt;li&gt;色彩低调，不刺眼&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://via.placeholder.com/800x400/0d1117/c9d1d9?text=Neumorphic+Design&quot; alt=&quot;Neumorphic 示例&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;📱 ZenKitX 系列应用&lt;/h2&gt;
&lt;h3&gt;1. ZenCalc - 禅意计算器&lt;/h3&gt;
&lt;p&gt;一个融合禅意美学的 Flutter 计算器应用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;特点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Neumorphic 按钮设计&lt;/li&gt;
&lt;li&gt;极简的界面布局&lt;/li&gt;
&lt;li&gt;流畅的动画过渡&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. ZenCalendar - 禅意日历&lt;/h3&gt;
&lt;p&gt;结合 Material 3 与禅意设计的日历应用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;特点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;简洁的日期视图&lt;/li&gt;
&lt;li&gt;意图管理功能&lt;/li&gt;
&lt;li&gt;柔和的色彩搭配&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. ZenWeather - 禅意天气&lt;/h3&gt;
&lt;p&gt;优雅的天气应用，带给你每一天的宁静。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;特点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;动态天气背景&lt;/li&gt;
&lt;li&gt;简洁的温度显示&lt;/li&gt;
&lt;li&gt;舒适的视觉体验&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. ZenNotes - 禅意笔记&lt;/h3&gt;
&lt;p&gt;专注于写作的笔记应用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;特点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;极简编辑器&lt;/li&gt;
&lt;li&gt;分类管理&lt;/li&gt;
&lt;li&gt;云端同步&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;🛠 技术栈&lt;/h2&gt;
&lt;h3&gt;框架&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Flutter&lt;/strong&gt; 3.x - 跨平台 UI 框架&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;状态管理&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Riverpod&lt;/strong&gt; - Flutter 官方推荐的状态管理方案&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;架构&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Clean Architecture&lt;/strong&gt; - 清晰的架构分层&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Provider&lt;/strong&gt; - 依赖注入&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;CI/CD&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GitHub Actions&lt;/strong&gt; - 自动化测试与部署&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;💡 开发心得&lt;/h2&gt;
&lt;h3&gt;1. 设计先行&lt;/h3&gt;
&lt;p&gt;在写代码之前，我先在 Figma 上完成了所有的 UI 设计。确定配色、间距、动画方案后再开始开发，避免了后期的频繁修改。&lt;/p&gt;
&lt;h3&gt;2. 组件复用&lt;/h3&gt;
&lt;p&gt;四个应用之间有很多共同的 UI 组件（如按钮、卡片、输入框），我提取成了独立的 Widget，减少了代码冗余。&lt;/p&gt;
&lt;h3&gt;3. 响应式设计&lt;/h3&gt;
&lt;p&gt;Flutter 的 LayoutBuilder 和 MediaQuery 帮助我很好地处理了不同屏幕尺寸的适配问题。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;🚀 未来计划&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[x] 统一四个应用的设计语言&lt;/li&gt;
&lt;li&gt;[x] 提取公共组件库&lt;/li&gt;
&lt;li&gt;[x] 添加深色模式&lt;/li&gt;
&lt;li&gt;[x] 支持更多平台（iOS、Web）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;📚 参考资源&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://flutter.dev&quot;&gt;Flutter 官方文档&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://neumorphism.io/&quot;&gt;Neumorphism UI 设计趋势&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://m3.material.io/&quot;&gt;Material Design 3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;� 最后的最后&lt;/h2&gt;
&lt;p&gt;如果你对 ZenKitX 感兴趣，欢迎：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;⭐ Star 项目：https://github.com/ZenKitX&lt;/li&gt;
&lt;li&gt;💬 提交 Issue：发现 bug 或有新功能建议&lt;/li&gt;
&lt;li&gt;🤝 参与贡献：一起完善这个系列&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;让每一个应用，都成为一次宁静的体验。&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;本文首发于 &lt;a href=&quot;https://h1s97x.cloud&quot;&gt;h1s97x.cloud&lt;/a&gt;，由 GitHub Actions 自动部署。&lt;/em&gt;&lt;/p&gt;
</content:encoded></item><item><title>Git Help</title><link>https://h1s97x.github.io/posts/notes/notes-git-help/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-git-help/</guid><description>我来为您整理一份全面的 Git 指令集，涵盖从基础到高级的各种操作场景。 --- bash git config --global user.name &quot;You</description><pubDate>Sun, 28 Dec 2025 07:21:24 GMT</pubDate><content:encoded>&lt;p&gt;我来为您整理一份全面的 Git 指令集，涵盖从基础到高级的各种操作场景。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;🚀 Git 指令集大全&lt;/h2&gt;
&lt;h3&gt;一、基础配置&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 配置用户名和邮箱
git config --global user.name &quot;Your Name&quot;
git config --global user.email &quot;your.email@example.com&quot;

# 查看配置
git config --list
git config user.name

# 设置默认编辑器
git config --global core.editor &quot;vim&quot;

# 设置别名（快捷命令）
git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;二、仓库操作&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 初始化新仓库
git init

# 克隆远程仓库
git clone &amp;lt;repository-url&amp;gt;
git clone &amp;lt;repository-url&amp;gt; &amp;lt;directory-name&amp;gt;  # 指定目录名

# 查看远程仓库地址
git remote -v

# 添加远程仓库
git remote add origin &amp;lt;repository-url&amp;gt;

# 修改远程仓库地址
git remote set-url origin &amp;lt;new-url&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;三、基本工作流程&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 查看仓库状态
git status

# 添加文件到暂存区
git add &amp;lt;file-name&amp;gt;           # 添加指定文件
git add .                     # 添加所有修改
git add -A                    # 添加所有变化（包括删除）

# 提交更改
git commit -m &quot;提交信息&quot;
git commit -am &quot;提交信息&quot;     # 跳过 add，直接提交已跟踪文件

# 查看提交历史
git log
git log --oneline             # 简洁模式
git log --graph --oneline     # 图形化显示分支
git log -n 5                  # 最近5条
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;四、分支管理&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 查看分支
git branch                    # 本地分支
git branch -a                 # 所有分支（含远程）
git branch -r                 # 仅远程分支

# 创建分支
git branch &amp;lt;branch-name&amp;gt;      # 创建新分支
git checkout -b &amp;lt;branch-name&amp;gt; # 创建并切换
git switch -c &amp;lt;branch-name&amp;gt;   # 新方式（Git 2.23+）

# 切换分支
git checkout &amp;lt;branch-name&amp;gt;
git switch &amp;lt;branch-name&amp;gt;      # 新方式

# 合并分支
git merge &amp;lt;branch-name&amp;gt;       # 合并指定分支到当前分支

# 删除分支
git branch -d &amp;lt;branch-name&amp;gt;   # 删除已合并分支
git branch -D &amp;lt;branch-name&amp;gt;   # 强制删除

# 重命名分支
git branch -m &amp;lt;old-name&amp;gt; &amp;lt;new-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;五、远程同步&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 拉取代码
git pull origin &amp;lt;branch&amp;gt;
git pull --rebase origin &amp;lt;branch&amp;gt;  # 使用 rebase 方式

# 推送代码
git push origin &amp;lt;branch&amp;gt;
git push -u origin &amp;lt;branch&amp;gt;        # 首次推送并关联
git push origin --delete &amp;lt;branch&amp;gt;  # 删除远程分支

# 获取远程更新（不合并）
git fetch origin

# 同步远程分支列表
git remote prune origin
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;六、撤销与回退&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 撤销工作区修改
git checkout -- &amp;lt;file&amp;gt;        # 丢弃文件修改
git restore &amp;lt;file&amp;gt;            # 新方式（Git 2.23+）

# 撤销暂存区
git reset HEAD &amp;lt;file&amp;gt;         # 取消暂存
git restore --staged &amp;lt;file&amp;gt;   # 新方式

# 版本回退
git reset --soft HEAD~1       # 回退到上一版本，保留修改到暂存区
git reset --mixed HEAD~1      # 回退到上一版本，保留修改到工作区（默认）
git reset --hard HEAD~1       # 彻底回退，丢弃修改

# 查看所有操作记录（用于找回）
git reflog
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;七、储藏（Stash）&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 临时保存修改
git stash
git stash save &quot;描述信息&quot;

# 查看储藏列表
git stash list

# 恢复储藏
git stash pop                 # 恢复并删除
git stash apply               # 恢复不删除
git stash apply stash@{0}     # 恢复指定

# 删除储藏
git stash drop stash@{0}      # 删除指定
git stash clear               # 清空所有
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;八、标签管理&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 创建标签
git tag &amp;lt;tag-name&amp;gt;            # 轻量标签
git tag -a &amp;lt;tag-name&amp;gt; -m &quot;描述&quot;  # 附注标签
git tag &amp;lt;tag-name&amp;gt; &amp;lt;commit-id&amp;gt;   # 给指定提交打标签

# 推送标签到远程
git push origin &amp;lt;tag-name&amp;gt;
git push origin --tags        # 推送所有标签

# 删除标签
git tag -d &amp;lt;tag-name&amp;gt;
git push origin :refs/tags/&amp;lt;tag-name&amp;gt;  # 删除远程标签
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;九、高级操作&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 变基（Rebase）
git rebase &amp;lt;branch&amp;gt;           # 将当前分支变基到指定分支
git rebase -i HEAD~3          # 交互式变基最近3个提交

# 拣选提交
git cherry-pick &amp;lt;commit-id&amp;gt;   # 将指定提交应用到当前分支

# 二分查找（定位问题提交）
git bisect start
git bisect bad
git bisect good &amp;lt;commit-id&amp;gt;

# 查看文件修改历史
git blame &amp;lt;file&amp;gt;

# 查看某行代码最后修改者
git blame -L 10,20 &amp;lt;file&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;十、常用组合场景&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;场景&lt;/th&gt;
&lt;th&gt;命令组合&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;日常提交&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git add .&lt;/code&gt; → &lt;code&gt;git commit -m &quot;msg&quot;&lt;/code&gt; → &lt;code&gt;git push&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;拉取最新代码&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git pull origin main&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;创建功能分支&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git checkout -b feature/xxx&lt;/code&gt; → 开发 → &lt;code&gt;git push -u origin feature/xxx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;合并功能分支&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git checkout main&lt;/code&gt; → &lt;code&gt;git pull&lt;/code&gt; → &lt;code&gt;git merge feature/xxx&lt;/code&gt; → &lt;code&gt;git push&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;临时保存工作&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git stash&lt;/code&gt; → 切换分支处理 → &lt;code&gt;git checkout -&lt;/code&gt; → &lt;code&gt;git stash pop&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;撤销错误提交&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git reset --soft HEAD~1&lt;/code&gt; → 修改后重新提交&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h3&gt;十一、.gitignore 模板&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 依赖
node_modules/
vendor/

# 编译输出
dist/
build/
*.exe
*.dll

# IDE
.idea/
.vscode/
*.iml

# 日志与临时文件
*.log
*.tmp
.DS_Store

# 敏感信息
.env
config.local.php
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;十二、实用技巧&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 查看某文件的修改历史
git log -p &amp;lt;file&amp;gt;

# 查看两个分支差异
git diff branch1 branch2

# 查看工作区与暂存区差异
git diff

# 查看暂存区与最新提交差异
git diff --cached

# 只查看文件名
git diff --name-only

# 统计提交贡献
git shortlog -sn

# 生成补丁文件
git format-patch HEAD~3

# 应用补丁
git apply patch-file
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;如需了解某个指令的详细用法，可以使用 &lt;code&gt;git &amp;lt;command&amp;gt; --help&lt;/code&gt; 查看官方文档。&lt;/p&gt;
</content:encoded></item><item><title>Minecraft</title><link>https://h1s97x.github.io/posts/notes/notes-minecraft/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-minecraft/</guid><description>Wiki： 中文 Minecraft Wiki 中文Minecraft Wiki - 最详细的我的世界百科 (fandom.com) 我的世界中文站 - 国内知</description><pubDate>Sat, 27 Dec 2025 15:46:11 GMT</pubDate><content:encoded>&lt;h3&gt;站点&lt;/h3&gt;
&lt;p&gt;Wiki：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://zh.minecraft.wiki/&quot;&gt;中文 Minecraft Wiki&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://minecraft.fandom.com/zh/wiki/Minecraft_Wiki&quot;&gt;中文Minecraft Wiki - 最详细的我的世界百科 (fandom.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.minecraftzw.com/&quot;&gt;我的世界中文站 - 国内知名Minecraft中文主题网站 (minecraftzw.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.mc-bbs.vip/&quot;&gt;Minecraft(我的世界)中文论坛——Minecraft中文站,我的世界中文论坛,我的世界论坛 - (mc-bbs.vip)&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>WizTree</title><link>https://h1s97x.github.io/posts/notes/notes-wiztree/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-wiztree/</guid><description>在 Windows 上，存储感知 这个小功能可以自动地、按计划地清除无用的系统缓存文件和应用程序主动声明的缓存文件，CCleaner 这类第三方清理工具则可以清</description><pubDate>Sat, 27 Dec 2025 15:46:11 GMT</pubDate><content:encoded>&lt;h2&gt;快速清理 Windows 大文件，它比「老牌」更好用：WizTree | App +1&lt;/h2&gt;
&lt;p&gt;在 Windows 上，&lt;a href=&quot;https://sspai.com/post/58167&quot;&gt;存储感知&lt;/a&gt; 这个小功能可以自动地、按计划地清除无用的系统缓存文件和应用程序主动声明的缓存文件，&lt;a href=&quot;https://sspai.com/item/92&quot;&gt;CCleaner&lt;/a&gt; 这类第三方清理工具则可以清理掉更多不同软件留下来的垃圾。&lt;/p&gt;
&lt;p&gt;但上述方法一般用于自动化、周期化的自动清理，针对一些一时有用、有意存放的大文件或无意间散落在磁盘里的冗余数据，分析、定位然后根据实际需求决定去留是更加保险的方法。&lt;/p&gt;
&lt;p&gt;针对大文件扫描和分析，Windows 上比较老牌的工具有 &lt;a href=&quot;https://sspai.com/post/42425&quot;&gt;SpaceSniffer&lt;/a&gt;。2017 年 &lt;a href=&quot;https://sspai.com/u/jfo2ptxo&quot;&gt;@Jerry___&lt;/a&gt; 在他的文章中介绍说：SpaceSniffer 是一款免费便携的绿色软件，整体安装包只有 1.58MB……但到了 2021 年，距离 2016 年 10 月 2 日的最后一次更新已经有四年多的时间了，这样的  SpaceSniffer 放在今天使用，最直观的感受就是&lt;strong&gt;慢、而且不够准确。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;例如，在 Windows 10 支持网盘的「&lt;a href=&quot;https://sspai.com/post/58566&quot;&gt;按需同步&lt;/a&gt;」功能后，OneDrive 的文件夹采用了 &lt;a href=&quot;https://sspai.com/link?target=https%3A%2F%2Funix.stackexchange.com%2Fquestions%2F404159%2Faccessing-onedrive-folder-on-windows-partition&quot;&gt;特殊 NTFS 挂载点&lt;/a&gt; 的方法挂载在硬盘上，这就使得 SpaceSniffer 无法扫描到；除此之外，SpaceSniffer 的基本扫描原理其实就是在硬盘内实实在在地检索文件大小，在固态硬盘上的扫描速度尚且可以接受，但如果在有很多小文件的机械硬盘上，搜索速度就肉眼可见地慢了下来。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/6f93e853d433fc5fe630b4ae270735bf&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;相比之下，今天要介绍的 WizTree 则在速度和新特性兼容上好了不少。&lt;/p&gt;
&lt;p&gt;WizTree 在自家官网上号称最快的磁盘空间分析工具（The FASTEST Disk Space Analyser），虽然有点不太「广告法」，但实测下来它的快也是可以感知到的：WizTree 使用了与 Everything 相类似的技术，利用 NTFS 分区的特性直接读取 MFT 记录进行分析，达到了「秒排序」的效果。同时它也能够识别通过网络存储（Samba）挂载于 Windows 资源管理器中的驱动器。&lt;/p&gt;
&lt;p&gt;具体的速度我录制了一个 GIF 来展示，注意 GIF 停止录制时右边的 SpaceSniffer 并未完成扫描：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/aa09508bb17a1563ef30ed0ca39144a1.gif&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;对于使用 NTFS 新特性的文件夹，WizTree 可以通过「大小」「分配」两个不同的属性来分辨文件是否已经被「释放空间」。这两个概念你可以这样理解：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;「大小」即文件的大小，在没有使用「按需同步」时，它与文件的占用空间理论上一致&lt;/li&gt;
&lt;li&gt;「分配」即文件被分配的空间。如果一个文件在 OneDrive 云端存在，但没有被下载到本地，或文件被主动释放空间。这一类情况下文件被「分配」的空间将会是 0&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如下图，我的OneDrive 中存放着一些已经被「释放」的系统镜像，它们的大小可以正常显示，「分配」一栏也能正确显示分配（占用）空间为 0.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.sspai.com/2021/01/02/article/1aaf27c4326caeb1005f861b83f7b04d?imageView2/2/w/1120/q/90/interlace/1/ignore-error/1&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;当然，对于没有明显「捷径」可走的 FAT / exFAT 分区，WizTree 的表现也不错。以下是一个综合的性能测试表格：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;SpaceSniffer&lt;/th&gt;
&lt;th&gt;WizTree&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;机械，exFAT&lt;/td&gt;
&lt;td&gt;11.15s&lt;/td&gt;
&lt;td&gt;8.02s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;机械，NTFS&lt;/td&gt;
&lt;td&gt;24.23s&lt;/td&gt;
&lt;td&gt;5.24s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;固态，NTFS（NVMe，系统分区）&lt;/td&gt;
&lt;td&gt;76.97s&lt;/td&gt;
&lt;td&gt;2.53s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;固态，exFAT（Portable）&lt;/td&gt;
&lt;td&gt;1.75s&lt;/td&gt;
&lt;td&gt;0.55s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;除了传统的树形大小比较、自动绘制的文件夹大小图之外，WizTree 还提供了分扩展名占用的空间统计，方便你搞懂这个盘究竟装了些什么，或是找出「影音收藏」在哪个硬盘。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/2d4d97716086944550afac9a794c8949&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最后还是在这里提醒大家：此类扫描软件可以让你清晰、直观地看到硬盘内的空间占用，但在执行删除操作前，&lt;strong&gt;请确保你知道自己在删除的文件确实没有作用，否则可能会导致系统的崩溃&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.sspai.com/2021/01/02/article/1a9093d1de2f499929c4d7bb7498d7fb?imageView2/2/w/1120/q/90/interlace/1/ignore-error/1&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;你可以在官网下载 &lt;a href=&quot;https://sspai.com/link?target=https%3A%2F%2Fwiztreefree.com%2Fdownload&quot;&gt;WizTree&lt;/a&gt;，个人使用完全免费、自带中文。由于开发商打包时的问题，中文安装界面存在乱码的情况。建议在安装时选择英文的安装界面，安装完后再将语言改成中文。&lt;/p&gt;
</content:encoded></item><item><title>GDPR zh</title><link>https://h1s97x.github.io/posts/notes/notes-gdpr-zh/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-gdpr-zh/</guid><description>原文链接：欧盟首个数据保护条例GDPR明日生效，你可能需要这份中文版的全文 (tisi.org) 经过欧盟议会长达四年的讨论，欧盟《一般数据保护条例》（Gene</description><pubDate>Sat, 27 Dec 2025 15:46:11 GMT</pubDate><content:encoded>&lt;p&gt;原文链接：&lt;a href=&quot;https://www.tisi.org/5029&quot;&gt;欧盟首个数据保护条例GDPR明日生效，你可能需要这份中文版的全文 (tisi.org)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;经过欧盟议会长达四年的讨论，欧盟《一般数据保护条例》（General Data Protection Regulation，简称GDPR）终于将在2018年5月25日生效。&lt;/p&gt;
&lt;p&gt;在一些媒体的报道中，这一保护条例被称为“史上最严数据保护条例”。尽管这是现代社会保护个人数据与安全迈出的重要一步，但在国内外的许多媒体报道中，GDPR中的一些条款被误读或是错误理解引起了一些用户、公司、学者的恐慌。
在GDPR即将正式实施之际，严谨的阅读并理解GDPR的原文显得尤为重要。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;一般数据保护条例&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一章 一般条款&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第二章 原则&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第三章 数据主体的权利&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第四章 控制者和处理者&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第五章 将个人数据转移到第三国或国际组织&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第六章 独立监管机构&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第七章 合作与一致性&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第八章 救济、责任与惩罚&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第九章 和特定处理情形相关的条款&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第十章 授权法案与实施性法案&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一章 一般条款&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第1条 主要事项与目标&lt;/strong&gt;
1．本条例制定关于处理个人数据中对自然人进行保护的规则，以及个人 数据自由流动的规则。
2．本条例保护自然人的基本权利与自由，特别是自然人享有的个人数据 保护的权利。
3．不能以保护处理个人数据中的相关自然人为由，对欧盟内部个人数据 的自由流动进行限制或禁止。
&lt;strong&gt;第2条 适用范围&lt;/strong&gt;
1．本条例适用于全自动个人数据处理、半自动个人数据处理，以及形成 或旨在形成用户画像的非自动个人数据处理。
2．本条例不适用以下情形：
(a)欧盟法管辖之外的活动中所进行的个人数据处理；
(b)欧盟成员国为履行《欧盟基本条约》（TEU）第2章第5款所规定的活动而进行的个人数据处理；
(c)自然人在纯粹个人或家庭活动中所进行的个人数据处理；
(d) ）有关主管部门为预防、调查、侦查、起诉刑事犯罪、执行刑事处罚、防范及预防公共安全威胁而进行的个人数据处理。
3．欧盟机构、实体、办事处和规制机构所进行的个人数据处理，适用(EC)第 45/2001条例。根据本条例第98条，(EC)第45/2001条例和其他适用于此类个人数据处理的欧盟法案应当进行调整，以符合本条例的原则和规则。
4．本条例不影响2000/31/EC指令的适用，特别是2000/31/EC指令第12至15条所规定的中间服务商的责任规则的适用。
&lt;strong&gt;第3条 地域范围&lt;/strong&gt;
1．本例适用于在欧盟内部设立的数据控制者或处理者对个人数据的处理，不论其实际数据处理行为是否在欧盟内进行。
2．本条例适用于如下相关活动中的个人数据处理，即使数据控制者或处理者不在欧盟设立：
(a)为欧盟内的数据主体提供商品或服务——不论此项商品或服务是否要求数据主体支付对价；或
(b)对发生在欧洲范围内的数据主体的活动进行监控。
3．本条例适用于在欧盟之外设立，但基于国际公法成员国的法律对其有管辖权的数据控制者的个人数据处理。
&lt;strong&gt;第4条 定义&lt;/strong&gt;
就本条例而言：
(1)“个人数据”指的是任何已识别或可识别的自然人（“数据主体”）相关的信息；一个可识别的自然人是一个能够被直接或间接识别的个体，特别是通过诸如姓名、身份编号、地址数据、网上标识或者自然人所特有的一项或多项的身体性、生理性、遗传性、精神性、经济性、文化性或社会性身份而识别个体。
(2)“处理”是指任何一项或多项针对单一个人数据或系列个人数据所进行的操作行为，不论该操作行为是否采取收集、记录、组织、构造、存储、调整、更改、检索、咨询、使用、通过传输而公开、散布或其他方式对他人公开、排列或组合、限制、删除或销毁而公开等自动化方式。
(3)“限制处理”是指对存储的个人数据进行标记，以限制此后对该数据的处理行为。
(4)“用户画像”指的是为了评估自然人的某些条件而对个人数据进行的任何自动化处理，特别是为了评估自然人的工作表现、经济状况、健康、个人偏好、兴趣、可靠性、行为方式、位置或行踪而进行的处理。
(5)“匿名化”指的是在采取某种方式对个人数据进行处理后，如果没有额外的信息就不能识别数据主体的处理方式。此类额外信息应当单独保存，并且已有技术与组织方式确保个人数据不能关联到某个已识别或可识别的自然人。
(6)“档案系统”指的是根据某种特定标准——不论这种标准是去中心化的、分散的、功能性的或是基于地理而设置的——而可以访问的个人数据的结构化集合。
(7)“控制者”指的是那些决定——不论是单独决定还是共同决定——个人数据处理目的与方式的自然人或法人、公共机构、规制机构或其他实体；如果此类处理的方式是由欧盟或成员国的法律决定的，那么对控制者的定义或确定控制者的标准应当由欧盟或成员国的法律来规定。
(8)“处理者”指的是为数据控制者而处理个人数据的自然人或法人、公共机构、规制机构或其他实体。
(9)“接收者”指的是接收数据的自然人、法人、公共机构、规制机构或另一实体，不论其是否为第三方。然而，公共机构基于欧盟或成员国法律的某项特定调查框架而接收个人数据，则不应当被视为接收者；公共机构对此类数据的处理，应当根据处理目的遵循可适用的数据保护规则。
(10)“第三方”指的是除了数据主体、控制者、处理者、控制者或处理者直接授权其处理个人数据之外的自然人或法人、公共机构、规制机构或组织。
(11)数据主体的“同意”指的是数据主体通过一个声明，或者通过某项清晰的确信行动而自由作出的、充分知悉的、不含混的、表明同意对其相关个人数据进行处理的意愿。
(12) “个人数据泄露”是指由于违反安全政策而导致传输、储存、处理中的个人数据被意外或非法损毁、丢失、更改或未经同意而被公开或访问。
(13)“基因数据”指的是和自然人的遗传性或获得性基因特征相关的个人数据，这些数据可以提供自然人生理或健康的独特信息，尤其是通过对自然人生物性样本进行分析而可以得出的独特信息。
(14)“生物性识别数据”指的是基于特别技术处理自然人的相关身体、生理或行为特征而得出的个人数据，这种个人数据能够识别或确定自然人的独特标识，例如脸部形象或指纹数据。
(15)“和健康相关的数据”指的是那些和自然人的身体或精神健康相关的、显示其个人健康状况信息的个人数据，包括和卫生保健服务相关的服务。
(16)“主要营业机构”指的是：
(a)如果控制者在不止一个成员国内有多处营业机构，那么其在欧盟的管理中心所在地是主要营业机构，除非个人数据处理的目的与方式是由控制者的另一个机构决定的，并且这一机构有权实施此决定，在这种情况下，做出此类决定的机构应当被认为是主要营业机构；
(b)如果处理者在不止一个成员国内具有多处机构，那么其在欧盟的管理中心所在地是主要营业机构。如果处理者在欧盟没有管理中心，那么在处理者需要遵守本条例所规定的特殊责任的前提下，其在欧盟的主要处理活动发生地的机构应当被视为主要营业机构。
(17)“代表”指的是控制者或处理者根据第27条在欧盟书面委任，代表控制者或处理者承担本条例所规定的相应责任的自然人或法人。
(18)“经济主体”的含义是采用任意法律形式的进行经济活动的自然人或法人，包括经常进行经济活动的合伙企业或协会；
(19)“企业集团”的含义是控股企业和被控股企业；
(20)“有约束力的公司规则”指的是在某成员国内设立的控制者或处理者，为了在企业集团内部或进行联合经济活动的经济主体内部将个人数据转移或多次转移给位于第三国或多个第三国的控制者或处理者，所遵循的个人数据保护政策。
(21)“监管机构”指的是成员国根据第51条而设立的独立性公共机构。
(22)“相关监管机构”指的是基于如下原因而和个人数据处理相关的监管机构：
(a)控制者或处理者是在某监管机构所在的成员国的境内所设立的；
(b)数据处理对居住在某监管机构所在地成员国的数据主体具有实质性影响；或者
(c)该监管机构已经收到一项申诉；
(23)“跨境处理”指的是：
(a)个人数据处理发生在一个控制者或处理者在多个成员国所设立的多个营业机构内；或者
(b)个人数据处理是在欧盟内的控制者或处理者的单一营业机构内进行的，但其对不止一国的数据主体具有实质性影响。
(24)“相关和合理的异议”指的是对是否存在违反本条例的情形，或者某项和控制者或处理者相关的初步设想是否符合本条例的异议——已有证据表明，这种初步设想的决定会对数据主体的基本权利和自由，以及在某些情形下对欧盟的个人数据的自由流通会带来风险。
(25)“信息社会服务”指的是欧洲议会和欧盟理事会的(EU) 2015/1535指令在第1（1）条（b）点所定义的服务。
(26)“国际组织”指的是依照国际公法、或根据两个或多个国家协议所设立的组织及其下属机构。
1．本例适用于在欧盟内部设立的数据控制者或处理者对个人数据的处理，不论其实际数据处理行为是否在欧盟内进行。
2．本条例适用于如下相关活动中的个人数据处理，即使数据控制者或处理者不在欧盟设立：
(a)为欧盟内的数据主体提供商品或服务——不论此项商品或服务是否要求数据主体支付对价；或
(b)对发生在欧洲范围内的数据主体的活动进行监控。
3．本条例适用于在欧盟之外设立，但基于国际公法成员国的法律对其有管辖权的数据控制者的个人数据处理。
&lt;strong&gt;第二章 原则&lt;/strong&gt;
&lt;strong&gt;第5条 个人数据处理原则&lt;/strong&gt;
1．对于个人数据，应遵循下列规定：
(a)对涉及到数据主体的个人数据，应当以合法的、合理的和透明的方式来进行处理（“合法性、合理性和透明性”）；
(b)个人数据的收集应当具有具体的、清晰的和正当的目的，对个人数据的处理不应当违反初始目的。根据第89（1）条，因为公共利益、科学或历史研究或统计目的而进一步处理数据，不视为违反初始目的（“目的限制”）；
(c)个人数据的处理应当是为了实现数据处理目的而适当的、相关的和必要的（“数据最小化”）；
(d)个人数据应当是准确的，如有必要，必须及时更新；必须采取合理措施确保不准确的个人数据，即违反初始目的的个人数据，及时得到擦除或更正（“准确性”）；
(e)对于能够识别数据主体的个人数据，其储存时间不得超过实现其处理目的所必需的时间；超过此期限的数据处理只有在如下情况下才能被允许：为了实现公共利益、科学或历史研究目的或统计目的，为了保障数据主体的权利和自由，并采取了本条例第89（1）条所规定的合理技术与组织措施。（“限期储存”）；
(f) 处理过程中应确保个人数据的安全，采取合理的技术手段、组织措施，避免数据未经授权即被处理或遭到非法处理，避免数据发生意外毁损或灭失（“数据的完整性与保密性”）。
2．控制者有责任遵守以上第1段，并且有责任对此提供证明。（“可问责性”）。
&lt;strong&gt;第6条 处理的合法性&lt;/strong&gt;
1．只有满足至少如下一项条件时，处理才是合法的，且处理的合法性只限于满足条件内的处理：
(a)数据主体已经同意基于一项或多项目的而对其个人数据进行处理；
(b)处理对于完成某项数据主体所参与的契约是必要的，或者在签订契约前基于数据主体的请求而进行的处理；
(c) 处理是控制商履行其法定义务所必需的；
(d)处理对于保护数据主体或另一个自然人的核心利益所必要的；
(e)处理是数据控制者为了公共利益或基于官方权威而履行某项任务而进行的；
(f)处理对于控制者或第三方所追求的正当利益是必要的，这不包括需要通过个人数据保护以实现数据主体的优先性利益或基本权利与自由，特别是儿童的优先性利益或基本权利与自由。
第1段（f）点不适用公共机构在履行其任务时的处理。
2．对于第1段（c）和（e）所规定的处理，成员国可以维持或新制定更多具体条款，以适应本条例规则的适用，成员国为了确保合法与合理处理，可以制定更为明确的规定，包括第9章所规定的其他特定的处理情形。
3．第1段（c）和（e）所规定的处理的基准应当通过如下法律进行规定：
(a)欧盟法；或者
(b)控制者所属的成员国的法律。
处理的目的应当在此法律基准上进行确定，而对于第1段（e）所规定的处理，处理的目的应当是控制者为了公共利益或基于官方权威而履行某项任务。此法律基准可以包含如下特定条款，以适应对本条例规则的适用：对控制者处理的合法性进行监控的一般条件；可以被处理的数据类型；相关数据主体；个人数据公开的目的，以及其可能被公开给的对象；目的限定；储存期限；包括第9章所规定的其他特定的处理情形在内的处理操作和处理程序。欧盟或成员国的法律应当满足公共利益的目标，且应当与实现正当目的成比例。
4．若处理是出于收集个人数据以外的其他目的，如果该目的未经数据主体同意或并非是基于联盟或成员国的法律（在一个民主社会中，若要实现第23（1）条中的目的，法律是必要且合适的），那么为确保该目的与初始目相容，控制商应当考虑以下因素，但不限于以下因素：
(a)个人数据收集时的目的与计划进一步处理的目的之间的所有关联性；
(b)个人数据收集时的语境，特别是数据主体与控制者之间的关系；
(c)个人数据的性质，特别是某些特定类型的个人数据是否符合第9条的规定，或者与刑事定罪和刑事违法相关的个人数据是否符合第10条的规定；
(d) 数据主体计划进一步处理可能造成的结果；
(e)是否具有加密与匿名化措施等恰当保护措施；
&lt;strong&gt;第7条 同意的条件&lt;/strong&gt;
1．当处理是建立在同意基础上的，控制者需要能证明，数据主体已经同意对其个人数据进行处理。
2．如果数据主体的同意是在涉及到其他事项的书面声明的情形下作出的，请求获得同意应当完全区别于其他事项，并且应当以一种容易理解的形式，使用清晰和平白的语言。任何违反本条例的声明都不具有约束力。
3．数据主体应当有权随时撤回其同意。在撤回之前，对于基于同意的处理，其合法性不受影响。在数据主体表达同意之前，数据主体应当被告知这点。撤回同意应当和表达同意一样简单。
4．分析同意是否是自由做出的，应当最大限度地考虑一点是：对契约的履行——包括履行条款所规定的服务——是否要求同意履行契约所不必要的个人数据处理。
&lt;strong&gt;第8条 信息社会服务中适用儿童同意的条件&lt;/strong&gt;
1．在第6（1）条（a）适用的情形下，对于为儿童直接提供信息社会服务的请求，当儿童年满16周岁，对儿童个人数据的处理是合法的。当儿童不满16周岁，只有当对儿童具有父母监护责任的主体同意或授权，此类处理才是合法的。
2．对于年满13周岁的情形，成员国的法律可以降低年龄要求。
3．控制者应当采取合理的努力，结合技术可行性，确保此类情形中对儿童具有父母监护责任的主体已经授权或同意。
第1段不应影响成员国的一般合同法，例如关于儿童的合同有效性、形成与效力的规则。
&lt;strong&gt;第9条 对特殊类型个人数据的处理&lt;/strong&gt;
1．对于那些显示种族或民族背景、政治观念、宗教或哲学信仰或工会成员的个人数据、基因数据、为了特定识别自然人的生物性识别数据、以及和自然人健康、个人性生活或性取向相关的数据，应当禁止处理。
2．如果具有如下条件之一，第1段将不适用：
(a)数据主体明确同意基于一个或多个特定目的而授权处理其个人数据，但依照欧盟或成员国的法律规定，数据主体无权解除第1段中所规定的禁令的除外；
(b)处理对于控制者履行责任以及行使其特定权利是必要的，或者对于在雇佣、社会安全与社会保障法领域采取符合欧盟或成员国法律或集体协议的措施以保护数据主体的根本权利和利益是必要的；
(c)数据主体因为身体原因或法律原因而无法表达同意，但处理对于保护数据主体或另一自然人的核心利益却是必要的；
(d)基金、协会或其它具有政治、哲学、宗教或工会目的的非盈利机构的正当性活动中所进行的处理，并且已经采取了恰当的保护措施；或者处理目的仅仅和机构成员、之前成员或具有经常联系的人相关，并且个人数据在未经数据主体同意前不对实体外的人公开；
(e)对数据主体已经明显公开的相关个人数据的处理；
(f)当处理对于提起、行使或辩护法律性主张必要时，或者法院在其所有的司法活动中所进行的处理；
(g)处理对实现实质性的公共利益必要的，建立在欧盟或成员国的法律基准之上、对实现目标是相称的，尊重数据保护权的核心要素，并且为数据主体的基本权利和利益提供合适和特定的保护措施；
(h)处理对于预防性医学或临床医学目的是必要的，或者对于评估雇员的工作能力、医疗诊断、提供——基于欧盟或成员国法律，或遵循和健康职业机构签订的契约并遵循第3段所规定的情形与保障措施——健康或社会保健或治疗或管理健康或社会保健体系是必要的；
(i)在公共健康领域，处理是为了实现公共利益所必要的，例如，在欧盟或成员国内已经为保障数据主体的权利与自由而采取合适与特定措施的法律基础上，处理对于预防严重的跨境健康威胁是必要的，或者为了保障医疗质量和安全、医疗产品或医疗设备的高质量和安全是必要的；或者
(j)处理对于实现符合第89(1)条公共利益、科学或历史研究目的或统计目的是必要的，处理采取了与其期望目的所相称的处理，尊重数据保护权的核心要素，并且对数据主体的基本权利与利益采取了合适与特定的措施。
3．根据欧盟或成员国的有权机构所制定的法律或规则而具有保守职业性秘密责任的职业主体，或者根据欧盟或成员国的有权机构所制定的法律或规则而具有保守秘密责任的自然人，可以为了第2段（h）点所规定的目的而处理第1段所规定的个人数据。
4．对于基因数据、生物性识别数据或健康相关数据的处理，成员国可以维持原有规定，或者作出新的规定，包括对处理基因数据、生物性识别数据或健康相关数据进行限定。
&lt;strong&gt;第10条 处理涉及犯罪定罪与违法的个人数据&lt;/strong&gt;
处理和犯罪定罪与违法相关的个人数据，或处理第6（1）条规定的与安全措施相关的个人数据，只有如下情形才能被允许：当个人数据处理为官方机构控制，或者当欧盟或成员国的法律授权进行处理，并且采取了恰当的措施保障数据主体的权利与自由。任何犯罪定罪的全面性登记只能由官方机构进行。
&lt;strong&gt;第11条 不需要识别的处理&lt;/strong&gt;
1．如果控制者处理个人数据的目的不需要或不再需要控制者对数据主体进行识别，控制者就不再具有为了遵循本条例而维持、获取或处理额外信息以识别数据主体的责任。
2．对于第1段所规定的情形，如果控制者能够证明其不适合识别数据主体，如有可能，数据控制者应当告知数据主体。在此类情形下，除非数据主体为了行使第15至20条所规定的权利，需要提供额外信息而使得对其识别变得可能，第15至20条将不应适用。
&lt;strong&gt;第三章 数据主体的权利&lt;/strong&gt;
&lt;strong&gt;第一部分 透明性与模式&lt;/strong&gt;
&lt;strong&gt;第12条 信息、交流与模式的透明性——保证数据主体权利的行使&lt;/strong&gt;
1．对于和个人信息处理相关的第13和第14条规定的所有信息、或者第15条至22条以及34条所规定的所有交流，控制者应当以一种简洁、透明、易懂和容易获取的形式，以清晰和平白的语言来提供；对于针对儿童的所有信息，尤其应当如此。信息应当以书面形式或其他形式提供，包括在合适的情况下通过电子方式提供。若数据主体的身份可通过其他途径得到证实，那么控制者可依主体申请以口头方式提供相关信息。
2．控制者应当对数据主体行使第15至22条的权利而提供帮助。对于第11（2）条所规定的情形，当数据主体请求其行使第15至22条的权利，控制者不应拒绝，除非控制者能够证明其并不适宜识别数据主体。
3．在数据主体根据第15至22条的规定提出请求后，控制者应当提供信息，不应无故拖延，在任何情形下应当在收到请求后一个月内提供信息。在必要的情形下，考虑到请求的复杂性和多样性，这个期限可以再延长两个月。如果有此类延长，控制者应当在收到请求的一个月内将此类延长以及延长原因告知数据主体。当数据主体以电子形式做出请求，在可行的情况下，对信息的提供也应当以电子形式提供，除非数据主体有不同请求。
4．如果控制者没有采取相应的行动对数据主体的请求做出回应，那么应当及时告知该数据主体其在收到请求后一个月内未能采取行动的具体原因，同时可向监管机构提出申诉，寻求司法救济。
5．第13和第14条所规定的信息以及第15至22条和34条所规定的所有交流与行为都应当是免费的。当数据主体的请求明显不具备正当理由或超过必要限度，特别是当请求是重复性的时候，控制者可以：
(a)结合提供信息、交流或相应行动的行政花费，收取一定的合理费用；或者
(b)拒绝对请求作出行动。
控制者有责任证明数据主体的请求明显是毫无根据的或过分的。
6．在不影响第11条的前提下，控制者可以对第15至21条中提出要求的自然人的身份有合理怀疑，要求数据主体提供必要的额外信息以确认数据主体的身份。
7．根据第13条和14条提供给数据主体的信息可以和标准化的图标一起提供，以便于数据主体以一种一目了然的、易懂的和清晰的方式对计划的数据处理有全盘理解。当图标以电子化的方式提供，它们必须是机器可读的。
8．对于确定图标所提供的信息以及提供标准化图标的程序，欧盟理事会将有权根据第92条制定授权行动。
&lt;strong&gt;第二部分 信息与对个人数据的访问&lt;/strong&gt;
&lt;strong&gt;第13条 收集数据主体个人数据时应当提供的信息&lt;/strong&gt;
1．当收集和数据主体相关的个人数据时，控制者应当为数据主体提供如下信息：
(a)控制者的身份与详细联系方式，以及如果适用的话，控制者的代表；
(b)数据保护官的详细联系方式，如果适用的话；
(c)处理将要涉及到的个人数据的目的，以及处理的法律基础；
(d)当处理是基于（f）点或第6（1）条的时候，控制者或第三方的正当利益；
(e)个人数据的接收者或者接收者的类型，如果有的话；
(f)如果适用的话，控制者期望将数据转移到第三国或国际组织的事实、欧盟委员会作出或未作出充分决定的事实，或者，在第46或47条或者第49（1）条的第二小段所规定的转移情形中，所采取的适当保障措施的参考资料、获取它们备份的方式，或者在那里可以获取它们。
2．除了第1段所规定的信息，控制者应当在获取个人数据时为数据主体提供确保合理与透明处理所必要的进一步信息：
(a)个人数据将被储存的期限，以及确定此期限的标准；
(b)数据主体所拥有的权利：可以要求控制者提供对个人数据的访问、更正或擦除，或者限制或反对相关处理的权利；数据携带权；
(c)当处理是根据第6（1）条或第9（2）条的（a）点而进行的，数据主体拥有可以随时撤回——这种撤回不会影响撤回之前根据同意而进行处理的合法性——同意的权利；
(d)向监管机构进行申诉的权利；
(e)提供个人数据是一项制定法还是合同法的要求，是否对于缔结一项契约是必要的，数据主体是否有责任提供个人数据，以及没有提供此类数据会造成的可能后果。
(f)存在自动化的决策，包括第22（1）和（4）条所规定的用户画像，以及在此类情形下，对于相关逻辑、包括此类处理对于数据主体的预期后果的有效信息。
3．若控制者进一步处理个人信息的目的与收集个人信息的目的不一致，那么，控制者应当在进一步处理之前向数据主体提供此类目的的信息，以及提供第2段所规定的相关进一步信息。
4．在数据主体已经拥有信息的情况下，第1，2，3段不应当适用。
&lt;strong&gt;第14条 未获得数据主体个人数据的情形下，应当提供的信息&lt;/strong&gt;
1．当个人数据还没有从数据主体那里收集，控制者应当向数据主体提供如下信息：
(a)控制者的身份与详细联系方式，以及如果适用的话，控制者的代表；
(b)如果适用的话，数据保护官的详细联系方式；
(c)处理将要涉及到的个人数据的目的，以及处理的法律基础；
(d)相关个人数据的类型；
(e)个人数据的接收者或者接收者的类型，如果有的话；
(f)如果适用的话，控制者期望将数据转移到第三国或国际组织、欧盟委员会作出或未作出的充足保护的认定，或者，在第46或47条或者第49（1）条的第二小段所规定的转移情形中，所采取的适当保障措施的参考资料、获取它们备份的方式，或者在那里可以获取它们。
2．除了第1段所规定的信息，控制者应当向数据主体提供如下确保涉及到数据主体的处理是合理与透明的必要信息：
(a)个人数据将被储存的期限，或者如果不可能的话，用来确定此期限的标准；
(b)当处理是根据第6（1）条（f）点而进行的，控制者或第三方所追求的正当利益；
(c)数据主体存在如下权利，可以要求控制者提供对个人数据的访问、更正或擦除，或者限制或反对相关处理，数据携带权；
(d)当处理是根据第6（1）条或第9（2）条的（a）点而进行的，数据主体拥有可以随时撤回——这种撤回不会影响撤回之前根据同意而进行处理的合法性——同意的权利；
(e)向监管机构进行申诉的权利；
(f)个人数据的来源，以及如果适用的话，其来源是否可以是公开性的资源；
(g)存在自动化的决策，包括第22（1）和（4）条所规定的用户画像，以及在此类情形下，对于相关逻辑、包括此类处理对于数据主体的预期后果的有效信息。
3．控制者应当按如下方式提供第1段和第2段所规定的信息：
(a)应当在获得个人数据后的一段合理期限内提供信息，如果考虑到个人数据处理的特定情形，应当至少在一个月以内；
(b)如果个人数据是被用来和数据主体进行沟通的，最晚应当在其和数据主体进行第一次沟通时提供信息；
(c)如果个人数据将被计划披露给另一个接收者，那么最晚应当在个人数据被第一次披露时提供信息。
4．当控制者因为与收集个人信息时不一致的目的进一步处理个人信息，控制者应当在进一步处理之前向数据主体提供此类目的的信息，以及提供第2段所规定的相关进一步信息。
5．在如下情形中，第1至4段不适用：
(a)数据主体已经拥有信息；
(b)此类信息的提供是不可能的，或者说需要付出某种不相称的工作，在如下情形中尤其不适用：为了实现公共利益、科学或历史研究目的或统计目的，为了保障数据主体的权利和自由，并采取了本条例第89（1）条所规定的合理技术与组织措施；或者本条第1段所规定的责任会严重妨碍实现处理的目标。在此类情形中，控制者应当采取恰当的措施保护数据主体的权利与自由与正当利益，包括使得信息可以公开获取；
(c)欧盟或成员国为控制者特别制定了获取或公开信息的法律，并且已经对保护数据主体的正当利益制定了恰当的措施；
(d)当个人数据必须保密，必须遵守欧盟或成员国法律所规定的职业秘密责任，包括制定法上的保守秘密责任。
&lt;strong&gt;第15条 数据主体的访问权&lt;/strong&gt;
1．数据主体应当有权从控制者那里得知，关于其的个人数据是否正在被处理，如果正在被处理的话，其应当有权访问个人数据和获知如下信息：
(a)处理的目的；
(b)相关个人数据的类型；
(c)个人数据已经被或将被披露给接收者或接收者的类型，特别是当接收者属于第三国或国际组织时；
(d)在可能的情形下，个人数据将被储存的预期期限，或者如果不可能的话，确定此期限的标准；
(e)数据主体要求控制者纠正或擦除个人数据、限制或反对对数据主体相关的个人数据进行处理的权利；
(f)向监管机构进行申诉的权利；
(g)当个人数据不是从数据主体那里收集的，关于来源的任何信息；
(h)存在自动化的决策，包括第22（1）和（4）条所规定的数据分析，以及在此类情形下，对于相关逻辑、包括此类处理对于数据主体的预期后果的有效信息。
2．当个人数据被转移到第三国或一个国际组织，数据主体应当有权获知和转移相关的符合第46条的恰当的保障措施。
3．控制者应当对进行处理的个人数据提供一份备份。对于任何数据主体所要求的额外备份，控制者可以根据管理花费而收取合理的费用。当数据主体通过电子方式而请求，且除非数据主体有其他请求，信息应当以通常使用的电子形式提供。
4．获取第三段中所规定的备份的权利不应当对他人的权利与自由产生负面影响。
&lt;strong&gt;第三部分 更正与擦除&lt;/strong&gt;
&lt;strong&gt;第16条 更正权&lt;/strong&gt;
数据主体应当有权从控制者那里及时得知对与其相关的不正确信息的更正。在考虑处理目的的前提下，数据主体应当有权完善不充分的个人数据，包括通过提供额外声明的方式来进行完善。
&lt;strong&gt;第17条 擦除权（“被遗忘权”）&lt;/strong&gt;
1．数据主体有权要求控制者擦除关于其个人数据的权利，当具有如下情形之一时，控制者有责任及时擦除个人数据：
(a)个人数据对于实现其被收集或处理的相关目的不再必要；
(b)处理是根据第6（1）条（a）点，或者第9（2）条（a）点而进行的，并且没有处理的其他法律根据，数据主体撤回在此类处理中的同意；
(c)数据主体反对根据第21（1）条进行的处理，并且没有压倒性的正当理由可以进行处理，或者数据主体反对根据第21（2）条进行的处理；
(d)已经存在非法的个人数据处理；
(e)为了履行欧盟或成员国法律为控制者所设定的法律责任，个人数据需要被擦除；
(f)已经收集了第8（1）条所规定的和提供信息社会服务相关的个人人数据。
2．当控制者已经公开个人数据，并且负有第1段所规定的擦除个人数据的责任，控制者应当考虑可行技术与执行成本，采取包括技术措施在内的合理措施告知正在处理个人数据的控制者们，数据主体已经要求他们擦除那些和个人数据相关的链接、备份或复制。
3．当处理对于如下目的是必要的，第1和第2段将不适用：
(a)为了行使表达自由和信息自由的权利；
(b)控制者执行或者为了执行基于公共利益的某项任务，或者基于被授予的官方权威而履行某项任务，欧盟或成员国的法律要求进行处理，以便履行其法律职责；
(c)为了实现公共健康领域符合第9（2）条（h）和（i）点以及第9（3）条的公共利益而进行的处理；
(d)如果第1段所提到权利会受严重影响，或者会彻底阻碍实现第89(1)条的公共利益目的、科学或历史研究目的或统计目的；或者
(e)为了提起、行使或辩护法律性主张。
&lt;strong&gt;第18条 限制处理权&lt;/strong&gt;
1．当存在如下情形之一时，数据主体有权要求控制者对处理进行限制：
(a)数据主体对个人数据的准确性有争议，并给与控制者以一定的期限以核实个人数据的准确性；
(b)处理是非法的，并且数据主体反对擦除个人数据，要求对使用其个人数据进行限制；
(c)控制者不再需要个人数据以实现其处理的目的，但数据主体为了提起、行使或辩护法律性主张而需要该个人数据；
(d)数据主体根据第21（1）条的规定而反对处理，因其需要确定控制者的正当理由是否优先于数据主体的正当理由。
2．当处理受第1段的规定所限制，除了储存的情形，此类个人数据只有在如下情形中才能进行处理：获取了数据主体的同意，或者为了提起、行使或辩护法律性主张，或者为了保护另一个自然人或法人的权利，或者为了欧盟或某个成员国的重要公共利益。
3．那些根据第1段规定已经获取了对处理进行限制的数据主体，在限制被解除前，控制者应当告知数据主体。
&lt;strong&gt;第19条 关于更正或擦除或限制处理中的通知责任&lt;/strong&gt;
对于所有根据第16、17（1）、18条而限制或擦除个人数据，或限制处理个人数据，控制者都应当将其告知个人数据已经被披露给的每个接收者——除非此类告知是不可能的，或者需要付出不相称的工作。如果数据主体提出要求，控制者应当将关于接收者的情形告知数据主体。
&lt;strong&gt;第20条 数据携带权&lt;/strong&gt;
1．当存在如下情形时，数据主体有权获得其提供给控制者的相关个人数据，且其获得个人数据应当是经过整理的、普遍使用的和机器可读的，数据主体有权无障碍地将此类数据从其提供给的控制者那里传输给给另一个控制者：
(a)处理是建立在第6（1）条（a）点或9（2）条（a）点所规定的同意，或者6（1）条所规定的合同的基础上的；
(b)处理是通过自动化方式的。
2．在行使第1段所规定的携带权时，如果技术可行，数据主体应当有权将个人数据直接从一个控制者传输到另一个控制者。
3．行使第1段所规定的权利，不能影响第17条的规定。对于控制者为了公共利益，或者为了行使其被授权的官方权威而进行的必要处理，这种权利不适用。
4．第1段所规定的权利不能对他人的权利或自由产生负面影响。
&lt;strong&gt;第四部分 反对的权利和自动化的个人决策&lt;/strong&gt;
&lt;strong&gt;第21条 反对权&lt;/strong&gt;
1．对于根据第6（1）条（e）或（f）点而进行的关乎数据主体的数据处理，包括根据这些条款而进行的用户画像，数据主体应当有权随时反对。此时，控制者须立即停止针对这部分个人数据的处理行为，除非控制者证明，相比数据主体的利益、权利和自由，具有压倒性的正当理由需要进行处理，或者处理是为了提起、行使或辩护法律性主张。
2．当因为直接营销目的而处理个人数据，数据主体有权随时反对为了此类营销而处理相关个人数据，包括反对和此类直接营销相关的用户画像。
3．当数据主体反对为了直接营销目的而处理，将不能为了此类目的而处理个人数据。
4．至晚在和数据主体所进行的第一次沟通中，第1段和第2段所规定的权利应当让数据主体明确知晓，且应当与其他信息区分开来，清晰地告知数据主体。
5．在适用信息社会服务的语境中，尽管存在2002/58/EC指令的规定，数据主体仍可以使用技术性条件、通过自动化方式行使反对权。
6．当个人数据是为了第89（1）条所规定的科学目的或历史研究目的或统计目的，数据主体基于其特定情形应当有权反对对关乎其的个人数据进行处理，除非处理对于实现公共利益的某项任务是必要的。
&lt;strong&gt;第22条 自动化的个人决策，包括用户画像&lt;/strong&gt;
1．数据主体有权反对此类决策：完全依靠自动化处理——包括用户画像——对对数据主体做出具有法律影响或类似严重影响的决策。
2．当决策存在如下情形时，第1段不适用：
(a)当决策对于数据主体与数据控制者的合同签订或合同履行是必要的；
(b)当决策是欧盟或成员国的法律所授权的，控制者是决策的主体，并且已经制定了恰当的措施保证数据主体的权利、自由与正当利益；或者
(c)当决策建立在数据主体的明确同意基础之上。
3．在第2段所规定的（a）和（c）点的情形中，数据控制者应当采取适当措施保障数据主体的权利、自由、正当利益，以及数据主体对控制者进行人工干涉，以便表达其观点和对决策进行异议的基本权利。
4．第2段所规定的决策的基础不适用于第9（1）条所规定的特定类型的个人数据，除非符合第9（2）条（a）点或（g）点的规定，并且已经采取了保护数据主体权利、自由与正当利益的措施。
第五部分限制
&lt;strong&gt;第23条 限制&lt;/strong&gt;
1．若控制者或处理者受欧盟法律或某成员国法律的调整，那么欧盟法律或该成员国法律可以通过立法手段限制第12至22条、34条以及第5条所赋予的责任范围与权利范围，只要其法律条款和第12至22条所赋予的责任与权利相对应。如果此类限制尊重基本权利与自由的核心要素，并且此类限制是实现如下民主社会中的目的所必要和成比例的措施，那么此类限制应当被允许：
(a)国家安全；
(b)国防；
(c)公共安全；
(d)预防、调查、侦查、起诉刑事违法进行或者执行刑法，包括保障公共安全和预防对公共安全的威胁；
(e)其他些欧盟或某个成员国的重要一般公共利益，特别是欧盟或某个成员国的经济或金融利益，包括财政、预算、税收事项、公共健康和社会安全；
(f)司法独立和司法诉讼的保护；
(g)为了规制性职业而预防、调查、保护和起诉违反伦理的行为；
(h)和行使（a）（b）（c）（d）（e）（g）点中所规定的官方权威相联系的某项监控、调查或规制功能；
(i)保护数据主体或其他人的权利和自由；
(j)实施民事法律主张。
2．需要特别注意的是，至少在涉及到如下情形时，任何第1段所规定的立法措施都应当包含特定条款，规定：
(a)处理的目的或处理的类型；
(b)个人数据的类型；
(c)施加限制的范围；
(d)防止滥用或非法性访问或转移的措施；
(e)控制者的具体情况或控制者类型的具体情况；
(f)在考虑了处理的性质、范围和目的或处理类型之后所制定的储存期限和可适用的保障措施；
(g)数据主体的权利和自由所面临的风险；以及
(h)数据主体获知限制的权利，除非这种权利可能影响实现限制的目的。
&lt;strong&gt;第四章 控制者和处理者&lt;/strong&gt;
&lt;strong&gt;第一部分 一般性责任&lt;/strong&gt;
&lt;strong&gt;第24条 控制者的责任&lt;/strong&gt;
1．在考虑了处理的性质、范围、语境与目的，以及考虑了处理对自然人权利与自由所带来的不同概率和程度的风险后，控制者应当采取恰当的技术与组织措施，保证处理符合本条例规定的，并且能够证明处理符合本条例规定。必要时，这些措施应当被审查。
2．第1段所规定的措施，当和处理活动成比例时，应当包括控制者所采用的合适的数据保护政策。
3．遵守第40条所规定的已生效的行为准则，或遵守第42条规定的已生效的认证机制，这可以被用以证明控制者责任的合规性。
&lt;strong&gt;第25条 通过设计的数据保护和默认的数据保护&lt;/strong&gt;
1．在考虑了最新水平、实施成本、处理的性质、处理的范围、处理的语境与目的，以及处理给自然人权利与自由带来的伤害可能性与严重性之后，控制者应当在决定处理方式时和决定处理时，应当采取合适的技术与组织措施，并且在处理中整合必要的保障措施，以便符合本条例的要求和保护数据主体的权利。例如，控制者可以采取匿名化，一种设计用来实施数据保护原则——比如数据最小化原则——的措施。
2．控制者有责任采取适当的技术与组织措施，以保障在默认情况下，只有某个特定处理目的所必要的个人数据被处理。这种责任适用于收集的个人数据的数量、处理的限度，储存的期限以及可访问性。尤其需要注意的是，此类措施必须确保，在默认情况下，如果没有个体介入，个人数据不能为不特定数量的自然人所访问。
3．根据第42条的某种已生效的认证机制，可以被用来证明本条第1段和第2段所规定的合规要求。
&lt;strong&gt;第26条 共同控制者&lt;/strong&gt;
1．当两个或更多控制者联合确定处理的目的与方法，它们就是共同控制者。它们应当以一种透明的方式确定遵守本条例责任的相应责任，尤其当其涉及到行使数据主体个人权利，以及涉及控制者为数据主体——根据他们的合约安排——提供第13条和第14条所规定的信息的相应责任，除非欧盟或成员国的法律已经对对控制者施加了相应责任。
2．第1段所规定的合约安排应当恰当地反映相对于数据主体的共同控制者的相应角色和相互关系。数据主体应当可以知晓安排的实质。
3．不论第1段所规定的合约安排的条款如何，数据主体都可以向任一控制者主张其本条例所赋予的权利。
&lt;strong&gt;第27条 不在欧盟所设立的控制者或处理者的代表&lt;/strong&gt;
1．在第3（2）条适用的情形下，控制者或处理者应当以书面形式在欧盟委任一名代表。
2．此项责任不应当适用于：
(a)除了第9（1）条所规定的特定类型数据的大规模处理，或者第10条所规定的和刑事定罪或违法相关的个人数据处理之外的偶尔性处理，以及考虑到处理的性质、语境、范围和目的，不太可能对自然人的权利与自由带来风险的处理；或者
(b)公共机构或实体。
3．为数据主体提供相关商品或服务，或者监控数据主体的行为，数据主体的所在国之一应当设立代表。
4．为了确保对本条例的遵守，对于所有涉及到处理的事项，控制者或处理者应当做出强制性规定，确保其代表能在控制者或处理者之外收到信息，或者替代控制者或处理者收到信息，对于监管机构和数据主体所要求的事项尤其如此。
5．控制者或处理者委任代表，不能影响控制者或处理者进行的法律行动。
&lt;strong&gt;第28条 处理者&lt;/strong&gt;
1．处理者代表控制者进行处理，控制者只能选用有充分保证的、可采取合适技术与组织措施的、其处理方式符合本条例要求并且保障数据主体权利的处理者。
2．如果没有控制者之前的特别授权或一般书面授权，处理者不应聘用另一个处理者。在具有一般书面授权的情形下，对于涉及到补充或替换其他处理者的变动，处理者都应当告知控制者，以便使控制者有机会反对此类变化。
3．处理者的处理应当受某类合同或其他欧盟法与成员国法的约束，这类合同或法律应当规定处理者相对于控制者的责任、主体事项、处理期限、处理性质与目的、个人数据的类型、数据主体的类型以及控制者的责任与权利的。此类合同或法律尤其应当对如下情形做出规定：
(a)只有在收到控制者的书面指示时才可以处理个人数据，在涉及到将个人数据转移到第三国或某个国际组织的事项中亦是如此，除非欧盟法或成员国法对处理者有要求；在这种情形下，处理者应当在处理之前将法律要求告知控制者，除非告知会影响重要的公共利益；
(b)对于被授权处理个人数据的人，确保其履行保密义务或法律上的适当保密责任；
(c)采取第32条所要求的所有措施；
(d)尊重第2段和第4段规定的聘用另一个处理者的条件；
(e)结合处理的性质，在可能的情形下，通过合适的技术与组织手段帮助控制者履行其责任，以便使得数据主体能够行使其第三章所规定的权利；
(f)结合处理的性质和处理者所能得到的信息，帮助控制者履行第32至36条所规定的责任；
(g)基于控制者的选择，在提供和处理相关的服务结束后，将个人数据删除或返还给控制者，并且删除已有备份，除非欧盟或成员国的法律要求储存个人数据；
(h)给控制者提供所有能够证明其已经遵循本条款规定责任的信息，以及有利于控制者或控制者委任的审计员进行审计和核查的信息。
关于第1段（h）点，如果处理者认为某项指示违反了本条例或其它欧盟或成员国的数据保护条款，其应当立即告知控制者。
4．当处理者代表控制者为了进行特定的处理活动而应聘另一处理者，第3段所规定的控制者和处理者之间的合同或其它法律条款所规定的数据保护责任应当通过合同或欧盟或成员国的法律条款而同等适用于另一处理者，尤其是应当采取充分的保障措施、恰当的技术与组织手段以满足本条例的要求。当另一个处理者无法完成其数据保护职责时，对其责任，处理者应当完全负担。
5．处理者遵守第40条所规定的已生效的行为准则，或者遵守第42条所规定的已生效的验证机制，这可以被作为证据之一，证明处理者已经采取了本条款第1段和第4段所规定的充分保障。
6．在不影响控制者和处理者之间的单独合同的前提下，第3段和第4段所规定的合同或法律条款可以全部或部分运用本条第7段和第8段所规定的格式合同条款，包括它们何时属于根据第42条和第43条规定的赋予给控制者或处理者的验证机制。
7．欧盟委员会可以对于本条第3段和第4段所规定的事项，根据第93（2）条所规定的检查程序而制定格式合同条款。
8．监管机构可以对本条第3段和第4段所规定的事项，根据第63条所规定的一致性机制而制定格式合同条款。
9．第3段和第4段所规定的合同或法律条款必须是书面的，包括以电子形式做出的书面记录。
10．在不影响第82、83、84条的情形下，如果某个处理者因为确定处理目的与方法方而违反了本条例，处理者应当在此次处理中被视为控制者。
&lt;strong&gt;第29条 代表控制者或处理者进行的处理&lt;/strong&gt;
对个人数据有访问权的处理者或控制者、处理者的代表人，未经控制者允许，不得处理该个人数据。欧盟法律或成员国法律另有规定的除外。
&lt;strong&gt;第30条 处理活动的记录&lt;/strong&gt;
1．每个控制者——以及如果有的话——每个控制者的代表，都应当保持其所负责的处理活动的记录。这种记录应当包含所有如下信息：
(a)控制者以及——如果有的话——共同控制者、控制者的代表、数据保护官的姓名、详细联系方式；
(b)处理的目的；
(c)对数据主体的类型以及个人数据的类型的描述；
(d)个人数据已经被披露或将被披露给的接收者——包括位于第三国或国际组织的接收者——的类型；
(e)如果适用的话，将个人数据转移到第三国或国际组织的记录，包括识别此第三国或国际组织的记录，以及在第49（1）条第二分段所提到转移的情形中，对适当保障措施的记录；
(f)如果适用的话，擦除不同种数据类型的预计期限；
(g)如果适用的话，对第32（1）条所规定的技术性与组织性安全措施的一般性描述。
2．每个处理者以及——如果适用的话——处理者的代表对于以控制者名义进行的处理都应当保持保存一份记录，包含如下信息：
(a)处理者或处理者们的名字和详细联系方式、处理者所代表的每个控制者以及——如果有的话——控制者或处理者的代表、数据保护官；
(b)代表每个控制者进行处理的类型；
(c)如果适用的话，将个人数据转移到第三国或国际组织的记录，包括识别此第三国或国际组织的记录，以及在第49（1）条第二分段所提到转移的情形中，对适当保障措施的记录；
(d)如果有的话，对第32（1）条所规定的技术性和组织性安全措施的一般性描述。
3．第1段和第2段所规定的记录应当是书面的，包括以电子形式作出的书面记录。
4．基于监管机构的要求，控制者或处理者以及——在有的情况下——控制者或处理者的代表，应当提供可获取的记录。
5．第1和第2段所规定的责任不适用于雇员少于250人的经济主体或组织，除非其进行的处理不是偶尔性的，而且可能会对数据主体的权利与自由带来风险，或者其处理包含了第9（1）条规定的特定种类的数据或第10条规定的和刑事犯罪和违法相关的个人数据。
&lt;strong&gt;第31条 和监管机构的合作&lt;/strong&gt;
在监管机构的要求下，控制者和处理者以及——在适用的情况下——它们的代表应当配合监管机构的工作。
&lt;strong&gt;第二部分 个人数据的安全&lt;/strong&gt;
&lt;strong&gt;第32条 处理的安全&lt;/strong&gt;
1．在考虑了最新水平、实施成本、处理的性质、处理的范围、处理的语境与目的之后，以及处理给自然人权利与自由带来的伤害可能性与严重性之后，控制者和处理者应当采取包括但不限于如下的适当技术与组织措施，以便保证和风险相称的安全水平：
(a)个人数据的匿名化和加密；
(b)保持处理系统与服务的保密性、公正性、有效性以及重新恢复的能力；
(c)在遭受物理性或技术性事件的情形中，有能力恢复对个人数据的获取与访问；
(d)具有为保证处理安全而常规性地测试、评估与评价技术性与组织性手段有效性的流程。
2．在评估合适的安全级别的时候，应当特别考虑处理所带来的风险，特别是在个人数据传输、储存或处理过程中的的意外或非法销毁、丢失、篡改、未经授权的披露或访问。
3．遵守第40条所规定的已生效的行为准则，或者遵守第42条所规定的已生效的验证机制，这可以被作为证据之一，证明已经遵守了本条款第1段的要求。
4．控制者和处理者应当采取措施确保，除非接到控制者的指示，任何有权访问个人数据的处理者或任何代表控制者和处理者的自然人都不会进行处理，除非欧盟或成员国法律要求进行处理。
&lt;strong&gt;第33条 向监管机构报告对个人数据的泄露&lt;/strong&gt;
1．在个人数据泄露的情形中，如果可行，控制者在知悉后应当及时——至迟在72小时内——将个人数据泄露告知第55条所规定的有权监管机构，除非个人数据泄露对于自然人的权利与自由不太可能会带来风险。对于不能在72小时以内告知监管机构的情形，应当提供延迟告知的原因。
2．处理者在获知个人数据泄露后，应当及时告知控制者。
3．第1段所规定的告知应当至少包括：
(a)描述个人数据泄露的性质，在可能的情形下，描述包括相关数据主体的类型和大致数量，以及涉及到个人数据的类型与大致数量；
(b)告知数据保护官的姓名与详细联系方式，或者可以获取更多信息的其他联系方式；
(c)描述个人数据泄露的可能后果；
(d)描述控制者应对个人数据泄露已经采用或计划采用的措施，包括——如果合适的话——减少负面影响的措施。
4．在不可能同时提供信息的情形下，可以分阶段地及时提供信息。
5．控制者应当记录所有对个人数据的泄露，包括泄露个人数据相关的事实、影响与已经采取的救济行动。参照该记录，监管机构得以核实控制者是否遵守本条例的有关规定。
&lt;strong&gt;第34条 向数据主体传达个人数据泄露&lt;/strong&gt;
1．当个人数据泄露很可能给自然人的权利与自由带来高风险时，控制者应当及时向数据主体传达对个人数据泄露。
2．本条第1段所规定的向数据主体传达，应当以清晰和平白的语言传达个人数据泄露的性质，并且应当至少包括第33（3）条（b）（c）（d）点所提供的信息与建议。
3．当满足如下情形之一时，不要求控制者告知数据主体其个人数据被泄露的信息：
(a)控制者已经采取合适的技术与组织保证措施，并且那些措施已经应用于那些被个人数据泄露所影响的个人数据，特别是已经应用那些使得未被授权访问的个人无法辨识个人数据的措施，例如加密；
(b)控制者已经采取后续措施，保证第1段所规定的给数据主体的权利与自由带来的高风险不再有实现的可能；
(c)告知将需要付出不相称的努力。此时，应存在公告机制或类似措施来承担控制者的告知义务，并且与控制者告知相比，这种措施的告知效果应当至少有相同效果。
4．如果控制者仍然没有将个人数据泄露告知数据主体，监管机构在考虑了个人数据泄露所可能带来的高风险可能性后，可以要求其告知，或者可以认为符合第3段所规定的情形。
&lt;strong&gt;第三部分 数据保护影响评估与提前咨询&lt;/strong&gt;
&lt;strong&gt;第35条 数据保护影响评估&lt;/strong&gt;
1．当某种类型的处理——特别是适用新技术进行的处理——很可能会对自然人的权利与自由带来高风险时，在考虑了处理的性质、范围、语境与目的后，控制者应当在处理之前评估计划的处理进程对个人数据保护的影响。若多项高风险处理活动属于同一种类，那么此时仅对其中某一项活动进行评估即可。
2．如果控制者已经委任数据保护官，当其进行数据保护影响评估时，控制者应当向数据保护官进行咨询。
3．在如下情形中，第1段所规定的数据保护影响评估是尤其必须的：
(a)对与自然人相关的个人因素进行系统性与全面性的评价，此类评价建立在自动化处理——包括用户画像——基础上的，并且其决策对自然人产生法律影响或类似重大影响；
(b)以大规模处理的方式处理第9（1）条所规定的特定类型的数据，或者和第10条规定的定罪与违法相关的个人数据；或者
(c)以大规模的方式系统性地监控某个公众可以访问的空间。
4．监管机构应当建立并公开一个列表，列明符合第1段所要求的数据保护影响评估的处理操作的类型。监管机构应当将此类列表告知第68条所提到欧盟数据保护委员会。
5．监管机构还可以建立一个公开性的列表，列明符合不需要进行数据保护影响评估的处理操作的类型。监管机构应当将此类列表告知欧盟数据保护委员会。
6．在设置第4段与第5段所规定的列表之前，当此类列表涉及到为数据主体提供商品或服务，或者涉及到对多个成员国行为的监管，或者可能实质性地影响欧盟内部个人数据的自由流动，有职权的监管机构应当首先适用第63条所规定的一致性机制。
7．评估应当至少包括：
(a)对计划的处理操作和处理目的的系统性描述，以及——如果适用的话——对控制者所追求的正当利益的描述；
(b)对和目的相关的处理操作的必要性与相称性进行分析；
(c)对第1段所规定的给数据主体的权利与自由带来的风险的评估；
(d)结合数据主体和其他相关个人的权利与正当利益，采取的计划性风险应对措施，包括保障个人数据保护和证明遵循本条例的安全保障、安全措施和机制。
8．评估相关控制者或处理者的处理操作的影响时，特别是评估数据保护影响时，应当合理考虑其对第40条所规定的已生效的行为准则的遵守。
9．在合适的情形下，如果其不影响保护商业或公共利益或处理操作的安全性，控制者应当咨询数据主体或数据主体代表对于其预期处理的观点。
10．当基于第6（1）条（c）或（e）点而进行的处理符合欧盟或成员国为控制者制定涉及到处理操作的法律，并且在制定其法律基准时已经进行了作为一般性影响评估一部分的数据保护影响评估时，第1至7段不应当适用，除非成员国认为，有必要在处理活动前进行此类评估。
11．必要时，控制者应当进行核查，评估处理是否是符合数据保护影响评估，至少当处理操作所带来的风险存在变化时，应进行核查。
&lt;strong&gt;第36条 提前咨询&lt;/strong&gt;
1．当第35条所规定的数据保护影响评估表明，如果控制者不采取措施，处理会带来高风险，那么控制者应当在处理之前咨询监管机构。
2．当监管机构认为，第1段所规定的预期的处理将违反本条例，特别是当控制者无法识别或减小风险，监管机构应当在收到咨询请求的八个星期以内向控制者以及——在适用的情况下——处理者提供书面建议，并且可以使用第58条所规定的权力。考虑到预期处理的复杂性，这种期限可以延长六个星期。监管机构应当在收到咨询请求的一个月内向控制者以及——在适用的情况下——处理者告知延期以及延期的原因。监管机构可以延长期限，直到其获取了咨询所要求的信息。
3．当咨询第1段所规定的监管机构时，控制者应当向监管机构提供如下信息：
(a)在适用的情形下，涉及到处理——特别是当处理是在一群企业内部进行的——的控制者、共同控制者和处理者的相应责任；
(b)预期处理的目的与方法；
(c)为了保障数据主体权利与自由所采取的符合本条例的方法与措施；
(d)在适用的情形下，数据保护官的详细联系方式；
(e)第35条所规定的数据保护影响评估；以及
(f)监管机构要求的所有其它信息。
4．成员国在起草相关立法草案以获得国会通过时，或者根据此类立法措施制定处理相关的规制措施时，应当咨询监管机构。
5．虽然有第1段的规定，但在和控制者履行实现公共利益任务相关的处理中，包括和社会保障与公共健康相关的处理中，成员国法律可以要求控制者在其处理相关的事项中咨询监管机构并且提前获取监管机构的授权。
&lt;strong&gt;第四部分 数据保护官&lt;/strong&gt;
&lt;strong&gt;第37条 数据保护官的委任&lt;/strong&gt;
1．在如下任一情形中，控制者和处理者应当委任数据保护官：
(a)处理是公共机构或公共实体进行操作的，法庭在履行其司法职能时除外；
(b)控制者或处理者的核心处理活动天然性地需要大规模性地对数据主体进行常规和系统性的监控；或者
(c)控制者或处理者的核心活动包含了第9条规定的对某种特殊类型数据的大规模处理和第10条规定的对定罪和违法相关的个人数据的处理。
2．如果一组企业的每一个机构都能很容易联系数据保护官，这一组企业可以任命一个单独的数据保护官。
3．当控制者或处理者是一个公共机构或公共实体，基于它们的组织结构和规模，多个此类公共机构或实体可以共同委任一个数据保护官。
4．除了第1段所规定的情形，在欧盟或成员国法律要求的情形下，控制者或处理者，或代表某类控制者或处理者的协会和其他实体可以委任一名数据保护官。对于此类协会，或代表控制者或处理者的其他实体的活动，数据保护官有权代表它们进行活动。
5．数据保护官的委任必须基于其专业性的素质，其需要具有数据保护法律与实践的专业知识，以及完成第39条所规定的任务的能力。
6．数据保护官应当是控制者或处理者或基于服务合同而完成任务的一名职员。
7．控制者或处理者应当发布数据保护官的详细联系方式，并向监管机构进行报告。
&lt;strong&gt;第38条 数据保护官的职位&lt;/strong&gt;
1．控制者和处理者应当确保，在所有与个人数据保护相关的事项中，数据保护官都应当以一种恰当和及时的方式介入。
2．控制者和处理者应当支持数据保护官履行第39条所规定的责任，应当提供其履行此类责任、访问个人数据、进行处理操作，以及维持其专业性知识的必要资源。
3．控制者和处理者应当确保个人数据保护官不会收到任何关于履行此类责任的指示。个人数据保护官不能因为完成其任务而被控制者或处理者解雇。其可以直接向控制者或处理者的最高管理层进行报告。
4．数据主体可以在所有和处理其个人数据相关的事项中，以及和行使本条例所赋予的权利相关的事项中联系数据保护官。
5．数据保护官在完成其任务时，应当遵守欧盟或成员国的法律，负有保密义务。
6．数据保护官可以完成其他任务或责任。控制者或处理者应当保证任何此类任务和责任不会导致利益冲突。
&lt;strong&gt;第39条 数据保护官的任务&lt;/strong&gt;
1．数据保护官应当至少具有如下任务：
(a)对控制者或处理者，以及那些履行本条例和欧盟其他成员国数据保护条款所规定的处理责任的雇员进行告知，提供建议；
(b)确保遵守本条例、其他欧盟或成员国数据保护条款、和个人数据保护相关的控制者或处理者的政策，包括分配处理操作中以及相关审计中的责任、增强意识以及培训职员；
(c)根据要求，应当对数据保护影响评估以及根据第35条对其实施进行监管的事项提供建议；
(d)和监管机构进行合作；
(e)在与处理相关的事项中，包括第36条所规定的提前咨询中，以及——在适用的情况下——在其他所有相关事项的咨询中，充当监管机构的联系人。
2．数据保护官在履行其任务时，应当结合处理的性质、范围、语境与目的，合理地考虑处理操作所伴随的风险。
&lt;strong&gt;第五部分 行为准则与认证&lt;/strong&gt;
&lt;strong&gt;第40条 行为准则&lt;/strong&gt;
1．成员国、监管机构以及欧盟数据保护委员会与欧盟委员会鼓励在考虑不同处理部门的特征以及微型、小型以及中型经济主体的特定需求的基础上起草促进本条例合理适用的行为准则。
2．协会以及其它代表某类控制者或处理者的实体为了对适用本规则进行细化，可以起草行为准则，或修正或延长此类准则，例如，它们可以起草涉及到如下事项的准则：
(a)合理与透明的处理；
(b)在特定情境下控制者所追求的正当利益；
(c)对个人数据的收集；
(d)对个人数据进行匿名化处置；
(e)提供给公众与数据主体的信息；
(f)数据主体权利的行使；
(g)提供给儿童和保护儿童的信息，以及为了获取儿童监护人同意所采取的形式；
(h)第24条和第25条所规定的措施与程序，以及为了保障第32条所规定的处理安全所采取的措施；
(i)向监管机构通报个人数据泄露，以及将此类个人数据泄露告知数据主体；
(j)将个人数据转移到第三国或国际组织；或者
(k)不影响第77条和第99条所规定的数据主体权利的庭外诉讼性活动，以及为了解决控制者与数据主体在处理相关事项中争议的纠纷解决程序。
3．控制者或处理者除了受本条例约束之外，对于根据第3条不受本条例约束的情形，为了保证在第46（2）条（e）点所规定的将个人数据转移到第三国或国际组织的框架中提供合适的安全措施，也可以受本条第5段所规定的已生效的行为准则约束，或者受本条第9段规定的具有一般性效力的行为准则所约束。为了提供此类合适的安全措施，包括和数据主体权利相关的安全措施，此类控制者或处理者应当通过合同或其他具有法律强制力的措施制定有约束力和可执行的承诺。
4．在不影响第55或56条所规定的有权监管机构的任务与权利的前提下，本条第2段所规定的行为准则应当包括使第41（1）条所规定的实体能履行其监管任务的有效措施，保证负责实施行为准则的控制者或处理者遵循其条款的规定。
5．本条第2段所规定的计划起草、修改行为准则或延长现有准则的协会或其他实体，应当将准则草案、修正案或延期提议提交给符合第55条的有权监管机构。监管机构应当提供一份意见书，表明草案、修正案或延期提议是否符合本条例的规定，如果监管机构认定已经采取了足够和适当的安全保障，其应当批准草案、修正案或延期提议。
6．当准则草案、或修正案或延期提议是根据第5段的规定而被批准的，并且行为准则不涉及多个成员国的处理活动，监管机构应当进行登记并发表准则。
7．当行为准则的草案涉及到多个国家的处理活动，第55条所规定的有权监管机构应当在批准准则草案、修订或延期之前将其按照第63条规定的程序提交给欧盟数据保护委员会，并应提供一份意见书，表明准则草案、修正案或延期是否遵循了本条例，或者——在第3段所规定的情形中——是否提供了恰当的安全措施。
8．当第7段中规定的意见书确认了准则草案、修正案或延期遵循了本条例，或者——在第3段所规定的情形中——提供了恰当的安全措施，欧盟数据保护委员会应当将意见书提交给欧盟委员会。
9．欧盟委员会应当通过制定实施法案确定，根据第8段规定而提交的已生效的行为准则、修正案或延期是否在欧盟具有一般效力。此类法案的制定应当符合第94（2）条所规定的核查程序。
10．对于已经被认定符合第9段中所规定的具有一般有效性的已生效准则，欧盟委员会应当保证其具有适当的公开性。
11．欧盟数据保护委员会应当核查所有登记的已生效行为准则、修正案以及延期，并且应当以恰当的方式使得公众能够获取。
&lt;strong&gt;第41条 对已生效行为准则的监控&lt;/strong&gt;
1．在不影响第57和第58条规定的有权监管机构的任务与权利的前提下，对根据第40条制定的行为准则的合规性监管可以交给如下实体：在准则所规定事项方面具有适当的专业性，并且其合规性监管权力已经得到有权监管机构认证。
2．第1段所规定的实体，当存在如下条件时，可以被委任为有权监管是否遵守行为准则的机构：
(a)已经证明在准则所规定事项方面具有独立性与专业性，满足有权监管机构的要求；
(b)已经确立了相关程序，可以通过程序评估相关控制者和处理者适用准则的资质，监控其对准则条款的遵守，以及间歇性地评估其操作；
(c)已经设立程序和体系，解决关于违反准则，或关于控制者或处理者已经实施、或正在实施准则的方式的申诉，并且已使得此类程序与体系对数据主体和公众透明化；并且
(d)已经表明其符合有权监管机构的要求，其任务和职责不存在利益冲突的情形。
3．有权监管机构应当按照第63条所规定的一致性机制，将认证第1段中所规定的实体的标准草案提交给欧盟数据保护委员会。
4．当控制者或处理者违反准则，第1段所规定的实体在不影响有权监管机构的任务和权利、第八章条款的前提下，应当在适当安全措施的保障下采取合适的行动，包括准则中中止或剔除相关控制者或处理者。实体应当将此类行动以及行动的理由告知有权监管机构。
5．如果第1段所规定的实体不符合或不再符合认证的条件，或者其行为违反了本条例，有权监管机构应当撤回对其的认证。
6．本条不适用于公共机构和公共实体所进行的处理。
&lt;strong&gt;第42条 认证&lt;/strong&gt;
1．成员国、监管机构、欧盟数据保护委员会和欧盟委员会应当鼓励——尤其是在欧盟层面——建立数据保护认证机制、数据保护印章和标记，以证明控制者和处理者的处理操作符合本条例。对此应当考虑微型、小型以及中型经济主体的特定需求。
2．控制者或处理者除了受本条例约束之外，也可以设立符合本条第5段的数据保护认证机制、印章或标记，以便证明，对于根据第3条不受本条例约束的情形，已经对第46（2）条（f）点所规定的将个人数据转移到第三国或国际组织的情形采取了合适的安全措施。为了提供此类合适的安全措施，包括和数据主体权利相关的安全措施，此类控制者或处理者应当通过合同或其他具有法律强制力的措施制定有约束力和可执行的承诺。
3．认证应当是自愿的，而且可以通过透明程序而获得。
4．根据本条而进行的认证，不能减轻控制者或处理者遵循本条例的责任，而且也不对第55条或56条所规定的有权监管机构的任务和权利产生影响。
5．符合本条的认证应当为第43条所规定的认证机构所批准，应当建立在第58（3）条的有权监管机构或第63条的欧盟数据保护委员会所批准的标准之上。当标准被欧盟数据保护委员会所批准，这可以产生一个通用性认证——欧盟数据保护印章。
6．那些将其处理提交认证机制的控制者或处理者，应当将进行认证程序所必需的所有信息与访问权提交给第43条规定的认证机构，在适用的情形下，还应当提交给有权监管机构。
7．颁发给控制者或处理者的认证的有效期最长是三年，如果相关条件满足，同样的情形下有效期可以延长。当认证的条件不满足或不再满足时，在适用的情形下，第43条规定的认证实体或有权监管机构可以撤回认证。
8．欧盟数据保护委员会应当核查所有已登记的验证机制、数据保护印章和标记，而且应当以恰当的方式使得公众能够获取。
&lt;strong&gt;第43条 认证机构&lt;/strong&gt;
1．在不影响第57条和第58条规定的有权监管机构的任务与权利的前提下，具有相应专业性的认证机构可以在告知监管机构后——以便监管机构可以行使第58（2）点h点所规定的权利——颁发和更新认证。成员国应当确保这些认证机构是如下一个机构认可或两个机构同时认可的：
(a)第55或56条所规定的有权监管机构；
(b)按照欧洲议会和理事会的(EC)No 765/2008条例、EN-ISO/IEC 17065/2012设定的，以及满足第55条或第56条的有权监管机构所规定的额外要求的全国性认证机构。
2．只有存在如下情形时，第1段所规定的认证机构才能根据第1段的规定被认证：
(a)已经证明在准则所规定事项方面具有独立性与专业性，满足有权监管机构的要求；
(b)采取措施遵从第42（5）条所规定的标准，并且已经为第55条所规定的有权监管机构或第63条规定的欧盟数据保护委员会所批准；
(c)建立了发行、定期审查和撤回数据保护认证、印章和标记的程序；
(d)已经设立了解决关于违反准则，或关于控制者或处理者已经实施、或正在实施准则的方式的申诉程序和体系，并且数据主体和公众已知悉此类程序和体系；且
(e)已经表明其符合有权监管机构的要求，其任务和职责不存在利益冲突的情形。
3．第1段和第2段所规定的委任认证机构应当建立在第55条或第66条所规定的有权监管机构所批准的基础性标准之上，或者第63条所规定的欧盟数据保护委员会所批准的基础性标准之上。对于本条第1段（b）点所规定的授权，此类要求应当补充(EC) No 765/2008指令所设想的要求，以及描述认证机构方法与程序的技术性规则。
4．在不影响控制者或处理者对本条例的遵守的前提下，第1段所规定的认证机构应当负责颁发认证或撤销此类认证的有效评估。颁发给控制者或处理者的认证的有效期最长是五年，如果相关条件满足，同样的情形下有效期可以延长。
5．第1段所规定的验证机构应当向有权监管机构报告颁发或撤销所要求认证的理由。
6．监管机构应当以容易获取的方式公开本条第3段所规定的要求，以及第42（5）段所规定的标准。监管机构还应当将那些要求和标准传输给欧盟数据保护委员会。欧盟数据保护委员会应当核查所有登记的认证机制与数据保护印章，而且应当通过某种恰当的方式将它们公开。
7．在不影响第八章的前提下，当认证的条件不符合或不再符合，或者当认证机构所采取的行为侵犯了本条例，有权监管机构或全国性的认证机构应当取消根据本条第1段对认证机构的认证。
8．为了细化第42（1）条所规定的数据保护验证机制所需要考虑的条件，欧盟委员会有权制定符合第92条的授权法案。&lt;/p&gt;
&lt;p&gt;9．欧盟委员会可以制定实施法案，为验证机制与数据保护印章、标记与机制设定技术标准，以便促进和认可那些验证机制、印章与标记。此类实施法条的制定应当符合第94（2）条所规定的验证程序。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第五章 将个人数据转移到第三国或国际组织&lt;/strong&gt;
&lt;strong&gt;第44条 转移的一般性原则&lt;/strong&gt;
对于正在处理或计划进行处理的个人数据，将其转移到第三国或国际组织，包括将个人数据从第三国或国际组织转移到另一第三国或另一国际组织，控制者和处理者只有满足本条例的其他条款，以及满足本章规定的条件才能进行转移。为了保证本条例对于自然人的保护程度不会被削弱，本章的所有条款都应当被遵守。
&lt;strong&gt;第45条 基于认定具有充足保护的转移&lt;/strong&gt;
1．当欧盟委员会作出认定，认为相关的第三国、第三国中的某区域或一个或多个特定部门、或国际组织具有充足保护，可以将个人数据转移到第三国或国际组织。此类转移不需要特定的授权。
2．当评估保护程度的充足性时，欧盟委员会应当特别考虑如下因素：
(a)法治、对人权与基本自由的尊重、包括关于公共安全、国防、国家安全、刑法和公共机构访问个人数据的一般性与部门性立法，以及此类立法的实施、数据保护规则、职业规则和安全措施，包括将个人数据转移到另一第三国或国际组织所必须遵循的第三国或国际组织的规则、判例法以及有效可执行的数据主体权利、对其个人数据正在转移的数据主体的司法救济；
(b)在国际组织是主体的情形中，第三国内存在一个或多个有效运作的独立监管机构，保证数据保护规则的实施，包括具有充分的执行权力，在数据主体行使其权利时和与成员国的监管机构合作时提供帮助和建议；
(c)第三国或国际组织已经许下的国际性承诺，或者承诺愿意承担有法律约束力的条约或法律文件所引起的其它责任，以及参加多边或地区性的体系，特别是和数据保护相关的体系所引起的其它责任。
3．在评估了保护程度的充足性之后，欧盟委员会可以通过制定实施性法案，确定本条第2段含义内的第三国、第三国内的领地或一个或多个特定部门或一个国际组织是否具有充足的保护。实施性法案应当提供一种周期性审查，至少每四年对第三国或国际组织的所有相关发展进行审查。实施性法案应当细化其领域性与部门性的实施，以及在适用的情况下确定本条第2段（b）点所规定的一个或多个监管机构。实施性法案的制定应当遵循第93（2）条所规定的验证程序。
4．欧盟委员会应当持续性地监控第三国或国际组织的某些可能会影响根据本条第3款而作出的决定和建立在95/46/EC指令第25（6）条基础之上的决定发挥作用的某些发展。
5．当已有信息显示，第三国或第三国内的一个或多个特殊部门或国际组织不再提供本条第2段所规定的充足的保护，欧盟委员会应当——尤其是在经过第3段所规定的核查后——通过制定不具有溯及力的实施性法案，在必要限度内废止、修正或中止本条第3段所规定的决定。此类实施性法案的制定应当遵循第93（2）条所规定的验证程序。
在具有高度正当性的紧急状态情形中，欧盟委员会应当立即根据第93（3）条规定的程序而制定实施性法案。
6．为了补救导致第5条决定的情形，欧盟委员会应当与第三国或国际组织磋商。
7．符合本条第5段的决定不会影响到将个人数据转移到第三国、第三国内的领地或一个或多个部门、或者第46条至49条所规定的相关国际组织。
8．欧盟委员会应当在欧盟的官方杂志及其网站上发表名单，列明其确定已经具备充足保护或不再具有充足保护的第三国、第三国内的特定部门和国际组织。
9．欧盟委员会在95/46/EC指令第25（6）条基础上而做出的决定，在被欧盟委员会根据本条第3段或第5段而修改、替代或废止前应具有效力。
&lt;strong&gt;第46条 转移所需要的适当安全保障&lt;/strong&gt;
1．如果没有根据第45（3）条而做出的决定，控制者或处理者只有提供适当的保障措施，以及为数据主体提供可执行的权利与有效的法律救济措施，才能将个人数据转移到第三国或一个国际组织。
2．在不要求监管机构提供任何具体授权的情形下，第1段所规定的适当保障措施可以如下方式提供：
(a)公共机构或实体之间之间签订的具有法律约束力和可执行性的文件；
(b)符合第47条的有约束力的公司规则；
(c)欧盟委员会根据第93（2）条规定的核查程序而制定的数据保护标准条款；
(d)监管机构根据第93（2）条规定的核查程序制定并且为欧盟委员会批准的数据保护标准条款；
(e)根据第40条制定的行为准则，以及第三国的控制者或处理者为了采取合适的安全保障而做出的具有约束力和执行力的承诺，包括数据主体的权利；或者
(f)根据第42条而被批准的验证机制，以及第三国的控制者或处理者为了采取合适的安全保障而做出的具有约束力和执行力的承诺，包括数据主体的权利。
3．在需要有权监管机构授权的情形下，第1段所规定的合适安全措施尤其可以通过如下方式进行规定：
(a)控制者或处理者与控制者、处理者或第三国或国际组织的个人数据接收者之间的合同条款；或者
(b)公共机构或公共实体之间在行政性安排中所插入的条款，包括可执行的与有效的数据主体权利。
4．在本条第3段所规定的情形中，监管机构应当适用第63段所规定的一致性机制。
5．成员国或监管机构根据95/46/EC指令的第26（2）条而做出的授权，在被监管机构修改、替代或废止之前应当一直有效。欧盟委员会根据95/46/EC指令第26（4）条而做出的决定，在欧盟委员会按照本条第2段做出必要性的修改、替换或废止决定前应当一直有效。
&lt;strong&gt;第47条 有约束力的公司规则&lt;/strong&gt;
1．在满足如下条件时，对于符合第63条所规定的一致性机制的有约束力的公司规则，有权监管机关应当批准：
(a)具有法律约束力，适用于进行联合经济活动的企业集团或一系列经济主体的所有相关成员——包括其雇员，并且为他们所执行。
(b)在处理个人数据方面明确赋予数据主体以可执行的权利；以及
(c)满足第2段所规定的要求。
2．第1段所规定的有约束力的规则应当至少明确：
(a)进行联合经济活动的企业集团或一系列经济主体，及其每一个成员的架构和详细联系方式；
(b)数据转移或一系列的数据转移，包括个人数据的类型；处理类型及其目的；受影响的数据主体的类型；以及涉及到的对第三国或多个第三国的确定；
(c)规则的法律约束效力，既包括内部的约束力，也包括外部的约束力；
(d)对一般数据保护原则的适用，特别是目的限定、数据最小化、有限的储存期限、数据质量、通过设计的数据保护与默认的数据保护、处理的法律基础、对特定类型个人数据的处理；保障数据安全的措施；以及将数据转移到不受约束性公司规则所约束的实体所做的要求；
(e)和处理相关的数据主体的权利以及行使这些权利的方式，包括有权不被仅仅根据自动化处理——包括符合第22条的用户画像——而对数据主体做出决定，有权按照第79条向有权监管机构和成员国的有权管辖的法庭申诉，以及有权在违反有约束力的公司规则的情形下获取救济和——如果适用的话——赔偿；
(f)对于任何不在欧盟设立的控制者或处理者的相关成员违反约束性公司规则，在成员国的领域内设立的控制者或处理者愿意承担责任；只有当控制者或处理者证明，该成员对于导致损害的事件没有责任，控制者或处理者的此种责任才能被免除；
(g)关于约束性公司规则的信息如何提供给数据主体，特别是第13和14条之外关于本段所规定的(d)(e)(f)点的信息如何提供给数据主体；
(h)根据第37条所委任的所有数据保护官的任务，或者企业集团、或进行联合经济活动的一系列经济主体内部负责监控遵守约束性公司规则、监控培训和处置申诉的所有人或实体的任务；
(i)申诉程序；
(j)企业集团或进行联合经济活动的一系列经济主体，为了核实对约束性公司规则的遵守的而在内部所设立机制。此类机制应当包括数据保护核查以及能够确保采取矫正性活动保护数据主体权利的方法。此类核实结果应当告知（h）点所规定的个人或实体，企业集团或进行联合经济活动的一系列经济主体，而且在有权监管机构的要求下应当能够提供其核实结果；
(k)报告和记录规则变化的机制，以及将此类变化报告给监管机构的机制；
(l)为了保证企业集团或进行联合经济活动的一系列经济主体的合规性而和监管机构一起设立的合作机制，特别是向监管机构提供（j）点所规定的方法的核查结果；
(m)企业集团或进行联合经济活动的一系列经济主体的成员是第三国的主体，可能会对约束性企业规则所提供的保障产生实质性的负面影响，向有权监管机构报告对此类主体是否有法律要求的机制；以及
(n)对于可永久性或经常性访问个人数据的员工进行的适当数据保护培训。
3．欧盟委员会可以明确控制者、处理者和监管机构之间为了本条含义内的约束性公司规则而进行信息交换的形式和程序。此类实施性法案的制定应当遵循第93（2）条所规定的验证程序。
&lt;strong&gt;第48条 未经欧盟法授权的转移或披露&lt;/strong&gt;
任何法庭判决、仲裁裁决或第三国行政机构的决定，若要求控制者或处理者对个人数据进行转移或披露，同时满足以下条件时方能得到认可或执行：一是该判决、裁决或决定必须基于提出请求的第三国与欧盟或其成员国之间订立的法律互助协议等国际条约，二是该判决、裁决或决定不会对本章规定的其他转移形式产生消极影响。
&lt;strong&gt;第49条 特殊情形下的克减&lt;/strong&gt;
1．如果不存在根据第45（3）而做出的充足保护认定或根据第46条而制定的适当安全措施——包括约束性公司规则，将个人数据转移到第三国或国际机构，只有满足如下情形之一才能进行：
(a)数据主体被明确告知，不存在充足保护或适当的安全措施，预期的数据转移存在风险，但之后数据主体仍然明确表示同意预期的数据转移；
(b)转移对于履行数据主体与控制者之间的合同，或者履行数据主体在签订契约前所提出要求是必要的；
(c)控制者和另一自然人或法人之间签订或履行合同时，转移对于实现数据主体的利益是必要的；
(d)转移对于实现公共利益是必要的；
(e)转移对于确立、行使或辩护法律性主张是必要的；
(f)当数据主体基于身体性或法律性原因无法表达同意，为了保护数据主体或其他人的关键利益是必要的；
(g)转移是根据登记册而进行的——这种登记册是欧盟法或成员国法律为了向具有正当利益的一般性公众或个人提供咨询。但是，只有满足欧盟法或成员国法对咨询所规定必要条件，此类个案中的转移才能进行克减。
当转移无法基于第45或第46条，包括基于约束性公司规则的条款的规定而进行，且从（a）点到（g）的克减条件都不符合，将数据转移到第三国或国际组织，这只有在转移满足如下条件时才可以：转移是非重复性的；关乎很小一部分数据主体的权利；对于实现控制者压倒性的正当利益是必要的，并且不会违反数据主体的有限性的利益或权利与自由；控制者已经对围绕数据传输的情形进行评估，而且基于这种评估对个人数据保护采取了合适的安全保障。控制者除了提供第13条和第14条所规定的信息之外，应当将转移和追求的压倒性正当利益告知数据主体。
2．符合第1段（g）点的转移不应当包括登记册里的全部个人数据或所有类型的个人数据。当登记册是为了给具有正当利益的人提供咨询的，只有那些人提出要求，或者那些人是接收者的情形才能进行转移。
3．对于公共机构在行使其公共权力时的活动，第1段的（a）（b）（c）点以及第1段的第二分段不适用。
4．第1段（d）点规定的公共利益应当为欧盟或成员国为控制者所制定的法律所确认。
5．如果不存在充足保护的认定，欧盟或成员国的法律可以基于公共利益而明确做出限制，限制将个人数据转移到第三国或国际组织的特定类型。成员国应当将此类条款告知欧盟委员会。
6．控制者或处理者应当在第30条规定的档案中记录本条第1段第二分段所规定的评估以及合适的安全措施。
&lt;strong&gt;第50条 为保护个人数据的国际合作&lt;/strong&gt;
在涉及到第三国或国际组织的情形中，欧盟委员会和监管机构应当采取合适的措施以：
(a)发展国际合作机制，以便促进对个人数据保护立法的有效实施；
(b)在采取合适安全措施保障个人数据保护和其它基本权利与自由的前提下，通过告知、申诉转介、调查帮助和信息互换为个人数据保护立法的实施提供国际性互助；
(c)在实施个人数据保护立法中，使相关利益方密切参与为了进一步国际合作而进行的讨论和活动；
(d)促进个人数据立法与实践——包括与第三国管辖权冲突——的交换与记录。
&lt;strong&gt;第六章 独立监管机构&lt;/strong&gt;
&lt;strong&gt;第一部分 独立性地位&lt;/strong&gt;
&lt;strong&gt;第51条 监管机构&lt;/strong&gt;
1．为了保护自然人在处理过程中的基本权利与自由，以及促进欧盟内部的个人数据的自由流通，每个成员国应当建立一个或多个独立公共机构，负责监控本条例的实施。
2．每个监管机构都应当帮助本条例在欧盟的一致性适用。基于这种目的，监管机构应当按照第七章的规定彼此合作以及和欧盟委员会合作。
3．当一个成员国确立了不止一个监管机构，该成员国应当在欧盟数据保护委员会委任一个监管机构代表其他机构，而且应当建立一套机制，保证其他机构遵守第63条规定的一致性机制相关的规则。
4．每个成员国都应当将其根据本章所制定的法律条款告知欧盟委员会，[最迟应当在本条例生效的两年内]，而且应当及时将影响条款的修订告知欧盟委员会。
&lt;strong&gt;第52条 独立性&lt;/strong&gt;
1．每个监管机构在行使其任务和行使符合本条例的权力时，应当保持完全的独立性。
2．每个监管机构的一个或多个成员在行使其任务和行使符合本条例的权力时，应当不受外部影响，不论是直接的还是间接性的，而且不应接收任何人的指示。
3．监管机构的成员不得从事违反其监管职责的活动，任职期间不得担任任何与其监管工作相冲突的有偿或无偿的职务。
4．每个成员国都必须确保，每个监管机构都具有为了有效履行其任务和行使其权利——包括在欧盟数据保护委员会中互助、合作和参与的履行任务和行使权利——所必需的人力性、技术性与资金资源，前提性与基础性要素。
5．每个成员国都应当确保，每个监管机构都具有选择和雇佣其成员的权力，这只受相关监管机构的一个或多个成员的专门指令的约束。
6．每个成员国都必须确保，在不影响其独立性以及其具有单独和公共性的年度预算的前提下，每个监管机构都受资金控制——此类资金控制可能是州预算或国家预算一部分的——的约束。
&lt;strong&gt;第53条 监管机构成员的一般性要求&lt;/strong&gt;
1．成员国应当通过如下机构以透明化的方式委任其监管机构的每个成员：
-它们的议会；
-它们的政府；
-它们的国家元首；或者
-成员国法律指派的独立性实体。
2．每个成员都应当具有履行其职责和行使其权力所应当具有的资质、经验与技巧，特别是在个人数据保护领域的资质、经验与技巧。
3．成员根据成员国的相关法律结束其任期、辞职或强制性退休时，其职责也相应结束。
4．只有存在严重的不当行为，或者不再符合履行其职责的条件时，成员才可以被解职。
&lt;strong&gt;第54条 设立监管机构的规则&lt;/strong&gt;
1．每个成员国都应当通过法律规定如下事项：
(a)每个监管机构的设立；
(b)被任命为每个监管机构的成员所需要的资质与合适的条件；
(c)任命每个监管机构的一个或多个成员的规则和程序；
(d)每个监管机构的一个或多个成员的不少于四年的任期，（在此条例生效之后的第一次任命例外），如果有必要通过间断性的任命程序来保护监管机构的独立性，一部分成员的任期可以更短；
(e)每个监管机构的一个或多个成员是否可以连任，如果可以的话，可以连任多少个任期；
(f)每个监管机构的成员和员工需要负责的情形，对于其任期内或任期结束后的具有冲突性的行为、任职和收益的禁止条款，以及中止雇佣的规则。
2．每个监管机构的成员和员工都应当遵循欧盟或成员国的法律，对于其履行任务或行使其权力期间所获取的秘密信息，在任职期间或任期结束后都具有保守职业秘密的职责。尤其在自然人报告具有违反本条例的情形下，成员或员工应当履行其保守职业秘密的职责。
&lt;strong&gt;第二部分 职权、任务与权力&lt;/strong&gt;
&lt;strong&gt;第55条 职权&lt;/strong&gt;
1．每个监管机构都有权在其所属的成员国境内根据本条例履行分配给其的任务，行使授予其的权力。
2．当公共机构或私人实体基于第6（1）条的（c）或（e）点而进行处理，成员国的相关监管机构应当拥有职权。在此类情形中，第56条不适用。
3．对于法庭在其司法活动中进行处理操作，监管机构不具有监管职权。
&lt;strong&gt;第56条 领导性监管机构的职权&lt;/strong&gt;
1．在不影响第55条的前提下，控制者或处理者的主要营业机构或唯一营业机构所在地的监管机构应可以充当领导性监管机构，监管控制者或处理者根据第60条程序而进行的跨境处理。
2．第1段的规定可以进行减免，如果主要事项只和成员国内的一个机构相关，或者只在一个成员国内对数据主体产生实质性影响，每个监管机构应当都有权对向其进行的申诉或违反本条例的行为进行处置。
3．对于第2段所规定的情形，监管机构应当将此事项及时告知领导性监管机构。在被告知的三个星期以内，领导性的监管机构应当——结合控制者或处理者是否在通知其的监管机构所在的成员国内有拥有机构——决定，其是否要根据第60条的规定的程序而处置该案例。
4．当领导性监管机构决定处理案件，第60条所规定的程序应当适用。那个告知领导性监管机构的监管机构可以向领导性监管机构提交一份决定草案。当领导性监管机构起草第60（3）条所规定的决定时，其应当尽最大限度地考虑提交的决定草案。
5．当领导性监管机构决定不处置案子，通知领导性监管机构的监管机构应当根据第61条和第62条进行处置。
6．对于控制者或处理者所进行的跨境处理，领导性监管机构应当是该控制者或处理者的唯一面谈者。
&lt;strong&gt;第57条 任务&lt;/strong&gt;
1．在不影响本条例规定的其他任务的前提下，在其管辖范围内，每个监管机构应当：
(a)监控和执行对本条例的实施；
(b)提高公众意识，对和处理相关的风险、规则、安全保障和权利的理解。对针对儿童的活动保持特别注意；
(c)根据成员国的法律、全国性议会、政府以及其他制度和实体对与处理相关的自然人的权利与自由提供建议；
(d)提高控制者与处理者对本条例所规定责任的意识；
(e) 基于要求为所有数据主体提供行使本条例所规定的权利，以及——如果适用的话——和其它成员国的监管机构为了实现这一目的而进行合作；
(f)处置数据主体或实体、组织或协会根据第80条的申诉，采用合适的手段调查申诉的主要事项，在合理期限内向申诉者告知进展和调查结论——特别是如果需要进一步的调查或和监管机构协调；
(g)为保证对本条例适用与执行的一致性和其他监管机构合作，包括分享信息和提供相互协助；
(h)为本条例的适用进行调查，包括基于另一监管机构或其它公共机构提供的信息而进行的调查；
(i)在相关发展——特别是信息和通讯技术、商业实践发展——对个人数据保护产生影响的情况下，对相关发展进行监控；
(j)采用第28（8）条和第46（2）条（d）点规定的标准格式合同；
(k)建立并维持和第35（4）条规定的个人数据保护影响评估相关的条目；
(l)对第36（2）条规定的处理操作给出建议；
(m)鼓励起草符合第40条的行为准则，对符合第40（5）条提供充分安全保障的此类行为准则提供意见并进行批准；
(n)鼓励设立数据保护认证机制以及符合第42（1）条的数据保护印章与标记，并批准符合第42（5）条的认证标准；
(o)在适用的情形下，对根据第42（7）条而颁发的认证进行阶段性审查；
(p)对符合第41条规定的监控行为准则的委派实体，以及符合第43条规定的认证实体，对其标准进行起草并发布；
(q)委任符合第41条规定的监控行为准则的实体，以及符合第43条规定的认证实体；
(r)授权合同条款与第46（3）条规定的条款；
(s)批准符合第47条的约束性合同规则；
(t)欧盟数据保护委员会的活动提供帮助；
(u)对违反本条例的情形以及根据第58（2）条而采取的措施保持内部纪录；并且
(v)完成和个人数据保护相关的其它任务。
2．每个监管机构都应当为第1段（f）点规定的提交申诉提供便利，例如在不排除其它通讯方式的前提下，提供可以通过电子方式填写和提交的申诉方式。
3．每个监管机构的任务履行对于数据主体都应当是免费的，如果适用的话，对于数据保护官也应当是免费的。
4．当请求是明显毫无根据的或过分的，特别是当请求是重复性的，监管可以基于行政花费而收取一定的合理费用，或拒绝对请求作出行动。监管机构有责任证明，请求是明显毫无根据的或过分的。
&lt;strong&gt;第58条 权力&lt;/strong&gt;
1．每个监管机构都具有所有如下调查权力：
(a)要求控制者和处理者，以及——在适合的情形下——控制者或处理者的代表提供履行其任务所需要的所有信息；
(b)以数据保护核查的方式进行调查；
(c)对根据第42（7）所颁布的认证进行审查；
(d)将可能侵犯本条例的情况告知控制者或处理者；
(e)从控制者或处理者那里获取访问个人数据的权力，以及为了行使其任务而所需的所有信息；
(f)按照欧盟与成员国法律的程序法，获取对控制者和处理者的所有房屋建筑及场地，包括数据处理设施和方法的访问权。
2．每个监管机构都有所有如下矫正性权力：
(a)对控制者或处理者颁发警告，警告预期的处理操作可能会侵犯本条例的条款；
(b)当处理操作侵犯本条例条款的时候，对控制者或处理者进行申诫；
(c)命令控制者或处理者尊重数据主体行使符合本条例的权利；
(d)命令控制者或处理者的处理操作符合本条例条款，如果适合的话，应当在特定的期限内以特定的方式完成；
(e)命令控制者将个人数据泄露的情况告知数据主体；
(f)对处理施加暂时性或具有明确期限的禁令；
(g)要求对个人数据进行纠正或擦除，或根据第16条，17条和18条而对处理进行限制，以及将此类行动告知第17（2）条和第19条规定的个人数据披露给的接收者；
(h)撤回认证，或命令认证机构撤回根据第42条和第43条而颁发的认证，或者当认证的要求不满足或不再满足时，命令认证机构不要颁发认证；
(i)视每个案例的情形不同，在本段所规定的措施之外，或者替代本段所规定的措施而采取第83条规定的行政处罚；
(j)要求中止将数据传输到第三国或国际组织。
3．每个监管机构都有所有如下授权和建议的权力：
(a)根据第36条规定的提前咨询条款向控制者提出建议；
(b)主动或根据要求为全国性议会、成员国政府提供意见，或者根据成员国法为其他机构、实体与公众提供和个人数据保护相关的保护；
(c)如果成员国的法律要求此类提前咨询，根据第36（5）条而授权处理；
(d)根据第40（5）条而发布意见以及行为准则；
(e)根据第43条而委任认证机构；
(f)根据第42（5）条颁发认证和批准认证的标准；
(g)制定第28（8）条以及第46（2）条（d）点规定的数据保护标准条款；
(h)授权第46（3）条（a）点规定的合同条款；
(i)授权第46（3）条（b）点规定的行政性安排；
(j)批准符合第47条的约束性公司规则。
4．根据本条而行使赋予给监管机构的权力应当满足合适的安全保障，包括根据欧盟宪章而在欧盟和成员国法律中规定的有效司法救济和正当程序。
5．每个成员国应当通过法律规定，其监管机构为了执行本条例的条款，有权将违反本条例的情形诉诸司法机构，在合适的情形下可以提起或参与法律诉讼。
6．每个成员国都应当通过法律规定，其监管机构具有第1、2和3段规定的附加权力。对那些权利的行使不应当削弱第七章规定的有效运行。
&lt;strong&gt;第59条 活动报告&lt;/strong&gt;
每个监管机构都应当起草一份关于其活动的年度报告，这可以包括其被告知的违法类型以及根据第58（2）条而采取的措施类型。此类报告应当传输给全国性议会、政府以及成员国法律所委任的其他机构。公众、欧盟委员会和欧盟数据保护委员会应当能够获取这些报告。
&lt;strong&gt;第七章 合作与一致性&lt;/strong&gt;
&lt;strong&gt;第一部分 合作&lt;/strong&gt;
&lt;strong&gt;第60条 领导性监管机构和其他相关监管机构的合作&lt;/strong&gt;
1．领导性监管机构应当根据本条和其他相关监管机构进行合作，努力达成共识。领导性监管机构和相关监管机构应当彼此分享相关信息。
2．领导性监管机构可以随时要求其他相关监管机构提供第61条规定的互助合作，而且可以根据第62条而进行联合行动，这尤其适用于如下情形：为了进行调查，或者为了实施涉及到设立在另一成员国的控制者或处理者的措施。
3．领导性监管机构应当及时将事项相关信息告知给其他相关监管机构。对于其他相关监管机构的意见，其应当充分考虑，并及时向其他相关监管机构提交一份决定草案。
4．当其他任何相关监管机构收到第3段中所规定的咨询，并在四周内表达了对决定草案的相关与合理的反对，领导性监管机构如果不同意此相关与合理的反对，或者认为其意见是不相关或不合理的，应当将此事项提交给第63条规定的一致性机制。
5．如果领导性的监管机构同意相关与合理的反对意见，对于此反对意见，其应当将一份修订后的草案决定提交给其他监管机构。修订后的草案决定应当遵守第4段所规定的程序，并且应当在两个星期内做出。
6．如果在第4段和第5段所规定的期间内，其他相关监管机构都没有反对领导性监管机构所提交的决定草案，应当推定领导性的监管机构和相关监管机构对于决定草案具有一致意见，而且应当受其约束。
7．领导性监管机构应当做出决定，将决定的情况——包括相关事实和理由的总结——通知给控制者或处理者的主要营业机构或唯一营业机构，并视情况通过其他相关监管机构以及做出该决定的欧盟数据保护委员会。收到申诉的监管机构应当将决定的情况告知给申诉者。
8．在申诉被撤销或驳回的情形中，第7段的规定可以进行克减，收到申诉的监管机构应当采用决定并将其告知申诉者，由此也告知了控制者。
9．当领导性监管机构和相关监管机构同意撤销或驳回申诉的一部分，对申诉的其他部分采取行动，对于此类其他部分的事项，应当采取单独的决定。领导性监管机构应当采用和控制者行动相关的那部分决定，将其通告给控制者或处理者在成员国境内的主要营业机构或唯一营业机构，由此也告知了申诉者。另一方面，申诉者的监管机构应当采用和撤销或驳回申诉相关的那部分决定，将其告知申诉者，由此也告知了控制者或处理者。
10．当收到领导性监管机构根据第7段和第9段而进行的告知后，控制者或处理者应当采取必要措施，保证其在欧盟所有的所有机构的处理活动都符合决定。控制者或处理者应当向领导性监管机构告知为遵守决定而采取的措施，并通知其他相关监管机构。
11．在极端情形下，当某相关监管机构认为，有充分理由证明需要采取紧急行动以保护数据主体的利益，应当援引第66条有关紧急程序的规定。
12．领导性监管机构和其他相关监管机构应当通过电子方式，以标准化的格式为彼此提供本条所要求提供的信息。
&lt;strong&gt;第61条 互相协助&lt;/strong&gt;
1．监管机构应当为彼此提供信息和互相协助，以便以一种一致性的方式执行和适用本条例，而且应当拥有有效信息以进行有效的相互合作。互相协助尤其应当包括信息请求和监管措施，例如在授权与咨询、检验与调查之前请求信息和采取监管措施。
2．对于另一监管机构的请求，每个监管机构都应当采取恰当的合适措施及时回应，而且至迟应当在收到请求内的一个月内进行。此类措施尤其可以包括传输和调查相关的信息。
3．请求协助应当包括所有必要信息，包括请求的目的与原因。被交换的信息只能被用于实现请求协助的目的。
4．除非存在如下情形，被请求的监管机构不应当拒绝请求：
(a)被请求的监管机构对被请求的主体事项或被请求执行的措施没有职权；或者
(b)被请求的监管机构对请求进行照办，这会侵犯本条例或欧盟或成员国的为被请求的监管机构所制定的法律。
5．被请求的监管机构应将结果告知发出请求的监管机构，而且应当视情况告知为了实现请求而采取的措施。被请求的监管机构如果拒绝按第4段而提出的请求，应当提供说明。
6．基于其他监管机构的请求，被请求的监管机构应当以电子形式，使用标准化的格式提供信息，这应当成为一项规则。
7．所有被请求的监管机构根据请求而进行的互相协作，都不应当收取费用。对于特定情形下因为提供互相协作而产生的特定花费，监管机构之间可以签订补偿规则。
8．当某监管机构在收到另一监管的请求后一个月内仍然不提供第5段所规定的信息，做出请求的监管机构可以根据第55（1）条在其成员国境内采取临时性措施。在这种情形中，可以推定为符合第66（1）条的紧急情况，欧盟数据保护委员会应根据第66（1）条而作出紧急约束性决定。
9．欧盟数据保护委员会可以通过制定实施性法案而细化本条规定的互相协助的形式与程序，在监管机构之间、监管机构和欧盟委员会之间以电子方式进行的信息交换，特别是本条第6段所规定的标准化格式。此类实施性法案的制定应当遵循第93（2）条规定的验证程序。
&lt;strong&gt;第62条 监管机构的联合行动&lt;/strong&gt;
1．在合适的时候，监管机构应当进行联合行动，包括在涉及到其他成员国监管机构的成员或员工的情形下进行联合调查和采取联合执行措施。
2．当控制者或处理者在多个成员国设立机构，或者当两国或两国以上的数据主体可能会受处理操作的实质性影响，这些成员国的监管机构都有权参与联合行动。按照第56（1）或56（4）条规定而拥有职权的监管机构可以邀请这些成员国中的每个国家的监管机构参与联合行动，而且应当及时回应某监管机构的参与请求。
3．一个监管机构可以按照成员国的法律，以及临时调派的监管机构的授权，将调查权等权力授权给临时调查的监管机构的成员或员工。或者，如果监管机构的成员国的法律允许，应当允许临时调派的监管机构的成员或员工行使其符合成员国法律对其做出规定的调查权。只有在东道主监管机构的成员或员工的指导和见证之下，此类权力才能被行使。临时调派的监管机构的成员或员工应当遵守东道主监管机构所在的成员国国家的法律。
4．当根据第一段的规定临时调派的监管机构在另一成员国内活动，东道主监管机构所在的成员国应当对其行动承担责任，包括对活动期间所引起的损害，应当按照其活动地所属的成员国法律承担责任。
5．对于成员国境内所造成的损害，如果其可以适用其成员国的损害赔偿，成员国应当进行赔偿。临时调派的监管机构的某成员国的员工对另一成员国境内的人造成伤害，在另一成员国对个人进行补偿后，某成员国应当对另一成员国进行补偿。
6．除了第5段所规定的情形，在不影响行使相对于第三人权利的前提下，若出现第1段规定的情形，各成员国不得就第4段的损害向相关成员国提出损害赔偿的要求。
7．当存在联合行动的计划，而且当监管机构拒绝遵守本条第2段第二句所设定的责任，其他监管机构可以根据第55条在其境内采取临时性措施。在这种情形中，可以推定为符合第66（1）条的紧急情况，欧盟数据保护委员会应根据第66（2）条而作出紧急约束性决定。
&lt;strong&gt;第二部分 一致性&lt;/strong&gt;
&lt;strong&gt;第63条 一致性机制&lt;/strong&gt;
为了帮助本条例在欧盟的一致性适用，监管机构应当相互合作，以及在相关的情形下通过本部分规定的一致性机制而和欧盟委员会进行合作。
&lt;strong&gt;第64条 欧盟数据保护委员会的意见&lt;/strong&gt;
1．当某个有权监管机构计划采取如下任何一项措施，欧盟数据保护委员会应当发布意见。为此，有权监管机构应当将决定草案告知欧盟数据保护委员会，如果：
(a)决定草案的目标是采取一系列符合第35（4）条所规定的数据保护影响评估要求的处理操作；
(b)决定草案涉及到第40（7）条规定的行为准则草案，或行为准则草案的修订案或延期是否符合本条例；
(c)决定草案的目标是批准符合第41（3）条规定的委派实体，以及符合第43（3）条规定的认证实体的标准；
(d)决定草案的目标是确定第46（2）条（d）点和第28（8）条规定的标准数据保护条款；
(e)决定草案的目标是批准第46（3）条（a）点规定的合同条款；或者
(f)决定草案的目标是批准第47条所指的有效性公司规则。
2．任何监管机构、欧盟数据保护委员会或欧盟委员会的主席都可以提出要求，为了给出意见——特别是当有权监管机构不遵守第61条规定的相互协助的责任或第62条规定的联合行动时——可以对任何关乎一般性使用的事项，或在不止一个成员国产生影响的事项进行核查。
3．对于第1段和第2段提到的情形，欧盟数据保护委员会如果此前没有对类似事项发表过意见，应当对提交给它的事项发布一份意见。这份意见应当在八周内根据欧盟数据保护委员会成员的简单多数来决定。考虑到主要事项的复杂性，八周的期限可以再延长六周。关于第1段规定的按照第5段而在欧盟数据保护委员会中流通的决议草案，如果某成员在欧盟数据保护委员会主席所表明的合理期限内不提出异议，就应当视为同意决议草案。
4．监管机构和欧盟数据保护委员会应当及时以电子化手段，使用标准化的格式将任何相关信息进行沟通。此类信息可以是事实的总结、决议草案、采取此类必要措施的理由，以及其他相关机构的观点。
5．欧盟数据保护委员会的主席应当及时通过电子手段：
(a)通过标准化格式将任何已经获知的相关信息告知欧盟数据保护委员会和欧盟委员会的成员。如有需要，欧盟数据保护委员会的秘书应当提供相关信息的翻译；并且
(b)将意见告知第1段和第2段规定的监管机构和欧盟委员会，并公开意见。
6．在第3段规定的期间内，有权监管机构不应当采用第1段所规定的决议草案。
7．第1段中所规定的监管机构应当最大限度地考虑欧盟数据保护委员会的意见，而且应在收到意见的两周内以电子方式告知欧盟数据保护委员会的主席，其是否会维持或修改其决议草案，以及修改后的决议草案——如果有的话。
8．当相关监管机构在本条第7段规定的期限内通知委员会主席，其并无意遵守委员会的所有意见或意见的一部分，并且提供了相关理由，此种情形下第65（1）条应当适用。
&lt;strong&gt;第65条 欧盟数据保护委员会的纠纷解决&lt;/strong&gt;
1．为了确保在个案中对本条例的正确与融贯适用，欧盟数据保护委员会应当在如下情形中做出有约束力的决定：
(a)在第60（4）条规定的情形中，相关监管机构对领导性机构的草案决定提出了相关与合理的反对，或者领导性机构驳回了反对，认为其不相关或不合理。约束性决定应当涉及相关与合理反对所涉及的所有事项，特别是当其存在违反本条例的情形；
(b)对于哪个监管机构有权管辖主要营业机构存在不同意见；
(c)在第64（1）条规定的情形中，有权监管机构并不请求获得欧盟数据保护委员会的意见，或者并不遵守欧盟数据保护委员会按照第64条发布的意见。在这种情形下，任何相关监管机构或欧盟数据保护委员会都可以将此事项告知欧盟数据保护委员会。
2．三分之二多数的欧盟数据保护委员会成员在将主体事项转交后，应当在1个月以内做出第1段所规定的决定。考虑到主体事项的复杂性，这个期间可以再延长一个月。第1段所规定的决定应当是合理的，应当告知领导性监管机构和所有相关监管机构，并且对它们具有约束力。
3．当欧盟数据保护委员会无法在第2段所规定的期限内做出决定，其应当以欧盟数据保护委员会成员简单多数的方式在第2段所规定的第二个月的期限结束后的两星期内做出决定。如果欧盟数据保护委员会成员的投票刚好完全分裂，那么决定将根据主席的投票而做出。
4．在第2段和第3段所规定的期限内，相关监管机构不应当对根据第1段而提交给欧盟数据保护委员会的主体事项做出决定。
5．欧盟数据保护委员会的主席应当及时将第1段所规定的决定告知相关监管机构。这也就告知了欧盟委员会。在监管机构告知第6段规定的最终决定后，决定应当在欧盟数据保护委员会的网站上及时发表。
6．领导性监管机构或者收到申诉的监管机构应当根据本条第1段所规定的决定性基础及时做出最终决定，至迟应当在欧盟数据保护委员会告知其决定后的一个月以内做出。领导性的监管机构或收到申诉的监管机构应当向欧盟数据保护委员会报告其将该决定告知控制者或处理者以及数据主体的时间。相关监管机构的最终决定应当根据第60（7）（8）（9）条的条款而做出。最终决定应当涉及本条第1段所规定的决定，而且应当具体说明，本条第1段所规定的决定将会根据本条第5段而在欧盟数据保护委员会的网站上发表。最终决定应当附上本条第1段所规定的决定。
&lt;strong&gt;第66条 紧急程序&lt;/strong&gt;
1．在例外情形中，当相关监管机构认为有必要对保护数据主体的权利与自由采取紧急行动，其可以通过第63、64和65条规定的一致性机制或第60条规定的程序来进行克减，立即采取在其境内一段时间内——不超过3个月——具有法律效力的临时性措施。监管机构应当及时将采取这些措施的手段与原因告知其他相关监管机构、欧盟数据保护委员会与欧盟委员会。
2．当监管机构采取符合第1段的措施，以及考虑亟需采用的最终措施，其可以请求欧盟数据保护委员会出具一份紧急意见或紧急约束性决定，并说明提出此请求的原因。
3．如果有必要对保护数据主体的权利与自由采取紧急行动，而有权监管机构却没有采取合适措施，任何监管机构都可以向欧盟数据保护委员会请求一份紧急意见或紧急约束性决定，说明提出此请求的原因，包括需要采取紧急行动的原因。
4．对于第64（3）条和第65（2）条规定的的克减，欧盟数据保护委员会成员的简单多数应当在两个星期内做出本条第2段和第3段规定的紧急意见或紧急约束性决定。
&lt;strong&gt;第67条 信息交换&lt;/strong&gt;
对于监管机构之间、监管机构与欧盟数据保护委员会之间以电子方式进行的信息交换，特别是对于第64条规定的标准化格式，欧盟委员会可以进一步制定细化的实施性法案。
这些实施性法案应当根据第93（2）条规定的验证程序制定。
&lt;strong&gt;第三部分 欧盟数据保护委员会&lt;/strong&gt;
&lt;strong&gt;第68条 欧盟数据保护委员会&lt;/strong&gt;
1．欧盟数据保护委员会特此被设立为欧盟的一个机构，而且将具有法人身份。
2．欧盟数据保护委员会的代表是其主席。
3．欧盟数据保护委员会应当包括每个成员国的每个监管机构的首长、欧盟数据保护监管者的首长，或者他们的代表。
4．当一个成员国内不止一个监管机构负责监控对本条例条款的适用，应当按照成员国的法律任命一个联合代表。
5．欧盟委员会应当有权参与欧盟数据保护委员会的活动与会议，但没有投票权。欧盟委员会应当委任一名代表。欧盟数据保护委员会的主席应当将其活动告知欧盟委员会。
6．对于第65条规定的情形，只有当决议涉及到适用于和本条例规定有实质性对应的欧盟机构、实体、办公室、规制机构的原则和规则时，欧盟数据保护监管者才具有投票权。
&lt;strong&gt;第69条 独立性&lt;/strong&gt;
1．当根据第70条和第71条履行其任务或行使其权力时，欧盟数据保护委员会应当保持其独立性。
2．在不影响第70（1）条（b）点和第70（2）条所规定的欧盟委员会的请求的前提下，欧盟数据保护委员会在履行其任务或行使其权力时，应当避免从任何人那里获取指示。
&lt;strong&gt;第70条 欧盟数据保护委员会的任务&lt;/strong&gt;
1．欧盟数据保护委员会应当确保对本条例的一致性适用。为了实现这一目的，在相关情形中，欧盟数据保护委员应当主动或根据欧盟委员会的请求而采取如下行动：
(a)在不影响全国性监管机构的任务的前提下，确保在第64条和65条所规定的情形中正确适用本条例；
(b)对欧盟数据保护相关的所有事项，包括对本条例的修改动议，向欧盟委员会提供建议；
(c)对为制定约束性公司规则而在控制者、处理者和监管机构之间进行的信息交换的格式与程序向欧盟委员会提供建议；
(d)从第17（2）条规定的公众可以获取的通讯服务中擦除个人信息的链接、备份或复制品，对这种活动的程序发布指导方针、建议和最佳操作；
(e)主动或根据其成员的请求，或根据欧盟委员会的请求核查涉及本条例适用的任何问题，为了鼓励对本条例的适用，发布指导方针、建议和最佳操作；
(f)为了进一步细化第22（2）条规定的基于用户画像的决策的标准和条件，发布符合本段（e）点的指导方针、建议和最佳操作；
(g)为了认定个人数据泄露，确定是否存在第33（1）、（2）条所规定的无理拖延，以及控制者或处理者是否需要告知个人数据泄露，发布符合本段（e）点的指导方针、建议和最佳操作；
(h)对于个人数据违法可能会对第34（1）条规定的自然人的权利与自由带来高风险的情形，发布符合本段（e）点的纲领、建议和最佳操作；
(i)对于符合控制者所遵守的约束性公司规则、处理者所遵守的约束性公司规则的数据转移，以及符合为了保证第47条规定的对数据主体的个人数据保障而采取的必要措施的个人数据转移，为了细化此类转移的标准和要求，发布符合本段（e）点的指导方针、建议和最佳操作；
(j)为了进一步细化第49（1）条规定的个人数据转移所需要的标准和要求，发布符合本段（e）点的指导方针、建议和最佳操作；
(k)对于涉及第58（1）、（2）、（3）条规定的适用措施和确定第83条规定的行政处罚，为监管机构起草指导方针；
(l)对本段（e）点和（f）点规定的指导方针、建议和最佳操作的实际运用进行审查；
(m)对自然人设报告侵犯本条例的行为，为其设立符合第54（2）条的一般程序，，发布符合本段（e）点的指导方针、建议和最佳操作；
(n)鼓励起草行为准则，设立符合第40条和第42条的数据保护认证机制、数据保护印章和标记；
(o)对认证机构进行委任，根据第43条而进行阶段性审查，对符合第43（6）条的委任机构、符合第42（7）条而在第三国设立的被认证的控制者或处理者进行持续性的公共登记；
(p)为了委任第42条规定的认证机构而细化第43（3）条规定的要求；
(q)向欧盟委员会提供关于第43（8）条规定的验证要求的意见；
(r)向欧盟委员会提供关于第12（7）条规定的图标的意见；
(s)评估第三国或国际组织的保护程度，包括评估第三国、某个地区、或该第三国的一个或多个特定部门，或国际组织是否仍然提供足够程度的保护。为了实现这一目的，欧盟委员会应当向欧盟数据保护委员会提供所有必要的记录，包括和该第三国政府的进行的涉及到第三国、某个地区、或该第三国的一个或多个特定部门，或国际组织的通信。
(t)发布按照第64（1）条规定的一致性机制而做出的关于监管机构的决议草案，按第64（2）条提交的事项，以及发布根据第65条，包括第66条规定的约束性决定。
(u)促进监管机构之间的合作，有效的双边或多边信息交换，以及最好的实践；
(v)促进共同培训项目，协助监管机构之间以及——如果适用的话——监管机构与第三国监管机构或国际组织之间的人员交换；
(w)促进与全球数据保护监管机构的知识交流、数据保护立法的记录与实践。
(x)发布关于根据第40（9）条在欧盟层面起草的行为准则的意见；以及
(y)对于监管机构和法庭做出的决定以及根据一致性机制所处置的事项，保持一份公众可以访问的电子登记。
2．当欧盟委员会请求欧盟数据保护委员会提供意见，欧盟委员会可以在考虑事项的紧急程度后表明期限要求。
3．欧盟数据保护委员会应当将其意见、指导纲领、推荐以及最佳操作告知欧盟委员会和第93条规定的理事会，而且应当将它们公开。
4．如果适用的话，欧盟数据保护委员会应当咨询当事人，給与他们在一段合理期限内进行评论的机会。在不影响第76条的前提下，欧盟数据保护委员会应当将咨询程序的结果公之于众。
&lt;strong&gt;第71条 报告&lt;/strong&gt;
1．对于欧盟内部、相关第三国以及国际组织中的数据处理活动，若涉及自然人的保护，欧盟数据保护委员会应当起草年度报告。报告应当公开，而且应当传输给欧洲议会、欧盟理事会和欧盟委员会。
2．年度报告应当包括第70（1）条（l）点规定的对指导方针、建议和最佳操作的实际运用进行审查，以及第65条规定的约束性决议。
&lt;strong&gt;第72条 程序&lt;/strong&gt;
1．欧盟数据保护委员会应当通过其成员的简单多数做出决定，除非本条例有相反规定。
2．欧盟数据保护委员会应当以其成员的三分之二多数制定程序规则，组建其自身的操作机制。
&lt;strong&gt;第73条 主席&lt;/strong&gt;
1．欧盟数据保护委员会应当通过简单多数的方式从其成员中选举一位主席、两位副主席。
2．主席以及副主席职位的任期应当是5年，可以连任一届。
&lt;strong&gt;第74条 主席的任务&lt;/strong&gt;
1．主席具有如下任务：
(a)召集欧盟数据保护委员会的会议，准备会议议程；
(b)将委员会根据第65条而做出的决定告知第65条规定的领导性监管机构和相关监管机构；
(c)保证欧盟数据保护委员会任务的及时履行，特别是和第63规定的一致性机制相关的任务。
2．欧盟数据保护委员会应当在其程序规则中对主席与副主席的任务分工进行分配。
&lt;strong&gt;第75条 秘书&lt;/strong&gt;
1．欧盟数据保护委员会应当有一名秘书，其应当由欧盟数据保护监督者来任命。
2．秘书应当严格按照欧盟数据保护委员会主席的指示履行其职责。
3．欧盟数据保护监管者的员工，如果涉及履行到本条例赋予给欧盟数据保护委员会的任务，应当与涉及履行赋予给欧盟数据保护监管者的任务的员工遵守不同的报告程序。
4．在适用的情况下，欧盟数据保护委员会和欧盟数据保护监管者应当撰写与发布一份实施本条的谅解备忘录，确定它们之间合作的条款，在涉及履行本条例赋予给欧盟数据保护委员会的任务时，谅解备忘录适用于欧盟数据保护监管者的员工。
5．秘书应当向欧盟数据保护委员会提供分析、管理与后期支持。
6．秘书应当对如下事项负责：
(a)欧盟数据保护委员会的日常事务；
(b)欧盟数据保护委员会、欧盟数据保护委员会主席与欧盟委员会之间的交流；
(c)与其他机构及公众的交流；
(d) 内部交流与外部交流中对电子手段的使用；
(e)对相关信息的翻译；
(f)对欧盟数据保护委员会会议的准备与跟踪；
(g)准备、起草与发布欧盟数据保护委员会对监管机构之间分歧的意见与决定，以及其他文本。
&lt;strong&gt;第76条 机密性&lt;/strong&gt;
1．欧盟数据保护委员会若认为根据程序规则的要求，有必要秘密开展某项讨论活动，那么该讨论活动就应当严格保密。
2．访问提交给欧盟数据保护委员会的成员、专家与第三方代表的文件，应当遵守欧洲议会和欧盟理事会的 (EC) No 1049/2001条例[1]。
&lt;strong&gt;第八章 救济、责任与惩罚&lt;/strong&gt;
&lt;strong&gt;第77条 向监管机构提起申诉的权利&lt;/strong&gt;
1．在不影响任何其他行政或司法救济的前提下，每个数据主体都有向监管机构进行申诉的权利，这尤其适用于以下地点的监管机构：数据主体所属的成员国或经常居住地、工作地、或数据主体认为处理其个人数据违反本条例的发生地。
2．收到申诉的监管机构应当告知申诉者申诉的进展和结果，包括符合第78条的司法救济的可能性。
&lt;strong&gt;第78条 针对监管机构的有效司法救济权&lt;/strong&gt;
1．在不影响其他任何行政或司法救济的前提下，任何自然人或法人都有权对关乎他们的监管机构的有法律约束力的决定获得有效的司法救济。
2．在不影响其他任何行政或司法救济的前提下，如果根据第55条和第56条的有权监管机构不处置申诉，或者在三个月内没有向数据主体告知第77条规定的申诉的进展或结果，任何自然人或法人都有权获得有效的司法救济。
3．针对监管机构的法律诉讼应当在监管机构所在的成员国的法庭提起。
4．如果针对监管机构决定的法律诉讼发生在欧盟数据保护委员会根据一致性机制而做出意见或决定之前，监管机构应当将其意见或决定告知法院。
&lt;strong&gt;第79条 针对控制者或处理者的有效司法救济权&lt;/strong&gt;
1．在不影响其他任何行政或司法救济的前提下，包括在不影响第77条规定的向监管机构提交申诉的前提下，任何数据主体认为，由于违反本条例而处理其个人数据，导致其被本条例所赋予的权利被侵犯，在这些情形下其都有获取司法救济的权利。
2．针对控制者或处理者的法律诉讼应当在它们拥有机构的成员国的法庭提起。在其他情形下，此类法律诉讼可以在数据主体的经常居住地的法庭提起，除非控制者或处理者是成员国行使其公共权力的公共机构。
&lt;strong&gt;第80条 对数据主体的代表&lt;/strong&gt;
1．数据主体有权委托非盈利机构、实体或协会代表其行使第77、78、79条规定的权利，以及在成员国法律规定的情形下，代表其行使第82条规定的获得赔偿的权利。非盈利机构、实体或协会应具备如下条件：按照成员国法律设立，其章程目标是实现公共利益，在为了保护数据主体的权利与自由而代表个人提起申诉方面表现积极。
2．不论数据主体是否委托，成员国都可以规定，本条第1段所规定的任何机构、组织或协会如果认为本条例所规定的数据主体的权利已经因为处理而受到侵犯，都有权在成员国向第77条规定的有权监管机构提起申诉，行使第78条和第79条规定的权利。
&lt;strong&gt;第81条 法律诉讼的中止&lt;/strong&gt;
1．当一个成员国的有管辖权的法院获知，另一成员国的法院准备对涉及同一个控制者或处理者处理的同一主要事项进行判决，该法院应当通知另一成员国的法院已经存在此类法律程序。
2．当另一成员国法院准备对涉及同一个控制者或处理者处理的同一主要事项进行判决，除了首先接收案件的法院，所有有权审理的法院都可以停止其法律程序。
3．在那些诉讼等待初审的情形中，如果首先接收案件的法院对涉及的活动具有管辖权而且其法律允许合并审理，所有除了首先接收案件的法院都可以基于相关一方的申请而拒绝管辖。
&lt;strong&gt;第82条 获取赔偿的权利与责任&lt;/strong&gt;
1．任何因为违反本条例而受到物质或非物质性伤害的人都有权从控制者或数据者那里获得对损害的赔偿。
2．任何涉及到处理的控制者都应当对因为违反本条例的处理而受到的损害承担责任。对于处理者，当其没有遵守本条例明确规定的对处理者的要求，或者当其违反控制者的合法指示时，其应当对处理所造成的损失负责。
3．控制者或处理者如果证明自己对引起损失的事件没有任何责任，那么其第2段所规定的责任可以免除。
4．当不止一个控制者或处理者，或控制者与处理者同时涉及到同一处理，而且它们对第2段和第3段规定的处理所引起的所有损失承担责任，每个控制者或处理者都应当对损失负有连带责任，以便保证对数据主体的有效赔偿。
5．当控制者或处理者已经根据第4段的规定对所受损失进行全额赔偿，该控制者或处理者可以按照第2段所规定的条件，要求另一控制者或处理者返回其造成的那部分损失。
6．为了行使其获得赔偿的权利，根据第79（2）条的规定，应当在成员国认可的有管辖权的法院提起诉讼请求。
&lt;strong&gt;第83条 行政罚款的一般条件&lt;/strong&gt;
1．.每个监管机构都应当保证，其根据本条而对第4、5、6条所规定的违反本条例的行为进行罚款，在每个案件中都应当是有效的、成比例的和劝诫性的。
2．根据每个案件的具体情形，行政处罚应当在第58（2）条的（a）至（h）点以及（j）点规定的措施基础上进行追加，或者应当代替这些措施。当在每个具体案件中决定是否应当进行行政处罚，以及决定行政处罚的金额，应当充分考虑如下因素：
(a)结合相关处理的性质、范围或目的，被影响的数据主体的数量以及损害程度而确定的违法的性质、严重性与持续时间；
(b)违法的性质是基于故意还是过失；
(c)控制者或处理者为了减轻数据主体损失而采取的所有行动；
(d)结合控制者或处理者采取的符合第25条和第32条的技术性与组织性措施而认定的控制者或处理者的责任程度；
(e)控制者或处理者之前的所有相关违法行为；
(f)为了纠正违法行为和减轻违法所造成的可能负面影响而和监管机构进行合作的程度；
(g)为违法行为所影响的个人数据类型；
(h)监管机构得知违法行为的方式，特别是控制者或处理者是否对违法行为进行了报告，以及在何种程度上进行了报告；
(i)如果对同一主题事项已经对控制者或处理者发布第58（2）条规定的措施，对这些措施是否遵守；
(j)遵守符合第40条的已生效的行为准则或符合第42条的已生效的认证机制；以及
(k)对于案件情形可以适用的所有加重或减轻因素，例如因为违法而直接或间接导致的经济收益、避免的损失。
3．如果控制者或处理者故意或过失性地因为同一或相关的处理操作而违反本条例的条款，行政罚款的总额不应当超过最严重违法所确定的额度。
4．违反如下条款，应当按第2段的规定施加最高10 000 000欧元的行政罚款，如果是企业的话，最高可处相当于其上一年全球总营业额2%的金额的罚款，两者取其高的一项进行罚款：
(a)第8，11，25，26，27，28，29，30，31，32，33，34，35，36，37，38，39，42和43条规定的控制者和处理者的责任；
(b)第42条和第43条规定的认证机构的责任；
(c)第41（4）条规定的监管机构的责任。
5．违反如下条款，应当按第2段的规定施加最高20 000 000欧元的行政罚款，如果是企业的话，最高可处相当于其上一年全球总营业额4%的金额的罚款，两者取其高的一项进行罚款：
(a)处理的基本原则，包括第5、6、7和9条规定的同意的条件；
(b)第12条至22条规定的数据主体的权利；
(c)第44条至第49条规定的将个人数据转移到第三国或一个国际组织的接收者；
(d)所有第九章规定的符合成员国法律的责任；
(e)违反监管机构根据第58（2）条对处理所发布的命令、或暂时性或确定性的限制，或对数据流动的中止，或违反第58（1）条拒绝提供访问。
6．违反第58（2）条规定的监管机构发布的命令，应当按第2段的规定施加最高20 000 000欧元的行政罚款，如果是集团的话，可以施加最高前一年全球总营业额4%的罚款，两者取其高的一项进行罚款。
7．在不影响符合第58（2）条的监管机构的矫正权力的前提下，每个成员国都可以制定规则，确定在什么情况下对在其境内设立的公共机构和实体进行行政处罚。
8．监管机构行使本条所规定的权力，应当采取符合欧盟和成员国法律所规定的合适的程序性保障，包括有效的司法救济和正当程序。
9．当成员国的法律体系并不提供行政处罚，本条可以以如下方式适用：可以通过有权监管机构提出行政处罚，然后有职权的全国性法院进行适用，同时，应保证那些法律救济是有效的，而且这些法律救济与监管机构所施加的行政处罚具有同等效力。不论在何种情形中，所施加的处罚必须是有效的、成比例的和劝诫性的。那些成员国应当[在本条例生效两年内]将根据本段所制定的法律条款、所有后续的修正性法律或影响它们的法律修订及时告知欧盟委员会。
&lt;strong&gt;第84条 惩罚&lt;/strong&gt;
1．成员国应当制定可适用于违反本条例的其他惩罚的规则，特别是对于那些不受第83条规定的行政处罚约束的违法行为，成员国应当制定必要措施保证这些惩罚规则得到执行。此类惩罚应当是有效的、成比例的和劝诫性的。
2．对于符合第1段所制定的法律，每个成员国的应当[在本条例生效的两年内]将其法律条款告知欧盟委员会，而且应当及时告知影响条款的后续修订。
&lt;strong&gt;第九章 和特定处理情形相关的条款&lt;/strong&gt;
&lt;strong&gt;第85条 处理、表达自由与信息&lt;/strong&gt;
1．成员国应当通过制定法律调和符合本条例制定的个人数据保护权与表达自由权与信息权，包括调和为了新闻目的和学术、艺术或文学表达目的而进行的处理。
2．对于出于新闻目的和学术、艺术或文学表达目的而进行的处理，如果对于调和符合本条例制定的个人数据保护权与表达自由权与信息权有必要，成员国应当对第二章（原则）、第三章（数据主体的权利）、第四章（控制者和处理者）、第五章（个人数据转移到第三国或国际组织）、第六章（独立监管机构）、第七章（合作与一致性）和第九章（特定数据处理的情形）的规定进行豁免或克减。
3．每个成员国都应当将其按照第2段所制定的法律条款告知欧盟委员会，而且应当将所有后续的修正性法律或影响它们的法律修订及时告知欧盟委员会。
&lt;strong&gt;第86条 处理与公众对官方文件的访问&lt;/strong&gt;
为了调和公众对官方文件的访问与本条例规定的个人数据保护权，对于公共机构或公共实体或为了实现公共利益而履行任务的私人实体所拥有的官方文件中的个人数据，机构或实体可以根据成员国为机构或实体而制定的法律而公开。
&lt;strong&gt;第87条 对全国性身份识别号码的处理&lt;/strong&gt;
成员国可以对处理全国性身份识别号码或其他一般性识别标识的特定情形做出规定。在这种情形下，只有对本条例规定的数据主体的权利与自由采取适当安全保障，才能使用全国性身份识别号码或其他一般性识别标识。
&lt;strong&gt;第88条 雇佣语境下的处理&lt;/strong&gt;
1．多个成员国可以通过法律或通过协定制定特定规则，以保证在雇佣语境下处理雇员个人数据保证其权利与自由。这在如下情形中尤其适用：为了招聘、履行雇佣合同，包括法律或集体合同规定的免除合同；对工作的管理、计划与组织；工作场所的合理性与多样性；工作中的健康与安全，对员工与顾客财产的保护；为了行使和享受雇佣相关的权利与收益；以及为了终止雇佣关系。
2．此类规则应当包括为保障数据主体人身尊严、正当利益与基本权利的合适与特定的措施。这在涉及到如下事项时尤其适用：处理的透明性；在一群企业中转移个人数据；或进行联合经济活动的一群企业和工作场所的监管系统。
3．每个成员国应当[在本条例生效的两年内]将其按照第1段所制定的那些法律条款告知欧盟委员会，而且应当及时告知影响条款的后续修订。
&lt;strong&gt;第89条 为了实现公共利益、科学或历史研究或统计目的处理中的安全保障与克减&lt;/strong&gt;
1．为了实现公共利益、科学或历史研究或统计目而处理，应当采取符合本条例的恰当防护措施，保障数据主体的权利与自由。这些防护措施应当确保，为了保证数据最小化原则，已经采取技术与组织性的措施。这些措施可以包括匿名化，如果匿名化也能实现上诉目的。如果在进一步处理中实现对数据主体无法识别也可以实现上诉目的，那就应当采取这种方式处理。
2．对于为了实现公共利益、科学或历史研究或统计目的处理，成员国的法律可以按照本条第1段所规定的情形与防护措施对第15、16、18、21条所规定的权利进行克减——如果此类权利可能彻底阻碍或严重阻碍实现上述目的，而此类克减对于实现上诉目的是必要的。
3．当个人数据处理是为了实现公共利益，欧盟或成员国的法律可以按照本条第1段所规定的情形与防护措施对第15、16、18、19、20和21条规定的权利进行克减——如果此类权利可能彻底阻碍或严重阻碍实现上述目的，而此类克减对于实现上诉目的是必要的。
4．如果第2段和第3段所规定的处理还有其他目的，克减将只适用于为了实现第2段和第3段中所规定的目的的处理。
&lt;strong&gt;第90条 保密责任&lt;/strong&gt;
1．成员国可以制定特定的规则，对第58（1）条（3）和（f）点所规定的、和作为主体的控制者或处理者相关的、全国性有权机构所设立的监管机构的权力进行规定，如果有必要对个人数据保护与保守秘密进行调和与比例性保护，此特定规则可以施加职业性秘密保守责任或其他同等责任。只有在那些保守秘密责任所涉及的活动中或因为此类活动而接收个人数据，此类规则才适用于控制者或处理者。
2．每个成员国的应当[在本条例生效的两年内] 将其按照第1段所制定的那些法律条款告知欧盟委员会，而且应当及时告知影响条款的后续修订。
&lt;strong&gt;第91条 现有的的对教会和宗教协会的数据保护规则&lt;/strong&gt;
1．在本条例生效后，对于适用于某成员国境内教会、宗教协会或团体的保护自然人在处理相关中的综合性规则，如果它们和本条例保持一致，仍然应当适用。
2．对于那些适用符合第1段的综合性规则的教会和宗教协会，其应当接受一个独立监管机构的监管，如果其满足本条例第六章规定的条件，这种独立监管机构可以是特别指定的。
&lt;strong&gt;第十章 授权法案与实施性法案&lt;/strong&gt;
&lt;strong&gt;第92条 对授权的行使&lt;/strong&gt;
1．欧盟委员会享有授权法案的制定权，此权力受本条所规定的条件所约束。
2．第12（8）条和43（8）条所规定的授权应当[在本条例生效后]的一段不确定的时间内赋予给欧盟委员会。
3．第12（8）条和43（8）条所规定的授权可以随时被欧洲议会或欧盟理事会撤销。撤销决定应当终止决定所特别指明的授予性权力。撤销决定生效日是欧盟官方杂志发布后的第二天或决定所特别标明的日期。撤销决定不应影响任何已经生效的授权性法案。
4．欧盟委员会一旦制定授权性法案，其应当立刻同时告知欧洲议会和欧盟理事会。
5．根据第12（8）条和第43（8）条而指定的授权性法案，只有欧洲议会或欧盟理事会在其收到通知后三个月内都没有表达反对，或者在三个月内欧洲议会或欧盟理事会已经告知欧洲委员会它们不会反对，其才能生效。如果欧洲议会或欧盟理事会提出延期，这个期间可以再延长三个月。
&lt;strong&gt;第93条 委员会程序&lt;/strong&gt;
1．欧盟委员会应当有一个小组对其进行协助。该小组应当是(EU) No 182/2011条例所规定的小组。
2．涉及到此段时，(EU) No 182/2011指令第5条应当适用。
3．涉及到此段时，与(EU) No 182/2011指令第5条配合的(EU) No 182/2011指令第8条应当适用。
&lt;strong&gt;第十一章 最后条款&lt;/strong&gt;
&lt;strong&gt;第94条 95/46/EC指令的废止&lt;/strong&gt;
1．在[本条例生效后的两年]后95/46/EC指令将被废止。
2．当参照废止指令时，应当通过参照本条例来进行解释。对于参照工作小组在95/46/EC指令第29条所规定的处理个人数据中个人保护，这应当以参照本条例所规定的欧盟数据保护委员会来进行解释。
&lt;strong&gt;第95条 与2002/58/EC的关系&lt;/strong&gt;
在欧盟的公共通讯网络中提供公众可获取的电子通讯服务的情形中，对于2002/58/EC指令已经施加特殊责任的事项，本条例不应再对同一事项再向自然人或法人施加额外责任。
&lt;strong&gt;第96条 和之前已经达成的协议的关系&lt;/strong&gt;
对于[在此条例生效]之前的，符合[在此条例生效]之前所制定的法律的，涉及到将个人数据传输到第三国或国际组织的成员国之间已经达成的国际性协议，在其被修改、替代或撤销之前，应当一直具有效力。
&lt;strong&gt;第97条 委员会报告&lt;/strong&gt;
1．在[本条例生效后的四年后]，以及在这之后的每四年，欧盟委员会应当向欧洲议会和欧盟理事会提交一份对本条例的评价与审查。该报告应当公之于众。
2．在第1段所规定的评价与审查情形中，欧盟委员会应当尤其检查如下事项的适用与运作：
(a)第五章规定的将个人数据转移到第三国或国际组织，特别是按照本条例第45（3）条而做出的决定，以及根据95/46/EC第25（6）条而做出的决定；
(b)第七章规定的合作与一致性。
3．为了实现第1段的目的，欧盟委员会可以要求成员国和监管机构提供相关信息。
4．为了进行第1段和第2段规定的评价与审查，欧盟理事会应当考虑欧洲议会、欧盟理事会以及其他相关实体与生产商的立场与调查。
5．在必要的情形下，欧盟委员会应当提交修改本条例的合适动议，特别是如果考虑了信息科技的发展以及信息社会中的发展状态。
&lt;strong&gt;第98条 对欧盟其他数据保护法案的审查&lt;/strong&gt;
如果合适的话，欧盟委员会应当提交立法性动议，以便对欧盟的其他个人数据保护法案进行保护，以便保证在处理中对自然人进行一致与一致性的保护。这尤其应当涉及到欧盟机构、实体、办公室和规制机构处理中和自然人保护相关的规则，以及此类数据的自由流动。
&lt;strong&gt;第99条 生效与适用&lt;/strong&gt;
1．本条例的生效时间是其在欧盟官方杂志发布后的二十天后。
2．其适用时间是[本条例生效后的两年后]。
本条例的所有条款都具有约束力，而且应当直接适用于成员国。
[1]欧洲议会和欧盟理事会关于公众访问欧洲议会、欧盟理事会与欧盟委员会文件(OJ L 145, 31.5.2001, p. 43)的(EC) No 1049/2001条例。&lt;/p&gt;
</content:encoded></item><item><title>Linux</title><link>https://h1s97x.github.io/posts/notes/notes-linux/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-linux/</guid><description>VIM 快速入门之常用命令解析 1,VIM 复制，粘贴，剪切，替换，查找\哔哩哔哩\bilibili 符号：『』❑ ➢ 图书名称: Ubuntu Linux 应</description><pubDate>Sat, 27 Dec 2025 15:46:11 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1YY4y1n75r/?vd_source=206a091077ff783d662b49e64cc9589d&quot;&gt;VIM 快速入门之常用命令解析 1,VIM 复制，粘贴，剪切，替换，查找_哔哩哔哩_bilibili&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;符号：『』❑ ➢&lt;/p&gt;
&lt;h2&gt;参考资料&lt;/h2&gt;
&lt;h3&gt;主要参考书&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;图书名称:&lt;/th&gt;
&lt;th&gt;Ubuntu Linux 应用技术教程&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;封面图片:&lt;/td&gt;
&lt;td&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/linux-04-20261014.jpg&quot; alt=&quot;img&quot; /&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;出版印刷时间:&lt;/td&gt;
&lt;td&gt;2009-4&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;出版社:&lt;/td&gt;
&lt;td&gt;清华大学出版社&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;图书作者:&lt;/td&gt;
&lt;td&gt;陈明 编著&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ISBN:&lt;/td&gt;
&lt;td&gt;9787302193289&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;图书名称:&lt;/th&gt;
&lt;th&gt;Linux 就该这么学（第 2 版）&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;封面图片:&lt;/td&gt;
&lt;td&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/linux-03-20261014.jpg&quot; alt=&quot;Linux就该这么学（第2版）&quot; /&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;出版印刷时间:&lt;/td&gt;
&lt;td&gt;2021-9&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;出版社:&lt;/td&gt;
&lt;td&gt;人民邮电出版社&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;图书作者:&lt;/td&gt;
&lt;td&gt;刘遄 编著&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ISBN:&lt;/td&gt;
&lt;td&gt;9787115570116&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;链接&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.linuxprobe.com/chapter-00.html&quot;&gt;第 0 章 咱们先来谈谈学习方法和红帽系统。 | 《Linux 就该这么学》 (linuxprobe.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.gnu.org/gnu/manifesto.zh-cn.html&quot;&gt;GNU 宣言&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;目录&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Linux 应用课程基础&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Linux 基础命令&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;文件系统&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;权限管理&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Vi 应用&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;进程管理&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SHELL 编程&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Chapter 1 Linux 概述&lt;/h2&gt;
&lt;h3&gt;1.1 Linux 操作系统简述&lt;/h3&gt;
&lt;p&gt;操作系统是计算机系统的基本系统软件。操作系统是所有软件的核心。操作系统负责控制、管理计算机的所有软件、硬件资源，是惟一直接和硬件系统打交道的软件，是整个软件系统的基础部分，同时还为计算机用户提供良好的界面。因此，操作系统直接面对所有硬件、软件和用户，它是协调计算机各组成部分之间、人机之间关系的重要软件系统。&lt;/p&gt;
&lt;h4&gt;强大好用的 Shell&lt;/h4&gt;
&lt;p&gt;一台完整的计算机是由运算器、控制器、存储器、输入/输出等多种硬件设备共同组成的，而能让各种硬件设备各司其职且又能协同运行的东西就是系统内核。Linux 系统的内核负责完成对硬件资源的分配、调度等管理任务，对系统的正常运行起着十分重要的作用。&lt;/p&gt;
&lt;p&gt;与修改 Windows 系统中的注册表类似，直接改动内核参数的难度比较大，而且一旦“手滑”还有可能导致系统直接崩溃。因此不建议同学们直接去编辑内核中的参数，而是用基于系统调用接口开发出来的程序或服务来管理计算机，以满足日常的工作需要。&lt;/p&gt;
&lt;p&gt;如图所示，人类是无法直接控制硬件的（想象一个人捧着块硬盘自言自语的滑稽场景）。硬件设备由系统内核直接管理，但由于内核的复杂性太高，在访问时存在较大的风险，因此用户不能直接访问内核。虽然通过调用系统提供的 API（应用程序编程接口）就能实现某个功能，但哪怕实现“将一条信息通过互联网传输给别人”这样简单的任务，都要手动调用几十次 API 接口，使用起来太不切实际。而最外层的服务程序则是最贴近于用户端的，这些服务程序是集成了大量 API 接口的完整软件，微信、QQ 就是这样的服务程序。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.linuxprobe.com/wp-content/uploads/2020/05/%E7%94%A8%E6%88%B7%E4%B8%8E%E7%A1%AC%E4%BB%B6-1.jpg&quot; alt=&quot;第2章 新手必须掌握的Linux命令第2章 新手必须掌握的Linux命令&quot; /&gt;&lt;/p&gt;
&lt;p&gt;讲到这里，相信大家已经能够明白服务程序对用户和硬件所能发挥的作用了。如果把整台电脑比喻成人类社会，那么服务程序就是一名翻译官，负责将用户提出的需求转换成硬件能够接收的指令代码，然后再将处理结果反馈成用户能够读懂的内容格式。这样一来一回，用户就能使用硬件资源了。&lt;/p&gt;
&lt;p&gt;看到被一层层“包裹”起来的硬件设备，大家有没有感觉像一只蜗牛的壳呢？英文中的壳叫作 Shell，我们在行业中也将用户终端程序称之为 Shell，方便好记。&lt;/p&gt;
&lt;p&gt;Shell 就是终端程序的统称，它充当了人与内核（硬件）之间的翻译官，用户把一些命令“告诉”终端程序，它就会调用相应的程序服务去完成某些工作。现在包括红帽系统在内的许多主流 Linux 系统默认使用的终端是 Bash（Bourne-Again SHell）解释器，这个 Bash 解释器主要有以下 4 项优势：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;通过上下方向键来调取执行过的 Linux 命令；&lt;/p&gt;
&lt;p&gt;命令或参数仅需输入前几位就可以用 Tab 键补全；&lt;/p&gt;
&lt;p&gt;具有强大的批处理脚本；&lt;/p&gt;
&lt;p&gt;具有实用的环境变量功能。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;1.2 发展史&lt;/h3&gt;
&lt;h3&gt;1.3 Linux 的特点与组成&lt;/h3&gt;
&lt;p&gt;Linux 是多用户、多任务、抢占式操作系统，原作者 Linus，是开源操作系统。&lt;/p&gt;
&lt;p&gt;Linux 系统基本组成:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Kernel 内核&lt;/li&gt;
&lt;li&gt;SHELL（bash）&lt;/li&gt;
&lt;li&gt;Applications 应用程序&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;§ Kernel&lt;/p&gt;
&lt;p&gt;1、 管理程序的运行，为程序分派资源，处理程序之间的通信
2、 管理对文件系统的读写
3、 把对文件系统的操作映射成对磁盘或其他设备的操作
4、 管理输入，输出，将设备映射成设备文件
5、 管理网络
§ Shell&lt;/p&gt;
&lt;p&gt;1、 Shell:是一个命令解释器，是内核和用户之间的接口。
2、 Shell:是一个编程语言。(Shell script)
3、 系统的启动过程：init-&amp;gt; login -&amp;gt; passwd -&amp;gt; shell&lt;/p&gt;
&lt;p&gt;§ Linux 应用程序&lt;/p&gt;
&lt;p&gt;• 标准的 Linux 系统都具有一整套称为应用程序的程序集，包括文本编辑器、Xwindow、办公套件、Internet 工具和数据库等。&lt;/p&gt;
&lt;h3&gt;1.4 Linux 发行版本&lt;/h3&gt;
&lt;p&gt;在介绍常见的 Linux 系统版本之前，首先需要区分 Linux 系统内核与 Linux 发行套件系统的不同。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Linux 系统内核指的是一个由 Linus Torvalds 负责维护，提供硬件抽象层、磁盘、文件系统控制及多任务功能的系统核心程序。&lt;/p&gt;
&lt;p&gt;Linux 发行套件系统是我们常说的 Linux 操作系统，也就是由 Linux 内核与各种常用软件的集合产品。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Linux 发行版是由包含 Linux 内核和软件包管理系统的软件集合组成的操作系统。Linux 用户通常通过下载其中一个 Linux 发行版来获取他们的操作系统，这些发行版可用于各种系统，从嵌入式设备（例如，OpenWrt）和个人计算机（例如，Linux Mint）到强大的超级计算机（例如，Rocks Cluster Distribution）。&lt;/p&gt;
&lt;p&gt;典型的 Linux 发行版包括一个（通常经过修改的）Linux 内核、一个初始化系统（如 systemd、OpenRC 或 runit）、GNU 工具和库、文档和许多其他软件（如 IP 网络配置实用程序和 getty TTY 安装程序等）。（可选）提供桌面体验（最常见的是 Mesa 用户空间图形驱动程序）、显示服务器（最常见的是 X.org Server，或者最近的 Wayland 合成器，如 Sway、KDE 的 KWin 或 GNOME 的 Mutter）、桌面环境、声音服务器（通常是 PulseAudio 或最近的 PipeWire）），其他相关程序可能包含在发行版中，也可以由用户安装。&lt;/p&gt;
&lt;p&gt;大多数包含的软件都是免费的开源软件，既可以作为编译的二进制文件提供，也可以以源代码形式提供，允许对原始软件进行修改。通常，Linux 发行版可以选择性地包含一些可能不以源代码形式提供的专有软件，例如某些设备驱动程序所需的二进制 Blob。&lt;/p&gt;
&lt;h4&gt;红帽企业版系统（RHEL,RedHatEnterpriseLinux）&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://www.linuxprobe.com/wp-content/uploads/2020/04/redhat.png&quot; alt=&quot;第0章 咱们先来谈谈为什么要学习Linux系统第0章 咱们先来谈谈为什么要学习Linux系统&quot; /&gt;&lt;/p&gt;
&lt;p&gt;前文在介绍 Linux 系统的发展历史时，曾提到过红帽公司。红帽公司作为全球知名的开源技术厂商，其产品值得我们放到第一位来介绍。红帽公司成立于 1993 年，于 1998 年在纳斯达克上市，自从 1999 年起陆续收购了包括 JBoss 中间件供应商、CentOS（社区企业操作系统）、Ceph 企业级存储业务等在内的数十家高科技公司及热门产品，这么做的目的当然是为了对主营业务红帽企业版 Linux 进行增强。&lt;/p&gt;
&lt;p&gt;红帽企业版 Linux 最初于 2002 年 3 月面世，当年 Dell、HP、Oracle 以及 IBM 公司便纷纷表示支持该系统平台的硬件开发，因此红帽企业版 Linux 系统的市场份额在近 20 年时间内不断猛增。红帽企业版 Linux 当时是全世界使用最广泛的 Linux 系统之一，在世界 500 强企业中，所有的航空公司、电信服务提供商、商业银行、医疗保健公司均无一例外地通过该系统向外提供服务。&lt;/p&gt;
&lt;p&gt;红帽企业版 Linux 当前的最新版本是 RHEL 8，该系统具有极强的稳定性，在全球范围内都可以获得完善的技术支持。该系统也是本书和红帽认证考试中默认使用的操作系统。&lt;/p&gt;
&lt;h4&gt;CentOS 社区企业操作系统（Community Enterprise Operating System）&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://www.linuxprobe.com/wp-content/uploads/2015/01/centos.jpg&quot; alt=&quot;第0章 咱们先来谈谈为什么要学习Linux系统第0章 咱们先来谈谈为什么要学习Linux系统&quot; /&gt;&lt;/p&gt;
&lt;p&gt;顾名思义，CentOS 是由开源社区研发和维护的一款企业级 Linux 操作系统，在 2014 年 1 月被红帽公司正式收购。CentOS 系统最为人广泛熟悉的标签就是“免费”。如果您问一个运维“老鸟”选择 CentOS 系统的理由，他绝对不会跟你说更安全或更稳定，而只是说两个字—免费！由于红帽企业版 Linux 是开源软件，任何人都有修改和创建衍生品的权利，因此 CentOS 便是将红帽企业版 Linux 中的收费功能通通去掉，然后将新系统重新编译后发布给用户免费使用的 Linux 系统。也正因为其免费的特性，CentOS 拥有了广泛的用户。&lt;/p&gt;
&lt;p&gt;从本质上来说，由于 CentOS 是针对红帽企业版 Linux 进行修改后再发布的版本，因此不会针对它单独开发新功能，CentOS 的版本号也是随红帽企业版 Linux 而变更。例如，CentOS 8.0 对应的就是 RHEL 8.0，CentOS 8.1 对应的就是 RHEL 8.1；以此类推。再就是，CentOS 系统和 RHEL 系统的软件包可以通用。也就是说，如果工作中用的是 RHEL，但是在安装某款软件时只找到了该软件的 CentOS 系统软件源，也是可以正常安装该软件的。&lt;/p&gt;
&lt;h4&gt;Fedora Linux&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://www.linuxprobe.com/wp-content/uploads/2015/01/fedora-e1422160855872.png&quot; alt=&quot;第0章 咱们先来谈谈为什么要学习Linux系统第0章 咱们先来谈谈为什么要学习Linux系统&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Fedora 翻译为中文是“浅顶软呢男帽”的意思，翻译之后跟 Linux 系统很不搭界，所以更多人干脆将其音译为“费多拉”系统。Fedora Linux 是正正经经的红帽公司自己的产品，最初是为了给红帽企业版 Linux 制作和测试第三方软件而构建的产品，孕育了最早的开源社群，固定每 6 个月发布一个新版本，当前在全球已经有几百万的用户。&lt;/p&gt;
&lt;p&gt;Fedora 是桌面版本的 Linux 系统，可以理解成是微软公司的 Windows XP 或者 Windows 10。它的目标用户是应付日常的工作需要，而不会追求稳定性的人群。用户可以在这个系统中体验到最新的技术和工具，当这些技术和工具成熟后才会被移植到红帽企业版 Linux 中，因此 Fedora 也被称为 RHEL 系统的“试验田”。运维人员如果想每天都强迫自己多学点 Linux 知识，保持自己技术的领先性，就应该多关注此类 Linux 系统的发展变化和新特征，不断调整自己的学习方向。&lt;/p&gt;
&lt;h4&gt;Debian Linux&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://www.linuxprobe.com/wp-content/uploads/2015/01/debian.png&quot; alt=&quot;第0章 咱们先来谈谈为什么要学习Linux系统第0章 咱们先来谈谈为什么要学习Linux系统&quot; /&gt;&lt;/p&gt;
&lt;p&gt;一款基于 GNU 开源许可证的 Linux 系统，历史久远，最初发布于 1993 年 9 月。Debian 的名字取自创始人 Ian Murdock 和他女朋友 Debra 的姓氏组合。在维基百科中，Debian 被翻译为“蝶变”系统，多么浪漫而富有诗意的名字。但可惜国内的用户不买账，看着 Logo 一圈一圈的形状，硬生生地将经念歪了。这么多年下来，现在反而很少有人听说过蝶变系统这个名字了。&lt;/p&gt;
&lt;p&gt;Debian 系统具有很强的稳定性和安全性，并且提供了免费的基础支持，可以良好地适应各种硬件架构，以及提供近十万种不同的开源软件，在国外拥有很高的认可度和使用率。虽然 Debian 也是基于 Linux 内核，但是在实际操作中还是跟红帽公司的产品有一些差别，例如 RHEL 7 和 RHEL 8 分别使用 Yum 和 DNF 工具来安装软件，而 Debian 使用的则是 APT 工具。&lt;/p&gt;
&lt;h4&gt;Ubuntu Linux&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://www.linuxprobe.com/wp-content/uploads/2020/04/ubuntu-e1624206289343.png&quot; alt=&quot;第0章 咱们先来谈谈为什么要学习Linux系统第0章 咱们先来谈谈为什么要学习Linux系统&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ubuntu 是一款桌面版 Linux 系统，以 Debian 为蓝本进行修改和衍生而来，发布周期为 6 个月。Ubuntu 的中文音译为“乌班图”，这个词最初来自于非洲南部部落使用的祖鲁语，意思是“我的存在是因为大家的存在”，体现了一种谦卑、感恩的价值观，寓意非常好。&lt;/p&gt;
&lt;p&gt;Ubuntu 系统的第一个版本发布于 2004 年 10 月。2005 年 7 月，Ubuntu 基金会成立，Ubuntu 后续不断增加开发分支，有了桌面版系统、服务器版系统和手机版系统。据调查，Ubuntu 最高峰时的用户达到了 10 亿人。尽管 Ubuntu 基于 Debian 系统衍生而来，但会对系统进行深度化定制，因此两者之间的软件并不一定完全兼容。Ubuntu 系统现在由 Canonical 公司提供商业技术支持，只要购买付费技术支持服务就能获得帮助，桌面版系统最长时间 3 年，服务器版系统最长时间 5 年。&lt;/p&gt;
&lt;h4&gt;Kali Linux&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://www.linuxprobe.com/wp-content/uploads/2020/04/kali.jpg&quot; alt=&quot;第0章 咱们先来谈谈为什么要学习Linux系统第0章 咱们先来谈谈为什么要学习Linux系统&quot; /&gt;&lt;/p&gt;
&lt;p&gt;跟上面的呆萌大蜥蜴相比，Kali Linux 的 Logo 似乎有点凶巴巴，一副不好惹的样子。这款系统一般是供黑客或安全人员使用的，能够以此为平台对网站进行渗透测试，通俗来讲就是能“攻击”网站。Kali Linux 系统的前身名为 BackTrack，其设计用途就是进行数字鉴识和渗透测试，内置有 600 多款网站及系统的渗透测试软件，包括大名鼎鼎的 Nmap、Wireshark、sqlmap 等。Kali Linux 能够被安装到个人电脑、公司服务器，甚至手掌大小的树莓派（一款微型电脑）上，可以让人有一种随身携带了一个武器库的感觉，有机会真应该单独写本书聊聊它。&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;开源共享精神&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;一般情况下，软件的源代码只由编写者拥有，而开源（即开放源代码，Open Source Code）是指一种更自由的软件发布模式。简单来说，开源软件的特点就是把软件程序和源代码文件一起打包提供给用户，让用户在不受限制地使用某个软件功能的基础上还可以对代码按需修改，让软件更贴合硬件环境，让功能更符合工作需求。用户还可以将其编制成衍生产品再发布出去。用户一般享有使用自由、复制自由、修改自由、创建衍生品自由，以及收费自由。是的，您没有看错，用户具备创建衍生品和收费的自由。这也就是说，可以对一个开源软件进行深度定制化加工。如果修改过的程序更加好用，或者颇具新的特色，只要符合原作者的许可要求，我们就完全可以合法地将软件进行商标注册，以商业版的形式再发布出去，只要有新用户使用您的软件并支付相应的费用，那就是您的收入。这也正好符合了黑客和极客对自由的追求，因此在合作与竞争中，国内外的开源社区慢慢生长出了强健的根基，人气也非常高。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.linuxprobe.com/wp-content/uploads/2020/04/OSI_Approved_License.png&quot; alt=&quot;第0章 咱们先来谈谈为什么要学习Linux系统第0章 咱们先来谈谈为什么要学习Linux系统&quot; /&gt;&lt;/p&gt;
&lt;p&gt;但是，如果开源软件只单纯追求“自由”而牺牲了程序员的利益，这肯定会影响程序员的创作热情。为了平衡两者的关系，截至目前，世界上已经有 100 多种被开源促进组织（OSI，Open Source Initiative）确认的开源许可证，用于保护开源工作者的权益。对于那些只知道一味抄袭、篡改、破解或者盗版他人作品的不法之徒，终归会在某一天收到法院的传票。&lt;/p&gt;
&lt;p&gt;开源许可证总览：https://opensource.org/licenses/alphabetical&lt;/p&gt;
&lt;h2&gt;Chapter 2 Linux 基本命令&lt;/h2&gt;
&lt;h3&gt;执行命令的必备知识&lt;/h3&gt;
&lt;p&gt;既然 Linux 系统中已经有了 Bash 这么好用的“翻译官”，那么接下来就有必要好好学习一下怎么跟它沟通了。要想准确、高效地完成各种任务，仅依赖于命令本身是不够的，还应该根据实际情况来灵活调整各种命令的参数。比如，我们切寿司时尽管可以用菜刀，但米粒一定会撒得满地都是，因此寿司刀上设计的用于透气的圆孔就是为了更好地适应场景而额外增加的参数。当您学完本书并具备一定的工作经验之后，一定能够领悟 Linux 命令的奥秘。&lt;/p&gt;
&lt;p&gt;常见的执行 Linux 命令的格式是下面这样的。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;命令名称 [命令参数] [命令对象]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;命令名称&lt;/strong&gt;：就是语法中的“动词”，表达的是想要做的事情，例如创建用户、查看文件、重启系统等操作。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;命令参数&lt;/strong&gt;：用于对命令进行调整，让“修改”过的命令能更好地贴合工作需求，达到事半功倍的效果。就像买衣服一样，衣服的尺码总会感觉偏大或偏小，要么只能将就着穿，要么就再裁剪修改一下，而这种对命令进行“裁剪”的行为就是加参数。例如创建一个编码为 888 的用户、仅查看文件的前 20 行、重启系统前先提醒其他用户等。参数可以用长格式（完整的选项名称），也可以用短格式（单个字母的缩写），两者分别用“--”与“-”作为前缀（示例请见表 2-1）。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;表 2-1 Linux 命令参数的长格式与短格式示例&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;长格式&lt;/td&gt;
&lt;td&gt;man --help&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;短格式&lt;/td&gt;
&lt;td&gt;man -h&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;命令对象&lt;/strong&gt;：一般指要处理的文件、目录、用户等资源名称，也就是命令执行后的“承受方”。例如创建一位叫小明的用户、查看一个叫工资表的文件、重启一个 IP 为 192.168.10.10 的系统等。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Tips&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;命令名称、命令参数与命令对象之间要用空格进行分隔，且字母严格区分大小写。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;常用的按键&lt;/h4&gt;
&lt;p&gt;Tab 键：&lt;/p&gt;
&lt;p&gt;​ 在 Bash 解释器的快捷键中，Tab 键绝对是使用频率最高的，它能够实现对命令、参数或文件的内容补全。例如，如果想执行 reboot 重启命令，但一时想不起来该命令的完整拼写，则可以这样输入：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;ctrl+c&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;​ 当同时按下键盘上的 Ctrl 和字母 C 的时候，意味着终止当前进程的运行。假如执行了一个错误命令，或者是执行某个命令后迟迟无法结束，这时就可以冷静地按下 Ctrl+C 组合键，命令行终端的控制权会立刻回到我们手中。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;Ctrl+d&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;​ 表示键盘输入结束，中断当前的通信或从文件中退出。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;ctrl+l&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;​ 清空当前终端中已有的内容（相当于清屏操作）。&lt;/p&gt;
&lt;h3&gt;2.1 常用系统工作命令&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;echo&lt;/td&gt;
&lt;td&gt;显示一串字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;passwd&lt;/td&gt;
&lt;td&gt;修改密码&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;clear&lt;/td&gt;
&lt;td&gt;清除显示器&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lpr&lt;/td&gt;
&lt;td&gt;打印&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;shutdown&lt;/td&gt;
&lt;td&gt;关机&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;reboot&lt;/td&gt;
&lt;td&gt;重启计算机&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ps&lt;/td&gt;
&lt;td&gt;查看目前程序执行情况&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;top&lt;/td&gt;
&lt;td&gt;查看目前程序执行情景和内存使用情况&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;kill&lt;/td&gt;
&lt;td&gt;终止一个进程&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;date&lt;/td&gt;
&lt;td&gt;更改或者查看目前时间&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;echo 命令&lt;/h4&gt;
&lt;p&gt;echo 命令用于在终端设备上输出字符串或变量提取后的值，语法格式为&lt;code&gt;echo [字符串] [$变量]&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这是 Linux 系统中最常用的几个命令之一，它的操作却非常简单，执行&lt;code&gt;echo 字符串&lt;/code&gt;或&lt;code&gt;echo $变量&lt;/code&gt;就行，其中$符号的意思是提取变量的实际值，以便后续的输出操作。&lt;/p&gt;
&lt;h4&gt;date 命令&lt;/h4&gt;
&lt;p&gt;date 命令用于显示或设置系统的时间与日期，语法格式为&lt;code&gt;date [+指定的格式]&lt;/code&gt;。&lt;/p&gt;
&lt;h4&gt;timedatectl 命令&lt;/h4&gt;
&lt;p&gt;timedatectl 命令用于设置系统的时间，英文全称为“time date control”，语法格式为&lt;code&gt;timedatectl [参数]&lt;/code&gt;。&lt;/p&gt;
&lt;h4&gt;reboot 命令&lt;/h4&gt;
&lt;p&gt;reboot 命令用于重启系统，输入该命令后按回车键执行即可。&lt;/p&gt;
&lt;p&gt;由于重启计算机这种操作会涉及硬件资源的管理权限，因此最好是以 root 管理员的身份来重启，普通用户在执行该命令时可能会被拒绝。&lt;/p&gt;
&lt;h4&gt;poweroff 命令&lt;/h4&gt;
&lt;p&gt;poweroff 命令用于关闭系统，输入该命令后按回车键执行即可。&lt;/p&gt;
&lt;p&gt;与上面相同，该命令也会涉及硬件资源的管理权限，因此最好还是以 root 管理员的身份来关闭电脑&lt;/p&gt;
&lt;h4&gt;wget 命令&lt;/h4&gt;
&lt;p&gt;wget 命令用于在终端命令行中下载网络文件，英文全称为“web get”，语法格式为“wget [参数] 网址”。&lt;/p&gt;
&lt;p&gt;借助于 wget 命令，可以无须打开浏览器，直接在命令行界面中就能下载文件。如果您没有 Linux 系统的管理经验，当前只需了解一下 wget 命令的参数以及作用，然后看一眼下面的演示实验就够了，切记不要急于求成。后面章节将逐步讲解 Linux 系统的配置管理方法，可以等掌握了网卡的配置方法后再来进行这个实验操作。表 2-6 所示为 wget 命令中的参数以及参数的作用。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;-b&lt;/td&gt;
&lt;td&gt;后台下载模式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-P&lt;/td&gt;
&lt;td&gt;下载到指定目录&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-t&lt;/td&gt;
&lt;td&gt;最大尝试次数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-c&lt;/td&gt;
&lt;td&gt;断点续传&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-p&lt;/td&gt;
&lt;td&gt;下载页面内所有资源，包括图片、视频等&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-r&lt;/td&gt;
&lt;td&gt;递归下载&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;history 命令&lt;/h4&gt;
&lt;p&gt;history 命令用于显示执行过的命令历史，语法格式为“history [-c]”。&lt;/p&gt;
&lt;p&gt;history 命令应该是运维人员最喜欢的命令。执行 history 命令能显示出当前用户在本地计算机中执行过的最近 1000 条命令记录。如果觉得 1000 不够用，可以自定义/etc/profile 文件中的 HISTSIZE 变量值。在使用 history 命令时，可以使用-c 参数清空所有的命令历史记录。还可以使用“!编码数字”的方式来重复执行某一次的命令。总之，history 命令有很多有趣的玩法等待您去开发。&lt;/p&gt;
&lt;h3&gt;2.2 文件管理&lt;/h3&gt;
&lt;h4&gt;管理文件和目录的命令&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;pwd&lt;/td&gt;
&lt;td&gt;显示当前工作目录&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cd&lt;/td&gt;
&lt;td&gt;将活动目录更改为指定的路径&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ls&lt;/td&gt;
&lt;td&gt;显示指定工作目录的内容&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cat&lt;/td&gt;
&lt;td&gt;显示文件的内容&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;grep&lt;/td&gt;
&lt;td&gt;在文件中查找某字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cp&lt;/td&gt;
&lt;td&gt;复制文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;touch&lt;/td&gt;
&lt;td&gt;创建文件&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;•locate 命令用于查找符合条件的文件。&lt;/p&gt;
&lt;p&gt;•whereis 命令也用于查找文件。&lt;/p&gt;
&lt;p&gt;•which 命令会在环境变量$PATH 设置的目录里查找符合条件的文件。&lt;/p&gt;
&lt;p&gt;•mkdir 是 make directory（创建目录）的缩写，用于创建新的目录。&lt;/p&gt;
&lt;p&gt;•rmdir 用来删除一个空的目录，rm 命令来删除非空目录。&lt;/p&gt;
&lt;h5&gt;pwd 命令&lt;/h5&gt;
&lt;p&gt;pwd 是 Print Working Directory（打印工作目录）的缩写，其功能是显示当前工作目录。&lt;/p&gt;
&lt;h5&gt;cd 命令&lt;/h5&gt;
&lt;p&gt;cd 是 Change Directory（更改目录）的缩写，其功能是将活动目录更改为指定的路径。&lt;/p&gt;
&lt;h5&gt;ls 命令&lt;/h5&gt;
&lt;p&gt;ls（英文全拼： list directory contents）命令用于显示指定工作目录下之内容（列出目前工作目录所含的文件及子目录)。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;-a 显示所有文件及目录 (&lt;strong&gt;.&lt;/strong&gt; 开头的隐藏文件也会列出)&lt;/li&gt;
&lt;li&gt;-d 只列出目录（不递归列出目录内的文件）。&lt;/li&gt;
&lt;li&gt;-l 以长格式显示文件和目录信息，包括权限、所有者、大小、创建时间等。&lt;/li&gt;
&lt;li&gt;-r 倒序显示文件和目录。&lt;/li&gt;
&lt;li&gt;-R 递归显示目录中的所有文件和子目录。&lt;/li&gt;
&lt;li&gt;-t 将按照修改时间排序，最新的文件在最前面。&lt;/li&gt;
&lt;li&gt;-A 同 -a ，但不列出 &quot;.&quot; (目前目录) 及 &quot;..&quot; (父目录)&lt;/li&gt;
&lt;li&gt;-F 在列出的文件名称后加一符号；例如可执行档则加 &quot;*&quot;, 目录则加 &quot;/&quot;&lt;/li&gt;
&lt;li&gt;-s 大小(size)，按文件大小排序。&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;cat 命令&lt;/h5&gt;
&lt;p&gt;cat（英文全拼：concatenate）命令用于连接文件并打印到标准输出设备上。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;重定向&lt;/strong&gt;:&lt;code&gt;&amp;gt;&lt;/code&gt;符号&lt;/p&gt;
&lt;p&gt;把 textfile1 的文档内容加上行号后输入 textfile2 这个文档里：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat -n textfile1 &amp;gt; textfile2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;把 textfile1 和 textfile2 的文档内容加上行号（空白行不加）之后将内容附加到 textfile3 文档里：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat -b textfile1 textfile2 &amp;gt;&amp;gt; textfile3
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;grep 命令&lt;/h5&gt;
&lt;p&gt;Linux grep (global regular expression) 命令用于查找文件里符合条件的字符串或正则表达式。&lt;/p&gt;
&lt;p&gt;grep 指令用于查找内容包含指定的范本样式的文件，如果发现某文件的内容符合所指定的范本样式，预设 grep 指令会把含有范本样式的那一列显示出来。若不指定任何文件名称，或是所给予的文件名为 &lt;strong&gt;-&lt;/strong&gt;，则 grep 指令会从标准输入设备读取数据。&lt;/p&gt;
&lt;p&gt;1、在文件 file.txt 中查找字符串 &quot;hello&quot;，并打印匹配的行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;grep hello file.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;cp 命令&lt;/h5&gt;
&lt;p&gt;•cp: 复制文件或目录，语法为“cp 源文件 目标文件”&lt;/p&gt;
&lt;h5&gt;touch 命令&lt;/h5&gt;
&lt;p&gt;Linux touch 命令用于修改文件或者目录的时间属性，包括存取时间和更改时间。若文件不存在，系统会建立一个新的文件。&lt;/p&gt;
&lt;p&gt;使用指令&quot;touch&quot;时，如果指定的文件不存在，则将创建一个新的空白文件。例如，在当前目录下，使用该指令创建一个空白文件&quot;file&quot;，输入如下命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;touch file            #创建一个名为“file”的新的空白文件
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;mv 命令&lt;/h5&gt;
&lt;h5&gt;rm 命令&lt;/h5&gt;
&lt;h4&gt;文件阅读命令&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;head&lt;/td&gt;
&lt;td&gt;查看文件开头部分&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tail&lt;/td&gt;
&lt;td&gt;查看文件结尾的 10 行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;less&lt;/td&gt;
&lt;td&gt;分页工具，一页一页查看信息&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;more&lt;/td&gt;
&lt;td&gt;分页工具，一页一页查看信息&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;文件备份和压缩命令&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;bzip2/bunzip2&lt;/td&gt;
&lt;td&gt;扩展文件名为 bz2 的压缩/解压工具&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gzip/gunzip&lt;/td&gt;
&lt;td&gt;扩展文件名为 gz 的压缩/解压工具&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;zip/unzip&lt;/td&gt;
&lt;td&gt;扩展文件名为 zip 的压缩/解压工具&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tar&lt;/td&gt;
&lt;td&gt;创建备份和归档&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;有关磁盘空间的命令&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;mount&lt;/td&gt;
&lt;td&gt;挂上文件系统&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;umount&lt;/td&gt;
&lt;td&gt;卸下已经挂上的文件系统&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;df&lt;/td&gt;
&lt;td&gt;检查个硬盘分区和已挂上来的文件系统的磁盘空间&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;du&lt;/td&gt;
&lt;td&gt;显示文件目录或大小&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fsck&lt;/td&gt;
&lt;td&gt;主要是检查和修复 Linux 文件系统&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;2.3 权限管理&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;chmod&lt;/td&gt;
&lt;td&gt;改变权限&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;useradd&lt;/td&gt;
&lt;td&gt;增加用户&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;su&lt;/td&gt;
&lt;td&gt;修改用户&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;2.4 进程管理&lt;/h3&gt;
&lt;h4&gt;ps 命令&lt;/h4&gt;
&lt;p&gt;ps 命令用于查看系统中的进程状态，英文全称为“processes”，语法格式为“ps [参数]”。&lt;/p&gt;
&lt;p&gt;估计读者在第一次执行这个命令时都要惊呆一下—怎么会有这么多输出值，这可怎么看得过来？其实，高手通常会将 ps 命令与第 3 章的管道符技术搭配使用，用来抓取与某个指定服务进程相对应的 PID 号码。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;-a&lt;/td&gt;
&lt;td&gt;显示所有进程（包括其他用户的进程）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-u&lt;/td&gt;
&lt;td&gt;用户以及其他详细信息&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-x&lt;/td&gt;
&lt;td&gt;显示没有控制终端的进程&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Linux 系统中时刻运行着许多进程，如果能够合理地管理它们，则可以优化系统的性能。在 Linux 系统中有 5 种常见的进程状态，分别为运行、中断、不可中断、僵死与停止，其各自含义如下所示。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;R（运行）&lt;/strong&gt;：进程正在运行或在运行队列中等待。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;S（中断）&lt;/strong&gt;：进程处于休眠中，当某个条件形成后或者接收到信号时，则脱离该 状态。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;D（不可中断）&lt;/strong&gt;：进程不响应系统异步信号，即便用 kill 命令也不能将其中断。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Z（僵死）&lt;/strong&gt;：进程已经终止，但进程描述符依然存在, 直到父进程调用 wait4()系统函数后将进程释放。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;T（停止）&lt;/strong&gt;：进程收到停止信号后停止运行。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;pstree 命令&lt;/h4&gt;
&lt;p&gt;pstree 命令用于以树状图的形式展示进程之间的关系，英文全称为“process tree”，输入该命令后按回车键执行即可。&lt;/p&gt;
&lt;p&gt;前文提到，在执行 ps 命令后，产生的信息量太大又没有规律，很难让人再想看第二眼。如果想让进程以树状图的形式，有层次地展示出进程之间的关系，则可以使用 pstree 命令。&lt;/p&gt;
&lt;h4&gt;top 命令&lt;/h4&gt;
&lt;p&gt;top 命令用于动态地监视进程活动及系统负载等信息，输入该命令后按回车键执行即可。&lt;/p&gt;
&lt;p&gt;前面介绍的命令都是静态地查看系统状态，不能实时滚动最新数据，而 top 命令能够动态地查看系统状态，因此完全可以将它看作是 Linux 中“强化版的 Windows 任务管理器”。&lt;/p&gt;
&lt;h4&gt;nice 命令&lt;/h4&gt;
&lt;p&gt;nice 命令用于调整进程的优先级，语法格式为“nice 优先级数字 服务名称”。&lt;/p&gt;
&lt;p&gt;在 top 命令输出的结果中，PR 和 NI 值代表的是进程的优先级，数字越低（取值范围是-20 ～ 19），优先级越高。在日常的生产工作中，可以将一些不重要进程的优先级调低，让紧迫的服务更多地利用 CPU 和内存资源，以达到合理分配系统资源的目的。&lt;/p&gt;
&lt;h4&gt;pidof 命令&lt;/h4&gt;
&lt;p&gt;pidof 命令用于查询某个指定服务进程的 PID 号码值，语法格式为“pidof [参数] 服务名称”。第 2 章 新手必须掌握的 Linux 命令第 2 章 新手必须掌握的 Linux 命令&lt;/p&gt;
&lt;p&gt;每个进程的进程号码值（PID）是唯一的，可以用于区分不同的进程。&lt;/p&gt;
&lt;h4&gt;kill 命令&lt;/h4&gt;
&lt;p&gt;kill 命令用于终止某个指定 PID 值的服务进程，语法格式为“kill [参数] 进程的 PID”。&lt;/p&gt;
&lt;h4&gt;killall 命令&lt;/h4&gt;
&lt;p&gt;killall 命令用于终止某个指定名称的服务所对应的全部进程，语法格式为“killall [参数] 服务名称”。&lt;/p&gt;
&lt;h3&gt;2.5 有关网络操作的命令&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FTP&lt;/td&gt;
&lt;td&gt;传送文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;telnet&lt;/td&gt;
&lt;td&gt;远程登陆&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bye&lt;/td&gt;
&lt;td&gt;结束连线并结束程序&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rlogin&lt;/td&gt;
&lt;td&gt;远端登入&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ping&lt;/td&gt;
&lt;td&gt;检查主机&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;netstat&lt;/td&gt;
&lt;td&gt;显示网络状态&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;ping 命令&lt;/h4&gt;
&lt;p&gt;ping 命令用于测试主机之间的网络连通性，语法格式为“ping [参数] 主机地址”。&lt;/p&gt;
&lt;p&gt;即便大家没有学习过 Linux 系统，相信也肯定见过别人使用 ping 命令。执行 ping 命令时，系统会使用 ICMP 向远端主机发出要求回应的信息，若连接远端主机的网络没有问题，远端主机会回应该信息。由此可见，ping 命令可用于判断远端主机是否在线并且网络是否正常。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;-c&lt;/td&gt;
&lt;td&gt;总共发送次数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-l&lt;/td&gt;
&lt;td&gt;指定网卡名称&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-i&lt;/td&gt;
&lt;td&gt;每次间隔时间（秒）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-W&lt;/td&gt;
&lt;td&gt;最长等待时间（秒）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;netstat 命令&lt;/h4&gt;
&lt;p&gt;netstat 命令用于显示如网络连接、路由表、接口状态等的网络相关信息，英文全称为“network status”，语法格式为“netstat [参数]”。&lt;/p&gt;
&lt;p&gt;只要 netstat 命令使用得当，便可以查看到网络状态的方方面面信息。我们找出一些常用的参数让大家感受一下。&lt;/p&gt;
&lt;p&gt;表 2-12 netstat 命令中的参数以及作用&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;-a&lt;/th&gt;
&lt;th&gt;显示所有连接中的 Socket&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;-p&lt;/td&gt;
&lt;td&gt;显示正在使用的 Socket 信息&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-t&lt;/td&gt;
&lt;td&gt;显示 TCP 协议的连接状态&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-u&lt;/td&gt;
&lt;td&gt;显示 UDP 协议的连接状态&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-n&lt;/td&gt;
&lt;td&gt;使用 IP 地址，不使用域名&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-l&lt;/td&gt;
&lt;td&gt;仅列出正在监听的服务状态&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-i&lt;/td&gt;
&lt;td&gt;显示网卡列表信息&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-r&lt;/td&gt;
&lt;td&gt;显示路由表信息&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;输入输出重定向&lt;/h3&gt;
&lt;p&gt;既然已经在上一章学完了几乎所有基础且常用的 Linux 命令，那么接下来的任务就是把多个 Linux 命令适当地组合到一起，使其协同工作，以便我们更加高效地处理数据。要做到这一点，就必须搞明白命令的输入重定向和输出重定向的原理。&lt;/p&gt;
&lt;p&gt;简而言之，输入重定向是指把文件导入到命令中，而输出重定向则是指把原本要输出到屏幕的数据信息写入到指定文件中。在日常的学习和工作中，相较于输入重定向，我们使用输出重定向的频率更高，所以又将输出重定向分为了标准输出重定向和错误输出重定向两种不同的技术，以及覆盖写入与追加写入两种模式。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;标准输入重定向（STDIN，文件描述符为 0）&lt;/strong&gt;：默认从键盘输入，也可从其他文件或命令中输入。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;标准输出重定向（STDOUT，文件描述符为 1）&lt;/strong&gt;：默认输出到屏幕。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;错误输出重定向（STDERR，文件描述符为 2）&lt;/strong&gt;：默认输出到屏幕。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;输入重定向中用到的符号及其作用&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;符号&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;命令 &amp;lt; 文件&lt;/td&gt;
&lt;td&gt;将文件作为命令的标准输入&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;命令 &amp;lt;&amp;lt; 分界符&lt;/td&gt;
&lt;td&gt;从标准输入中读入，直到遇见分界符才停止&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;命令 &amp;lt; 文件 1 &amp;gt; 文件 2&lt;/td&gt;
&lt;td&gt;将文件 1 作为命令的标准输入并将标准输出到文件 2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;输出重定向中用到的符号及其作用&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;符号&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;命令 &amp;gt; 文件&lt;/td&gt;
&lt;td&gt;将标准输出重定向到一个文件中（清空原有文件的数据）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;命令 2&amp;gt; 文件&lt;/td&gt;
&lt;td&gt;将错误输出重定向到一个文件中（清空原有文件的数据）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;命令 &amp;gt;&amp;gt; 文件&lt;/td&gt;
&lt;td&gt;将标准输出重定向到一个文件中（追加到原有内容的后面）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;命令 2&amp;gt;&amp;gt; 文件&lt;/td&gt;
&lt;td&gt;将错误输出重定向到一个文件中（追加到原有内容的后面）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;命令 &amp;gt;&amp;gt; 文件 2&amp;gt;&amp;amp;1 或 命令 &amp;amp;&amp;gt;&amp;gt; 文件&lt;/td&gt;
&lt;td&gt;将标准输出与错误输出共同写入到文件中（追加到原有内容的后面）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;管道命令符&lt;/h3&gt;
&lt;p&gt;同时按下键盘上的 Shift+反斜杠（\）键即可输入管道符，其执行格式为“命令 A | 命令 B”。管道命令符的作用也可以用一句话概括为“&lt;strong&gt;把前一个命令原本要输出到屏幕的信息当作后一个命令的标准输入&lt;/strong&gt;”。&lt;/p&gt;
&lt;h3&gt;常用的转义字符&lt;/h3&gt;
&lt;p&gt;为了能够更好地理解用户的表达，Shell 解释器还提供了特别丰富的转义字符来处理输入的特殊数据。刘遄老师以 10 多年的工作和培训为基础，愣是用了两周时间从数十个转义字符中提炼出了 4 个最常用的转义字符！这件事情也让我深刻反省了很长时间，原本认为图书写得越厚，作者越是大牛，现在发现这种观念完全是错误的，希望读者在读完本书后能体会到刘遄老师的用心付出。&lt;/p&gt;
&lt;p&gt;4 个最常用的转义字符如下所示。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;反斜杠（\）&lt;/strong&gt;：使反斜杠后面的一个变量变为单纯的字符。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;单引号（&apos; &apos;）&lt;/strong&gt;：转义其中所有的变量为单纯的字符串。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;双引号（&quot; &quot;）&lt;/strong&gt;：保留其中的变量属性，不进行转义处理。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;反引号（&lt;code&gt; &lt;/code&gt;）&lt;/strong&gt;：把其中的命令执行后返回结果。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Chapter 3 Linux 文件系统&lt;/h2&gt;
&lt;h3&gt;文件系统概述&lt;/h3&gt;
&lt;p&gt;文件系统是对一个存储设备上的数据和元数据进行组织的机制，其目的是易于实现数据的查询和存取。不同的操作系统具有不同的文件系统，一个操作系统可能无法兼容所有的文件系统。Linux 文件系统接口实现为分层的体系结构，从而将用户接口层、文件系统实现和操作存储设备的驱动程序分开。&lt;/p&gt;
&lt;p&gt;文件系统是操作系统中负责管理持久数据的子系统，说简单点，就是负责把用户的文件存到磁盘硬件中，因为即使计算机断电了，磁盘里的数据并不会丢失，所以可以持久化的保存文件。&lt;/p&gt;
&lt;p&gt;文件系统的基本数据单位是文件，它的目的是对磁盘上的文件进行组织管理，那组织的方式不同，就会形成不同的文件系统。&lt;/p&gt;
&lt;p&gt;Linux 最经典的一句话是：&lt;strong&gt;「一切皆文件」&lt;/strong&gt;，不仅普通的文件和目录，就连块设备、管道、socket 等，也都是统一交给文件系统管理的。&lt;/p&gt;
&lt;p&gt;要透彻理解文件系统，需要明确以下几个名词：&lt;/p&gt;
&lt;p&gt;存储介质&lt;/p&gt;
&lt;p&gt;用以存储数据的物理设备，如软盘、硬盘、光盘、磁带、网络存储设备等。&lt;/p&gt;
&lt;p&gt;磁盘的分割&lt;/p&gt;
&lt;p&gt;对于容量较大的存储介质来说，通常是硬盘。在使用时，需要合理地规划分区。常用的 Linux 磁盘分割工具有 fdisk、cfdisk、parted 等。此外，还有 PQ 等第三方工具。&lt;/p&gt;
&lt;p&gt;创建文件系统&lt;/p&gt;
&lt;p&gt;创建新的文件系统是一个过程，通常成为初始化或格式化，整个过程是针对存储介质进行的。一般情况下，操作系统都有自己相应的工具。创建文件系统是在分割磁盘空间的基础上进行的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;挂载&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在 Linux 或 Unix 系统中，没有磁盘分区的逻辑概念（如 C 盘、D 盘等），仍和一个种类的文件系统被创建后，都需要挂载到某个特定的目录才能使用，这个过程相当于激活一个文件系统，使它能够使用。&lt;/p&gt;
&lt;p&gt;Windows 的文件系统挂载使用其内部机制完成，用户基本无法探知其过程。而 Linux 使用 mount 工具对文件系统进行挂载。挂载文件系统时，需要明确挂载点。在创建文件系统后，操作系统会提示将此文件系统挂载至哪个位置，这个位置就是挂载点，通常选择“/”即根目录。挂载点的实质是一个空的目录。&lt;/p&gt;
&lt;h3&gt;文件和文件类型&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;怎样将“散落”存在磁盘等存储介质上的二进制数据，变换、映射为拿名字即可访问的“信息”是文件系统要解决的问题。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;从用户应用软件到文件系统介质示意图&lt;/p&gt;
&lt;h4&gt;定义&lt;/h4&gt;
&lt;p&gt;文件：是 Linux 用来存储信息的基本结构， 它是被命名（称为文件名）的、存储在某种媒介（如磁盘、光盘和磁带等）上的一组信息的集合。&lt;/p&gt;
&lt;p&gt;文件系统：文件系统实现数据存储的物理（介质）独立性。&lt;/p&gt;
&lt;h4&gt;文件命名规则&lt;/h4&gt;
&lt;p&gt;❑Linux 文件名的最大长度为 256 个字符&lt;/p&gt;
&lt;p&gt;❑ 通常由字母、数字、“.”（点号）、“_”（下划线）或“-”（减号）组成，文件名中不能含有“/”符号。&lt;/p&gt;
&lt;p&gt;❑ 避免使用具有特别意义的字符：? * @ # $ &amp;amp; () \ | ; ‘ “ ` &amp;lt; &amp;gt; [ ]等（大括号{}不是？）&lt;/p&gt;
&lt;p&gt;❑ 隐藏文件一般是以“.”符号开头；&lt;/p&gt;
&lt;p&gt;❑ 在文件名中的空格或制表符，在引用文件时必须用引号将其括起来；&lt;/p&gt;
&lt;p&gt;❑ 避免使用 ‘+’和‘-‘符号作为文件名的第一个字符&lt;/p&gt;
&lt;p&gt;❑ 大小写敏感&lt;/p&gt;
&lt;h4&gt;文件类型&lt;/h4&gt;
&lt;p&gt;那就先简单说说 Linux 中的文件类型，主要关注普通文件、目录文件和符号连接文件。
三种基本的文件类型&lt;/p&gt;
&lt;p&gt;普通文件（regular file）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;二进制文件&lt;/p&gt;
&lt;p&gt;以二进制形式存储在电脑中
用户一般只有通过相应的软件才能将其显示出来
一般是可执行程序、图形、图像、声音等&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;文本(txt)文件&lt;/p&gt;
&lt;p&gt;以文本的 ASCII 码形式存储在电脑中，其内容都是可读字符。
以“行”为基本结构的信息组织和存储方式&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;从本质上来说他们之间没有什么区别，因为他们在硬盘上都有一种的存放方式--二进制。
但是如果要对他们有些区分的话，那可以这样理解。
我们现在的每个字符 由一个或多个字节组成，每个字节都是用的-128—127 之间的部分数值来表示的，也就是说，-128——127 之间还有一些数据没有对应任何字符的任何字节。
如果一个文件中的每个字节的内容都是可以表示成字符的数据，我们就可以称这个文件为文本文件，可见，文本文件只是二进制文件中的一种特例，
为了与文 本文件相区别，人们又把除了文本文件以外的文件称为二进制文件，
由于很难严格区分文本文件和二进制文件的概念，所以我们可以简单地认为，如果一个文件专门 用于存储文本字符的数据，没有包含字符以外的其他数据，我们就称之为文本文件，除此之外的文件就是二进制文件&lt;/p&gt;
&lt;p&gt;目录文件（directory）&lt;/p&gt;
&lt;p&gt;是一个包含文件的容器，用于存放目录中文件列表信息。&lt;/p&gt;
&lt;p&gt;设备文件&lt;/p&gt;
&lt;p&gt;块设备（block device）：是一种具有一定结构的随机存取设备，对这种设备的读写是按块进行的&lt;/p&gt;
&lt;p&gt;◆ 以块为单位进行随机存取。
◆ 常见块设备：软盘、光盘、硬盘。&lt;/p&gt;
&lt;p&gt;字符设备（Character device）：这是一个顺序的数据流设备，对这种设备的读写是按字符进行的，而且这些字符是连续地形成一个数据流。&lt;/p&gt;
&lt;p&gt;◆ 以单个字符为单位进行顺序存取。
◆ 常见的字符设备：打印机、终端、键盘、鼠标&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;普通文件（-）
&lt;ol&gt;
&lt;li&gt;从 Linux 的角度来说，类似 mp4、pdf、html 这样应用层面上的文件类型都属于普通文件&lt;/li&gt;
&lt;li&gt;Linux 用户可以根据访问权限对普通文件进行查看、更改和删除&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;目录文件（d，directory file）
&lt;ol&gt;
&lt;li&gt;目录文件对于用惯 Windows 的用户来说不太容易理解，目录也是文件的一种&lt;/li&gt;
&lt;li&gt;目录文件包含了各自目录下的文件名和指向这些文件的指针，打开目录事实上就是打开目录文件，只要有访问权限，你就可以随意访问这些目录下的文件（普通文件的执行权限就是目录文件的访问权限），但是只有内核的进程能够修改它们&lt;/li&gt;
&lt;li&gt;虽然不能修改，但是我们能够通过 vim 去查看目录文件的内容&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;符号链接（l，symbolic link）
&lt;ol&gt;
&lt;li&gt;这种类型的文件类似 Windows 中的快捷方式，是指向另一个文件的间接指针，也就是我们常说的软链接&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;块设备文件（b，block）和字符设备文件（c，char）
&lt;ol&gt;
&lt;li&gt;这些文件一般隐藏在/dev 目录下，在进行设备读取和外设交互时会被使用到&lt;/li&gt;
&lt;li&gt;比如磁盘光驱就是块设备文件，串口设备则属于字符设备文件&lt;/li&gt;
&lt;li&gt;系统中的所有设备要么是块设备文件，要么是字符设备文件，无一例外&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;FIFO（p，pipe）
&lt;ol&gt;
&lt;li&gt;管道文件主要用于进程间通讯。比如使用 mkfifo 命令可以创建一个 FIFO 文件，启用一个进程 A 从 FIFO 文件里读数据，启动进程 B 往 FIFO 里写数据，先进先出，随写随读。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;套接字（s，socket）
&lt;ol&gt;
&lt;li&gt;用于进程间的网络通信，也可以用于本机之间的非网络通信&lt;/li&gt;
&lt;li&gt;这些文件一般隐藏在/var/run 目录下，证明着相关进程的存在&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Linux 的文件是没有所谓的扩展名的，一个 Linux 文件能不能被执行与它是否可执行的属性有关，只要你的权限中有 x ，比如[ -rwx-r-xr-x ] 就代表这个文件可以被执行，与文件名没有关系。跟在 Windows 下能被执行的文件扩展名通常是 .com .exe .bat 等不同。&lt;/p&gt;
&lt;p&gt;不过，可以被执行跟可以执行成功不一样。比如在 root 主目彔下的 install.log 是一个文本文件，修改权限成为 -rwxrwxrwx 后这个文件能够真的执行成功吗？ 当然不行，因为它的内容根本就没有可以执行的数据。所以说，这个 x 代表这个文件具有可执行的能力， 但是能不能执行成功，当然就得要看该文件的内容了。
虽然如此，不过我们仍然希望能从扩展名来了解该文件是什么东西，所以一般我们还是会以适当的扩展名来表示该文件是什么种类的。&lt;/p&gt;
&lt;p&gt;所以 Linux 系统上的文件名真的只是让你了解该文件可能的用途而已， 真正的执行与否仍然需要权限的规范才行。比如常见的/bin/ls 这个显示文件属性的指令要是权限被修改为无法执行，那么 ls 就变成不能执行了。这种问题最常发生在文件传送的过程中。例如你在网络上下载一个可执行文件，但是偏偏在你的 Linux 系统中就是无法执行，那就可能是档案的属性被改变了。而且从网络上传送到你 的 Linux 系统中，文件的属性权限确实是会被改变的&lt;/p&gt;
&lt;p&gt;Linux 文件系统会为每个文件分配两个数据结构：&lt;strong&gt;索引节点（index node）&lt;/strong&gt; 和 &lt;strong&gt;目录项（directory entry）&lt;/strong&gt;，它们主要用来记录文件的元信息和目录层次结构。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;索引节点，也就是 &lt;code&gt;inode&lt;/code&gt;，用来记录文件的元信息，比如 inode 编号、文件大小、访问权限、创建时间、修改时间、数据在磁盘的位置等等。索引节点是文件的唯一标识，它们之间一一对应，也同样都会被存储在硬盘中，所以索引节点同样占用磁盘空间。&lt;/li&gt;
&lt;li&gt;目录项，也就是 &lt;code&gt;dentry&lt;/code&gt;，用来记录文件的名字、索引节点指针以及与其他目录项的层级关联关系。多个目录项关联起来，就会形成目录结构，但它与索引节点不同的是，目录项是由内核维护的一个数据结构，不存放于磁盘，而是缓存在内存。
由于索引节点唯一标识一个文件，而目录项记录着文件的名，所以目录项和索引节点的关系是多对一，也就是说，一个文件可以有多个别字。比如，硬链接的实现就是多个目录项中的索引节点指向同一个文件。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;注意，目录也是文件，也是用索引节点唯一标识，和普通文件不同的是，普通文件在磁盘里面保存的是文件数据，而目录文件在磁盘里面保存子目录或文件。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;目录项和目录是一个东西吗？
虽然名字很相近，但是它们不是一个东西，目录是个文件，持久化存储在磁盘，而目录项是内核一个数据结构，缓存在内存。
如果查询目录频繁从磁盘读，效率会很低，所以内核会把已经读过的目录用目录项这个数据结构缓存在内存，下次再次读到相同的目录时，只需从内存读就可以，大大提高了文件系统的效率。
注意，目录项这个数据结构不只是表示目录，也是可以表示文件的。
那文件数据是如何存储在磁盘的呢？
磁盘读写的最小单位是扇区，扇区的大小只有 512B 大小，很明显，如果每次读写都以这么小为单位，那这读写的效率会非常低。
所以，文件系统把多个扇区组成了一个逻辑块，每次读写的最小单位就是逻辑块（数据块），Linux 中的逻辑块大小为 4KB，也就是一次性读写 8 个扇区，这将大大提高了磁盘的读写的效率。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;文件系统结构&lt;/h3&gt;
&lt;p&gt;文件系统指文件存在的物理空间&lt;/p&gt;
&lt;p&gt;Linux 文件系统由一组普通文件、目录文件、设备文件和链接（符号链接、硬链接）组成。&lt;/p&gt;
&lt;p&gt;在 Linux 中支持多种不同的文件系统&lt;/p&gt;
&lt;p&gt;Linux 将分属不同分区的、单独的文件系统整理形成一个系统的，总的目录层次结构，也即树状、层次结构。&lt;/p&gt;
&lt;p&gt;树状层次结构使得 Linux 核心的其它部分及系统中运行的程序会看到统一的文件系统，为用户提供了一种组织、检索和管理信息的便捷、高效的方法。&lt;/p&gt;
&lt;p&gt;文件系统-相关基本概念&lt;/p&gt;
&lt;p&gt;§当前目录即当前工作目录、环境变量 PWD&lt;/p&gt;
&lt;p&gt;§用户主目录：/home/&amp;lt;username&amp;gt;; 环境变量 HOME、~&lt;/p&gt;
&lt;p&gt;§绝对路径&lt;/p&gt;
&lt;p&gt;§相对路径&lt;/p&gt;
&lt;p&gt;§符号链接（可跨文件系统）&lt;/p&gt;
&lt;p&gt;§软链接（不可跨文件系统）&lt;/p&gt;
&lt;h4&gt;路径：绝对路径和相对路径&lt;/h4&gt;
&lt;p&gt;§linux 系统中每个文件都有一个唯一的路径名。路径名表明了文件的位置。&lt;/p&gt;
&lt;p&gt;§路径名分为两种：绝对路径和相对路径&lt;/p&gt;
&lt;p&gt;§绝对路径指出从根目录到此文件的路径，一般来说由/符号开始。&lt;/p&gt;
&lt;p&gt;§相对路径指明从当前目录到此文件的路径，相对路径名以下列方式开始.&lt;/p&gt;
&lt;p&gt;• “.”，代表当前目录&lt;/p&gt;
&lt;p&gt;• “..”，代表当前目录的父目录&lt;/p&gt;
&lt;h4&gt;软链接和硬链接&lt;/h4&gt;
&lt;p&gt;有时候我们希望给某个文件取个别名，那么在 Linux 中可以通过硬链接（Hard Link） 和软链接（Symbolic Link） 的方式来实现，它们都是比较特殊的文件，但是实现方式也是不相同的。&lt;/p&gt;
&lt;p&gt;硬链接是多个目录项中的「索引节点」指向一个文件，也就是指向同一个 inode，但是 inode 是不可能跨越文件系统的，每个文件系统都有各自的 inode 数据结构和列表，所以硬链接是不可用于跨文件系统的。由于多个目录项都是指向一个 inode，那么只有删除文件的所有硬链接以及源文件时，系统才会彻底删除该文件。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/linux-01-20261014.webp&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;软链接&lt;/h4&gt;
&lt;p&gt;当我们需要在不同的目录，用到相同的文件时，我们不需要在每一个需要的目录下都放一个必须相同的文件，我们只要在某个固定的目录，放上该文件，然后在其它的目录下用 ln 命令链接（link）它就可以，不必重复的占用磁盘空间。&lt;/p&gt;
&lt;p&gt;软连接的功能是为某一个文件在另外一个位置建立一个同步的链接，这个命令最常用的参数是-s。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;具体用法是：&lt;code&gt;ln -s 源文件 目标文件&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;例如：ln -s /bin/less /usr/local/bin/less&lt;/p&gt;
&lt;p&gt;-s 是代号（symbolic）的意思。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这里有两点要注意：第一，ln 命令会保持每一处链接文件的同步性，也就是说，不论你改动了哪一处，其它的文件都会发生相同的变化；第二，ln 的链接有软链接和硬链接两种，软链接就是 ln -s ** **,它只会在你选定的位置上生成一个文件的镜像，不会占用磁盘空间，硬链接 ln ** **,没有参数-s, 它会在你选定的位置上生成一个和源文件大小相同的文件，无论是软链接还是硬链接，文件都保持同步变化。&lt;/p&gt;
&lt;p&gt;软链接相当于重新创建一个文件，这个文件有独立的 inode，但是这个文件的内容是另外一个文件的路径，所以访问软链接的时候，实际上相当于访问到了另外一个文件，所以软链接是可以跨文件系统的，甚至目标文件被删除了，链接文件还是在的，只不过指向的文件找不到了而已。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/linux-02-20261014.webp&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Chapter 4 Linux 权限管理&lt;/h2&gt;
&lt;p&gt;§多用户、多任务系统必须进行权限控制，权限控制就是确定用户（或者说代表用户执行的程序）对某资源有或无某种操作的权利。&lt;/p&gt;
&lt;p&gt;§直接表示会使得权限的表示、判断效率低下。&lt;/p&gt;
&lt;p&gt;q 多用户、多任务的操作系统决定了常常会有多人同时使用这部主机工作&lt;/p&gt;
&lt;p&gt;q 保证每个人的隐私权--&amp;gt;文件所有者&lt;/p&gt;
&lt;p&gt;q 资源共享，团队开发--&amp;gt;文件所属用户组&lt;/p&gt;
&lt;p&gt;Linux 中每个用户都至少属于一个用户组，以保证对一个用户组中的所有用户进行集中管理&lt;/p&gt;
&lt;h3&gt;4.1 用户身份与能力&lt;/h3&gt;
&lt;p&gt;受到 20 世纪 70 年代计算机发展的影响，Linux 系统的设计初衷之一就是为了满足多个用户同时工作的需求，因此必须具备很好的安全性，尤其是不能因为一两个服务出错而影响到整台服务器。在第 1 章学习安装 Linux 操作系统时，特别要求设置 root 管理员的密码，这个 root 管理员就是存在于所有类 UNIX 系统中的超级用户。它拥有最高的系统所有权，能够管理系统的各项功能，如添加/删除用户、启动/关闭服务进程、开启/禁用硬件设备等。虽然以 root 管理员的身份工作时不会受到系统的限制，但俗话讲“能力越大，责任就越大”，因此一旦使用这个高能的 root 管理员权限执行了错误的命令，可能会直接毁掉整个系统。使用与否，确实需要好好权衡一下。&lt;/p&gt;
&lt;p&gt;开发人员推荐使用 root 管理员权限来学习 Linux 系统，等到工作时再根据生产环境决定使用哪个用户权限；这些仅与选择相关，而非技术性问题。&lt;/p&gt;
&lt;p&gt;Linux 系统中的管理员就是 root。这其实是错误的，Linux 系统的管理员之所以是 root，并不是因为它的名字叫 root，而是因为该用户的身份号码即 UID（User IDentification）的数值为 0。在 Linux 系统中，UID 就像我们的身份证号码一样具有唯一性，因此可通过用户的 UID 值来判断用户身份。在 RHEL 8 系统中，用户身份有下面这些。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;管理员 UID 为 0&lt;/strong&gt;：系统的管理员用户。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;系统用户 UID 为 1 ～ 999&lt;/strong&gt;：Linux 系统为了避免因某个服务程序出现漏洞而被黑客提权至整台服务器，默认服务程序会由独立的系统用户负责运行，进而有效控制被破坏范围。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;普通用户 UID 从 1000 开始&lt;/strong&gt;：是由管理员创建的用于日常工作的用户。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;需要注意的是，UID 是不能冲突的，而且管理员创建的普通用户的 UID 默认是从 1000 开始的（即使前面有闲置的号码）。&lt;/p&gt;
&lt;p&gt;为了方便管理属于同一组的用户，Linux 系统中还引入了用户组的概念。通过使用用户组号码（GID，Group IDentification），可以把多个用户加入到同一个组中，从而方便为组中的用户统一规划权限或指定任务。假设一个公司中有多个部门，每个部门中又有很多员工，如果只想让员工访问本部门内的资源，则可以针对部门而非具体的员工来设置权限。例如，通过对技术部门设置权限，使得只有技术部门的员工可以访问公司的数据库信息等。&lt;/p&gt;
&lt;p&gt;另外，在 Linux 系统中创建每个用户时，将自动创建一个与其同名的基本用户组，而且这个基本用户组只有该用户一个人。如果该用户以后被归纳到其他用户组，则这个其他用户组称之为扩展用户组。一个用户只有一个基本用户组，但是可以有多个扩展用户组，从而满足日常的工作需要。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Tips&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;基本用户组就像是原生家庭，是在创建账号（出生）时就自动生成的；而扩展用户组则像工作单位，为了完成工作，需要加入到各个不同的群体中，这是需要手动添加的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt; 在设定文件权限时，用字符表示用户类型：&lt;/p&gt;
&lt;p&gt;u 文件的所有者
g 同组用户
o 其它用户
a all=ugo,即所有用户&lt;/p&gt;
&lt;h3&gt;4.2 文件权限与归属&lt;/h3&gt;
&lt;p&gt;在 Linux 系统中，每个文件都有归属的所有者和所属组，并且规定了文件的所有者、所属组以及其他人对文件所拥有的可读（r）、可写（w）、可执行（x）等权限。对于一般文件来说，权限比较容易理解：“可读”表示能够读取文件的实际内容；“可写”表示能够编辑、新增、修改、删除文件的实际内容；“可执行”则表示能够运行一个脚本程序。但是，对于目录文件来说，理解其权限设置就不那么容易了。很多资深 Linux 用户其实也没有真正搞明白。对于目录文件来说，“可读”表示能够读取目录内的文件列表；“可写”表示能够在目录内新增、删除、重命名文件；而“可执行”则表示能够进入该目录。&lt;/p&gt;
&lt;p&gt; 对文件和目录的三种权限&lt;/p&gt;
&lt;p&gt;•读（r）
•写（w）
•执行 （x）&lt;/p&gt;
&lt;p&gt; 十进制表示&lt;/p&gt;
&lt;p&gt;•用三个十进制数字表示文件权限：xyz&lt;/p&gt;
&lt;p&gt;•x、y、z 分别代表文件所有者、同组用户和其他用户的文件权限值（一个数
字对应一种人）。&lt;/p&gt;
&lt;p&gt;•文件权限值为该类型用户对文件的读、写、执行权限值的累加：
读(r)：4 写(w)：2 执行(x)：1 禁止(-)：0&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;重要&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt; !IMPORTANT 命令执行对文件权限的限制&lt;/p&gt;
&lt;p&gt;➢ 在移动文件时不需要被移动文件的权限，但需其所在目录具有写权限。
➢ 在目录下增删文件、子目录时要有目录的写权限（touch,rm,mkdir,rmdir）
➢ 用 ls 列目录要有目录的读权限&lt;/p&gt;
&lt;p&gt;➢ 进入目录或将该目录作路径分量时(cd)要有目录的执行权限，故要使用任一个
文件，必须有该文件及找到该文件的路径上所有目录分量的执行权限。
➢ 仅当要打开一个文件时，即执行涉及到文件内容的操作时，才需要文件的许可。&lt;/p&gt;
&lt;p&gt;一个目录同时具有读权限和执行权限才可以打开并查看内部文件，而一个目录要有写权限才允许在其中创建其它文件，这是因为目录文件实际保存着该目录里面的文件的列表等信息&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Chapter 5 Linux 常用工具&lt;/h2&gt;
&lt;h3&gt;文本编辑器&lt;/h3&gt;
&lt;h4&gt;gedit&lt;/h4&gt;
&lt;h4&gt;vi&lt;/h4&gt;
&lt;p&gt;❑ vi 是一个较大的 Linux 命令，在启动的时候也有它自己的选项和参数&lt;/p&gt;
&lt;p&gt;❑ 基本语法：
vi [-options] [+n] [file]&lt;/p&gt;
&lt;p&gt;❑ 常用选项：&lt;/p&gt;
&lt;p&gt;-r 恢复系统突然崩溃时正在编辑的文件。&lt;/p&gt;
&lt;p&gt;-R 以只读方式打开文件&lt;/p&gt;
&lt;p&gt;+[n] 指明进入 vi 后直接位于文件的第 n 行，如果只有“+”而不指定 n，则光标位于文本的最后一行&lt;/p&gt;
&lt;p&gt;如果该文件不存在，会自动建立新文件。&lt;/p&gt;
&lt;p&gt;❑ 基本上 vi/vim 共分为三种模式:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;命令模式&lt;/strong&gt;：控制光标移动，可对文本进行复制、粘贴、删除和查找等工作。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;输入模式&lt;/strong&gt;：正常的文本录入。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;末行模式&lt;/strong&gt;：保存或退出文档，以及设置编辑环境。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;命令模式（Command Mode）&lt;/strong&gt;:光标移动，搜寻、字符和字符串删除、粘贴、取代&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;用户刚刚启动 vi/vim，便进入了命令模式。&lt;/p&gt;
&lt;p&gt;以下摘要了一些常用的命令，更详细的命令在后面可以自行查看。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;x&lt;/strong&gt; -- 删除当前光标所在处的字符。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;dd&lt;/strong&gt; -- 删除当前行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;yy&lt;/strong&gt; -- 复制当前行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;p&lt;/strong&gt;（小写） -- 粘贴剪贴板内容到光标下方。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P&lt;/strong&gt;（大写）-- 粘贴剪贴板内容到光标上方。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;u&lt;/strong&gt; -- 撤销上一次操作。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ctrl + r&lt;/strong&gt; -- 重做上一次撤销的操作。&lt;/p&gt;
&lt;p&gt;nG 跳到第 n 行&lt;/p&gt;
&lt;p&gt;0 跳到行首&lt;/p&gt;
&lt;p&gt;$ 跳到行尾&lt;/p&gt;
&lt;p&gt;ctrl+b 前移一页&lt;/p&gt;
&lt;p&gt;ctrl+f 后移一页&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;➢ 移动光标 ：最简单的方式是按键盘的上、下、左、右方向键&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;h 或 向左箭头键(←)&lt;/td&gt;
&lt;td&gt;光标向左移动一个字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;j 或 向下箭头键(↓)&lt;/td&gt;
&lt;td&gt;光标向下移动一个字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;k 或 向上箭头键(↑)&lt;/td&gt;
&lt;td&gt;光标向上移动一个字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;l 或 向右箭头键(→)&lt;/td&gt;
&lt;td&gt;光标向右移动一个字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Ctrl] + [f]&lt;/td&gt;
&lt;td&gt;屏幕『向下』移动一页，相当于 [Page Down]按键 (常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Ctrl] + [b]&lt;/td&gt;
&lt;td&gt;屏幕『向上』移动一页，相当于 [Page Up] 按键 (常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Ctrl] + [d]&lt;/td&gt;
&lt;td&gt;屏幕『向下』移动半页&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Ctrl] + [u]&lt;/td&gt;
&lt;td&gt;屏幕『向上』移动半页&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;+&lt;/td&gt;
&lt;td&gt;光标移动到非空格符的下一行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;光标移动到非空格符的上一行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;n&amp;lt;space&amp;gt;&lt;/td&gt;
&lt;td&gt;那个 n 表示『数字』，例如 20 。按下数字后再按空格键，光标会向右移动这一行的 n 个字符。例如 20&amp;lt;space&amp;gt; 则光标会向后面移动 20 个字符距离。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0 或功能键[Home]&lt;/td&gt;
&lt;td&gt;这是数字『 0 』：移动到这一行的最前面字符处 (常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$ 或功能键[End]&lt;/td&gt;
&lt;td&gt;移动到这一行的最后面字符处(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;H&lt;/td&gt;
&lt;td&gt;光标移动到这个屏幕的最上方那一行的第一个字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;M&lt;/td&gt;
&lt;td&gt;光标移动到这个屏幕的中央那一行的第一个字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;L&lt;/td&gt;
&lt;td&gt;光标移动到这个屏幕的最下方那一行的第一个字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;G&lt;/td&gt;
&lt;td&gt;移动到这个档案的最后一行(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;nG&lt;/td&gt;
&lt;td&gt;n 为数字。移动到这个档案的第 n 行。例如 20G 则会移动到这个档案的第 20 行(可配合 :set nu)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gg&lt;/td&gt;
&lt;td&gt;移动到这个档案的第一行，相当于 1G 啊！ (常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;n&amp;lt;Enter&amp;gt;&lt;/td&gt;
&lt;td&gt;n 为数字。光标向下移动 n 行(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;➢ 搜索替换 ：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;/word&lt;/td&gt;
&lt;td&gt;向光标之下寻找一个名称为 word 的字符串。例如要在档案内搜寻 vbird 这个字符串，就输入 /vbird 即可！ (常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;?word&lt;/td&gt;
&lt;td&gt;向光标之上寻找一个字符串名称为 word 的字符串。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;n&lt;/td&gt;
&lt;td&gt;这个 n 是英文按键。代表重复前一个搜寻的动作。举例来说， 如果刚刚我们执行 /vbird 去向下搜寻 vbird 这个字符串，则按下 n 后，会向下继续搜寻下一个名称为 vbird 的字符串。如果是执行 ?vbird 的话，那么按下 n 则会向上继续搜寻名称为 vbird 的字符串！&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N&lt;/td&gt;
&lt;td&gt;这个 N 是英文按键。与 n 刚好相反，为『反向』进行前一个搜寻动作。 例如 /vbird 后，按下 N 则表示『向上』搜寻 vbird 。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;:n1,n2s/word1/word2/g&lt;/td&gt;
&lt;td&gt;n1 与 n2 为数字。在第 n1 与 n2 行之间寻找 word1 这个字符串，并将该字符串取代为 word2 ！举例来说，在 100 到 200 行之间搜寻 vbird 并取代为 VBIRD 则： 『:100,200s/vbird/VBIRD/g』。(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;:1,$s/word1/word2/g&lt;/strong&gt; 或 &lt;strong&gt;:%s/word1/word2/g&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;从第一行到最后一行寻找 word1 字符串，并将该字符串取代为 word2 ！(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;:1,$s/word1/word2/gc&lt;/strong&gt; 或 &lt;strong&gt;:%s/word1/word2/gc&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;从第一行到最后一行寻找 word1 字符串，并将该字符串取代为 word2 ！且在取代前显示提示字符给用户确认 (confirm) 是否需要取代！(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;➢ 复制、删除、粘贴 ：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;x, X&lt;/td&gt;
&lt;td&gt;在一行字当中，x 为向后删除一个字符 (相当于 [del] 按键)， X 为向前删除一个字符(相当于 [backspace] 亦即是退格键) (常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;nx&lt;/td&gt;
&lt;td&gt;n 为数字，连续向后删除 n 个字符。举例来说，我要连续删除 10 个字符， 『10x』。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dd&lt;/td&gt;
&lt;td&gt;剪切游标所在的那一整行(常用)，用 p/P 可以粘贴。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ndd&lt;/td&gt;
&lt;td&gt;n 为数字。剪切光标所在的向下 n 行，例如 20dd 则是剪切 20 行(常用)，用 p/P 可以粘贴。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;d1G&lt;/td&gt;
&lt;td&gt;删除光标所在到第一行的所有数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dG&lt;/td&gt;
&lt;td&gt;删除光标所在到最后一行的所有数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;d$&lt;/td&gt;
&lt;td&gt;删除游标所在处，到该行的最后一个字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;d0&lt;/td&gt;
&lt;td&gt;那个是数字的 0 ，删除游标所在处，到该行的最前面一个字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;yy&lt;/td&gt;
&lt;td&gt;复制游标所在的那一行(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;nyy&lt;/td&gt;
&lt;td&gt;n 为数字。复制光标所在的向下 n 行，例如 20yy 则是复制 20 行(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;y1G&lt;/td&gt;
&lt;td&gt;复制游标所在行到第一行的所有数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;yG&lt;/td&gt;
&lt;td&gt;复制游标所在行到最后一行的所有数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;y0&lt;/td&gt;
&lt;td&gt;复制光标所在的那个字符到该行行首的所有数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;y$&lt;/td&gt;
&lt;td&gt;复制光标所在的那个字符到该行行尾的所有数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;p, P&lt;/td&gt;
&lt;td&gt;p 为将已复制的数据在光标下一行贴上，P 则为贴在游标上一行！ 举例来说，我目前光标在第 20 行，且已经复制了 10 行数据。则按下 p 后， 那 10 行数据会贴在原本的 20 行之后，亦即由 21 行开始贴。但如果是按下 P 呢？ 那么原本的第 20 行会被推到变成 30 行。 (常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;J&lt;/td&gt;
&lt;td&gt;将光标所在行与下一行的数据结合成同一行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;c&lt;/td&gt;
&lt;td&gt;重复删除多个数据，例如向下删除 10 行，[ 10cj ]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;u&lt;/td&gt;
&lt;td&gt;复原前一个动作。(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Ctrl]+r&lt;/td&gt;
&lt;td&gt;重做上一个动作。(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;.&lt;/td&gt;
&lt;td&gt;意思是重复前一个动作的意思。 如果你想要重复删除、重复贴上等等动作，按下小数点『.』就好了！ (常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;输入模式（Insert Mode）&lt;/strong&gt;: 编辑文本&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在命令模式下按下 i 就进入了输入模式，使用 Esc 键可以返回到命令模式。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;字符按键以及 Shift 组合&lt;/strong&gt;，输入字符&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ENTER&lt;/strong&gt;，回车键，换行&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BACK SPACE&lt;/strong&gt;，退格键，删除光标前一个字符&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DEL&lt;/strong&gt;，删除键，删除光标后一个字符&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方向键&lt;/strong&gt;，在文本中移动光标&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HOME&lt;/strong&gt;/&lt;strong&gt;END&lt;/strong&gt;，移动光标到行首/行尾&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Page Up&lt;/strong&gt;/&lt;strong&gt;Page Down&lt;/strong&gt;，上/下翻页&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Insert&lt;/strong&gt;，切换光标为输入/替换模式，光标将变成竖线/下划线&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ESC&lt;/strong&gt;，退出输入模式，切换到命令模式&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;底线命令模式（Last Line Mode）&lt;/strong&gt;: 读取、保存文件及其他额外功能&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在命令模式下按下 &lt;code&gt;:&lt;/code&gt;（英文冒号）就进入了底线命令模式。使用 Esc 键可随时退出底线命令模式。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;:w&lt;/strong&gt; -- 保存文件。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;:q&lt;/strong&gt; -- 退出 Vim 编辑器。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;:wq&lt;/strong&gt; -- 保存文件并退出 Vim 编辑器。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;:q!&lt;/strong&gt; -- 强制退出 Vim 编辑器，不保存修改。&lt;/p&gt;
&lt;p&gt;➢ 正向搜索（/）：从光标所在位置起向文件末尾方向搜索。
➢ 反向搜索（?）：从光标所在位置起向文件开头方向搜索。
➢ 搜索得到结果后，可以使用重复命令 n 或 N 沿着相同或相反的方向重复上一次的搜索&lt;/p&gt;
&lt;p&gt;➢ 字符串替换（substitute）
语法：
[n1, n2]s/str1/str2/[g][c]
在第 n1 行到 n2 行的范围内将字符串 str1 用 str2 代替:
c 每次替换都由用户确认
g 对行中搜索字符串的每次出现进行替换。
不加 g 只对行中搜索字符串的首次出现进行替换；
例子：
:s/str1/str2/
用字符串 str2 替换该行中首次出现的字符串 str1
:s/str1/str2/g
用字符串 str2 替换该行中所有出现的字符串 str1
:.,$s/str1/str2/g
用字符串 str2 替换正文当前行到末尾所有出现的字符串 str1
:1,$s/str1/str2/g
用字符串 str2 替换正文中所有出现的字符串 str1&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;h5&gt;命令模式和输入模式切换&lt;/h5&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;i, I&lt;/td&gt;
&lt;td&gt;进入输入模式(Insert mode)：i 为『从目前光标所在处输入』， I 为『在目前所在行的第一个非空格符处开始输入』。 (常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;a, A&lt;/td&gt;
&lt;td&gt;进入输入模式(Insert mode)： a 为『从目前光标所在的下一个字符处开始输入』， A 为『从光标所在行的最后一个字符处开始输入』。(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;o, O&lt;/td&gt;
&lt;td&gt;进入输入模式(Insert mode)：o 为『在目前光标所在的下一行处输入新的一行』； O 为『在目前光标所在的上一行处输入新的一行！』(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;r, R&lt;/td&gt;
&lt;td&gt;进入取代模式(Replace mode)：r 只会取代光标所在的那一个字符一次；R 会一直取代光标所在的文字，直到按下 ESC 为止；(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Esc]&lt;/td&gt;
&lt;td&gt;退出编辑模式，回到一般模式中(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;上面这些按键中，在 vi 画面的左下角处会出现『--INSERT--』或『--REPLACE--』的字样。&lt;/p&gt;
&lt;h5&gt;命令模式和底线命令模式切换&lt;/h5&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;:&lt;/td&gt;
&lt;td&gt;切换到底线命令模式，以在最底一行输入命令。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;vim 环境的变更&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;:set nu&lt;/td&gt;
&lt;td&gt;显示行号，设定之后，会在每一行的前缀显示该行的行号&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;:set nonu&lt;/td&gt;
&lt;td&gt;与 set nu 相反，为取消行号！&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;▪:.+5 将光标移动到当前行往下的第 5 行。
▪:345 将光标移到第 345 行&lt;/p&gt;
&lt;p&gt;❑ Vim 和 gvim 的高级特色
• Vim 代表 Vi IMproved，如同其名称所暗示的那样，Vim 作为标准 UNIX 系统 vi 编辑器的提高版而存在。 Vim 除提供和 vi 编辑器一样强大的功能外，还提供有多级恢复、命令行历史以及命令及文件名补全等功能。
• gvim 是 vi 的 X Window 版本，该版本支持鼠标选中，一些高级的光标移动功能，并且带有菜单和工具按钮。&lt;/p&gt;
&lt;h3&gt;软件包管理&lt;/h3&gt;
&lt;h3&gt;Samba&lt;/h3&gt;
&lt;h2&gt;Chapter 6 Linux 进程管理&lt;/h2&gt;
&lt;p&gt;概念：一个进程是一个程序的一次执行的过程。&lt;/p&gt;
&lt;p&gt;❑ 操作系统通过进程来控制对 CPU 和其他系统资源的访问，并且使用进程来决定在 CPU 上运行哪个程序、运行多久。&lt;/p&gt;
&lt;p&gt;❑ Linux 系统的一个重要特点：可以同时启动多个进程。&lt;/p&gt;
&lt;p&gt;❑ 进程和程序不同：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;程序是静态的，是保存在磁盘上的可执行代码和数据的集合；&lt;/li&gt;
&lt;li&gt;进程是动态的，是 Linux 系统的基本调度单位。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;❑ 父进程和子进程&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个进程创建新进程称为创建了子进程(child process)。&lt;/li&gt;
&lt;li&gt;创建子进程的进程称为父进程。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;❑ 进程号&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PID：Process Identity number。一个 PID 唯一地标识一个进程。&lt;/li&gt;
&lt;li&gt;PPID：Parent Process ID。进程的父进程号。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Chapter 7 Linux Shell&lt;/h2&gt;
&lt;h3&gt;各种 shell&lt;/h3&gt;
&lt;p&gt;Bourne shell (sh) Unix 最初使用，且在每种 Unix 上都可以使用。在 shell 编程方面相当优秀，但在处理与用户的交互方面做得不如其他几种 shell。 sh 的全称是 Bourne shell，由 AT&amp;amp;T 公司的 Steve Bourne 开发，为了纪念他，就用他的名字命名了。&lt;/p&gt;
&lt;p&gt;C shell (csh) csh，一个语法上接近于 C 语言的 shell。 sh 之后另一个广为流传的 shell 是由柏克莱大学的 Bill Joy 设计的，这个 shell 的语法有点类似 C 语言，所以才得名为 C shell ，简称为 csh。&lt;/p&gt;
&lt;p&gt;Korn shell (ksh) 完全向上兼容 Bourne shell 并包含了 C shell 的很多特性。&lt;/p&gt;
&lt;p&gt;Bourne Again shell (Bash) 因为 Linux 操作系统缺省的 shell。即 Bash 是 Bourne shell 的扩展，与 Bourne shell 完全向后兼容。在 Bourne shell 的基础上增加、增强了很多特性。可以提供如命令补全、命令编辑和命令历史表等功能。包含了很多 C shell 和 Korn shell 中的优点，有灵活和强大的编程接口，同时又有很友好的用户界面。&lt;/p&gt;
&lt;p&gt;Debian Almquist shell (dash) 原来 bash 是 GNU/Linux 操作系统中的 /bin/sh 的符号连接，但由于 bash 过于复杂，有人把 Bash 从 NetBSD 移植到 Linux 并更名为 dash，且/bin/sh 符号连接到 dash。Dash Shell 比 Bash Shell 小的多（ubuntu16.04 上，bash 大概 1M，dash 只有 150K），符合 POSIX 标准。Ubuntu 6.10 开始默认是 Dash。&lt;/p&gt;
&lt;p&gt;Zsh zsh 配置复杂，所以很多人都不会使用。直到有一天出了 Oh My Zsh 项目（https://github.com/robbyrussell/oh-my-zsh），才让更多人发现并开始使用zsh。有人说zsh是终极shell。zsh很漂亮，很炫酷，以前是极客使用，现在小白通过oh-my-zsh可以炫耀。&lt;/p&gt;
&lt;h3&gt;Shell 脚本&lt;/h3&gt;
&lt;p&gt;可以将 Shell 终端解释器当作人与计算机硬件之间的“翻译官”，它作为用户与 Linux 系统内部的通信媒介，除了能够支持各种变量与参数外，还提供了诸如循环、分支等高级编程语言才有的控制结构特性。要想正确使用 Shell 中的这些功能特性，准确下达命令尤为重要。Shell 脚本命令的工作方式有下面两种。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;交互式（Interactive）&lt;/strong&gt;：用户每输入一条命令就立即执行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;批处理（Batch）&lt;/strong&gt;：由用户事先编写好一个完整的 Shell 脚本，Shell 会一次性执行脚本中诸多的命令。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在 Shell 脚本中不仅会用到前面学习过的很多 Linux 命令以及正则表达式、管道符、数据流重定向等语法规则，还需要把内部功能模块化后通过逻辑语句进行处理，最终形成日常所见的 Shell 脚本。&lt;/p&gt;
&lt;h3&gt;Shell 脚本练习&lt;/h3&gt;
&lt;p&gt;脚本 1:输入一个正整数，输出 0 到这个正整数的平方数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Read -p &quot;请输入一个正整数&quot; n
i=0
While [ $i -le $n ]
 do
 j= $((i*i))
 echo $j
 i=((i+1))
done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;脚本 2:输出序列的最大数、次最大数、最小数、次最小数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Read -p &quot;请输入系列正整数，0结束&quot; n
max=n; min=n; sMax= n;sMin=n
While [ $n -ne 0 ]
 do
 read n
 if [ $n -gt $max ] ;then
 sMax=max; max= $n;
 elif[$n -gt sMax];then
 sMax=$n
 fi
 if [ $n -It $min ] ;then
 sMin= $min; min= $n;
 elif[$n-ItsMin];then
 sMin= $n
 fi
done
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Linux 一些概念&lt;/h2&gt;
&lt;p&gt;Kernel 内核：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;管理程序运行，为系统分配资源，处理程序间的通信&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;管理对文件系统的读写，把对文件系统的操作映射成对磁盘或其他设备的操作&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;管理存储器，为程序分配内存，并且管理虚拟内存&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;管理输入输出，将设备映射成设备文件&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;管理网络&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Shell&lt;/p&gt;
&lt;p&gt;是一个命令解释器，是内核和用户之间的接口&lt;/p&gt;
&lt;p&gt;是一个编程语言&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Shell 和内核的关系
Shell 是内核的保护层，对用户屏蔽内核的复杂性。
保护内核以免用户误操作造成危害。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Linux
标准的 Linux 系统都具有一套称为应用程序的程序集，包括文本编辑器、Xwindow、办公套件、Internet 工具和数据库等。&lt;/p&gt;
&lt;p&gt;文件
文件是 Linux 用来存储信息的基本结构，它是被命名的，存储在某种媒介（如磁盘、光盘和磁带等）上的一种信息集合。&lt;/p&gt;
&lt;p&gt;文件系统
Linux 文件系统实现数据存储（物理）介质独立性&lt;/p&gt;
&lt;p&gt;目录文件
目录文件存储一组相关文件的位置、大小等与文件有关的信息，但是并不包含文件的具体内容。目录文件中的每一项主要表示的是一个文件名（或子目录名）以及文件的索引节点号。&lt;/p&gt;
&lt;p&gt;索引节点
一个文件的索引节点能够指向该文件内容所在的数据块的位置，除此以外它还记录了该文件的属性。目录文件就是通过 i-node 表与文件之间建立对应关系的。&lt;/p&gt;
&lt;p&gt;块设备：
以块为单位进行随机存取。如：软盘、光盘、硬盘。&lt;/p&gt;
&lt;p&gt;字符设备：
以单个字符为单位进行顺序存取。如：打印机、终端、键盘、鼠标。&lt;/p&gt;
&lt;p&gt;文件系统：
指文件存在的物理空间。&lt;/p&gt;
&lt;p&gt;树状层次结构
Linux 将分属不同分区的、单独的文件系统整理成一个系统的，总的目录层次结构，即树状结构。&lt;/p&gt;
&lt;p&gt;挂接
Linux 通过挂接一个文件系统将该新文件系统加入它的文件系统树中，所有的文件系统，不管是什么类型，都挂接在文件系统树的一个目录上并且该文件系统之上的文件将会掩盖掉这个挂接目录中原有的内容。这个目录称为挂接目录或挂接点。&lt;/p&gt;
&lt;p&gt;链接
目录中每一对文件名称名和索引节点号称为一个链接。&lt;/p&gt;
&lt;p&gt;进程：
一个进程是一个程序的一次执行的过程。
操作系统通过进程来控制对 CPU 和其他系统资源的访问，并且使用进程来决定 CPU 上运行哪个程序、运行多久。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;程序与进程的不同：
程序：程序是静态的，是保存在磁盘上的可执行代码和数据的集合。
进程：进程是动态的，是 Linux 系统的基本调度单位&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Linux 常见目录说明&lt;/h2&gt;
&lt;h2&gt;Linux 环境变量&lt;/h2&gt;
&lt;p&gt;Linux 系统中最重要的 10 个环境变量：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;变量名称&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HOME&lt;/td&gt;
&lt;td&gt;用户的主目录（即家目录）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SHELL&lt;/td&gt;
&lt;td&gt;用户在使用的 Shell 解释器名称&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HISTSIZE&lt;/td&gt;
&lt;td&gt;输出的历史命令记录条数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HISTFILESIZE&lt;/td&gt;
&lt;td&gt;保存的历史命令记录条数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MAIL&lt;/td&gt;
&lt;td&gt;邮件保存路径&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LANG&lt;/td&gt;
&lt;td&gt;系统语言、语系名称&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RANDOM&lt;/td&gt;
&lt;td&gt;生成一个随机数字&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PS1&lt;/td&gt;
&lt;td&gt;Bash 解释器的提示符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PATH&lt;/td&gt;
&lt;td&gt;定义解释器搜索用户执行命令的路径&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EDITOR&lt;/td&gt;
&lt;td&gt;用户默认的文本编辑器&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;考试复习&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/FGRboke/article/details/122095325&quot;&gt;linux 大学期末考试题库_linux 期末考试题库-CSDN 博客&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;后续&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.linux.org/pages/download/&quot;&gt;Download Linux | Linux.org&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.runoob.com/linux/linux-command-manual.html&quot;&gt;Linux 命令大全 | 菜鸟教程 (runoob.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Linux_distribution&quot;&gt;Linux distribution - Wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/bellkosmos/p/detail_of_linux_file_system.html&quot;&gt;Linux 文件系统详解 - 赛艇队长 - 博客园 (cnblogs.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://linux.cn/article-9798-1.html&quot;&gt;技术|Linux 文件系统详解&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>外挂开发</title><link>https://h1s97x.github.io/posts/notes/notes-%E5%A4%96%E6%8C%82%E5%BC%80%E5%8F%91/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-%E5%A4%96%E6%8C%82%E5%BC%80%E5%8F%91/</guid><description>1、 进程：每一个应用程序/游戏启动，都会产生至少一个进程Process，在任务管理器里可以看到进程名称和进程PID。 2、 句柄：英文HANDLE，一个整数值</description><pubDate>Sat, 27 Dec 2025 15:46:11 GMT</pubDate><content:encoded>&lt;h3&gt;基本概念&lt;/h3&gt;
&lt;p&gt;1、 &lt;strong&gt;进程&lt;/strong&gt;：每一个应用程序/游戏启动，都会产生至少一个进程Process，在任务管理器里可以看到进程名称和进程PID。&lt;/p&gt;
&lt;p&gt;2、 句柄：英文HANDLE，一个整数值。数据的地址需要变动，变动以后就需要有人来记录管理变动，就好像户籍管理一样，因此系统用句柄来记载数据地址的变更。肉眼看到的一个个文件夹，窗口，按钮，图标，应用程序能够通过句柄访问相应的对象的信息&lt;/p&gt;
&lt;p&gt;3、 数据类型：游戏中的血量、蓝、生命值，我们将他们称之为变量，变量包含了变量名称和数据类型，那么它的名字就是&quot;血量、生命值&quot;等等，而它的数据类型决定了数值以何种方式存储到计算机的内存中，想要找到自身需要的变量，如果可以确定这个变量的数据类型或者有根据的推测来缩小变量的数据类型范围，那么对于快速定位该变量是具有帮助性的。以下是几种常见的变量类型：&lt;/p&gt;
&lt;p&gt;整数型：游戏中比如血量、法力可能用到这种类型。&lt;/p&gt;
&lt;p&gt;字节型：根据不同的编辑器，1个整形占用N个字节(N&amp;gt;1)，一般很早之前出产的GBA游戏为了节省开销会用到这种类型。&lt;/p&gt;
&lt;p&gt;浮点型：带有小数点的数字，如果金币或者伤害值带有小数点，那很可能是这种数据类型保存的。&lt;/p&gt;
&lt;p&gt;文本型：比如世界喊话，人物命名，一般采用文本型保存这类数据。&lt;/p&gt;
&lt;p&gt;4、 **钩子：**钩子(Hook)，是Windows消息处理机制的一个平台，应用程序可以在上面设置子程序以监视指定窗口的某种消息，而且所监视的窗口可以是其他进程所创建的。当消息到达后，在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。&lt;/p&gt;
&lt;p&gt;外挂现在大体上来讲分为三种，分别是模拟按键，WPE封包和内存挂&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;模拟键盘的，鼠标的，这种就叫做按键模拟，也叫脚本精灵&lt;/li&gt;
&lt;li&gt;修改数据包的，这种就叫做WPE封包外挂，这种外挂相对而言比较稳定，但是对于技术要求难度也比较高，因为修改WPE封包，你需要知道和了解加密解密算法等等一系列的知识&lt;/li&gt;
&lt;li&gt;修改本地内存的，这种相对而言比较常见，市场上面比较常见的也是这种游戏外挂，相对而言技术要求也不是太高，资料也比较齐全，比较大众&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;修改服务器内存的比较少见，技术太高一般人没有办法入手&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;基本流程&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;找到游戏进程&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;然后找到游戏窗口的句柄&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;找到存储变量的内存地址&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;利用编程语言建立工具修改变量的数值&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;软件&lt;/h3&gt;
&lt;p&gt;CE&lt;/p&gt;
&lt;p&gt;WPE&lt;/p&gt;
&lt;p&gt;OD&lt;/p&gt;
&lt;h3&gt;内存挂&lt;/h3&gt;
&lt;p&gt;其实，制作内存挂也不是很难，步骤就这么几步&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;找游戏数据内存地址，偏移地址&lt;/li&gt;
&lt;li&gt;修改内存地址的值，达到外挂的目的&lt;/li&gt;
&lt;li&gt;用编程语言写出一个程序，使得外挂方便于携带传输，同时也方便于下次玩游戏直接使用&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其实第三步主要是方便使用，方便贩卖等等，很多时候对于一个高手而言，根本不需要写出来，记录下内存地址，偏移地址以后，下次上游戏直接工具里面修改就是了。但是如果经常玩某个游戏，可以写出来，用不着每次玩游戏都去修改。（Cheat engine）&lt;/p&gt;
&lt;h5&gt;内存基址和物理地址&lt;/h5&gt;
&lt;p&gt;物理地址 动态，每次启动应用程序附加到内存时的位置是不同的，但是基址是不变的。&lt;/p&gt;
&lt;p&gt;物理地址 = 一级偏移地址 + 偏移地址
一级偏移地址 = 二级偏移地址 + 偏移地址
二级偏移地址 = 三级偏移地址 + 偏移地址
三级偏移地址 = 游戏技能基址 + 偏移地址&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在CE工具中，绿色代表静态地址，红色代表上一次操作改变的地址，黑色代表动态地址但上一次操作没有改变。而要找的基址就是静态基址。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;模块：pywin32 ctypes&lt;/p&gt;
&lt;p&gt;请认准窗口名称：Plants vs. Zombies 1.2.0.1073 RELEASE&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;以下均为16进制&lt;/strong&gt;
基址: 007794f8
阳光：+868+5578 （两次偏移）
金钱： +950+50 （两次偏移）&lt;/p&gt;
&lt;p&gt;实战——植物大战僵尸PVZ&lt;/p&gt;
&lt;p&gt;进入游戏，查找阳光数值 -&amp;gt; 变动后再次查找，直到剩下一个&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;有时会发现有两个数值的情况，这个时候去检查一下每个地址的操作码执行情况，一般数值修改就是add和mov指令&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;找到这个地址后，用快捷键F6或者右键选中“找出是什么修改了这个地址”，回到游戏让数值发生变动（例如拾取阳光，放置植物等），就可以看到有操作码执行了。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;image-20240426233834591&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;首先分析一下&lt;code&gt;mov [edi+00005578],esi&lt;/code&gt;这条指令，我们在详细信息中看见，寄存器&lt;code&gt;esi&lt;/code&gt;中的代码是&lt;code&gt;0000039D&lt;/code&gt;这个值换算成十进制刚好是&lt;code&gt;925&lt;/code&gt;，发现什么了吗，对了，这个&lt;code&gt;925&lt;/code&gt;就是当前的阳光值。这条指令&lt;code&gt;mov mov [edi+00005578],esi&lt;/code&gt;就是把寄存器&lt;code&gt;esi&lt;/code&gt;的&lt;code&gt;925&lt;/code&gt;的值放入&lt;code&gt;edi+00005578&lt;/code&gt;这个地址中去，这个地址是谁呢？这个地址就是刚刚找出来的物理地址，刚好满足前面的公式：&lt;code&gt;物理地址 = 阳光基址地址 + 偏移地址&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;接下看看&lt;code&gt;add [eax+00005578],ecx&lt;/code&gt;这条指令，&lt;code&gt;add&lt;/code&gt;指令是相加的意思，前面的&lt;code&gt;mov&lt;/code&gt;是赋值或者移动的意思，这条代码的意思就是，把&lt;code&gt;ecx&lt;/code&gt;寄存器中的值加入&lt;code&gt;[eax+00005578]&lt;/code&gt;这个地址中的值当中去，再看看寄存器&lt;code&gt;eax&lt;/code&gt;的地址是多少？刚好又是&lt;code&gt;23560070&lt;/code&gt;这个指针数值，那么，我们就可以确定一个事情，那就是&lt;code&gt;23560070&lt;/code&gt;这个指针数值，肯定是上一级的地址的指针数值，那么上一级要么是基址，要么还是一个偏移地址，这个需要进一步来确认&lt;/li&gt;
&lt;li&gt;把&lt;code&gt;23560070&lt;/code&gt;这个指针数值，再次放到CE中搜索一波，看看这个值对应的地址是多少，看看出来的是一个动态地址还是静态地址（在CE中，黑色的地址表示动态地址，绿色的表示静态地址），这里这个值是十六进制的，所以在搜索的时候，需要勾选搜索框前面的hex，这里填好以后点击新的扫描,首次扫描如图所示：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt=&quot;image-20240426235454287&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这里搜索出来的值有很多，要怎么确认呢？&lt;/p&gt;
&lt;p&gt;多次点击再次扫描以后发现不管怎么点击都剩下来了52个结果，然后看了一下这些结果中并没有绿色的静态地址，说明至少有二级偏移。其他地址开头几位都比较相似，唯独有几个地址比较特殊，这可能就是我们需要的地址了&lt;/p&gt;
&lt;p&gt;百度了一波，结果所说的是这种开头比较像的是数组，也就是说，遇到这种，基本都不是我们需要的地址，直接放弃，把这两个特殊的拉进下边的地址列表，开始分析，双击地址这一栏，在出来的更改地址窗口中复制下这个地址，然后再勾选指针选项，在出来的内容框中填写入刚刚复制的地址，在上面的内容框中写入刚刚找到的00005560这个偏移地址，得到的结果刚好是阳光的物理地址，最后的阳光值也正好和当前的阳光值一样，并且，这两个地址都是一样的，而且都是动态地址&lt;/p&gt;
&lt;p&gt;如上图所示，这两个地址加上偏移地址都可以得到阳光物理地址，右键-找出是什么访问了这个地址，也就是快捷键的F5&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;image-20240426235347682&quot; /&gt;&lt;/p&gt;
&lt;p&gt;进去以后也是和前面一样出来一个窗口，但是这两个窗口里面的内容就不一样了，第一个地址028BBA80在窗口中会出现内容，但是第二个地址0956BD3C在出现的窗口里面是一片空白，所以，这两个地址中，028BBA80才是我们要找的真正的一级偏移地址，如图所示：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;image-20240426234356042&quot; /&gt;&lt;/p&gt;
&lt;p&gt;如上第二幅图所示，找到了真正的一级偏移地址&lt;code&gt;028BBA80&lt;/code&gt;后，访问这个地址的窗口中看这些代码指令，通过对比，发现这些代码都指向同一个指针数值&lt;code&gt;028BBA80&lt;/code&gt;且都有偏移地址&lt;code&gt;00000868&lt;/code&gt;，如图所示：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;image-20240426235649647&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;image-20240427000346546&quot; /&gt;&lt;/p&gt;
&lt;p&gt;有四个怎么办？试一试&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;image-20240427000413321&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;image-20240427000451679&quot; /&gt;&lt;/p&gt;
&lt;p&gt;对了。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import win32gui
import win32process

# 窗口句柄
window_handle = win32gui.FindWindow(MainWindow, &quot;Plants vs. Zombies&quot;)

win32process.GetWindowThreadProcessId(window_handle)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img alt=&quot;image-20240427001003309&quot; /&gt;&lt;/p&gt;
&lt;p&gt;CD修改：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;rule：&lt;/p&gt;
&lt;p&gt;当植物处于可使用状态时，内存值为1&lt;/p&gt;
&lt;p&gt;当植物处于CD状态时，内存值为0&lt;/p&gt;
&lt;p&gt;植物种植后，内存值由0开始增加，这个增加的过程就是CD，当内存值到达一定数值后，就会自动归零，此时CD结束&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>战网</title><link>https://h1s97x.github.io/posts/notes/notes-%E6%88%98%E7%BD%91/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-%E6%88%98%E7%BD%91/</guid><description>最近实在无聊，想找个卡牌游戏玩玩，玩了些单机，没多久就腻了，或者是没下载途径，玩不到，思来想去还不如重玩炉石算了。 Q1:解决战网“正在更新battlenet </description><pubDate>Sat, 27 Dec 2025 15:46:11 GMT</pubDate><content:encoded>&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;最近实在无聊，想找个卡牌游戏玩玩，玩了些单机，没多久就腻了，或者是没下载途径，玩不到，思来想去还不如重玩炉石算了。&lt;/p&gt;
&lt;h3&gt;FAQ&lt;/h3&gt;
&lt;p&gt;Q1:解决战网“正在更新battlenet update agent”卡在45%的问题&lt;/p&gt;
&lt;p&gt;A1:&lt;/p&gt;
&lt;p&gt;1、首先关掉安装程序，打开任务管理器关掉与“Battle”有关的2个（图中只显示了一个但是如果安装卡在45%是有两个的）进程。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/0d7958e70162aec72c17f6efc380537d423760033.png%40progressive.webp&quot; alt=&quot;read-normal-img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;2、在电脑键盘上按下win+r键，输入regedit，打开注册表。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/356d3435fa76d892437a3ab778f5d822423760033.png%40progressive.webp&quot; alt=&quot;read-normal-img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;找到HKEY_CURRENT_USER\Software\Blizzard Entertainment这个注册表文件夹&lt;strong&gt;删除掉图中红框框起来的文件夹&lt;/strong&gt;如下图所示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/7d94cc0c3140044128672cdfc9f06412423760033.png%40progressive.webp&quot; alt=&quot;read-normal-img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;3、进入C盘在查看中勾选隐藏的项目，找到ProgramData这个文件夹。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/29599b997f61cc33790e7cdcbe889210423760033.png%40progressive.webp&quot; alt=&quot;read-normal-img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;打开ProgramData文件夹后找到Battle.net文件夹并删除文件夹如图所示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/6d1f921c47e43de190eabb92bda482aa423760033.png%40progressive.webp&quot; alt=&quot;read-normal-img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;做完上述步骤之后就可以重新打开安装程序了~&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;PS：重点来了！重点来了！重点来了！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;重新打开安装程序的时候选择使用语言：不要勾选简体中文（勾选会再次卡到45%的安装进程），选择英文进行安装（最好是英文，繁体中文也有可能会卡），再次安装就就解决了卡在45%的问题了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Q2:如何注册外服账号&lt;/p&gt;
&lt;p&gt;A2:&lt;a href=&quot;https://www.liuxing.com/news/32597.html&quot;&gt;暴雪战网外服账号注册教程_行业资讯_流星游戏加速器 (liuxing.com)&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>攻略 杀戮尖塔</title><link>https://h1s97x.github.io/posts/notes/notes-%E6%94%BB%E7%95%A5-%E6%9D%80%E6%88%AE%E5%B0%96%E5%A1%94/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-%E6%94%BB%E7%95%A5-%E6%9D%80%E6%88%AE%E5%B0%96%E5%A1%94/</guid><description>《杀戮尖塔》攻略(二)战士篇 - 知乎 (zhihu.com) 牌组很简单，14张牌，核心牌“壁垒+”“巩固+”“全身撞击+”，三张牌足以胡牌。这局不仅解锁了碎</description><pubDate>Sat, 27 Dec 2025 15:46:11 GMT</pubDate><content:encoded>&lt;h2&gt;杀戮尖塔/SLAY THE SPIRE&lt;/h2&gt;
&lt;h3&gt;游戏特色&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;动态构建的牌组&lt;/em&gt;：谨慎选择你的卡牌！在攀爬高塔的途中邂逅百余张不同的卡牌，选择是否将它们加入你的牌组，通过组合和搭配来让牌组发挥力量，打败敌人、挑战登顶。&lt;/li&gt;
&lt;li&gt;&lt;em&gt;不断变化的高塔&lt;/em&gt;：每次开始爬塔的旅程时，高塔的构造都会发生变化。是选择高风险高回报，还是谨慎前行？面对不同的敌人、选择不同的卡牌、发现不同的遗物、甚至挑战不同的Boss！&lt;/li&gt;
&lt;li&gt;&lt;em&gt;威力强大的遗物&lt;/em&gt;：在塔中你将发现名为遗物的各种强大物品，这些遗物可能会与你的牌组产生强大的化学反应，让你的战力得到飞跃。但也不要过于大意，为了获得遗物，有时你所付出的代价，可会超过区区金钱……&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;游戏介绍&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;四名角色，各自拥有独特的牌组。&lt;/li&gt;
&lt;li&gt;350多张可用的卡牌。&lt;/li&gt;
&lt;li&gt;200多种可被找到的物品与道具。&lt;/li&gt;
&lt;li&gt;50多场完全不同的战斗。&lt;/li&gt;
&lt;li&gt;50多件神秘的事件，可能给你带来帮助，也可能将你害得很惨。&lt;/li&gt;
&lt;li&gt;每日挑战模式，让你可以在进行独特游戏的同时，与世界各地的玩家们比较你的表现。&lt;/li&gt;
&lt;li&gt;自定模式，让你可以自由混合各种疯狂的特殊效果。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;游戏攻略&lt;/h2&gt;
&lt;h3&gt;战士篇&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/601780314&quot;&gt;《杀戮尖塔》攻略(二)战士篇 - 知乎 (zhihu.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;牌组很简单，14张牌，核心牌“壁垒+”“巩固+”“全身撞击+”，三张牌足以胡牌。这局不仅解锁了碎心成就，同时还解锁了单杀“倏忽魔”(第三层999血怪物)、“铜墙铁壁”(拥有999防御)等等几个稀有成就。最重要的是，这局游戏让我体验到了战士的强大， 战士的安全感，以及当你有用999防御时的君临天下的感觉。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;天下无敌之最强防御流&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;坐拥三大角色中最强的防御体系牌组，不玩防御你确定吗？&lt;strong&gt;乌龟流&lt;/strong&gt;，我愿称之为战士最强大的牌组流派，别说进阶20了，你就是进阶200我照样秒你。输出与防御一体，这就是乌龟流卡组；还是仰仗这张传说中的“肚皮”。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/v2-c5ca5431187c26d6e12f0a79c40488bf_720w.webp&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;神之白卡，肚皮+&lt;/p&gt;
&lt;p&gt;格挡值转换为伤害，这波属于是将乌龟变玄武了。再加上强化后的零费用优化，绝对是神之白卡之一。好了，我只是感慨下这张卡的强大，接下来正式介绍乌龟流战士牌组的构成：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/v2-8b80c31645c3182e4453dfbae3ed97aa_720w.webp&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;按重要性由左到右依次排开，第一行重要性大于第二行；核心牌只有前三张&lt;/p&gt;
&lt;p&gt;关于乌龟流的牌组，我这里列举了14张牌；核心牌只有前三张。可能有朋友会问为什么吹上天的肚皮居然只排在第三位？那是因为乌龟流你既然已经拥有999防御了，那怎么玩都随你的便了，肚皮的输出你也不再需要，肚皮只是提供了秒杀级别的DPS而已，你用打击一回合打6点伤害也一样可以获胜。&lt;/p&gt;
&lt;p&gt;首先最重要的，&lt;strong&gt;巩固+&lt;/strong&gt;。几乎没有巩固就没有乌龟流；翻倍在一个数值游戏里真的太过于变态了，指数级生长的格挡让任何攻击都显得如此苍白无力。第二张也非常核心，但不是必备，那就是&lt;strong&gt;壁垒+&lt;/strong&gt;。壁垒这张牌看似是乌龟流的核心，没有保留的格挡，翻倍再多又有何用？但是别忘了，还有外卡钳这玩意儿的存在，对于指数生长的格挡来说，一回合损失15点约等于没有，因此外卡钳是可以替代壁垒的一张牌。肚皮我们上面已经讲过了。&lt;/p&gt;
&lt;p&gt;下面就是一些锦上添花或者过渡的牌。&lt;strong&gt;武装&lt;/strong&gt;可以帮助度过前期，但后期基本没用；&lt;strong&gt;耸肩&lt;/strong&gt;是可以多拿的一张牌，好用程度推荐亲身体会，绝不只比防御多3点；&lt;strong&gt;重振&lt;/strong&gt;配合&lt;strong&gt;硬撑&lt;/strong&gt;可以在没有巩固的情况下快速堆叠格挡值，即便是没有硬撑，重振也可以独当一面，快速缩减牌组规模，让核心牌的抽牌率大大增加。&lt;strong&gt;岿然不动&lt;/strong&gt;就不用说了，40点格挡胜过一切言语；&lt;strong&gt;金属化&lt;/strong&gt;属于费用多了没处花，可以用一用；&lt;strong&gt;狂暴&lt;/strong&gt;是加费牌，百搭；&lt;strong&gt;燔祭&lt;/strong&gt;和&lt;strong&gt;残杀&lt;/strong&gt;则是渡过前期最好的输出牌，因为乌龟流在成型前是很脆弱的；后面的两张是万金油百搭牌，任何牌组均非常适用的&lt;strong&gt;战斗专注&lt;/strong&gt;和&lt;strong&gt;祭品&lt;/strong&gt;，两张非常好用的&lt;strong&gt;启动牌&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;接下来介绍适合乌龟流的遗物，首先是&lt;strong&gt;非BOSS遗物&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/slay-the-spire-01-20261014.webp&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;非BOSS遗物推荐&lt;/p&gt;
&lt;p&gt;首先是&lt;strong&gt;意外光滑的石头&lt;/strong&gt;，无需多言，一点敏捷，每次格挡都会+1；&lt;strong&gt;铜制鳞片&lt;/strong&gt;，反伤3点，对于乌龟流来说，一边龟一边可以打输出，乌龟流的最强白色遗物；&lt;strong&gt;瓶装旋风与闪电&lt;/strong&gt;，拿到时分别可以装&lt;strong&gt;壁垒&lt;/strong&gt;和&lt;strong&gt;祭品&lt;/strong&gt;，对于牌组稳定性来说提升巨大；&lt;strong&gt;外卡钳&lt;/strong&gt;，上面我有提到，有了外卡钳，妈妈再也不担心我找不到壁垒了；&lt;strong&gt;针线&lt;/strong&gt;对于乌龟流是锦上添花；&lt;strong&gt;多利之镜&lt;/strong&gt;拿到时可以选择复制巩固，让数值翻得更快一点~&lt;/p&gt;
&lt;p&gt;接下来是&lt;strong&gt;BOSS遗物&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/slay-the-spire-02-20261014.webp&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;BOSS遗物推荐&lt;/p&gt;
&lt;p&gt;由于壁垒这样的高费牌的存在，其实乌龟流还是比较缺费用的，因此BOSS遗物中除“&lt;strong&gt;潘多拉的魔盒&lt;/strong&gt;”这样的神之遗物外，更多的推荐一些加费的遗物；当然，基本所有加费的遗物都是存在负面效果的，因此在拿这些遗物的时候，要考虑清楚取舍。比如牌组大致成型了，再去选&lt;strong&gt;破碎金冠&lt;/strong&gt;；拿到&lt;strong&gt;诅咒钥匙&lt;/strong&gt;后，就放弃一些宝箱中的遗物；反而&lt;strong&gt;咖啡滤杯&lt;/strong&gt;是我比较喜欢的，因为我很少在休息处休息，基本都用来锻造——但在高进阶下，这样做容易暴毙；&lt;strong&gt;痛楚印记&lt;/strong&gt;则明显是为战士量身定做的遗物。&lt;/p&gt;
&lt;p&gt;最后是&lt;strong&gt;玩法&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;这里我就不配图了，在第一回合争取拿到更多的牌，获取更多的费用——推荐&lt;strong&gt;祭品+&lt;strong&gt;这张牌，能够让乌龟流成型的更快；第一回合打出&lt;/strong&gt;壁垒+&lt;/strong&gt;，使用&lt;strong&gt;耸肩无视+、岿然不动&lt;/strong&gt;等卡牌来堆叠基础的格挡值，然后在收尾时使用&lt;strong&gt;巩固+&lt;strong&gt;来最大化初始格挡，后续就非常简单了，每回合继续堆叠防御，收尾使用巩固翻倍，直到999。期间如果摸到了&lt;/strong&gt;全身撞击+&lt;/strong&gt;，就用来输出一下。&lt;/p&gt;
&lt;p&gt;以上就是铁甲战士最简单无脑、最强大的牌组了，不需要什么高级的计算，无脑堆格挡，就可以轻松碾压高塔。心脏在乌龟流面前也显得如此不堪一击，学会的玩家赶快试一试吧~&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;单杀一切的灼热攻击&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;灼热攻击，独立于一切的一张牌。无限锻造，战士独有。自然也就形成了独特的流派。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/slay-the-spire-03-20261014.webp&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;低调的蓝卡，+12灼热攻击，单体伤害126点&lt;/p&gt;
&lt;p&gt;至于&lt;strong&gt;灼热攻击的牌组&lt;/strong&gt;，我也就不一一列举了，就拿两把我通关的局来说吧：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/v2-5e17e033700abdcc59c113623354b662_720w.webp&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;进阶18灼热攻击，武装，战斗专注，重振精神是核心牌&lt;/p&gt;
&lt;p&gt;这一局是通关进阶18的一局，核心牌：&lt;strong&gt;灼热攻击+12&lt;/strong&gt;，&lt;strong&gt;战斗专注+&lt;/strong&gt;，&lt;strong&gt;武装&lt;/strong&gt;，&lt;strong&gt;重振精神&lt;/strong&gt;。重振精神用来快速消耗无用手牌，使牌组只剩下战斗专注+、灼热攻击+12、武装三张牌，战斗专注+配合武装、灼热攻击+12来达到每回合多次强化并输出灼热攻击的效果。&lt;/p&gt;
&lt;p&gt;这一套牌组讲究的就是一个爆炸，无需启动，裸伤126，越战越勇。三层怪也抗不过两个回合；当然牌组中若有&lt;strong&gt;死灵之书&lt;/strong&gt;存在，则是无敌，可惜摸不得。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/v2-f4bb1021e85ddc52f9985fac134cf369_720w.webp&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;单遗物通关挑战&lt;/p&gt;
&lt;p&gt;另一局则是追求单遗物通关成就的一局游戏；这局游戏也足以见得灼热攻击这张牌的强大，能够以单张牌匹敌其他的牌组战力，可谓非常之强大。朴实无华，不需要任何其余的牌，就可以打出巨额伤害。&lt;/p&gt;
&lt;p&gt;接下来是&lt;strong&gt;遗物推荐&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/v2-625a8a75b052f6622d0733f9ec09eec5_720w.webp&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;死灵之书：TOP1&lt;/p&gt;
&lt;p&gt;最搭配灼热攻击流派的，自然是&lt;strong&gt;死灵之书&lt;/strong&gt;；拿到死灵之书，直接天胡，神挡杀神，佛挡杀佛；其余则是一些可以加费的BOSS遗物，灼热攻击作为一张2费用牌，费用还是至关重要的。&lt;/p&gt;
&lt;p&gt;最后是&lt;strong&gt;玩法&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;其实上面已经有所介绍，这副牌组的玩法非常简单，直接&lt;strong&gt;灼热攻击砸脸输出&lt;/strong&gt;即可；有机会的情况下可以多使用&lt;strong&gt;武装来强化灼热攻击&lt;/strong&gt;，来造成更高的伤害；但是牌组有个缺点，就是&lt;strong&gt;灼热攻击这张牌的获取时机要求较高&lt;/strong&gt;，必须是在游戏的开始阶段，这样才能更多的锻造，同时路线要尽可能选择&lt;strong&gt;篝火较多&lt;/strong&gt;的路线，来最大化灼热攻击的输出；最后是&lt;strong&gt;拿到死灵之书的天胡局&lt;/strong&gt;，即使是心脏，在强大的攻击力面前也不够看。&lt;/p&gt;
&lt;h4&gt;力量流&lt;/h4&gt;
&lt;p&gt;这是我常玩的流派，因为直观而且简单，但是需要的胡牌多，&lt;/p&gt;
&lt;h2&gt;下载&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.zzzzz688.com/1961.html&quot;&gt;杀戮尖塔/Slay the Spire V2.3.4最新中文学习版 解压即撸 - 游戏天堂 (zzzzz688.com)&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>攻略 炉石传说冒险</title><link>https://h1s97x.github.io/posts/notes/notes-%E6%94%BB%E7%95%A5-%E7%82%89%E7%9F%B3%E4%BC%A0%E8%AF%B4%E5%86%92%E9%99%A9/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-%E6%94%BB%E7%95%A5-%E7%82%89%E7%9F%B3%E4%BC%A0%E8%AF%B4%E5%86%92%E9%99%A9/</guid><description>!img 在这次的佣兵之书里，晨拥的技能主要是新英雄牌的同款技能，2费造成2点伤害，荣誉消灭则永久增加2点伤害，在一些关卡技能会变成了冰冷触摸或者冻结之舞，冰冷</description><pubDate>Sat, 27 Dec 2025 15:46:11 GMT</pubDate><content:encoded>&lt;h2&gt;佣兵之书 晨拥篇 全关卡打法攻略&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://i1.max-c.com/imgx/2022/03/18/c5b5d6674e1342cb9fb6bd01ed5f5ae11647602194&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在这次的佣兵之书里，晨拥的技能主要是新英雄牌的同款技能，2费造成2点伤害，荣誉消灭则永久增加2点伤害，在一些关卡技能会变成了冰冷触摸或者冻结之舞，冰冷触摸也就是曾经的法师DK同款技能，晨拥的卡组大多都是大法术和低费随从，虽然是大法术法卡组，但也有火热促销这样低于5费的法术，清风的巴琳达必抽火热促销，人都麻了。&lt;/p&gt;
&lt;p&gt;关卡攻略详解&lt;/p&gt;
&lt;p&gt;第一关：晨拥VS墓碑&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i1.max-c.com/imgx/2022/03/18/e16fb5a872b9457fb1dd3c13c20386311647602194&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;布鲁坎和塔姆辛·罗姆同归于尽后，幸存下来的凯瑞尔和剩下的佣兵们回合，他们为老萨满立了墓碑，并且在墓碑前哀悼九回合，这一关没有任何怪物，墓碑的技能也是愿佣兵不再受到任何伤害，只需要静静听佣兵们缅怀老布鲁坎就好了。&lt;/p&gt;
&lt;p&gt;第二关：晨拥VS洛瑟玛·塞隆&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i1.max-c.com/imgx/2022/03/18/25109e4742484c2b83e7ac02a0882e2b1647602195&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;收拾好心情后，佣兵们打算回到自己的部族搬救兵，而瓦尔登·晨拥也打开了传送门回到了银月城，向银月城高层打探卡扎库杉的消息，奎尔萨拉斯的摄政王洛瑟玛·塞隆最终还是选择相信晨拥，并且派兵前往尘泥沼泽，也就是奥妮克希亚的巢穴所在地。这一关对方的随从基本上都是血精灵，卡组应该算是奥秘猎，不过技能能不停地给我方卡牌加费，所以随从能上尽快上，靠冰霜法术控场，多打脸抢伤害，如果脸好的话对方加费会全加到同一张牌上。&lt;/p&gt;
&lt;p&gt;第三关：晨拥VS瓦里安国王&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i1.max-c.com/imgx/2022/03/18/3b62c24c6fed4ea3a8a7e1a2b8e4207a1647602195&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;当晨拥的部队抵达卡利姆多时，其他佣兵也带着族人赶到了，凯瑞尔为晨拥引荐了暴风城国王瓦里安，这一关需要消灭四个随从推进战斗，其中两个是瓦王失踪后在竞技场结识的朋友：瓦莉拉和熊皮，以及被奥妮克希亚分裂出来，充满斗志的瓦里安，这一点也蛮符合魔兽世界的剧情的。这一关晨拥的技能是DK的技能，最好在前期点出一个水元素站场，不然很容易被瓦王铺死，让熊皮触发暴怒的时机也要选好。&lt;/p&gt;
&lt;p&gt;第四关：晨拥VS团本首领奥妮克希亚&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i1.max-c.com/imgx/2022/03/18/e99ace1b9f7e41b08e8b8d89938ec6721647602196&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;终于进入到龙巢对抗黑龙公主奥妮克希亚了，这次的佣兵之书里，奥妮克希亚多达四个阶段，非常有团队副本的感觉，第四关是第一阶段，也是非常难的一关。在这一关里，第四回合会准时下4费10/10突袭的卡扎库杉，击败卡扎库杉后会有三回合休眠，另外这一关的佣兵是不能复活的，随从也比较少，所以一定要小心使用，第四回合可以选择不上佣兵，奥妮克希亚的招牌法术扫尾可以让你的随从撞队友，所以尽量只保留一个大怪，有一个法术可以保证凯瑞尔和库尔特鲁斯一回合免疫，用在恰当的时机可以增加输出。清风是靠魔杖窃贼发现两个炎爆赢的，难度还是挺大的。&lt;/p&gt;
&lt;p&gt;第五关：晨拥VS龙巢之母奥妮克希亚&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i1.max-c.com/imgx/2022/03/18/8922b8d599da43448b8fc8358acbdc011647602197&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这一关的难度比较一般，奥妮克希亚会不停地招0/3嘲讽龙蛋，但是似乎没有主动破蛋的手段，所以可以放着他不管，找机会升级技能的伤害，然后到差不多时机把龙蛋全变羊了就行。输出的主力是技能，多找机会升级技能伤害就行了，难度没那么大。&lt;/p&gt;
&lt;p&gt;第六关：晨拥VS虚空之龙奥妮克希亚&lt;/p&gt;
&lt;p&gt;第1题：时间不多了，我们上吧&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i1.max-c.com/imgx/2022/03/18/9ab43a3b2652438280f76cf5fe626aa21647602197&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;解决了雏龙群，奥妮克希亚飞到了天上，面对我们的是各种谜题，这次的解密难度虽然有点大，但是大部分是天梯里可能会遇到的套路，基本上都是狮鹫年OTK卡组，属于是官方催更夏布去炉石骚套路了。第一关是火车王斩杀谜题，首先凯瑞尔上场贴风怒，先撞7/7，再撞2/3，受伤后使用荣誉护盾，然后撞满血的7/7和一个4/5，接下来上法强随从加奉献，此时库尔特鲁斯处于流放状态，上场解决掉最后的墙，火车王打脸。这一道解谜题里有一段库尔特鲁斯与凯瑞尔的对话，恰好是库尔特鲁斯英雄牌卡牌趣文里的内容。&lt;/p&gt;
&lt;p&gt;第2题：在此终结&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i1.max-c.com/imgx/2022/03/18/d7f87840fd694d109ca01909a3ba4f121647602198&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这题非常简单，经典的吸血瞎斩杀步骤，上两个莫尔葛工匠，上1/3然后两个眼棱打掉对面随从和除了伊格诺斯之外的随从，玩过吸血瞎的应该不用看攻略就能完成。&lt;/p&gt;
&lt;p&gt;第3题：修补弥合&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i1.max-c.com/imgx/2022/03/18/8139a663154141efa8e625466edb2f151647602198&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这一关是等差数列猎的OTK套路，先上大导师野爪给所有野兽+1/+1，然后上6/9攻击出犀牛，全部走脸，当场地满的时候，拉出的随从先不打脸，先用数量压制解掉一个怪(别解塔维什和犀牛就行)，再用随从打脸拿出牌库里最后一个随从。&lt;/p&gt;
&lt;p&gt;第4题：撼动之力&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i1.max-c.com/imgx/2022/03/18/60f62d0b13934892b759f875219240341647602199&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这一题是圣契骑卡组的秘诀：如何最大化利用智慧圣契，先上两个奥尔多侍从减费，然后下扫帚，给扫帚贴圣契和凯瑞尔一起撞掉5/6龙拿到硬币，接下来圣契贴给洛卡拉撞5/3圣盾龙，再用两个圣契贴给奥尔多侍从解掉圣盾龙，另一个侍从解掉一个雏龙，用完硬币后上小野猪，贴圣契，然后拍下莉亚德琳跟扫帚解掉剩下的雏龙，圣契全贴给小野猪，最大应该能养到11/11，斩杀是没问题的。&lt;/p&gt;
&lt;p&gt;第5题：值得为之而战&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i1.max-c.com/imgx/2022/03/18/6462f7dd68b64bd9bfa8b010644d4b591647602199&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;第五题是经典的无限发现暗影视界套路，先上两个光耀元素和雷象，使用暗影视界后把雷象痛掉，再上灵能魔给他贴上神圣化身吸血，然后无限发现暗影视界就行了。&lt;/p&gt;
&lt;p&gt;第6题：猪猪宝剑&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i1.max-c.com/imgx/2022/03/18/14503148e21146e983555457e06bfc591647602200&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;第六题是野猪贼套路，先上一个军官，两个野猪和一个自爆绵羊(绵羊要最后上)，然后用伺机待发挂上场地卡，用血魔吃掉这些亡语怪，自爆绵羊会自动破开血魔，最后用背刺杀掉自爆绵羊，刀就拿到手了，对面场上的硬币龙没啥用。&lt;/p&gt;
&lt;p&gt;第7题：斯卡布斯与龙&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i1.max-c.com/imgx/2022/03/18/ef349f6b75ab478e8954942e658a29551647602200&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;第七题是锁喉贼套路，先用虚灵改装师触发八爪鱼暴怒效果，然后用另一个虚灵改装师打掉异灵术老师，然后拍斯卡布斯·刀油，再上公会商人，用幻觉药水复制随从，再用两次虚灵改装师(不要打死随从)，然后用暗影步回手再上一次虚灵改装师，最后用掉影袭和锁喉，用复制的公会商人把流血牌交易出来，费用可以交易两次，必定斩的。&lt;/p&gt;
&lt;p&gt;第8题：为了艾泽拉斯!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i1.max-c.com/imgx/2022/03/18/5c3c4dd5bc94468f83adf12d4fc6f0b61647602201&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最后一题是火冲法套路，先用野火提升技能伤害，然后用迸射流星打左边两个怪(只打左边两个)用2/1突袭把9/9龙修成7血，用3/1突袭把左边的死亡之翼修成9血，用虚灵改装师将右边的死亡之翼修成11血，然后下学徒，用掉硬币龙掉的硬币，技能打脸，恢复水晶后下黑瘴林树精，用火球复原技能斩杀，此时场上恰好只留下残血的纳鲁，任务完成。&lt;/p&gt;
&lt;p&gt;第七关：晨拥VS复活的奥妮克希亚&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i1.max-c.com/imgx/2022/03/18/98dae0db11394f4b9db268e141c428b71647602201&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;打败了奥妮克希亚，晨拥带着纳鲁和泽瑞拉一起赶往埃索达复活泽瑞拉女儿，没想到黑龙公主复活成亡灵巨龙又追了出来。这一关是生存关卡，奥妮克希亚会不停的从牌库拉龙出来，而且越拉越多，苟活难度很大，前期一定要有随从，最好是一个2费的元素和1费的随从，能提升技能伤害就提升，如果七费能准时呼啦，八费能接群体变羊那就赢了，这完全是一个看起手和牌序的关卡。&lt;/p&gt;
&lt;p&gt;第八关：晨拥VS米达，纯粹虚空&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i1.max-c.com/imgx/2022/03/18/af701d025dd848c1b239c0118556963e1647602202&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;到达埃索达之后，泽瑞拉和晨拥试图净化纳鲁，这一关没什么难度，纳鲁在5费的时候会不停的使用技能，来回忆狮鹫年里遇到的所有故事，算是对于这一年剧情的总结和回顾，解场没什么难度，而且似乎如果你不解场的话，场面会自动换成下一个故事的敌人，如果是一路追着佣兵之书剧情看过来的话，对于这一关应该会非常感慨，真的挺不错的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i1.max-c.com/imgx/2022/03/18/8e8d0cd455594c789347efd2673def801647602202&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;故事的结尾虚空纳鲁终于被净化，回归成纯粹圣光，用最后一丝圣光的力量复活了泽瑞拉的女儿露妮，十位佣兵的故事终于落下帷幕，大家回到了家乡成为了英雄，晨拥也回到了银月城，赢回了大家的信任，并且获得魔导师的称号。不过晨拥说“我们的书已尽尾声，但另一本书，才刚刚开启。”这难道是说未来还会有类似新的单人&lt;a href=&quot;https://www.jiaochengzhijia.com/special/maoxyoux.html&quot;&gt;冒险&lt;/a&gt;模式吗&lt;/p&gt;
&lt;h2&gt;参考链接&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.jiaochengzhijia.com/game/pcgame/141130.html&quot;&gt;炉石传说佣兵之书晨拥篇全关卡打法攻略 - 单机游戏 - 教程之家 (jiaochengzhijia.com)&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>玩转kali</title><link>https://h1s97x.github.io/posts/notes/notes-%E7%8E%A9%E8%BD%ACkali/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-%E7%8E%A9%E8%BD%ACkali/</guid><description>[TOC] 图书名称: Kali Linux: Assuring Security by Penetration Testing ------------- -</description><pubDate>Sat, 27 Dec 2025 15:46:11 GMT</pubDate><content:encoded>&lt;p&gt;[TOC]&lt;/p&gt;
&lt;h2&gt;参考资料&lt;/h2&gt;
&lt;h3&gt;参考书&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;图书名称:&lt;/th&gt;
&lt;th&gt;Kali Linux: Assuring Security by Penetration Testing&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;封面图片:&lt;/td&gt;
&lt;td&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/kali-guide-02-20261014.jpg&quot; alt=&quot;封面&quot; /&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;出版印刷时间:&lt;/td&gt;
&lt;td&gt;2015-2&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;出版社:&lt;/td&gt;
&lt;td&gt;人民邮电出版社&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;图书作者:&lt;/td&gt;
&lt;td&gt;Lee Allen / Tedi Heriyanto / Shakeel Ali 著&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;译者&lt;/td&gt;
&lt;td&gt;Archer 译&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ISBN:&lt;/td&gt;
&lt;td&gt;9787115378446&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;图书名称:&lt;/th&gt;
&lt;th&gt;Kali Linux2 网络渗透测试实践指南 第 2 版&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;封面图片:&lt;/td&gt;
&lt;td&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/kali-guide-04-20261014.jpg&quot; alt=&quot;Kali Linux2 网络渗透测试实践指南 第2版&quot; /&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;出版印刷时间:&lt;/td&gt;
&lt;td&gt;2021-3&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;出版社:&lt;/td&gt;
&lt;td&gt;人民邮电出版社&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;图书作者:&lt;/td&gt;
&lt;td&gt;李华峰 著&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ISBN:&lt;/td&gt;
&lt;td&gt;9787115555410&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;友链&lt;/h3&gt;
&lt;p&gt;书籍在线阅读地址：&lt;a href=&quot;https://jobrest.gitbooks.io/kali-linux-cn/content/index.html&quot;&gt;Kali Linux 渗透测试的艺术（中文版） | Kali Linux 渗透测试的艺术（中文版） (gitbooks.io)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;官网：&lt;a href=&quot;https://www.kali.org/&quot;&gt;Kali Linux 操作系统 |渗透测试和道德黑客 Linux 发行版&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;文档：&lt;a href=&quot;https://www.kali.org/docs/&quot;&gt;Kali Docs | Kali Linux Documentation&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;h3&gt;何为玩转 kali？&lt;/h3&gt;
&lt;p&gt;当然是想随心所欲，让“所见即所得”，让“所想立即实现”，那么此篇就是完成这个任务。&lt;/p&gt;
&lt;h2&gt;第 1 章 Kali Linux 入门&lt;/h2&gt;
&lt;h3&gt;1.1 Kali 的发展简史&lt;/h3&gt;
&lt;p&gt;Kali Linux（Kali）是专门用于渗透测试的 Linux 操作系统，它由 BackTrack 发展而来。在整合了 IWHAX、WHOPPIX 和 Auditor 这 3 种渗透测试专用 Live Linux 之后，BackTrack 正式改名为 Kali Linux。&lt;/p&gt;
&lt;p&gt;BackTrack 是相当著名的 Linux 发行版本。在 BackTrack 发布 4.0 预览版的时候，它的下载次数已经超过了 400 万次。&lt;/p&gt;
&lt;p&gt;Kali Linux 1.0 版于 2013 年 3 月 12 日问世。在 5 天之后，官方为修复 USB 键盘的支持问题而发布了 1.0.1 版。在这短短的 5 天之内，Kali 的下载次数就超过了 9 万次。&lt;/p&gt;
&lt;p&gt;根据官网的介绍（http://docs.kali.org/introduction/what-is-kali-linux）， Kali 的主要特色有：&lt;/p&gt;
&lt;p&gt;● 它是基于 Debian 的 Linux 发行版；&lt;/p&gt;
&lt;p&gt;● 它集成 300 多个渗透测试程序；&lt;/p&gt;
&lt;p&gt;● 它支持绝大多数的无线网卡；&lt;/p&gt;
&lt;p&gt;● 它修改了内核以支持（无线）数据包注入；&lt;/p&gt;
&lt;p&gt;● 所有的软件包都有研发团队的 PGP 签名；&lt;/p&gt;
&lt;p&gt;● 用户可以自制满足各自需求的 Kali Linux 发行版；&lt;/p&gt;
&lt;p&gt;● 支持基于 ARM 的硬件系统。&lt;/p&gt;
&lt;h3&gt;1.2 Kali Linux 工具包&lt;/h3&gt;
&lt;p&gt;Kali Linux 含有可用于渗透测试的各种工具。这些工具程序大体可以分为以下几类。&lt;/p&gt;
&lt;p&gt;● 信息收集：这类工具可用来收集目标的 DNS、IDS/IPS、网络扫描、操作系统、路由、SSL、SMB、VPN、VoIP、SNMP 信息和 E-mail 地址。&lt;/p&gt;
&lt;p&gt;● 漏洞评估：这类工具都可以扫描目标系统上的漏洞。部分工具可以检测 Cisco 网络系统缺陷，有些还可以评估各种数据库系统的安全问题。很多模糊测试软件都属于漏洞评估工具。&lt;/p&gt;
&lt;p&gt;● Web 应用：即与 Web 应用有关的工具。它包括 CMS（内容管理系统）扫描器、数据库漏洞利用程序、Web 应用模糊测试、Web 应用代理、Web 爬虫及 Web 漏洞扫描器。&lt;/p&gt;
&lt;p&gt;● 密码攻击：无论是在线攻击还是离线破解，只要是能够实施密码攻击的工具都属于密码攻击类工具。&lt;/p&gt;
&lt;p&gt;● 漏洞利用：这类工具可以利用在目标系统中发现的漏洞。攻击网络、Web 和数据库漏洞的软件，都属于漏洞利用（exploitation）工具。Kali 中的某些软件可以针对漏洞情况进行社会工程学攻击。&lt;/p&gt;
&lt;p&gt;● 网络监听：这类工具用于监听网络和 Web 流量。网络监听需要进行网络欺骗，所以 Ettercap 和 Yersinia 这类软件也归于这类软件。&lt;/p&gt;
&lt;p&gt;● 访问维护：这类工具帮助渗透人员维持他们对目标主机的访问权。某些情况下，渗透人员必须先获取主机的最高权限才能安装这类软件。这类软件包括用于在 Web 应用和操作系统安装后门的程序，以及隧道类工具。&lt;/p&gt;
&lt;p&gt;● 报告工具：如果您需要撰写渗透测试的报告文件，您应该用得上这些软件。&lt;/p&gt;
&lt;p&gt;● 系统服务：这是渗透人员在渗透测试时可能用到的常见服务类软件，它包括 Apache 服务、MySQL 服务、SSH 服务和 Metasploit 服务。&lt;/p&gt;
&lt;p&gt;为了降低渗透测试人员筛选工具的难度，Kali Linux 单独划分了一类软件——Top 10 Security Tools，即 10 大首选安全工具。这 10 大工具分别是 aircrack-ng、burp-suite、hydra、john、maltego、metasploit、nmap、sqlmap、wireshark 和 zaproxy。&lt;/p&gt;
&lt;p&gt;除了可用于渗透测试的各种工具以外，Kali Linux 还整合了以下几类工具。&lt;/p&gt;
&lt;p&gt;● 无线攻击：可攻击蓝牙、RFID/NFC 和其他无线设备的工具。&lt;/p&gt;
&lt;p&gt;● 逆向工程：可用于调试程序或反汇编的工具。&lt;/p&gt;
&lt;p&gt;● 压力测试：用于各类压力测试的工具集。它们可测试网络、无线、Web 和 VoIP 系统的负载能力。&lt;/p&gt;
&lt;p&gt;● 硬件破解：用于调试 Android 和 Arduino 程序的工具。&lt;/p&gt;
&lt;p&gt;● 法证调查：即电子取证的工具。它的各种工具可以用于制作硬盘磁盘镜像、文件分析、硬盘镜像分析。如需使用这类程序，首先要在启动菜单里选择 Kali Linux Forensics | No Drives or Swap Mount。在开启这个选项以后，Kali Linux 不会自动加载硬盘驱动器，以保护硬盘数据的完整性。&lt;/p&gt;
&lt;h3&gt;1.3 下载 Kali Linux&lt;/h3&gt;
&lt;p&gt;下载是第一步要做的事情，经常使用计算机的人对下载肯定不陌生，但要想面向大众还是要解释清楚。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;[x] 待补充&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[x] 验证哈希值&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;在虚拟机里使用 VM 镜像安装 Kali Linux&lt;/h5&gt;
&lt;p&gt;Kali Linux 的 iso 镜像和虚拟机 vmx 文件等在 Kali 官网都有，直接下载就可以，而且下载速度也不赖。&lt;/p&gt;
&lt;p&gt;这里我推荐直接下载 Kali 官方的虚拟机 vmx 文件，下载好了直接打开就能用，免去了很多配置环节。之前也尝试过用 iso 镜像做 VMware，但试了几次遇到很多麻烦，因此就不搞了。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;使用硬盘镜像方式安装 Kali Linux 之后，系统的默认设置值如下所示。&lt;/p&gt;
&lt;p&gt;● 硬盘容量：30 GB。&lt;/p&gt;
&lt;p&gt;● 联网方式：NAT。&lt;/p&gt;
&lt;p&gt;● 用户名：root。&lt;/p&gt;
&lt;p&gt;● 密码：toor。&lt;/p&gt;
&lt;p&gt;如果要把 Kali 当做渗透测试平台使用，应当避免以 NAT 方式接入网络。本文推荐您以桥接（bridged）方式联网。&lt;/p&gt;
&lt;p&gt;在配置 Kali VM 的时候，应当尽快更改默认密码。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;portable Kali Linux&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;[x] 未实操，待修改&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;安装 Kali Linux 的第三种方法，就是把它安装到 USB 闪存里。通常，人们把安装在闪存上的 Kali Linux 叫做 portable（便携）Kali Linux。按照 Kali 官方文件的说法，这种安装方式的启动和安装速度最快，是 Kali 研发人员最喜欢的安装方式。相比在硬盘上安装，只能在一台机器上启动 Kali 系统而言，装有 Kali Linux 的闪存盘可以在所有支持 USB 启动的主机上使用 Kali 系统。&lt;/p&gt;
&lt;p&gt;这种安装方法同样适合在内存卡（SSD、SDHC、SDXC 等）上安装 Kali Linux。&lt;/p&gt;
&lt;p&gt;很多工具都可以制作 portable Kali Linux。其中，Rufus（http://rufus.akeo.ie）就不错。这个工具只能在Windows操作系统下运行。&lt;/p&gt;
&lt;p&gt;其他可从 ISO 镜像文件制作可启动 USB 的工具如下所示：&lt;/p&gt;
&lt;p&gt;● Win32DiskImager（https://launchpad.net/win32-image-writer）；&lt;/p&gt;
&lt;p&gt;● Universal USB Installer（http://www.pendrivelinux.com/universal-usbinstaller-easy-as-1-2-3/）；&lt;/p&gt;
&lt;p&gt;● Linux Live USB Creator（http://www.linuxliveusb.com)&lt;/p&gt;
&lt;p&gt;在制作 portable Kali Linux 之前，您需要准备好几样素材。&lt;/p&gt;
&lt;p&gt;● Kali Linux 的 ISO 镜像文件：虽然您可以使用启动磁盘创建工具直接下载镜像文件，但是我们仍然认为提前下载好 ISO 镜像文件，再用 Rufus 使用镜像文件比较稳妥。&lt;/p&gt;
&lt;p&gt;● USB 闪存盘：您需要一个容量足够大的 USB 闪存盘。我们推荐您使用 16GB 以上的闪存盘。&lt;/p&gt;
&lt;p&gt;在下载 Rufus 之后，在 Windows 里双击 rufus.exe 文件就可以运行它。它会显示出程序界面。&lt;/p&gt;
&lt;p&gt;如果您使用的是基于 UNIX 的操作系统，您可以直接使用 dd 指令创建可启动闪存盘。例如：&lt;/p&gt;
&lt;p&gt;dd if=kali-linux-1.0.1-i386.iso of=/dev/sdb bs=512k&lt;/p&gt;
&lt;p&gt;此处的/dev/sdb 应当是您 USB 闪存盘的设备名称。&lt;/p&gt;
&lt;p&gt;使用 Rufus 创建可启动的 Kali USB 闪存盘的设置如下。&lt;/p&gt;
&lt;p&gt;● Device：选择 USB 闪存驱动器。本例中，它是 Windows 系统的 E 盘。&lt;/p&gt;
&lt;p&gt;● Partition scheme and target system type：设置为 MBR partition scheme for BIOS or UEFI computers。&lt;/p&gt;
&lt;p&gt;● Create a bootable disk using：设置为 ISO Image 并使用右侧磁盘图标选取 ISO 镜像文件。&lt;/p&gt;
&lt;p&gt;然后点击 Start 创建可启动闪存盘。&lt;/p&gt;
&lt;p&gt;在完成这些步骤之后，如果您想要立即测试 USB 闪存盘，则应在保存好所有文件的情况下重启计算机。您可能需要配置计算机的 BIOS，使其从 USB 磁盘启动计算机。如果没有问题的话，您应该可以通过 USB 闪存盘启动 Kali Linux 系统。&lt;/p&gt;
&lt;p&gt;在 USB 闪存盘上安装系统之后，如果您想要让系统能够保存您所更改的文件（即 persistence capabilities），您可参照 Kali 官方文档进行设置。请参见 Adding Persistence to Your Kali Live USB，地址为http://docs.kali.org/installation/kali-linux-live-usb-install&lt;/p&gt;
&lt;h3&gt;1.4 虚拟机配置&lt;/h3&gt;
&lt;h4&gt;1.4.1 设置中文&lt;/h4&gt;
&lt;p&gt;刚下好 kali linux，就被全英文劝退可不太好。一看到密密麻麻的英文就敲起了退堂鼓，所以首先把系统语言设置成中文。&lt;/p&gt;
&lt;p&gt;Learn more：&lt;a href=&quot;https://blog.csdn.net/KNIGH_YUN/article/details/105543094&quot;&gt;Kali Linux 系统设置中文语言环境-1_kali 中文设置-CSDN 博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;具体步骤：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo dpkg-reconfigure locales
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;选择语言，使用空格键选中。选择 en_US.UTF-8、zh_CN.GBK、zh_CN.UTF-8&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/kali-guide-03-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;选中 zh_CN.UTF-8 为默认语言。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/kali-guide-01-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt-get install ttf-wqy-microhei ttf-wqy-zenhei xfonts-wqy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下载字体&lt;/p&gt;
&lt;p&gt;重启虚拟机。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;设置中文界面&quot; /&gt;&lt;/p&gt;
&lt;p&gt;当然，以上方法并非唯一方法，如果安装了图形界面也可以直接在设置里修改语言（X-windows），或者通过 CLI 修改系统配置文件。&lt;/p&gt;
&lt;h4&gt;1.4.2 网络设置&lt;/h4&gt;
&lt;p&gt;1．配置有线网络&lt;/p&gt;
&lt;p&gt;无论是通过 VMware 磁盘镜像还是通过 ISO 镜像安装 Kali Linux，默认情况下 Kali Linux 接入网络的方式都是 NAT（网络地址转换）。在 NAT 方式下，Kali Linux 的虚拟机可以通过物理主机联入外部网络，而外部网络甚至是物理主机自身都无法直接访问安装有 Kali Linux 的虚拟机。&lt;/p&gt;
&lt;p&gt;进行实地的渗透测试时，您可能需要把网络结构变更为 Bridged Adapter。&lt;/p&gt;
&lt;p&gt;如需使用桥接连接，首先要使物理主机与网络设备连接，例如路由器或交换机。同时，接入的网络里应当有 DHCP 服务，以分配 IP 地址给虚拟机。&lt;/p&gt;
&lt;p&gt;您可能已经注意到了，通过 DHCP 获取的 IP 地址并不是固定的 IP 地址，这种 IP 地址在一定时间后可能会发生变化。如果 Kali Linux 通过 DHCP 获取 IP 地址，在超过固定周期（DHCP 的租赁时间）之后，DHCP 会重新给虚拟机分配一次 IP 地址。重新分配的 IP 地址可能和上次分配的 IP 地址相同，也可能不同。&lt;/p&gt;
&lt;p&gt;如果虚拟机需要使用固定的 IP 地址，应该修改虚拟机的网络设置文件/etc/network/interfaces。&lt;/p&gt;
&lt;p&gt;默认情况下，Kali Linux 的网络设置文件如下。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;auto lo

iface lo inet loopback
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个配置文件指定所有网卡都通过 DHCP 获取 IP 地址。如需为虚拟机绑定固定 IP 地址，就不得不对这个文件进行相应修改。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;auto eth0

iface eth0 inet static

address 10.0.2.15

netmask 255.255.255.0

network 10.0.2.0

broadcast 10.0.2.255

gateway 10.0.2.2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上述文件令第一个有线网卡 eth0 绑定了 IP 地址 10.0.2.15。您可能需要根据实际情况修改上述设置。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;手动设置网络：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;error：&lt;/p&gt;
&lt;p&gt;配置了域名服务器后导致 ping 其他网站都无法解析域名。&lt;/p&gt;
&lt;h4&gt;1.5.3 文件夹共享&lt;/h4&gt;
&lt;h4&gt;1.5.4 快照备份&lt;/h4&gt;
&lt;p&gt;一旦您把虚拟机配置到理想的可工作状态，我们建议您立刻对虚拟机进行快照备份。万一日后出现配置故障，可利用快照备份把虚拟机迅速恢复到正常工作状态。&lt;/p&gt;
&lt;h4&gt;1.5.5 导出虚拟机&lt;/h4&gt;
&lt;p&gt;人们时常需要以文件形式备份虚拟机，或通过这种方法把虚拟机分享给他人使用。VirtualBox 的虚拟机导出功能简化了这种操作。在关闭需要导出的虚拟机之后，在菜单栏选中 File | Export Appliance 就可导出所选的虚拟机。&lt;/p&gt;
&lt;h3&gt;1.5 系统更新&lt;/h3&gt;
&lt;p&gt;Kali Linux 由操作系统内核和数百个软件构成。如果需要使用软件的最新功能，您就需要将其更新到最新的版本。&lt;/p&gt;
&lt;p&gt;我们建议您仅从 Kali Linux 官方的软件仓库（repository）进行更新。&lt;/p&gt;
&lt;p&gt;在您安装和配置好 Kali Linux 之后，就应当立即进行系统更新。因为 Kali 是基于 Debian 的操作系统，您需要使用 Debian 的指令（apt-get）进行系统更新。&lt;/p&gt;
&lt;p&gt;更新指令 apt-get 会查询/etc/apt/sources.list 文件，从中获取更新服务器的信息。您需要确定这个文件指定了正确的升级服务器。&lt;/p&gt;
&lt;p&gt;默认情况下，Kali Linux 的 sources.list 文件包含下述信息。&lt;/p&gt;
&lt;h4&gt;其他：apt 更换国内源&lt;/h4&gt;
&lt;p&gt;Kali Linux 会从系统默认的软件源去安装软件，但默认的软件源速度通常比较慢（国外的源），因此需要将其替换成国内的源。&lt;/p&gt;
&lt;p&gt;以下是常用的国内源网站：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://opsx.alibaba.com/mirror?lang=zh-CN&quot;&gt;阿里云镜像开源镜像站（已经更换地址）&lt;/a&gt;
&lt;a href=&quot;https://developer.aliyun.com/mirror/&quot;&gt;阿里云镜像开源社区镜像站（新地址）&lt;/a&gt;
&lt;a href=&quot;http://mirrors.163.com/.help/ubuntu.html&quot;&gt;网易开源镜像站&lt;/a&gt;
&lt;a href=&quot;https://mirrors.tuna.tsinghua.edu.cn/help/ubuntu/&quot;&gt;清华大学开源镜像站&lt;/a&gt;
&lt;a href=&quot;https://mirrors.ustc.edu.cn/&quot;&gt;中科大开源镜像站&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;修改方式：apt 源的文件位置：/etc/apt/sources.list&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：是/etc/apt/sources.list，不是/etc/apt/source.list（非常重要）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;sudo cp /etc/apt/sources.list /etc/apt/sources.bak1
#第一个参数时拷贝的文件路径和文件名称,第二个是拷贝到(粘贴)的文件路径和文件名

sudo mousepad /etc/apt/sources.list
#用mousepad可以直接从系统粘贴板粘贴，比vim和vi更方便操作

# 官方源
# deb http://http.kali.org/kali kali-rolling main non-free contrib
# deb-src http://http.kali.org/kali kali-rolling main non-free contrib
#根据需要自己选一个，中科大的还可以
#中科大
deb http://mirrors.ustc.edu.cn/kali kali-rolling main non-free contrib
deb-src http://mirrors.ustc.edu.cn/kali kali-rolling main non-free contrib
#阿里云
#deb http://mirrors.aliyun.com/kali kali-rolling main non-free contrib
#deb-src http://mirrors.aliyun.com/kali kali-rolling main non-free contrib
#清华大学
#deb http://mirrors.tuna.tsinghua.edu.cn/kali kali-rolling main contrib non-free
#deb-src https://mirrors.tuna.tsinghua.edu.cn/kali kali-rolling main contrib non-free
#浙大
#deb http://mirrors.zju.edu.cn/kali kali-rolling main contrib non-free
#deb-src http://mirrors.zju.edu.cn/kali kali-rolling main contrib non-free
#东软大学
#deb http://mirrors.neusoft.edu.cn/kali kali-rolling/main non-free contrib
#deb-src http://mirrors.neusoft.edu.cn/kali kali-rolling/main non-free contrib
#重庆大学
#deb http://http.kali.org/kali kali-rolling main non-free contrib
#deb-src http://http.kali.org/kali kali-rolling main non-free contrib
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;注：vim 和 vi 的操作习惯很&quot;程序员风格&quot;，不熟悉的情况下很难使用，以下是一个 vim 操作的视频&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1YY4y1n75r/?vd_source=206a091077ff783d662b49e64cc9589d&quot;&gt;VIM 快速入门之常用命令解析 1,VIM 复制，粘贴，剪切，替换，查找_哔哩哔哩_bilibili&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;一个小插曲：我之前的 Kali 虚拟机就是因为 vim 操作不当修改了 root 文件，加上安装了很多包之后经常卡顿死机，所以重新装了一个新的，实在是血的教训。&lt;/p&gt;
&lt;p&gt;更新完之后：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt-get update 更新索引

apt-get upgrade 更新软件

apt-get dist-upgrade 升级

apt-get clean 删除缓存包

apt-get autoclean 删除未安装的deb包
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;error:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;目前网络配置有问题，解决后再写 xia&apos;wen&lt;/p&gt;
</content:encoded></item><item><title>用户权限</title><link>https://h1s97x.github.io/posts/notes/notes-%E7%94%A8%E6%88%B7%E6%9D%83%E9%99%90/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-%E7%94%A8%E6%88%B7%E6%9D%83%E9%99%90/</guid><description>Linux下普通用户变为root权限的三种方法19.如何从普通用户xiaoming切换到root?-CSDN博客 0x01加普通用户 useradd xiaom</description><pubDate>Sat, 27 Dec 2025 15:46:11 GMT</pubDate><content:encoded>&lt;h3&gt;linux下普通用户变为root权限的三种方法&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/weixin_43886632/article/details/108244302&quot;&gt;Linux下普通用户变为root权限的三种方法_19.如何从普通用户xiaoming切换到root?-CSDN博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0x01加普通用户&lt;/strong&gt;
useradd xiaoming 创建用户小明
passwd xiaoming 创建小明的密码&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0x02 将xiaoming的权限变为root权限&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法1&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;1、修改 /etc/sudoers 文件，找到107行，把前面的注释（#）去掉&lt;/p&gt;
&lt;p&gt;2、将xiaoming添加到root组（wheel）
命令：&lt;code&gt;usermod -g root xiaoming&lt;/code&gt;
之后xiaoming就能执行root的命令了，但是权限不是root还是xiaoming权限.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法2&lt;/strong&gt;
1、修改 /etc/sudoers 文件，找到下面一行，在root下面添加一行
内容：&lt;code&gt;xiaoming All=(All) All&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;修改完后。登录xiaoming。输入命令 &lt;code&gt;sudo -l&lt;/code&gt;，然后输入xiaoming的密码认证后使用sudo执行任意命令即可&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法3&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;修改 /etc/passwd 文件，找到xiaoming，把用户ID修改为 0&lt;/p&gt;
</content:encoded></item><item><title>重置密码</title><link>https://h1s97x.github.io/posts/notes/notes-%E9%87%8D%E7%BD%AE%E5%AF%86%E7%A0%81/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-%E9%87%8D%E7%BD%AE%E5%AF%86%E7%A0%81/</guid><description>--- 如果您忘记了 Ubuntu 上的用户密码，请不要担心。您可以非常轻松地重置 Ubuntu 密码。本指南介绍了在 Ubuntu 上更改 root 密码的简</description><pubDate>Sat, 27 Dec 2025 15:46:11 GMT</pubDate><content:encoded>&lt;h1&gt;如何在 2 分钟内重置忘记的 Ubuntu 密码&lt;/h1&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;如果您忘记了 Ubuntu 上的用户密码，请不要担心。您可以非常轻松地重置 Ubuntu 密码。本指南介绍了在 Ubuntu 上更改 root 密码的简单方法。该方法应该也适用于其他 Linux 发行版。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;忘记了 Ubuntu 登录密码？它发生了。&lt;/p&gt;
&lt;p&gt;如果您已经有一段时间没有使用 Ubuntu，那么很自然地不记得密码。好消息是您不需要因此而重新安装整个操作系统。您可以轻松恢复 Ubuntu 密码。&lt;/p&gt;
&lt;p&gt;这里提到的方法适用于在 VMware、双启动或单安装中重置 Ubuntu 密码。您所需要的只是一点耐心并运行几个命令。您将在几分钟内重置 root 密码。&lt;/p&gt;
&lt;p&gt;**在 &lt;a href=&quot;https://cn.a-d.site/?cat=linux&quot;&gt;WSL&lt;/a&gt; 上使用 Linux？**对于 &lt;a href=&quot;https://cn.a-d.site/?cat=linux&quot;&gt;WSL&lt;/a&gt; 来说有点不同。您可以按照此处在 &lt;a href=&quot;https://cn.a-d.site/?cat=linux&quot;&gt;WSL&lt;/a&gt; 中重置 Linux 密码的步骤进行操作。&lt;/p&gt;
&lt;h2&gt;从恢复模式重置 Ubuntu 密码&lt;/h2&gt;
&lt;p&gt;如果您更喜欢观看视频而不是阅读文字，我还制作了本教程的视频。不要忘记订阅我们的 YouTube 频道以获取更多 Linux 教程视频。&lt;/p&gt;
&lt;h3&gt;第 1 步：启动进入恢复模式&lt;/h3&gt;
&lt;p&gt;打开计算机。转到&lt;strong&gt;grub 菜单&lt;/strong&gt;。一般来说，它会自动出现 - 如果没有，则&lt;strong&gt;按住 Shift 键&lt;/strong&gt;或&lt;strong&gt;按 Esc 键&lt;/strong&gt;直到出现启动菜单。&lt;/p&gt;
&lt;p&gt;如果您使用的是 Oracle VirtualBox 或 VMware，当 Oracle 或 VMware 徽标出现时，您必须&lt;strong&gt;按住 Shift 键&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;在 grub 菜单中，选择“Ubuntu 的高级选项”：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cn.linux-console.net/common-images/how-to-hack-ubuntu-password/new-grub-menu-ubuntu.png&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在这里，您将看到进入恢复模式的选项：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cn.linux-console.net/common-images/how-to-hack-ubuntu-password/boot-into-recovery-mode-ubuntu.jpg&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;它将带您进入黑屏，并快速显示几行输出。在这里等待几秒钟。&lt;/p&gt;
&lt;h3&gt;第 2 步：转到 root shell 提示符&lt;/h3&gt;
&lt;p&gt;现在您将看到不同的恢复模式选项。这里您需要选择“&lt;strong&gt;Root – Drop to root shell 提示&lt;/strong&gt;”。只需按 Enter 键即可选择此选项。就像下图这样：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cn.linux-console.net/common-images/how-to-hack-ubuntu-password/drop-to-root-prompt-1.png&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;您会看到，当您选择 root shell 提示选项时，底部会出现一个输入命令的选项。这是您的 root shell 提示符，您将在此处使用命令来重置密码。&lt;/p&gt;
&lt;h3&gt;步骤 3：重新挂载具有写权限的 root&lt;/h3&gt;
&lt;p&gt;您需要具有根分区的写入权限。默认情况下，它具有只读访问权限。&lt;/p&gt;
&lt;p&gt;使用以下命令以写访问权限重新安装它：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mount -rw -o remount /
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;第 4 步：重置用户名或密码&lt;/h3&gt;
&lt;p&gt;在这里，您将获得 root 访问权限。使用以下命令列出所有可用的用户：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ls /home
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;根据此命令，选择您要重置或（例如）破解密码的“&lt;strong&gt;用户名&lt;/strong&gt;”。现在，使用以下命令重置所选“&lt;strong&gt;用户名&lt;/strong&gt;”的密码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;passwd username
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它提示输入新密码。输入新密码两次。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;*请注意，当您开始输入密码时，屏幕上不会显示任何内容。这是完全正常的*&lt;/strong&gt;，实际上是 Linux 系统中的一个安全功能。只需盲目输入密码并按 Enter 键即可。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;Enter new UNIX password:
Retype new UNIX password:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;瞧！就这样吧。您刚刚成功重置了密码。现在退出 root shell 提示符：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;exit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;退出后，您将返回恢复模式菜单。此处选择正常启动选项。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cn.linux-console.net/common-images/how-to-hack-ubuntu-password/recovery-mode-menu-ubuntu.png&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;将会出现有关图形模式兼容性的警告。不用担心。完全重新启动将解决与此相关的任何问题。&lt;/p&gt;
&lt;p&gt;您现在应该可以使用新密码登录。&lt;/p&gt;
&lt;h3&gt;可能的故障排除：&lt;/h3&gt;
&lt;p&gt;输入新密码时，系统可能会提示您&lt;strong&gt;身份验证令牌操作错误&lt;/strong&gt;，如下所示：&lt;/p&gt;
&lt;p&gt;passwd 用户名 输入新的 Unix 密码： 重新输入新的 Unix 密码： passwd：身份验证令牌操作错误 passwd：密码未更改&lt;/p&gt;
&lt;p&gt;出现此错误的原因是文件系统仅以读访问权限挂载。按以下方式更改访问权限并重新挂载文件系统：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mount -rw -o remount /
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在再次尝试重置密码。现在应该可以了。&lt;/p&gt;
&lt;p&gt;正如您所看到的，即使您忘记了 Ubuntu 密码，更改它也非常容易。几乎不需要几分钟。&lt;/p&gt;
&lt;h2&gt;重置 Ubuntu 密码的替代方法&lt;/h2&gt;
&lt;p&gt;如果由于某种原因您难以进入 root shell 并更改密码，您可以尝试这些步骤。&lt;/p&gt;
&lt;h3&gt;步骤 1&lt;/h3&gt;
&lt;p&gt;重新启动计算机。按住 Shift 键调出 grub 屏幕（如果它没有自动出现）。在 grub 提示符处&lt;strong&gt;按 E&lt;/strong&gt; 编辑 grub 屏幕。&lt;/p&gt;
&lt;h3&gt;第 2 步&lt;/h3&gt;
&lt;p&gt;找到以 Linux 开头的行，将 ro 更改为 rw 并在该行末尾附加 init=/bin/Bash 。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cn.linux-console.net/common-images/how-to-hack-ubuntu-password/editing-grub-to-fix-nvidia-issue.jpg&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;步骤 3&lt;/h3&gt;
&lt;p&gt;按 ctrl-x 保存更改并启动。&lt;/p&gt;
&lt;p&gt;现在，您将使用读写权限启动 Linux 内核，并且不再进入图形用户界面，而是使用 Bash shell。&lt;/p&gt;
&lt;p&gt;换句话说，您的系统将启动到无密码 root shell。&lt;/p&gt;
&lt;h3&gt;步骤 4&lt;/h3&gt;
&lt;p&gt;使用您的用户名输入 passwd 命令。如果您不知道用户名，请使用 &lt;em&gt;ls /home&lt;/em&gt; 命令进行检查（如方法 1 中所述）。&lt;/p&gt;
&lt;p&gt;设置您的新密码。&lt;/p&gt;
&lt;p&gt;设置新密码后，退出终端。只需在终端中输入 &lt;strong&gt;reboot&lt;/strong&gt; 或使用 shutdown 命令即可。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;shutdown -r now
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;您的密码现在应该更改。&lt;/p&gt;
&lt;h2&gt;如果重置 Ubuntu 密码这么简单，这不是一个安全风险吗？&lt;/h2&gt;
&lt;p&gt;这是一个公平的问题。 Linux 相对于 Windows 的主要优势之一是其安全性。但如果“任何人”都可以重置密码，为什么 Ubuntu 或其他 Linux 发行版可以被认为是安全的呢？&lt;/p&gt;
&lt;p&gt;让我在这里解释一些事情。主要的安全风险是有人通过互联网从远程位置侵入您的帐户。这并没有发生在这里。&lt;/p&gt;
&lt;p&gt;如果任何人能够物理访问您的计算机，那么您计算机中的数据就已经面临风险。除非整个磁盘都经过加密，否则任何人都可以使用实时 USB“窃取”您的数据，甚至无需进入您安装的操作系统。&lt;/p&gt;
&lt;p&gt;默认情况下，Ubuntu 没有 root 密码，并且 root 帐户被锁定。您在安装 Ubuntu 时设置的用户帐户包含为具有 sudo 权限的管理员。但这并不意味着你就是 root。&lt;/p&gt;
&lt;p&gt;缺少 root 密码是一个故意的功能启动菜单中的“Ubuntu 高级选项”允许您从“root shell 提示符”执行一些特定的与 root 相关的任务。这就是为什么您可以通过此方法重置 Ubuntu 密码。&lt;/p&gt;
&lt;p&gt;如果你想让你的系统更安全，你可以加密磁盘（以保存你的数据）或在 Ubuntu 中为 root 用户设置密码。&lt;/p&gt;
&lt;h2&gt;额外提示：处理更改密码后可能出现的密钥环问题&lt;/h2&gt;
&lt;p&gt;Ubuntu 中有一个密钥环功能，用于保持密码锁定和安全。&lt;/p&gt;
&lt;p&gt;当您重置忘记的密码时，密钥环保持解锁状态，您可能会看到类似这样的错误消息。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cn.linux-console.net/common-images/how-to-hack-ubuntu-password/enter-password-to-unlock-your-login-keyring-ubuntu.jpg&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;打开“密码和密钥”应用程序，然后在此处删除登录密码。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cn.linux-console.net/common-images/how-to-hack-ubuntu-password/delete-keyring-ubuntu.jpg&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;当您尝试在 Ubuntu 中再次使用 Google Chrome 时，它会要求您创建一个新的密钥环。使用新的登录密码作为密钥环密码。&lt;/p&gt;
&lt;p&gt;我希望我已经清楚地说明了如何在 Ubuntu 中重置忘记的密码。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cn.linux-console.net/?p=19579&quot;&gt;如何在 2 分钟内重置忘记的 Ubuntu 密码 (linux-console.net)&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>魂3贴吧</title><link>https://h1s97x.github.io/posts/notes/notes-%E9%AD%823%E8%B4%B4%E5%90%A7/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-%E9%AD%823%E8%B4%B4%E5%90%A7/</guid><description>回复：【GIF图】黑暗之魂系列搞笑GIF黑暗之魂吧百度贴吧 (baidu.com) 求黑魂各种表情包【黑暗之魂3吧】百度贴吧 (baidu.com) 回复：发一</description><pubDate>Sat, 27 Dec 2025 15:46:11 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://tieba.baidu.com/p/4756827868?pn=6&quot;&gt;回复：【GIF图】黑暗之魂系列搞笑GIF~_黑暗之魂吧_百度贴吧 (baidu.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://tieba.baidu.com/p/5780866552&quot;&gt;求黑魂各种表情包【黑暗之魂3吧】_百度贴吧 (baidu.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://tieba.baidu.com/p/7329003572?pn=4&quot;&gt;回复：发一些我收集到的黑魂表情包/动图_黑暗之魂3吧_百度贴吧 (baidu.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://tieba.baidu.com/p/7440038203&quot;&gt;【图片】黑魂. gif【黑暗之魂3吧】_百度贴吧 (baidu.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://tieba.baidu.com/p/6008787581&quot;&gt;黑魂梗图，搞笑图集锦_黑暗之魂3吧_百度贴吧 (baidu.com)&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>魂学</title><link>https://h1s97x.github.io/posts/notes/notes-%E9%AD%82%E5%AD%A6/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-%E9%AD%82%E5%AD%A6/</guid><description>【魂学】我们真的了解《黑暗之魂》的故事吗？ 机核 GCORES “世界还未分化，笼罩在大雾之中，四处都是灰色的岩石，高耸的大树以及不朽古龙。” !img 这里要</description><pubDate>Sat, 27 Dec 2025 15:46:11 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.gcores.com/articles/111702&quot;&gt;【魂学】我们真的了解《黑暗之魂》的故事吗？ | 机核 GCORES&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;远古时代&lt;/h3&gt;
&lt;p&gt;“&lt;strong&gt;世界还未分化，笼罩在大雾之中，四处都是灰色的岩石，高耸的大树以及不朽古龙。&lt;/strong&gt;”&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/3b0219c0-c973-4aff-a9f1-c97dcb2840c3.jpg&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这里要注意一个很重要的色彩——&lt;strong&gt;灰色&lt;/strong&gt;。灰色代表世界的混沌与未完成，这个颜色会在一直在前后贯穿，所以大家一定要记住。而文本里说的岩石与大树成为游戏里重要的两个物质，岩石代表着武器升级的楔形石圆盘，大树代表着使用魔法所必须的法杖。而龙不仅仅承担了剧情上的交代，龙死亡后的获得的龙鳞也基本上可以增强武器。&lt;/p&gt;
&lt;h3&gt;火之初始与古龙战争时代&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;之后有一天燃起了第一团火，所有的差异因此而生，冷与热，生命与死亡，光明与黑暗。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/6b57ecc8-ddac-4de7-854e-68e3f8a6e747.jpg&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这时我们并不知晓这团火为何突然出现了，如果单纯分析《黑暗之魂》是无法找到答案的，直到我玩通了《黑暗之魂3》才有了答案，我们之后会说。因为这团火的升起，世界产生了差异，而这个差异，是一种对比，这种对比成了这个游戏里另外的两个重要的色调，&lt;strong&gt;光与暗&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这个色调是一个模糊的色调，这个世界可以有很多种光，也可以有很多种暗，这也衍生出来游戏里的多种信仰，虽然各不相同，但大抵上都是光与暗。比如属于光的白教，太阳战士，属于暗的深渊，幽邃。&lt;/p&gt;
&lt;p&gt;“&lt;strong&gt;然后，有几只从黑暗里诞生的物种，受到火焰引诱，并在火的周围找到了王的灵魂。&lt;/strong&gt;”&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/c11cf057-ab4c-42b1-a31d-a4096c445aa8.jpg&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我需要强调的是，这些物种里，不仅仅是有最初的死者尼特、伊扎里斯魔女和混沌的女儿、葛温与他的骑士，以及没人看到过的小人（汉语与英语都是矮人），还有各种各样的物种，或者我们换一个说法，这些物种根据获得的王的灵魂的多少而不同，而外形上他们都是人形态。&lt;/p&gt;
&lt;p&gt;无论是尼特（死后也是人形态的骷髅），魔女还是葛温以及从《黑暗之魂3》的环印城里我们都能知道拥有黑暗灵魂的小人。这些物种都是人形态。&lt;/p&gt;
&lt;p&gt;这是否是一种暗示，我们在下面的内容里会进行说明。注意这里日语里明明白白说的是&lt;strong&gt;小人（日语：しょうじん）&lt;/strong&gt;，而小人在日语里的是&lt;strong&gt;身份地位，力量弱小&lt;/strong&gt;的意思，我们从动画里每个人手里的火焰含量也能判断出来，虽然小人也获得了王的灵魂，但是它获得的却是最弱的灵魂。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/67a9d450-d8f7-4372-b7fe-3c39447aaa3e.jpg&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;当然如果大家不接受这种解释，可以把矮人看成一个物种，只要不等同于人类就好。因为在黑魂的世界里，物种的分别是根据灵魂的的样式，例如人的灵魂是白色的，而神的灵魂则多是跳跃着的火焰。在《黑暗之魂3》世界发生变异后，灵魂的样式则变得更加明显了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/b15bee47-f423-41df-9302-e07d85968742.png&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/af12278f-edee-43e1-8da6-924592e95974.png&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;此时便直接衍生出来了两个问题，**一个是小人去哪里了？第二个是王是谁？为什么他的灵魂会在火焰周围。**这两个问题，我们会在后面揭晓。&lt;/p&gt;
&lt;p&gt;新的物种们获得了力量，便挑战这个世界已经存在久远的不朽古龙们。这一场古龙与拥有了最初王的灵魂的物种战争里，龙族由于无鳞的白龙背叛而败了下来。&lt;/p&gt;
&lt;p&gt;于是龙族成为了被狩猎的对象，而获胜的物种们开启了新的时代。我们可以在亚诺尔隆德的一个储藏间里看到大量的龙头，这就代表着，获胜方对龙族的炫耀式的屠杀。龙族从世间高贵的物种变为被狩猎的物种。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;火的时代终于揭开了帷幕。&lt;strong&gt;而这些开创火的时代的人，几乎都被人类称为了&lt;/strong&gt;神&lt;/strong&gt;。人与这些物种是分开的，我们可以从游戏里的各个方面看到相关的信息。例如从道具金币则直接描述出来：“&lt;strong&gt;在人世，金币虽然价值连城，但是在以灵魂作为货币的罗德兰，则是毫无意义。&lt;/strong&gt;”&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/54aefe29-bab5-4a67-b81f-83dab30b9d18.png&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这条信息不光印证了，人有自己的世界，人的世界虽然依附于神的世界，但是与神的世界是不等同的。而我们操纵的不死人期间发生的故事，并非在人世间，而是在神的土地，古代诸王之地——&lt;strong&gt;罗德兰。&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;火之时代&lt;/h3&gt;
&lt;p&gt;火之昌盛的时代，诸神的城市——亚诺尔隆德建立了起来，许多神都留在了这里。这时一共发生了几件大事：&lt;/p&gt;
&lt;p&gt;1.白龙希斯背叛古龙后，被葛温赐予了公爵的称号，自此白龙便一直留在了大书库，为自己制造不死的方法。直到许多年后它利用原始结晶终于制作出了属于自己的不死。而白龙希斯的研究流传到人世间后，形成了主要的魔法分支之一。&lt;/p&gt;
&lt;p&gt;之后大帽子罗根来到了大书库，并在此处见识到了神的智慧，并被这些智慧所迷惑。虽然流传了许多魔法给后世，但是他最终也成为了游魂，迷失了自我。&lt;/p&gt;
&lt;p&gt;2.伊扎里斯的魔女，想要自己再复制一次初始之火，无论原因为何，是因为自己的欲望膨胀还是想要保持火不再熄灭。最后的结果就是，火焰反过来吞噬了伊扎里斯与她的许多儿女们。&lt;/p&gt;
&lt;p&gt;正如咒术混沌风暴里所说的一般：“&lt;strong&gt;吞噬伊扎里斯魔女及其女儿的混沌火焰，魔女试图创造出【初始之火】的野心，因而诞生出异形生命的温床，并孕育出混沌火焰。&lt;/strong&gt;”&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/0816ddeb-3519-4ba3-b383-cc3ea1b0eafa.png&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;而这里的异形生命的温床我们都知道就是后来的——&lt;strong&gt;恶魔族&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;魔女成为了恶魔的温床，而留在了她身边的儿女们则遭受了混沌的侵蚀。只有一个女儿——克拉娜逃了出去，并教授了一个徒弟“扎拉曼”，而这个扎拉曼成为了他人口中的咒术之王，并成为在大沼传播咒术的始祖之一。&lt;/p&gt;
&lt;p&gt;我们从在传火祭祀场里的咒术师里的对话里知道，咒术是取自咒术师身上的火焰，因此咒术必须是有师承的，徒弟要得到师父身上的火焰，来使用。&lt;/p&gt;
&lt;p&gt;这个咒术的创造者之一，也就是从废都逃走的魔女的女儿——&lt;strong&gt;克拉娜&lt;/strong&gt;，而她创造的咒术居然与母亲创造混沌火焰的方式是一样的。这或许就是一种无法逃脱的命运吧。&lt;/p&gt;
&lt;p&gt;3.葛温一族与尼特等诸神根据自身的故事创造出了奇迹。正如奇迹恢复里的言语：“&lt;strong&gt;所谓奇迹，便是学习诸神的故事，再藉由祈祷，蒙受恩惠的技能&lt;/strong&gt;。”&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/5def85d4-6d68-4e62-9a7e-14279cdefbfc.png&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;而奇迹的创造则直接造成了人类多神论的信仰，因此也为最后，诸神的分离与陨落埋下了伏笔。&lt;/p&gt;
&lt;h3&gt;深渊的出现&lt;/h3&gt;
&lt;p&gt;在火之昌盛的时代，龙族已经几近被消灭殆尽了，但是一个新的威胁——深渊，产生了。古代的魔法王国乌拉席露或许是第一个遭受深渊侵蚀的国家。在魔法黑暗弹里明确地描述了：“&lt;strong&gt;乌拉席露魔法师在疯狂中发现的深渊魔法。&lt;/strong&gt;”&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/24d4dc44-637f-4a8a-968b-3dd4348b29db.png&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;而我们在《黑暗之魂》的 DLC 里，看到了被深渊侵蚀的乌拉席露究竟是什么样子的。而且也看到了葛温王下的四骑士之一——深渊漫步者——亚尔特留斯讨伐深渊失败，反而自己成为了被深渊吞噬之人。&lt;/p&gt;
&lt;p&gt;在这个 DLC 中黑魂的世界里第一次给我们展现出来了未来的不死人挽救了过去的古王国——乌拉席露的行为。而这个可以穿越的行为，将在后面的故事描述里有着极其重要的含义。在击败亚尔特留斯的高台旁我们看到了另一个葛温王的四骑士之一——鹰眼戈夫。&lt;/p&gt;
&lt;p&gt;跟他对话的话，他会告诉我们：“&lt;strong&gt;狡兔死了，自然就不需要走狗了，没被煮成盘中餐就是万幸了。&lt;/strong&gt;”&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/e835e18c-97d3-4b25-8dbe-b550a181ba8c.jpg&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我们知晓了，这个时候必然是在与古龙战争结束之后，而葛温并不信任他的骑士们。从人脸面具里可以得到：“&lt;strong&gt;被囚禁的戈夫持续雕刻出来的古木人脸。&lt;/strong&gt;”&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/git/raw/main/images/_legacy/b29e92bb-4e74-4552-b19e-5931ab3481ef.png&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可以知道，他是关起来的。从这些行为里隐约可以感觉出来，葛温或许非是一个好的国王，他的长子的离开，必然有某些特殊的原因。虽然深渊之主马努斯死亡了，并没有阻挡深渊继续产生，另一个古王国——小隆德也被深渊侵蚀了。&lt;/p&gt;
&lt;p&gt;这个时候，阿尔特留斯已经死亡。深渊并没有被消除，小隆德被三个红袍的封印者通过水漫的方式来进行了封印。&lt;/p&gt;
&lt;p&gt;我们打开封印后，看到水流排泄而去，在水流的城底可以看出来堆积如山的骷髅，我们不知道这些人究竟经历了什么，在他们面临死亡前会有什么哀嚎，在他们的眼前或许只有要么在恐惧中死去，要么成为与深渊为伍的吸魂鬼。&lt;/p&gt;
&lt;h3&gt;火之将息与诸神离开的时代&lt;/h3&gt;
&lt;h3&gt;传火时代&lt;/h3&gt;
&lt;h3&gt;传薪时代&lt;/h3&gt;
&lt;h3&gt;余灰时代&lt;/h3&gt;
&lt;p&gt;待续-&lt;/p&gt;
</content:encoded></item><item><title>75. 颜色分类</title><link>https://h1s97x.github.io/posts/coding/coding-75-sort-colors/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-75-sort-colors/</guid><pubDate>Mon, 19 May 2025 16:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;75. 颜色分类&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给定一个包含红色、白色和蓝色、共 &lt;code&gt;n&lt;/code&gt; 个元素的数组 &lt;code&gt;nums&lt;/code&gt;，&lt;strong&gt;原地&lt;/strong&gt;对它们进行排序，使得相同颜色的元素相邻，并按照红色、白色、蓝色顺序排列。&lt;/p&gt;
&lt;p&gt;我们使用整数 &lt;code&gt;0&lt;/code&gt;、&lt;code&gt;1&lt;/code&gt; 和 &lt;code&gt;2&lt;/code&gt; 分别表示红色、白色和蓝色。&lt;/p&gt;
&lt;p&gt;必须在不使用库内置的 &lt;code&gt;sort&lt;/code&gt; 函数的情况下解决这个问题。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [2,0,2,1,1,0]
输出：[0,0,1,1,2,2]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [2,0,1]
输出：[0,1,2]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;n == nums.length&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= n &amp;lt;= 300&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nums[i]&lt;/code&gt; 为 &lt;code&gt;0&lt;/code&gt;、&lt;code&gt;1&lt;/code&gt; 或 &lt;code&gt;2&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;数组只含 &lt;code&gt;0/1/2&lt;/code&gt; 三种值。我们希望从左到右形成三段：&lt;code&gt;[0 段][1 段][2 段]&lt;/code&gt;。用三个指针维护分界：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;low&lt;/code&gt;：指向下一个 &lt;code&gt;0&lt;/code&gt; 该放的位置（&lt;code&gt;0&lt;/code&gt; 段的右边界后一位）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;high&lt;/code&gt;：指向下一个 &lt;code&gt;2&lt;/code&gt; 该放的位置（&lt;code&gt;2&lt;/code&gt; 段的左边界前一位）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mid&lt;/code&gt;：当前扫描位置。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;0&lt;/code&gt; 段在 &lt;code&gt;[0, low)&lt;/code&gt;，&lt;code&gt;1&lt;/code&gt; 段在 &lt;code&gt;[low, mid)&lt;/code&gt;（其实 mid 到 high 之间是未扫描区），&lt;code&gt;2&lt;/code&gt; 段在 &lt;code&gt;(high, n-1]&lt;/code&gt;。当 &lt;code&gt;mid&lt;/code&gt; 扫过 &lt;code&gt;high&lt;/code&gt; 时结束。&lt;/p&gt;
&lt;p&gt;这是一个经典的 &lt;strong&gt;「荷兰国旗问题」&lt;/strong&gt;，本质是带两个交换锚点的单遍扫描，时间 O(n)、空间 O(1)。&lt;/p&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;初始化 &lt;code&gt;low = 0&lt;/code&gt;、&lt;code&gt;mid = 0&lt;/code&gt;、&lt;code&gt;high = n - 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;循环直到 &lt;code&gt;mid &amp;gt; high&lt;/code&gt;：
&lt;ul&gt;
&lt;li&gt;若 &lt;code&gt;nums[mid] == 0&lt;/code&gt;：把它和 &lt;code&gt;nums[low]&lt;/code&gt; 交换，然后 &lt;code&gt;low += 1&lt;/code&gt;、&lt;code&gt;mid += 1&lt;/code&gt;（左边稳定的 0 段扩大）。&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;nums[mid] == 2&lt;/code&gt;：把它和 &lt;code&gt;nums[high]&lt;/code&gt; 交换，然后 &lt;code&gt;high -= 1&lt;/code&gt;（右边稳定的 2 段扩大）；&lt;strong&gt;不移动 &lt;code&gt;mid&lt;/code&gt;&lt;/strong&gt;，因为换回来的值还没处理。&lt;/li&gt;
&lt;li&gt;否则（&lt;code&gt;== 1&lt;/code&gt;）：&lt;code&gt;mid += 1&lt;/code&gt; 直接跳过。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;nums = [2, 0, 2, 1, 1, 0]&lt;/code&gt;（n=6）为例：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;轮次&lt;/th&gt;
&lt;th&gt;low&lt;/th&gt;
&lt;th&gt;mid&lt;/th&gt;
&lt;th&gt;high&lt;/th&gt;
&lt;th&gt;数组&lt;/th&gt;
&lt;th&gt;动作&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;初始&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;[2,0,2,1,1,0]&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;[&lt;strong&gt;0&lt;/strong&gt;,0,2,1,1,&lt;strong&gt;2&lt;/strong&gt;]&lt;/td&gt;
&lt;td&gt;nums[0]=2 ↔ nums[5]，high→4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;[0,0,2,1,1,2]&lt;/td&gt;
&lt;td&gt;nums[0]=0 ↔ nums[0]，low→1, mid→1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;[0,0,2,1,1,2]&lt;/td&gt;
&lt;td&gt;nums[1]=0 ↔ nums[1]，low→2, mid→2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;[0,0,&lt;strong&gt;1&lt;/strong&gt;,1,&lt;strong&gt;2&lt;/strong&gt;,2]&lt;/td&gt;
&lt;td&gt;nums[2]=2 ↔ nums[4]，high→3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;[0,0,1,1,2,2]&lt;/td&gt;
&lt;td&gt;nums[2]=1，mid→3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;[0,0,1,1,2,2]&lt;/td&gt;
&lt;td&gt;nums[3]=1，mid→4，结束&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最终 &lt;code&gt;[0,0,1,1,2,2]&lt;/code&gt; ✓&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，&lt;code&gt;mid&lt;/code&gt; 每轮至少前进一步。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)，只用了常数额外空间，原地交换。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from typing import List

class Solution:
    def sortColors(self, nums: List[int]) -&amp;gt; None:
        &quot;&quot;&quot;Do not return anything, modify nums in-place instead.&quot;&quot;&quot;
        n = len(nums)
        low, mid, high = 0, 0, n - 1
        while mid &amp;lt;= high:
            if nums[mid] == 0:
                nums[low], nums[mid] = nums[mid], nums[low]
                low += 1
                mid += 1
            elif nums[mid] == 2:
                nums[mid], nums[high] = nums[high], nums[mid]
                high -= 1
            else:
                mid += 1
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))

import unittest
from solution import Solution


class TestSolution(unittest.TestCase):

    def test_basic(self):
        nums = [2, 0, 2, 1, 1, 0]
        Solution().sortColors(nums)
        self.assertEqual(nums, [0, 0, 1, 1, 2, 2])

    def test_three(self):
        nums = [2, 0, 1]
        Solution().sortColors(nums)
        self.assertEqual(nums, [0, 1, 2])

    def test_already_sorted(self):
        nums = [0, 1, 2]
        Solution().sortColors(nums)
        self.assertEqual(nums, [0, 1, 2])

    def test_single(self):
        nums = [0]
        Solution().sortColors(nums)
        self.assertEqual(nums, [0])


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2235. 两整数相加</title><link>https://h1s97x.github.io/posts/coding/coding-01-basic-syntax/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-01-basic-syntax/</guid><description>给定两个整数 num1 和 num2，返回这两个整数的和</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;给你两个整数 &lt;code&gt;num1&lt;/code&gt; 和 &lt;code&gt;num2&lt;/code&gt;，返回这两个整数的和。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/add-two-integers/description/&quot;&gt;https://leetcode.cn/problems/add-two-integers/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：num1 = 12, num2 = 5
输出：17&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：num1 = -10, num2 = 4
输出：-6&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;约束：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-100 &amp;lt;= num1, num2 &amp;lt;= 100&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;p&gt;最基础的加法运算，直接返回 &lt;code&gt;num1 + num2&lt;/code&gt; 即可。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def sum(self, num1: int, num2: int) -&amp;gt; int:
        return num1 + num2
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestAddTwoIntegers(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_positive(self):
        self.assertEqual(self.solution.sum(12, 5), 17)

    def test_negative(self):
        self.assertEqual(self.solution.sum(-10, 4), -6)

    def test_zero(self):
        self.assertEqual(self.solution.sum(0, 0), 0)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>1. 两数之和</title><link>https://h1s97x.github.io/posts/coding/coding-1-two-sum/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1-two-sum/</guid><description>给定整数数组 nums 和目标值 target，找出和为目标值的两个整数的下标</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;给定一个整数数组 &lt;code&gt;nums&lt;/code&gt; 和一个整数目标值 &lt;code&gt;target&lt;/code&gt;，请你在该数组中找出 &lt;strong&gt;和为目标值&lt;/strong&gt; &lt;em&gt;&lt;code&gt;target&lt;/code&gt;&lt;/em&gt; 的那 &lt;strong&gt;两个&lt;/strong&gt; 整数，并返回它们的数组下标。&lt;/p&gt;
&lt;p&gt;你可以假设每种输入只会对应一个答案，并且你不能使用两次相同的元素。&lt;/p&gt;
&lt;p&gt;你可以按任意顺序返回答案。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/two-sum/description/&quot;&gt;https://leetcode.cn/problems/two-sum/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：nums = [2,7,11,15], target = 9
输出：[0,1]
解释：因为 nums[0] + nums[1] == 9，返回 [0, 1]。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：nums = [3,2,4], target = 6
输出：[1,2]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 3：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：nums = [3,3], target = 6
输出：[0,1]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;约束：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;2 &amp;lt;= nums.length &amp;lt;= 10⁴&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-10⁹ &amp;lt;= nums[i] &amp;lt;= 10⁹&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-10⁹ &amp;lt;= target &amp;lt;= 10⁹&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;只会存在一个有效答案&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;进阶：&lt;/strong&gt; 你可以想出一个时间复杂度小于 &lt;code&gt;O(n²)&lt;/code&gt; 的算法吗？&lt;/p&gt;
&lt;h2&gt;Test Cases&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def twoSum(self, nums: List[int], target: int) -&amp;gt; List[int]:
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import pytest

from solution import Solution
from solution2 import Solution as Solution2


@pytest.mark.parametrize(&apos;nums, target, expected&apos;, [
    ([2,7,11,15], 9, [0, 1]),
    ([3,2,4], 6, [1, 2]),
    ([3,3], 6, [0, 1]),
])
class Test:
    def test_solution(self, nums, target, expected):
        sol = Solution()
        result = sol.twoSum(nums, target)
        assert sorted(result) == sorted(expected)

    def test_solution2(self, nums, target, expected):
        sol = Solution2()
        result = sol.twoSum(nums, target)
        assert sorted(result) == sorted(expected)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Thoughts&lt;/h2&gt;
&lt;p&gt;一方面是要找到那两个相加等于目标值的数，另一方面需要能记录到这两数的原始索引下标。&lt;/p&gt;
&lt;p&gt;用 &lt;code&gt;O(n log n)&lt;/code&gt; 时间对数组排序。&lt;/p&gt;
&lt;p&gt;从最小的数字开始遍历，直到 &lt;code&gt;target / 2&lt;/code&gt;（包含）。&lt;/p&gt;
&lt;p&gt;对于一个数字 v，用二分法，在后半数组中查找 target - v。二分查找是 &lt;code&gt;O(log n)&lt;/code&gt;，总共也是 &lt;code&gt;O(n log n)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;需要记录数字在排序前的数组下标。&lt;/p&gt;
&lt;h2&gt;Code&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def twoSum(self, nums: List[int], target: int) -&amp;gt; List[int]:
        n = len(nums)
        indices = list(range(n))
        indices.sort(key=lambda i: nums[i])

        for i, p1 in enumerate(indices):
            v2 = target - nums[p1]
            p2 = self._bin_find(nums, indices, v2, i + 1)
            if p2 is not None:
                return [p1, p2]

        return []

    def _bin_find(self, nums: List[int], indices: List[int], value: int, left: int) -&amp;gt; int | None:
        l = left
        if l &amp;gt;= len(indices) or (t := nums[indices[l]]) &amp;gt; value:
            return None
        elif t == value:
            return indices[l]

        r = len(indices) - 1
        if (t := nums[indices[r]]) &amp;lt; value:
            return None
        elif t == value:
            return indices[r]

        while l &amp;lt;= r:
            m = (l + r) &amp;gt;&amp;gt; 1
            if (t := nums[indices[m]]) == value:
                return indices[m]
            elif t &amp;lt; value:
                l = m + 1
            else:
                r = m - 1

        return None
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;快一些&lt;/h2&gt;
&lt;p&gt;可以利用哈希表 &lt;code&gt;O(1)&lt;/code&gt; 查询时间的特点，把所有数字放进哈希表，然后直接利用哈希表查找 target - v 是否存在。&lt;/p&gt;
&lt;p&gt;因为题目限定了有唯一解，所以不用管重复的数字。唯一需要考虑的是，当 target 是偶数时，可能有两个 &lt;code&gt;target / 2&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;时间复杂度为 &lt;code&gt;O(n)&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def twoSum(self, nums: List[int], target: int) -&amp;gt; List[int]:
        indices = {}
        half = target &amp;gt;&amp;gt; 1 if target &amp;amp; 1 == 0 else None
        for i, v in enumerate(nums):
            # Special treatment of `target / 2` when target is even.
            if half is not None and v == half and v in indices:
                return [indices[v], i]

            indices[v] = i

        for i, v1 in enumerate(nums):
            if half is not None and v1 == half:
                continue

            v2 = target - v1
            if v2 in indices:
                return i, indices[v2]

        return []
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>1281. 整数的各位积和之差</title><link>https://h1s97x.github.io/posts/coding/coding-1281-subtract-product-and-sum/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1281-subtract-product-and-sum/</guid><description>计算整数各位数字的乘积与和的差</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;给你一个整数 &lt;code&gt;n&lt;/code&gt;，请你帮忙计算并返回该整数「各位数字之积」与「各位数字之和」的差。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/subtract-the-product-and-sum-of-digits-of-an-integer/description/&quot;&gt;https://leetcode.cn/problems/subtract-the-product-and-sum-of-digits-of-an-integer/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：n = 234
输出：15
解释：各位数之积 = 2 * 3 * 4 = 24，各位数之和 = 2 + 3 + 4 = 9，24 - 9 = 15&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：n = 4421
输出：21
解释：各位数之积 = 4 * 4 * 2 * 1 = 32，各位数之和 = 4 + 4 + 2 + 1 = 11，32 - 11 = 21&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;约束：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= n &amp;lt;= 10⁵&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;p&gt;循环取模 &lt;code&gt;n % 10&lt;/code&gt; 得到最后一位，累加/累乘后 &lt;code&gt;n //= 10&lt;/code&gt; 去掉最后一位。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def subtractProductAndSum(self, n: int) -&amp;gt; int:
        product = 1
        total = 0
        while n:
            digit = n % 10
            product *= digit
            total += digit
            n //= 10
        return product - total
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestSubtractProductAndSum(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.subtractProductAndSum(234), 15)

    def test_example2(self):
        self.assertEqual(self.solution.subtractProductAndSum(4421), 21)

    def test_single(self):
        self.assertEqual(self.solution.subtractProductAndSum(5), 0)

    def test_all_ones(self):
        self.assertEqual(self.solution.subtractProductAndSum(111), -1)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>1422. 分割字符串的最大得分</title><link>https://h1s97x.github.io/posts/coding/coding-1422-max-score-split/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1422-max-score-split/</guid><description>将字符串分割成左右两部分，左0右1的得分最大化</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;给你一个由若干 0 和 1 组成的字符串 &lt;code&gt;s&lt;/code&gt;，请你计算并返回将该字符串分割成两个 &lt;strong&gt;非空&lt;/strong&gt; 子字符串（即 &lt;strong&gt;左&lt;/strong&gt; 子字符串和 &lt;strong&gt;右&lt;/strong&gt; 子字符串）所能获得的最大得分。&lt;/p&gt;
&lt;p&gt;「分割字符串的得分」为：&lt;strong&gt;左&lt;/strong&gt; 子字符串中 &lt;strong&gt;0&lt;/strong&gt; 的数量 + &lt;strong&gt;右&lt;/strong&gt; 子字符串中 &lt;strong&gt;1&lt;/strong&gt; 的数量。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/maximum-score-after-splitting-a-string/description/&quot;&gt;https://leetcode.cn/problems/maximum-score-after-splitting-a-string/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：s = &quot;011101&quot;
输出：5
解释：所有可能的分割为：
左 = &quot;0&quot; 右 = &quot;11101&quot;，得分 = 1 + 4 = 5
左 = &quot;01&quot; 右 = &quot;1101&quot;，得分 = 1 + 3 = 4
左 = &quot;011&quot; 右 = &quot;101&quot;，得分 = 1 + 2 = 3
左 = &quot;0111&quot; 右 = &quot;01&quot;，得分 = 1 + 1 = 2
左 = &quot;01110&quot; 右 = &quot;1&quot;，得分 = 2 + 1 = 3&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：s = &quot;00111&quot;
输出：5
解释：左 = &quot;00&quot; 右 = &quot;111&quot;，得分 = 2 + 3 = 5&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;约束：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;2 &amp;lt;= s.length &amp;lt;= 500&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s&lt;/code&gt; 仅由字符 &lt;code&gt;&apos;0&apos;&lt;/code&gt; 和 &lt;code&gt;&apos;1&apos;&lt;/code&gt; 组成&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;p&gt;先统计总 1 的数量，然后遍历分割点，左边 0 递增，右边 1 递减，取最大值。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def maxScore(self, s: str) -&amp;gt; int:
        total_ones = s.count(&apos;1&apos;)
        zeros = 0
        ones = total_ones
        max_score = 0
        for i in range(len(s) - 1):
            if s[i] == &apos;0&apos;:
                zeros += 1
            else:
                ones -= 1
            max_score = max(max_score, zeros + ones)
        return max_score
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestMaxScore(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.maxScore(&quot;011101&quot;), 5)

    def test_example2(self):
        self.assertEqual(self.solution.maxScore(&quot;00111&quot;), 5)

    def test_all_zeros(self):
        self.assertEqual(self.solution.maxScore(&quot;00&quot;), 1)

    def test_all_ones(self):
        self.assertEqual(self.solution.maxScore(&quot;11&quot;), 1)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>1470. 重新排列数组</title><link>https://h1s97x.github.io/posts/coding/coding-1470-shuffle-the-array/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1470-shuffle-the-array/</guid><description>按 [x1,y1,x2,y2,...,xn,yn] 顺序重排数组</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;给你一个数组 &lt;code&gt;nums&lt;/code&gt;，数组中有 &lt;code&gt;2n&lt;/code&gt; 个元素，按 &lt;code&gt;[x1,x2,...,xn,y1,y2,...,yn]&lt;/code&gt; 的格式排列。&lt;/p&gt;
&lt;p&gt;请你将数组按 &lt;code&gt;[x1,y1,x2,y2,...,xn,yn]&lt;/code&gt; 格式重新排列，返回重排后的数组。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/shuffle-the-array/description/&quot;&gt;https://leetcode.cn/problems/shuffle-the-array/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：nums = [2,5,1,3,4,7], n = 3
输出：[2,3,5,4,1,7]
解释：由于 x1=2, x2=5, x3=1, y1=3, y2=4, y3=7，所以答案为 [2,3,5,4,1,7]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：nums = [1,2,3,4,4,3,2,1], n = 4
输出：[1,4,2,3,3,2,4,1]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;约束：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= n &amp;lt;= 500&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nums.length == 2n&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums[i] &amp;lt;= 10³&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;p&gt;遍历 &lt;code&gt;i&lt;/code&gt; 从 0 到 n-1，依次取 &lt;code&gt;nums[i]&lt;/code&gt; 和 &lt;code&gt;nums[i + n]&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def shuffle(self, nums: list[int], n: int) -&amp;gt; list[int]:
        result = []
        for i in range(n):
            result.append(nums[i])
            result.append(nums[i + n])
        return result
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestShuffle(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.shuffle([2, 5, 1, 3, 4, 7], 3), [2, 3, 5, 4, 1, 7])

    def test_example2(self):
        self.assertEqual(self.solution.shuffle([1, 2, 3, 4, 4, 3, 2, 1], 4), [1, 4, 2, 3, 3, 2, 4, 1])

    def test_single(self):
        self.assertEqual(self.solution.shuffle([1, 2], 1), [1, 2])


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>1486. 数组异或操作</title><link>https://h1s97x.github.io/posts/coding/coding-1486-xor-operation-in-an-array/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1486-xor-operation-in-an-array/</guid><description>从 0 开始，对每个 i 计算 nums[i] = start + 2 * i，返回所有元素的异或结果</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;给你两个整数 &lt;code&gt;n&lt;/code&gt; 和 &lt;code&gt;start&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;数组 &lt;code&gt;nums&lt;/code&gt; 定义为：&lt;code&gt;nums[i] = start + 2 * i&lt;/code&gt;（下标从 0 开始），长度为 &lt;code&gt;n&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;请返回 &lt;code&gt;nums&lt;/code&gt; 中所有元素按位异或（XOR）后得到的结果。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/xor-operation-in-an-array/description/&quot;&gt;https://leetcode.cn/problems/xor-operation-in-an-array/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：n = 5, start = 0
输出：8
解释：nums = [0, 2, 4, 6, 8]，0 ^ 2 ^ 4 ^ 6 ^ 8 = 8&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：n = 4, start = 3
输出：8
解释：nums = [3, 5, 7, 9]，3 ^ 5 ^ 7 ^ 9 = 8&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;约束：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= n &amp;lt;= 1000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= start &amp;lt;= 1000&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;p&gt;循环生成每个元素并累加异或即可。也可以用数学公式优化，但 n 很小，直接循环最清晰。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def xorOperation(self, n: int, start: int) -&amp;gt; int:
        result = 0
        for i in range(n):
            result ^= start + 2 * i
        return result
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestXorOperation(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.xorOperation(5, 0), 8)

    def test_example2(self):
        self.assertEqual(self.solution.xorOperation(4, 3), 8)

    def test_single(self):
        self.assertEqual(self.solution.xorOperation(1, 7), 7)

    def test_large(self):
        self.assertEqual(self.solution.xorOperation(10, 5), 2)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>1512. 好数对的数目</title><link>https://h1s97x.github.io/posts/coding/coding-1512-number-of-good-pairs/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1512-number-of-good-pairs/</guid><description>统计数组中满足 i &lt; j 且 nums[i] == nums[j] 的数对数目</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;给你一个整数数组 &lt;code&gt;nums&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;如果一组数字 &lt;code&gt;(i, j)&lt;/code&gt; 满足 &lt;code&gt;nums[i] == nums[j]&lt;/code&gt; 且 &lt;code&gt;i &amp;lt; j&lt;/code&gt;，就认为这是一组 &lt;strong&gt;好数对&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;返回好数对的数目。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/number-of-good-pairs/description/&quot;&gt;https://leetcode.cn/problems/number-of-good-pairs/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：nums = [1,2,3,1,1,3]
输出：4
解释：有 4 组好数对：(0,3), (0,4), (3,4), (2,5)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：nums = [1,1,1,1]
输出：6
解释：数组中的每组数字都是好数对&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;约束：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 100&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums[i] &amp;lt;= 100&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;p&gt;双重循环统计即可。也可以先用哈希表统计每个数字出现次数，每个数字贡献 &lt;code&gt;c * (c-1) / 2&lt;/code&gt; 对。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from collections import Counter


class Solution:
    def numIdenticalPairs(self, nums: list[int]) -&amp;gt; int:
        count = Counter(nums)
        return sum(c * (c - 1) // 2 for c in count.values())
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestNumIdenticalPairs(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.numIdenticalPairs([1, 2, 3, 1, 1, 3]), 4)

    def test_example2(self):
        self.assertEqual(self.solution.numIdenticalPairs([1, 1, 1, 1]), 6)

    def test_no_pairs(self):
        self.assertEqual(self.solution.numIdenticalPairs([1, 2, 3]), 0)

    def test_single(self):
        self.assertEqual(self.solution.numIdenticalPairs([1]), 0)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>1534. 统计好三元组</title><link>https://h1s97x.github.io/posts/coding/coding-1534-count-good-triplets/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1534-count-good-triplets/</guid><description>统计满足 |a-b| &lt;= a, |b-c| &lt;= b, |a-c| &lt;= c 的三元组数目</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;给你一个整数数组 &lt;code&gt;arr&lt;/code&gt;，以及三个整数 &lt;code&gt;a&lt;/code&gt;、&lt;code&gt;b&lt;/code&gt;、&lt;code&gt;c&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;请统计其中好三元组的数量。&lt;/p&gt;
&lt;p&gt;如果三元组 &lt;code&gt;(arr[i], arr[j], arr[k])&lt;/code&gt; 满足下列全部条件，则认为它是一个 &lt;strong&gt;好三元组&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= i &amp;lt; j &amp;lt; k &amp;lt; arr.length&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;|arr[i] - arr[j]| &amp;lt;= a&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;|arr[j] - arr[k]| &amp;lt;= b&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;|arr[i] - arr[k]| &amp;lt;= c&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/count-good-triplets/description/&quot;&gt;https://leetcode.cn/problems/count-good-triplets/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：arr = [3,0,1,1,9,7], a = 7, b = 2, c = 3
输出：4
解释：一共有 4 个好三元组：(3,0,1), (3,0,1), (3,1,1), (0,1,1)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：arr = [1,1,2,2,3], a = 0, b = 0, c = 1
输出：0&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;约束：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;3 &amp;lt;= arr.length &amp;lt;= 100&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= arr[i] &amp;lt;= 1000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= a, b, c &amp;lt;= 1000&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;p&gt;三重循环遍历所有 &lt;code&gt;i &amp;lt; j &amp;lt; k&lt;/code&gt; 组合，逐一判断条件。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def countGoodTriplets(self, arr: list[int], a: int, b: int, c: int) -&amp;gt; int:
        n = len(arr)
        count = 0
        for i in range(n - 2):
            for j in range(i + 1, n - 1):
                if abs(arr[i] - arr[j]) &amp;gt; a:
                    continue
                for k in range(j + 1, n):
                    if abs(arr[j] - arr[k]) &amp;lt;= b and abs(arr[i] - arr[k]) &amp;lt;= c:
                        count += 1
        return count
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestCountGoodTriplets(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.countGoodTriplets([3, 0, 1, 1, 9, 7], 7, 2, 3), 4)

    def test_example2(self):
        self.assertEqual(self.solution.countGoodTriplets([1, 1, 2, 2, 3], 0, 0, 1), 0)

    def test_all_valid(self):
        self.assertEqual(self.solution.countGoodTriplets([1, 1, 1], 0, 0, 0), 1)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>1757. 可回收且低脂的产品</title><link>https://h1s97x.github.io/posts/coding/coding-1757-recyclable-and-low-fat-products/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-1757-recyclable-and-low-fat-products/</guid><description>查询既是低脂又可回收的产品</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;表：&lt;code&gt;Products&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;+-------------+---------+
| Column Name | Type    |
+-------------+---------+
| product_id  | int     |
| low_fats    | varchar |
| recyclable  | varchar |
+-------------+---------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;product_id&lt;/code&gt; 是该表的主键。&lt;code&gt;low_fats&lt;/code&gt; 和 &lt;code&gt;recyclable&lt;/code&gt; 的取值为 &lt;code&gt;&apos;Y&apos;&lt;/code&gt; 或 &lt;code&gt;&apos;N&apos;&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;找出既是低脂又是可回收的产品 ID。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/recyclable-and-low-fat-products/description/&quot;&gt;https://leetcode.cn/problems/recyclable-and-low-fat-products/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;输入：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;| product_id | low_fats | recyclable |
|------------|----------|------------|
| 0          | Y        | N          |
| 1          | Y        | Y          |
| 2          | N        | Y          |
| 3          | Y        | Y          |
| 4          | N        | N          |
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;| product_id |
|------------|
| 1          |
| 3          |
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;p&gt;简单的 &lt;code&gt;WHERE&lt;/code&gt; 条件筛选。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;```sql
SELECT product_id
FROM Products
WHERE low_fats = &apos;Y&apos; AND recyclable = &apos;Y&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;
```sql
-- 测试数据
CREATE TABLE Products (
    product_id INT PRIMARY KEY,
    low_fats VARCHAR(1),
    recyclable VARCHAR(1)
);

INSERT INTO Products VALUES
(0, &apos;Y&apos;, &apos;N&apos;),
(1, &apos;Y&apos;, &apos;Y&apos;),
(2, &apos;N&apos;, &apos;Y&apos;),
(3, &apos;Y&apos;, &apos;Y&apos;),
(4, &apos;N&apos;, &apos;N&apos;);

-- 预期结果: 1, 3
SELECT product_id FROM Products WHERE low_fats = &apos;Y&apos; AND recyclable = &apos;Y&apos;;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2. 两数相加</title><link>https://h1s97x.github.io/posts/coding/coding-2-add-two-numbers/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2-add-two-numbers/</guid><description>给你两个非空链表表示两个非负整数，每位数字逆序存储，将两数相加并以相同形式返回</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;给你两个 &lt;strong&gt;非空&lt;/strong&gt; 的链表，表示两个非负的整数。它们每位数字都是按照 &lt;strong&gt;逆序&lt;/strong&gt; 的方式存储的，并且每个节点只能存储 &lt;strong&gt;一位&lt;/strong&gt; 数字。&lt;/p&gt;
&lt;p&gt;请你将两个数相加，并以相同形式返回一个表示和的链表。&lt;/p&gt;
&lt;p&gt;你可以假设除了数字 0 之外，这两个数都不会以 0 开头。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/add-two-numbers/description/&quot;&gt;https://leetcode.cn/problems/add-two-numbers/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2021/01/02/addtwonumber1.jpg&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：l1 = [2,4,3], l2 = [5,6,4]
输出：[7,0,8]
解释：342 + 465 = 807.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：l1 = [0], l2 = [0]
输出：[0]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 3：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出：[8,9,9,9,0,0,0,1]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;约束：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个链表中的节点数在范围 &lt;code&gt;[1, 100]&lt;/code&gt; 内&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= Node.val &amp;lt;= 9&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;题目数据保证列表表示的数字不含前导零&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;p&gt;两数相加本质上就是模拟竖式加法：从低位到高位逐位相加，记录进位。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  2 4 3
+ 5 6 4
--------
  7 0 8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为是逆序存储，链表头正好是最低位，可以直接从 head 开始遍历，与竖式加法从左到右的顺序一致。&lt;/p&gt;
&lt;h3&gt;解法 1：迭代&lt;/h3&gt;
&lt;p&gt;用 dummy head 简化边界处理，一个指针 &lt;code&gt;curr&lt;/code&gt; 逐位构建新链表，一个变量 &lt;code&gt;carry&lt;/code&gt; 记录进位。&lt;/p&gt;
&lt;p&gt;关键点：两个链表长度可能不同，且最后可能多出一位进位（如 5+5=10）。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -&amp;gt; ListNode:
        dummy = ListNode(0)
        curr = dummy
        carry = 0

        while l1 or l2 or carry:
            v1 = l1.val if l1 else 0
            v2 = l2.val if l2 else 0
            total = v1 + v2 + carry
            carry = total // 10
            curr.next = ListNode(total % 10)
            curr = curr.next
            l1 = l1.next if l1 else None
            l2 = l2.next if l2 else None

        return dummy.next
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;解法 2：递归&lt;/h3&gt;
&lt;p&gt;本质与迭代相同，但把&quot;逐位处理&quot;的逻辑用递归表达。每次递归处理一位，将进位传给下一层递归。&lt;/p&gt;
&lt;p&gt;递归终止条件：两个链表都遍历完且无进位。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode, carry: int = 0) -&amp;gt; ListNode:
        if not l1 and not l2 and not carry:
            return None

        v1 = l1.val if l1 else 0
        v2 = l2.val if l2 else 0
        total = v1 + v2 + carry

        node = ListNode(total % 10)
        node.next = self.addTwoNumbers(
            l1.next if l1 else None,
            l2.next if l2 else None,
            total // 10
        )
        return node
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;测试&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next


def list_to_linked(arr):
    dummy = ListNode(0)
    curr = dummy
    for v in arr:
        curr.next = ListNode(v)
        curr = curr.next
    return dummy.next


def linked_to_list(node):
    result = []
    while node:
        result.append(node.val)
        node = node.next
    return result


class TestAddTwoNumbers(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        l1 = list_to_linked([2, 4, 3])
        l2 = list_to_linked([5, 6, 4])
        result = self.solution.addTwoNumbers(l1, l2)
        self.assertEqual(linked_to_list(result), [7, 0, 8])

    def test_example2(self):
        l1 = list_to_linked([0])
        l2 = list_to_linked([0])
        result = self.solution.addTwoNumbers(l1, l2)
        self.assertEqual(linked_to_list(result), [0])

    def test_example3(self):
        l1 = list_to_linked([9, 9, 9, 9, 9, 9, 9])
        l2 = list_to_linked([9, 9, 9, 9])
        result = self.solution.addTwoNumbers(l1, l2)
        self.assertEqual(linked_to_list(result), [8, 9, 9, 9, 0, 0, 0, 1])

    def test_carry(self):
        &quot;&quot;&quot;5 + 5 = 10, 应进位&quot;&quot;&quot;
        l1 = list_to_linked([5])
        l2 = list_to_linked([5])
        result = self.solution.addTwoNumbers(l1, l2)
        self.assertEqual(linked_to_list(result), [0, 1])

    def test_diff_length(self):
        &quot;&quot;&quot;不同长度的链表&quot;&quot;&quot;
        l1 = list_to_linked([1, 8])
        l2 = list_to_linked([0])
        result = self.solution.addTwoNumbers(l1, l2)
        self.assertEqual(linked_to_list(result), [1, 8])


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2062. 统计范围内的元音字符串数</title><link>https://h1s97x.github.io/posts/coding/coding-2062-count-vowel-substrings/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2062-count-vowel-substrings/</guid><description>统计字符串数组中首尾都是元音字母的字符串个数</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;给你一个由 &lt;code&gt;n&lt;/code&gt; 个字符串组成的字符串数组 &lt;code&gt;words&lt;/code&gt;，以及一个左闭右开区间 &lt;code&gt;[left, right)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;请你统计并返回在该区间内，下标 &lt;code&gt;i&lt;/code&gt; 满足 &lt;code&gt;left &amp;lt;= i &amp;lt; right&lt;/code&gt; 的字符串中，那些 &lt;strong&gt;首尾字符都是元音字母&lt;/strong&gt; 的字符串的数目。&lt;/p&gt;
&lt;p&gt;元音字母：&lt;code&gt;a&lt;/code&gt;, &lt;code&gt;e&lt;/code&gt;, &lt;code&gt;i&lt;/code&gt;, &lt;code&gt;o&lt;/code&gt;, &lt;code&gt;u&lt;/code&gt;（不区分大小写）。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/count-vowel-substrings-of-a-string/description/&quot;&gt;https://leetcode.cn/problems/count-vowel-substrings-of-a-string/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：words = [&quot;are&quot;,&quot;amy&quot;,&quot;u&quot;], left = 0, right = 2
输出：2
解释：在 [0, 2) 范围内：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&quot;are&quot; 首尾都是元音&lt;/li&gt;
&lt;li&gt;&quot;amy&quot; 首不是元音，尾是元音&lt;/li&gt;
&lt;li&gt;&quot;u&quot; 首尾都是元音
因此返回 2。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：words = [&quot;hey&quot;,&quot;aeo&quot;,&quot;mu&quot;,&quot;ooo&quot;,&quot;artro&quot;], left = 1, right = 4
输出：3
解释：在 [1, 4) 范围内：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&quot;aeo&quot; 首尾都是元音&lt;/li&gt;
&lt;li&gt;&quot;mu&quot; 首尾都不是元音&lt;/li&gt;
&lt;li&gt;&quot;ooo&quot; 首尾都是元音
因此返回 3。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;约束：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= words.length &amp;lt;= 1000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= words[i].length &amp;lt;= 10&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= left &amp;lt; right &amp;lt;= words.length&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;p&gt;遍历区间内的每个字符串，检查首尾字符是否在元音集合中。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def vowelStrings(self, words: list[str], left: int, right: int) -&amp;gt; int:
        vowels = set(&apos;aeiou&apos;)
        count = 0
        for i in range(left, right):
            w = words[i]
            if w[0] in vowels and w[-1] in vowels:
                count += 1
        return count
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestVowelStrings(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.vowelStrings([&quot;are&quot;, &quot;amy&quot;, &quot;u&quot;], 0, 2), 2)

    def test_example2(self):
        self.assertEqual(self.solution.vowelStrings([&quot;hey&quot;, &quot;aeo&quot;, &quot;mu&quot;, &quot;ooo&quot;, &quot;artro&quot;], 1, 4), 2)

    def test_none(self):
        self.assertEqual(self.solution.vowelStrings([&quot;bc&quot;, &quot;df&quot;], 0, 2), 0)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2235. 两整数相加</title><link>https://h1s97x.github.io/posts/coding/coding-2235-add-two-integers/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2235-add-two-integers/</guid><description>给定两个整数 num1 和 num2，返回这两个整数的和</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;给你两个整数 &lt;code&gt;num1&lt;/code&gt; 和 &lt;code&gt;num2&lt;/code&gt;，返回这两个整数的和。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/add-two-integers/description/&quot;&gt;https://leetcode.cn/problems/add-two-integers/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：num1 = 12, num2 = 5
输出：17&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：num1 = -10, num2 = 4
输出：-6&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;约束：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-100 &amp;lt;= num1, num2 &amp;lt;= 100&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;p&gt;最基础的加法运算，直接返回 &lt;code&gt;num1 + num2&lt;/code&gt; 即可。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def sum(self, num1: int, num2: int) -&amp;gt; int:
        return num1 + num2
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestAddTwoIntegers(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_positive(self):
        self.assertEqual(self.solution.sum(12, 5), 17)

    def test_negative(self):
        self.assertEqual(self.solution.sum(-10, 4), -6)

    def test_zero(self):
        self.assertEqual(self.solution.sum(0, 0), 0)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2236. 判断根结点是否等于子结点之和</title><link>https://h1s97x.github.io/posts/coding/coding-2236-root-equals-sum-of-children/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2236-root-equals-sum-of-children/</guid><description>判断二叉树根结点值是否等于左右子结点值之和</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;给你一个二叉树根结点，判断该根结点是否等于它的左右子结点之和。&lt;/p&gt;
&lt;p&gt;树只包含根结点、左子结点和右子结点。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/root-equals-sum-of-children/description/&quot;&gt;https://leetcode.cn/problems/root-equals-sum-of-children/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：root = [10, 4, 6]
输出：true
解释：10 == 4 + 6，返回 true。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：root = [5, 3, 1]
输出：false
解释：5 != 3 + 1，返回 false。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;约束：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;树只包含 1 到 3 个结点&lt;/li&gt;
&lt;li&gt;每个结点值在 &lt;code&gt;[0, 100]&lt;/code&gt; 范围内&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;p&gt;直接判断 &lt;code&gt;root.val == root.left.val + root.right.val&lt;/code&gt;。注意题目保证左右子结点都存在。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution:
    def checkTree(self, root: TreeNode) -&amp;gt; bool:
        return root.val == root.left.val + root.right.val
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right


class TestCheckTree(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_equal(self):
        root = TreeNode(10, TreeNode(4), TreeNode(6))
        self.assertTrue(self.solution.checkTree(root))

    def test_not_equal(self):
        root = TreeNode(5, TreeNode(3), TreeNode(1))
        self.assertFalse(self.solution.checkTree(root))

    def test_zero(self):
        root = TreeNode(0, TreeNode(0), TreeNode(0))
        self.assertTrue(self.solution.checkTree(root))


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>231. 2 的幂</title><link>https://h1s97x.github.io/posts/coding/coding-231-power-of-two/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-231-power-of-two/</guid><description>判断一个整数是否为 2 的幂</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;给你一个整数 &lt;code&gt;n&lt;/code&gt;，请你判断该整数是否是 2 的幂次方。如果是，返回 &lt;code&gt;true&lt;/code&gt;；否则，返回 &lt;code&gt;false&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;如果存在一个整数 &lt;code&gt;x&lt;/code&gt; 使得 &lt;code&gt;n == 2ˣ&lt;/code&gt;，则认为 &lt;code&gt;n&lt;/code&gt; 是 2 的幂次方。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/power-of-two/description/&quot;&gt;https://leetcode.cn/problems/power-of-two/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：n = 1
输出：true
解释：2⁰ = 1&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：n = 16
输出：true
解释：2⁴ = 16&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 3：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：n = 3
输出：false&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;约束：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-2³¹ &amp;lt;= n &amp;lt;= 2³¹ - 1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;p&gt;2 的幂在二进制表示中只有一个 1。利用 &lt;code&gt;n &amp;amp; (n - 1)&lt;/code&gt; 可以清除最低位的 1，如果结果为 0 说明只有一个 1。&lt;/p&gt;
&lt;p&gt;注意 &lt;code&gt;n &amp;lt;= 0&lt;/code&gt; 的情况，2 的幂一定是正数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def isPowerOfTwo(self, n: int) -&amp;gt; bool:
        return n &amp;gt; 0 and (n &amp;amp; (n - 1)) == 0
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestIsPowerOfTwo(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_one(self):
        self.assertTrue(self.solution.isPowerOfTwo(1))

    def test_sixteen(self):
        self.assertTrue(self.solution.isPowerOfTwo(16))

    def test_not_power(self):
        self.assertFalse(self.solution.isPowerOfTwo(3))

    def test_zero(self):
        self.assertFalse(self.solution.isPowerOfTwo(0))

    def test_negative(self):
        self.assertFalse(self.solution.isPowerOfTwo(-16))


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2413. 最小偶倍数</title><link>https://h1s97x.github.io/posts/coding/coding-2413-smallest-even-multiple/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2413-smallest-even-multiple/</guid><description>给你一个正整数 n，返回 2 和 n 的最小公倍数</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;给你一个正整数 &lt;code&gt;n&lt;/code&gt; ，返回 &lt;code&gt;2&lt;/code&gt; 和 &lt;code&gt;n&lt;/code&gt; 的最小公倍数（正整数）。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/smallest-even-multiple/description/&quot;&gt;https://leetcode.cn/problems/smallest-even-multiple/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：n = 5
输出：10
解释：5 和 2 的最小公倍数是 10。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：n = 6
输出：6
解释：6 和 2 的最小公倍数是 6。注意数字会是它自身的倍数。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;约束：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= n &amp;lt;= 150&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;p&gt;2 和 n 的最小公倍数：如果 n 是偶数，最小公倍数就是 n 本身；否则是 2 * n。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def smallestEvenMultiple(self, n: int) -&amp;gt; int:
        return n if n % 2 == 0 else n * 2
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestSmallestEvenMultiple(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_odd(self):
        self.assertEqual(self.solution.smallestEvenMultiple(5), 10)

    def test_even(self):
        self.assertEqual(self.solution.smallestEvenMultiple(6), 6)

    def test_one(self):
        self.assertEqual(self.solution.smallestEvenMultiple(1), 2)

    def test_two(self):
        self.assertEqual(self.solution.smallestEvenMultiple(2), 2)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2469. 温度转换</title><link>https://h1s97x.github.io/posts/coding/coding-2469-convert-the-temperature/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-2469-convert-the-temperature/</guid><description>将摄氏度转换为开氏度和华氏度，以数组形式返回</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;给你一个四舍五入到两位小数的非负浮点数 &lt;code&gt;celsius&lt;/code&gt; 来表示温度，以 &lt;strong&gt;摄氏度&lt;/strong&gt;（Celsius）为单位。&lt;/p&gt;
&lt;p&gt;你需要将摄氏度转换为 &lt;strong&gt;开氏度&lt;/strong&gt;（Kelvin）和 &lt;strong&gt;华氏度&lt;/strong&gt;（Fahrenheit），并以数组 &lt;code&gt;ans = [kelvin, fahrenheit]&lt;/code&gt; 的形式返回结果。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/convert-the-temperature/description/&quot;&gt;https://leetcode.cn/problems/convert-the-temperature/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;公式：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;开氏度 = 摄氏度 + 273.15&lt;/li&gt;
&lt;li&gt;华氏度 = 摄氏度 * 1.80 + 32.00&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：celsius = 36.50
输出：[309.65000, 97.70000]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：celsius = 122.11
输出：[395.26000, 251.79800]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;约束：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= celsius &amp;lt;= 1000&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;p&gt;直接套公式计算，注意返回浮点数数组即可。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def convertTemperature(self, celsius: float) -&amp;gt; list[float]:
        kelvin = celsius + 273.15
        fahrenheit = celsius * 1.80 + 32.00
        return [kelvin, fahrenheit]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestConvertTemperature(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        result = self.solution.convertTemperature(36.50)
        self.assertAlmostEqual(result[0], 309.65000)
        self.assertAlmostEqual(result[1], 97.70000)

    def test_example2(self):
        result = self.solution.convertTemperature(122.11)
        self.assertAlmostEqual(result[0], 395.26000)
        self.assertAlmostEqual(result[1], 251.79800)

    def test_zero(self):
        result = self.solution.convertTemperature(0)
        self.assertAlmostEqual(result[0], 273.15)
        self.assertAlmostEqual(result[1], 32.00)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>258. 各位相加</title><link>https://h1s97x.github.io/posts/coding/coding-258-add-digits/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-258-add-digits/</guid><description>反复将数字的各位相加，直到结果为一位数</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;给定一个非负整数 &lt;code&gt;num&lt;/code&gt;，反复将各个位上的数字相加，直到结果为一位数。返回这个结果。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/add-digits/description/&quot;&gt;https://leetcode.cn/problems/add-digits/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：num = 38
输出：2
解释：3 + 8 = 11, 1 + 1 = 2。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：num = 0
输出：0&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;约束：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= num &amp;lt;= 2³¹ - 1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;h3&gt;解法 1：模拟&lt;/h3&gt;
&lt;p&gt;循环取各位数字之和，直到结果为一位数。&lt;/p&gt;
&lt;h3&gt;解法 2：数学公式（数根）&lt;/h3&gt;
&lt;p&gt;数根（digital root）公式：&lt;code&gt;1 + (num - 1) % 9&lt;/code&gt;，当 num 为 0 时结果为 0。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def addDigits(self, num: int) -&amp;gt; int:
        if num == 0:
            return 0
        return 1 + (num - 1) % 9
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestAddDigits(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.addDigits(38), 2)

    def test_zero(self):
        self.assertEqual(self.solution.addDigits(0), 0)

    def test_single(self):
        self.assertEqual(self.solution.addDigits(5), 5)

    def test_large(self):
        self.assertEqual(self.solution.addDigits(999), 9)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>263. 丑数</title><link>https://h1s97x.github.io/posts/coding/coding-263-ugly-number/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-263-ugly-number/</guid><description>判断一个整数是否为只包含质因数 2、3 和 5 的丑数</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;丑数&lt;/strong&gt; 就是只包含质因数 &lt;code&gt;2&lt;/code&gt;、&lt;code&gt;3&lt;/code&gt; 和 &lt;code&gt;5&lt;/code&gt; 的正整数。&lt;/p&gt;
&lt;p&gt;给你一个整数 &lt;code&gt;n&lt;/code&gt;，请你判断 &lt;code&gt;n&lt;/code&gt; 是否为 &lt;strong&gt;丑数&lt;/strong&gt;。如果是，返回 &lt;code&gt;true&lt;/code&gt;；否则，返回 &lt;code&gt;false&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/ugly-number/description/&quot;&gt;https://leetcode.cn/problems/ugly-number/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：n = 6
输出：true
解释：6 = 2 × 3&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：n = 1
输出：true
解释：1 通常被视为丑数。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 3：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：n = 14
输出：false
解释：14 不是丑数，因为它包含了质因数 7。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;约束：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-2³¹ &amp;lt;= n &amp;lt;= 2³¹ - 1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;p&gt;反复除以 2、3、5，直到不能整除为止。最后判断剩余是否为 1。注意 &lt;code&gt;n &amp;lt;= 0&lt;/code&gt; 不是丑数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def isUgly(self, n: int) -&amp;gt; bool:
        if n &amp;lt;= 0:
            return False
        for p in (2, 3, 5):
            while n % p == 0:
                n //= p
        return n == 1
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestIsUgly(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_six(self):
        self.assertTrue(self.solution.isUgly(6))

    def test_one(self):
        self.assertTrue(self.solution.isUgly(1))

    def test_fourteen(self):
        self.assertFalse(self.solution.isUgly(14))

    def test_zero(self):
        self.assertFalse(self.solution.isUgly(0))

    def test_negative(self):
        self.assertFalse(self.solution.isUgly(-6))


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>326. 3 的幂</title><link>https://h1s97x.github.io/posts/coding/coding-326-power-of-three/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-326-power-of-three/</guid><description>判断一个整数是否为 3 的幂</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;给定一个整数，写一个函数来判断它是否是 3 的幂次方。如果是，返回 &lt;code&gt;true&lt;/code&gt;；否则，返回 &lt;code&gt;false&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;如果存在一个整数 &lt;code&gt;x&lt;/code&gt; 使得 &lt;code&gt;n == 3ˣ&lt;/code&gt;，则认为 &lt;code&gt;n&lt;/code&gt; 是 3 的幂次方。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/power-of-three/description/&quot;&gt;https://leetcode.cn/problems/power-of-three/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：n = 27
输出：true&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：n = 0
输出：false&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 3：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：n = 9
输出：true&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;约束：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-2³¹ &amp;lt;= n &amp;lt;= 2³¹ - 1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;p&gt;循环除以 3，直到不能整除为止，最后判断是否等于 1。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def isPowerOfThree(self, n: int) -&amp;gt; bool:
        if n &amp;lt;= 0:
            return False
        while n % 3 == 0:
            n //= 3
        return n == 1
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestIsPowerOfThree(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_twentyseven(self):
        self.assertTrue(self.solution.isPowerOfThree(27))

    def test_nine(self):
        self.assertTrue(self.solution.isPowerOfThree(9))

    def test_zero(self):
        self.assertFalse(self.solution.isPowerOfThree(0))

    def test_not_power(self):
        self.assertFalse(self.solution.isPowerOfThree(45))

    def test_negative(self):
        self.assertFalse(self.solution.isPowerOfThree(-3))


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>584. 寻找用户推荐人</title><link>https://h1s97x.github.io/posts/coding/coding-584-find-customer-referee/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-584-find-customer-referee/</guid><description>查询没有推荐人 ID 为 2 的客户</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;表：&lt;code&gt;Customer&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;+-------------+---------+
| Column Name | Type    |
+-------------+---------+
| id          | int     |
| name        | varchar |
| referee_id  | int     |
+-------------+---------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;id&lt;/code&gt; 是该表的主键。该表包含客户信息，&lt;code&gt;referee_id&lt;/code&gt; 表示该客户的推荐人。&lt;/p&gt;
&lt;p&gt;找出那些 &lt;strong&gt;没有被&lt;/strong&gt; &lt;code&gt;referee_id = 2&lt;/code&gt; 的客户推荐的客户。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/find-customer-referee/description/&quot;&gt;https://leetcode.cn/problems/find-customer-referee/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;输入：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;| id | name | referee_id |
|----|------|------------|
| 1  | Will | null       |
| 2  | Jane | null       |
| 3  | Alex | 2          |
| 4  | Bill | null       |
| 5  | Zack | 1          |
| 6  | Mark | 2          |
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;| name |
|------|
| Will |
| Jane |
| Bill |
| Zack |
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;p&gt;注意 &lt;code&gt;referee_id&lt;/code&gt; 可能为 &lt;code&gt;NULL&lt;/code&gt;，需要用 &lt;code&gt;IS NULL&lt;/code&gt; 或 &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt; 来处理。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;```sql
SELECT name
FROM Customer
WHERE referee_id IS NULL OR referee_id != 2;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;
```sql
-- 测试数据
CREATE TABLE Customer (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    referee_id INT
);

INSERT INTO Customer VALUES
(1, &apos;Will&apos;, NULL),
(2, &apos;Jane&apos;, NULL),
(3, &apos;Alex&apos;, 2),
(4, &apos;Bill&apos;, NULL),
(5, &apos;Zack&apos;, 1),
(6, &apos;Mark&apos;, 2);

-- 预期结果: Will, Jane, Bill, Zack
SELECT name FROM Customer WHERE referee_id IS NULL OR referee_id != 2;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>709. 转换成小写字母</title><link>https://h1s97x.github.io/posts/coding/coding-709-to-lower-case/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-709-to-lower-case/</guid><description>将字符串中的大写字母转换成小写字母</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;给你一个字符串 &lt;code&gt;s&lt;/code&gt;，将该字符串中的大写字母转换成相同的小写字母，返回新的字符串。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/to-lower-case/description/&quot;&gt;https://leetcode.cn/problems/to-lower-case/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：s = &quot;Hello&quot;
输出：&quot;hello&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：s = &quot;here&quot;
输出：&quot;here&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 3：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：s = &quot;LOVELY&quot;
输出：&quot;lovely&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;约束：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= s.length &amp;lt;= 100&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s&lt;/code&gt; 由 ASCII 字符集中的可打印字符组成&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;p&gt;Python 直接用 &lt;code&gt;s.lower()&lt;/code&gt;。也可以用位运算：大写字母 &lt;code&gt;A-Z&lt;/code&gt; 对应 ASCII 65-90，小写字母 &lt;code&gt;a-z&lt;/code&gt; 对应 97-122，相差 32。大写字母按位或 &lt;code&gt;0x20&lt;/code&gt;（即 &lt;code&gt;| 32&lt;/code&gt;）即可转为小写。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def toLowerCase(self, s: str) -&amp;gt; str:
        return &apos;&apos;.join(chr(ord(c) | 32) if &apos;A&apos; &amp;lt;= c &amp;lt;= &apos;Z&apos; else c for c in s)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestToLowerCase(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_mixed(self):
        self.assertEqual(self.solution.toLowerCase(&quot;Hello&quot;), &quot;hello&quot;)

    def test_lower(self):
        self.assertEqual(self.solution.toLowerCase(&quot;here&quot;), &quot;here&quot;)

    def test_upper(self):
        self.assertEqual(self.solution.toLowerCase(&quot;LOVELY&quot;), &quot;lovely&quot;)

    def test_non_alpha(self):
        self.assertEqual(self.solution.toLowerCase(&quot;123!@#&quot;), &quot;123!@#&quot;)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>867. 转置矩阵</title><link>https://h1s97x.github.io/posts/coding/coding-867-transpose-matrix/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-867-transpose-matrix/</guid><description>返回二维矩阵的转置</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;给你一个二维整数数组 &lt;code&gt;matrix&lt;/code&gt;，返回 &lt;code&gt;matrix&lt;/code&gt; 的 &lt;strong&gt;转置矩阵&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;矩阵的 &lt;strong&gt;转置&lt;/strong&gt; 是指将矩阵的行列互换，即 &lt;code&gt;result[j][i] = matrix[i][j]&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/transpose-matrix/description/&quot;&gt;https://leetcode.cn/problems/transpose-matrix/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出：[[1,4,7],[2,5,8],[3,6,9]]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：matrix = [[1,2,3],[4,5,6]]
输出：[[1,4],[2,5],[3,6]]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;约束：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= matrix.length &amp;lt;= 1000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= matrix[0].length &amp;lt;= 1000&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;p&gt;转置后行列互换，新矩阵的列数等于原矩阵的行数，新矩阵的行数等于原矩阵的列数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def transpose(self, matrix: list[list[int]]) -&amp;gt; list[list[int]]:
        rows, cols = len(matrix), len(matrix[0])
        result = [[0] * rows for _ in range(cols)]
        for i in range(rows):
            for j in range(cols):
                result[j][i] = matrix[i][j]
        return result
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestTranspose(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_square(self):
        self.assertEqual(self.solution.transpose([[1, 2, 3], [4, 5, 6], [7, 8, 9]]),
                         [[1, 4, 7], [2, 5, 8], [3, 6, 9]])

    def test_rectangular(self):
        self.assertEqual(self.solution.transpose([[1, 2, 3], [4, 5, 6]]),
                         [[1, 4], [2, 5], [3, 6]])

    def test_single(self):
        self.assertEqual(self.solution.transpose([[1]]), [[1]])


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>852. 山脉数组的峰顶索引</title><link>https://h1s97x.github.io/posts/coding/coding-852-peak-index-in-mountain-array/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-852-peak-index-in-mountain-array/</guid><description>在山脉数组中找出峰顶元素的索引</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;符合下列属性的数组 &lt;code&gt;arr&lt;/code&gt; 称为 &lt;strong&gt;山脉数组&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;arr.length &amp;gt;= 3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;存在下标 &lt;code&gt;i&lt;/code&gt;（&lt;code&gt;0 &amp;lt; i &amp;lt; arr.length - 1&lt;/code&gt;），使得：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;arr[0] &amp;lt; arr[1] &amp;lt; ... &amp;lt; arr[i]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;arr[i] &amp;gt; arr[i + 1] &amp;gt; ... &amp;gt; arr[arr.length - 1]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;给你由整数组成的山脉数组 &lt;code&gt;arr&lt;/code&gt;，返回任何满足 &lt;code&gt;arr[0] &amp;lt; arr[1] &amp;lt; ... &amp;lt; arr[i] &amp;gt; arr[i + 1] &amp;gt; ... &amp;gt; arr[arr.length - 1]&lt;/code&gt; 的下标 &lt;code&gt;i&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/peak-index-in-a-mountain-array/description/&quot;&gt;https://leetcode.cn/problems/peak-index-in-a-mountain-array/description/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：arr = [0,1,0]
输出：1&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：arr = [0,2,1,0]
输出：1&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;示例 3：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入：arr = [24,69,100,99,79,78,67,36,26,19]
输出：2&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;约束：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;3 &amp;lt;= arr.length &amp;lt;= 10⁵&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= arr[i] &amp;lt;= 10⁶&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;题目数据保证 &lt;code&gt;arr&lt;/code&gt; 是一个山脉数组&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;h3&gt;解法 1：线性扫描&lt;/h3&gt;
&lt;p&gt;遍历直到找到 &lt;code&gt;arr[i] &amp;gt; arr[i + 1]&lt;/code&gt; 的位置。&lt;/p&gt;
&lt;h3&gt;解法 2：二分查找&lt;/h3&gt;
&lt;p&gt;山脉数组的峰顶就是「第一个下降点」，可以用二分查找在 &lt;code&gt;O(log n)&lt;/code&gt; 时间内找到。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def peakIndexInMountainArray(self, arr: list[int]) -&amp;gt; int:
        left, right = 0, len(arr) - 1
        while left &amp;lt; right:
            mid = (left + right) // 2
            if arr[mid] &amp;lt; arr[mid + 1]:
                left = mid + 1
            else:
                right = mid
        return left
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import unittest


class TestPeakIndexInMountainArray(unittest.TestCase):
    def setUp(self):
        self.solution = Solution()

    def test_example1(self):
        self.assertEqual(self.solution.peakIndexInMountainArray([0, 1, 0]), 1)

    def test_example2(self):
        self.assertEqual(self.solution.peakIndexInMountainArray([0, 2, 1, 0]), 1)

    def test_example3(self):
        self.assertEqual(self.solution.peakIndexInMountainArray([24, 69, 100, 99, 79, 78, 67, 36, 26, 19]), 2)

    def test_large(self):
        self.assertEqual(self.solution.peakIndexInMountainArray([1, 2, 3, 4, 5, 3, 1]), 4)


if __name__ == &apos;__main__&apos;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>编程入门 · 题单</title><link>https://h1s97x.github.io/posts/coding/coding-primers-list/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-primers-list/</guid><description>LeetCode「新」动计划 · 编程入门 20 题题解</description><pubDate>Sat, 09 Nov 2024 12:49:16 GMT</pubDate><content:encoded>&lt;h2&gt;基础语法&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;题号&lt;/th&gt;
&lt;th&gt;题目&lt;/th&gt;
&lt;th&gt;难度&lt;/th&gt;
&lt;th&gt;标签&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2235&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;./01-basic-syntax/2235-add-two-integers/&quot;&gt;两整数相加&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;简单&lt;/td&gt;
&lt;td&gt;基础语法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2469&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;./01-basic-syntax/2469-convert-the-temperature/&quot;&gt;温度转换&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;简单&lt;/td&gt;
&lt;td&gt;基础语法&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;条件判断&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;题号&lt;/th&gt;
&lt;th&gt;题目&lt;/th&gt;
&lt;th&gt;难度&lt;/th&gt;
&lt;th&gt;标签&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2413&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;./02-conditions/2413-smallest-even-multiple/&quot;&gt;最小偶倍数&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;简单&lt;/td&gt;
&lt;td&gt;条件判断&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2236&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;./02-conditions/2236-root-equals-sum-of-children/&quot;&gt;判断根结点是否等于子结点之和&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;简单&lt;/td&gt;
&lt;td&gt;条件判断&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;循环&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;题号&lt;/th&gt;
&lt;th&gt;题目&lt;/th&gt;
&lt;th&gt;难度&lt;/th&gt;
&lt;th&gt;标签&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;1486&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;./03-loops/1486-xor-operation-in-an-array/&quot;&gt;数组异或操作&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;简单&lt;/td&gt;
&lt;td&gt;循环&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;1512&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;./03-loops/1512-number-of-good-pairs/&quot;&gt;好数对的数目&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;简单&lt;/td&gt;
&lt;td&gt;循环&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;1534&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;./03-loops/1534-count-good-triplets/&quot;&gt;统计好三元组&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;简单&lt;/td&gt;
&lt;td&gt;循环嵌套&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;数据库&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;题号&lt;/th&gt;
&lt;th&gt;题目&lt;/th&gt;
&lt;th&gt;难度&lt;/th&gt;
&lt;th&gt;标签&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;584&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;./04-database/584-find-customer-referee/&quot;&gt;寻找用户推荐人&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;简单&lt;/td&gt;
&lt;td&gt;数据库&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;1757&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;./04-database/1757-recyclable-and-low-fat-products/&quot;&gt;可回收且低脂的产品&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;简单&lt;/td&gt;
&lt;td&gt;数据库&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;位运算&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;题号&lt;/th&gt;
&lt;th&gt;题目&lt;/th&gt;
&lt;th&gt;难度&lt;/th&gt;
&lt;th&gt;标签&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;709&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;./05-bit-manipulation/709-to-lower-case/&quot;&gt;转换成小写字母&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;简单&lt;/td&gt;
&lt;td&gt;位运算&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;数学&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;题号&lt;/th&gt;
&lt;th&gt;题目&lt;/th&gt;
&lt;th&gt;难度&lt;/th&gt;
&lt;th&gt;标签&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;258&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;./06-math/258-add-digits/&quot;&gt;各位相加&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;简单&lt;/td&gt;
&lt;td&gt;数学&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;1281&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;./06-math/1281-subtract-product-and-sum/&quot;&gt;整数的各位积和之差&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;简单&lt;/td&gt;
&lt;td&gt;数学&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;231&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;./06-math/231-power-of-two/&quot;&gt;2 的幂&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;简单&lt;/td&gt;
&lt;td&gt;数学&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;326&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;./06-math/326-power-of-three/&quot;&gt;3 的幂&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;简单&lt;/td&gt;
&lt;td&gt;数学&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;263&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;./06-math/263-ugly-number/&quot;&gt;丑数&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;简单&lt;/td&gt;
&lt;td&gt;数学&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;数组 &amp;amp; 字符串&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;题号&lt;/th&gt;
&lt;th&gt;题目&lt;/th&gt;
&lt;th&gt;难度&lt;/th&gt;
&lt;th&gt;标签&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;1470&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;./07-array-string/1470-shuffle-the-array/&quot;&gt;重新排列数组&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;简单&lt;/td&gt;
&lt;td&gt;数组&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;867&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;./07-array-string/867-transpose-matrix/&quot;&gt;转置矩阵&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;简单&lt;/td&gt;
&lt;td&gt;数组&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;1422&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;./07-array-string/1422-max-score-split/&quot;&gt;分割字符串的最大得分&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;简单&lt;/td&gt;
&lt;td&gt;字符串&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;2062&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;./07-array-string/2062-count-vowel-substrings/&quot;&gt;统计范围内的元音字符串数&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;简单&lt;/td&gt;
&lt;td&gt;字符串&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;852&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;./07-array-string/852-peak-index-in-mountain-array/&quot;&gt;山脉数组的峰顶索引&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;中等&lt;/td&gt;
&lt;td&gt;数组、二分查找&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</content:encoded></item><item><title>26. 删除有序数组中的重复项</title><link>https://h1s97x.github.io/posts/coding/coding-26-remove-duplicates-from-sorted-array/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-26-remove-duplicates-from-sorted-array/</guid><pubDate>Sun, 31 Dec 2023 16:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;26. 删除有序数组中的重复项&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个&lt;strong&gt;非严格递增排列&lt;/strong&gt;的数组 &lt;code&gt;nums&lt;/code&gt;，请你&lt;strong&gt;原地&lt;/strong&gt;删除重复出现的元素，使每个元素只出现一次，返回删除后数组的新长度。元素的相对顺序应该保持&lt;strong&gt;一致&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;不要使用额外的数组空间，必须仅使用 O(1) 额外空间并&lt;strong&gt;原地&lt;/strong&gt;修改输入数组。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [1,1,2]
输出：2, nums = [1,2,_]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [0,0,1,1,1,2,2,3,3,4]
输出：5, nums = [0,1,2,3,4]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;快慢双指针：&lt;code&gt;left&lt;/code&gt; 指向&quot;下一个不重复元素应写入的位置&quot;，&lt;code&gt;right&lt;/code&gt; 扫描数组。因为数组有序，只要 &lt;code&gt;nums[right]&lt;/code&gt; 不等于前一个已经固定好的元素 &lt;code&gt;nums[left-1]&lt;/code&gt;，就说明遇到了新的不重复值。&lt;/p&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;left&lt;/code&gt; 从 1 开始（&lt;code&gt;nums[0]&lt;/code&gt; 一定保留），&lt;code&gt;right&lt;/code&gt; 从 1 遍历到 &lt;code&gt;n-1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;nums[right] != nums[left-1]&lt;/code&gt;，则说明发现新元素，&lt;code&gt;nums[left] = nums[right]&lt;/code&gt; 并 &lt;code&gt;left += 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;最后返回 &lt;code&gt;left&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;关键点：比较对象是 &lt;code&gt;nums[left-1]&lt;/code&gt;（已固定区间的最后一个元素），而不是 &lt;code&gt;nums[right-1]&lt;/code&gt;，这样即便中间有多个重复也能正确跳过。&lt;/p&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;nums = [0,0,1,1,1,2,2,3,3,4]&lt;/code&gt; 为例：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;right&lt;/th&gt;
&lt;th&gt;nums[right]&lt;/th&gt;
&lt;th&gt;与 nums[left-1] 比较&lt;/th&gt;
&lt;th&gt;操作&lt;/th&gt;
&lt;th&gt;nums 序列&lt;/th&gt;
&lt;th&gt;left&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;==0&lt;/td&gt;
&lt;td&gt;跳过&lt;/td&gt;
&lt;td&gt;[0,0,1,...]&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;!=0&lt;/td&gt;
&lt;td&gt;写 nums[1]=1&lt;/td&gt;
&lt;td&gt;[0,1,1,...]&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;==1&lt;/td&gt;
&lt;td&gt;跳过&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;==1&lt;/td&gt;
&lt;td&gt;跳过&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;!=1&lt;/td&gt;
&lt;td&gt;写 nums[2]=2&lt;/td&gt;
&lt;td&gt;[0,1,2,...]&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;==2&lt;/td&gt;
&lt;td&gt;跳过&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;!=2&lt;/td&gt;
&lt;td&gt;写 nums[3]=3&lt;/td&gt;
&lt;td&gt;[0,1,2,3,...]&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;==3&lt;/td&gt;
&lt;td&gt;跳过&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;!=3&lt;/td&gt;
&lt;td&gt;写 nums[4]=4&lt;/td&gt;
&lt;td&gt;[0,1,2,3,4]&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;返回 5，前五位 &lt;code&gt;[0,1,2,3,4]&lt;/code&gt;，符合预期。&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def removeDuplicates(self, nums: List[int]) -&amp;gt; int:
        left = 1
        for right in range(1, len(nums)):
            if nums[right] != nums[left - 1]:
                nums[left] = nums[right]
                left += 1
        return left
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_basic(self):
        nums = [1, 1, 2]
        k = Solution().removeDuplicates(nums)
        self.assertEqual(k, 2)
        self.assertEqual(nums[:k], [1, 2])

    def test_multi_dup(self):
        nums = [0, 0, 1, 1, 1, 2, 2, 3, 3, 4]
        k = Solution().removeDuplicates(nums)
        self.assertEqual(k, 5)
        self.assertEqual(nums[:k], [0, 1, 2, 3, 4])

    def test_no_dup(self):
        nums = [1, 2, 3, 4]
        k = Solution().removeDuplicates(nums)
        self.assertEqual(k, 4)

    def test_single(self):
        nums = [5]
        k = Solution().removeDuplicates(nums)
        self.assertEqual(k, 1)


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>27. 移除元素</title><link>https://h1s97x.github.io/posts/coding/coding-27-remove-element/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-27-remove-element/</guid><pubDate>Sun, 31 Dec 2023 16:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;27. 移除元素&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给你一个数组 &lt;code&gt;nums&lt;/code&gt; 和一个值 &lt;code&gt;val&lt;/code&gt;，你需要&lt;strong&gt;原地&lt;/strong&gt;移除所有数值等于 &lt;code&gt;val&lt;/code&gt; 的元素，并返回移除后数组的新长度。&lt;/p&gt;
&lt;p&gt;不要使用额外的数组空间，必须仅使用 O(1) 额外空间并&lt;strong&gt;原地&lt;/strong&gt;修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [3,2,2,3], val = 3
输出：2, nums = [2,2,_,_]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入：nums = [0,1,2,2,3,0,4,2], val = 2
输出：5, nums = [0,1,4,0,3,_,_,_]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;快慢双指针：&lt;code&gt;left&lt;/code&gt; 指向&quot;下一个非 &lt;code&gt;val&lt;/code&gt; 元素应写入的位置&quot;，&lt;code&gt;right&lt;/code&gt; 扫描数组，凡是 &lt;code&gt;!= val&lt;/code&gt; 的元素就往前搬运。&lt;/p&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;left&lt;/code&gt; 初始为 0，表示有效前缀的长度 / 下一个可写位置。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;right&lt;/code&gt; 从 0 遍历到 &lt;code&gt;n-1&lt;/code&gt;：
&lt;ul&gt;
&lt;li&gt;若 &lt;code&gt;nums[right] != val&lt;/code&gt;，则 &lt;code&gt;nums[left] = nums[right]&lt;/code&gt;，&lt;code&gt;left += 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;否则跳过（该位置就是要被移除的元素）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;left&lt;/code&gt;，即移除后数组的长度。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;由于直接覆盖写回，前面被 &lt;code&gt;val&lt;/code&gt; 占据的位置会被后面的有效元素填满，无需真的删除。&lt;/p&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;nums = [3,2,2,3], val = 3&lt;/code&gt; 为例：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;right&lt;/th&gt;
&lt;th&gt;nums[right]&lt;/th&gt;
&lt;th&gt;操作&lt;/th&gt;
&lt;th&gt;nums 序列&lt;/th&gt;
&lt;th&gt;left&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;跳过（==val）&lt;/td&gt;
&lt;td&gt;[3,2,2,3]&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;写 nums[0]=2&lt;/td&gt;
&lt;td&gt;[2,2,2,3]&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;写 nums[1]=2&lt;/td&gt;
&lt;td&gt;[2,2,2,3]&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;跳过&lt;/td&gt;
&lt;td&gt;[2,2,2,3]&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;返回 2，前两位为 &lt;code&gt;[2,2]&lt;/code&gt;，符合预期。&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def removeElement(self, nums: List[int], val: int) -&amp;gt; int:
        left = 0
        for right in range(len(nums)):
            if nums[right] != val:
                nums[left] = nums[right]
                left += 1
        return left
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_basic(self):
        nums = [3, 2, 2, 3]
        k = Solution().removeElement(nums, 3)
        self.assertEqual(k, 2)
        self.assertEqual(nums[:k], [2, 2])

    def test_more_occurrences(self):
        nums = [0, 1, 2, 2, 3, 0, 4, 2]
        k = Solution().removeElement(nums, 2)
        self.assertEqual(k, 5)
        self.assertEqual(nums[:k], [0, 1, 3, 0, 4])

    def test_no_val(self):
        nums = [1, 2, 3]
        k = Solution().removeElement(nums, 0)
        self.assertEqual(k, 3)

    def test_all_val(self):
        nums = [2, 2, 2]
        k = Solution().removeElement(nums, 2)
        self.assertEqual(k, 0)


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>283. 移动零</title><link>https://h1s97x.github.io/posts/coding/coding-283-move-zeroes/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/coding/coding-283-move-zeroes/</guid><pubDate>Sun, 31 Dec 2023 16:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;283. 移动零&lt;/h1&gt;
&lt;h2&gt;题目描述&lt;/h2&gt;
&lt;p&gt;给定一个数组 &lt;code&gt;nums&lt;/code&gt;，编写一个函数将所有 &lt;code&gt;0&lt;/code&gt; 移动到数组的末尾，同时保持非零元素的相对顺序。&lt;/p&gt;
&lt;p&gt;注意：必须在不复制数组的情况下原地对数组进行操作。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例 1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例 2：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入: nums = [0]
输出: [0]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;提示：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 10^4&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-2^31 &amp;lt;= nums[i] &amp;lt;= 2^31 - 1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解题思路&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;双指针将所有非零元素&quot;左倾&quot;到数组头部，剩下的位置自然就是零。&lt;/p&gt;
&lt;h3&gt;算法步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;用 &lt;code&gt;left&lt;/code&gt; 指针指向&lt;strong&gt;下一个应存放非零元素的位置&lt;/strong&gt;（初始为 0）。&lt;/li&gt;
&lt;li&gt;用 &lt;code&gt;right&lt;/code&gt; 指针从左到右扫描整个数组。&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;nums[right] != 0&lt;/code&gt; 时，把 &lt;code&gt;nums[right]&lt;/code&gt; 与 &lt;code&gt;nums[left]&lt;/code&gt; 交换，然后 &lt;code&gt;left&lt;/code&gt; 右移。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;经过一轮扫描后，所有非零元素都被换到了 &lt;code&gt;[0, left)&lt;/code&gt; 区间，且相对顺序不变；它们各自原来的位置留下的是 0（或左移前被换过来的 0），因此所有 0 都到了末尾。&lt;/p&gt;
&lt;h3&gt;逐步举例&lt;/h3&gt;
&lt;p&gt;以 &lt;code&gt;nums = [0,1,0,3,12]&lt;/code&gt; 为例：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;right&lt;/th&gt;
&lt;th&gt;nums[right]&lt;/th&gt;
&lt;th&gt;操作&lt;/th&gt;
&lt;th&gt;nums 序列&lt;/th&gt;
&lt;th&gt;left&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;跳过&lt;/td&gt;
&lt;td&gt;[0,1,0,3,12]&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;交换 nums[0]&amp;lt;-&amp;gt;nums[1]&lt;/td&gt;
&lt;td&gt;[1,0,0,3,12]&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;跳过&lt;/td&gt;
&lt;td&gt;[1,0,0,3,12]&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;交换 nums[1]&amp;lt;-&amp;gt;nums[3]&lt;/td&gt;
&lt;td&gt;[1,3,0,0,12]&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;交换 nums[2]&amp;lt;-&amp;gt;nums[4]&lt;/td&gt;
&lt;td&gt;[1,3,12,0,0]&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最终得到 &lt;code&gt;[1,3,12,0,0]&lt;/code&gt;，与预期一致。&lt;/p&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(n)，只需一次遍历。&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)，原地交换。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码实现&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from typing import List


class Solution:
    def moveZeroes(self, nums: List[int]) -&amp;gt; None:
        left = 0
        for right in range(len(nums)):
            if nums[right] != 0:
                nums[left], nums[right] = nums[right], nums[left]
                left += 1
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))

from solution import Solution


class TestSolution(unittest.TestCase):
    def test_basic(self):
        nums = [0, 1, 0, 3, 12]
        Solution().moveZeroes(nums)
        self.assertEqual(nums, [1, 3, 12, 0, 0])

    def test_all_zero(self):
        nums = [0, 0, 0]
        Solution().moveZeroes(nums)
        self.assertEqual(nums, [0, 0, 0])

    def test_leading_zeros(self):
        nums = [0, 0, 1]
        Solution().moveZeroes(nums)
        self.assertEqual(nums, [1, 0, 0])

    def test_single(self):
        nums = [0]
        Solution().moveZeroes(nums)
        self.assertEqual(nums, [0])


if __name__ == &quot;__main__&quot;:
    unittest.main()
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>文件管理</title><link>https://h1s97x.github.io/posts/notes/notes-%E6%96%87%E4%BB%B6%E7%AE%A1%E7%90%86/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-%E6%96%87%E4%BB%B6%E7%AE%A1%E7%90%86/</guid><description>相比大家都有过文件太多难以管理的经历，现在各大应用程序都有自己存储文件的习惯，QQ 把下载的文件存储在 QQFileRecv 里，微信也按照每个用户与时间管理所</description><pubDate>Sun, 24 Dec 2023 06:12:05 GMT</pubDate><content:encoded>&lt;h2&gt;参考资料&lt;/h2&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;相比大家都有过文件太多难以管理的经历，现在各大应用程序都有自己存储文件的习惯，QQ 把下载的文件存储在 QQ_FileRecv 里，微信也按照每个用户与时间管理所有下载的文件。当然这是在 windows 操作系统下的文件管理。至于 IOS 系统、Linux 系统等系统的文件管理则大不相同，为了改善这些差异所带来的麻烦，如何将这些系统之间的差异填补，就是我们所要考虑的问题了，比如说我有几个文件分散在手机 Android 系统、iPad IOS 系统以及电脑 Windows 系统和 Linux 系统，那么有没有方法将它们统一管理呢？对于这种情况我没有什么解决的思路，在网络上也没找到类似的（或许应该有，需要关键词去搜索）如果正向走不动，把思路逆转过来如何？我们为什么要文件管理，不就是图个方便吗，既然如此，那就把所有的文件集中起来，放到服务器，需要的时候再从服务器下载，或者功能再强大一点，实现文件预览，直接在本地预览服务器的文件，就不用搞得文件到处都是了。这种方案我认为可行性很大，而且现有的一些开源软件已经有类似的实现。所需要做的工作就是：1.文件搜集：把散落在各个系统的文件找到并传输到服务器去。&lt;/p&gt;
&lt;p&gt;2.搭建服务器，承载功能。&lt;/p&gt;
&lt;p&gt;3.服务器开发上传和下载的接口。&lt;/p&gt;
&lt;p&gt;那么首先我们先了解各操作系统的文件系统：&lt;/p&gt;
&lt;h2&gt;Android 系统&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://zarchiver.online/&quot;&gt;ZArchiver APK For Android - Free Download&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;IOS 系统&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://sspai.com/post/61729#!&quot;&gt;是时候认识一下 iPad 的文件管理了 - 少数派 (sspai.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.zhihu.com/question/64610035&quot;&gt;(9 封私信 / 80 条消息) 苹果手机下载的东西都存到哪里了？ - 知乎 (zhihu.com)&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Windows&lt;/h2&gt;
&lt;h2&gt;Linux&lt;/h2&gt;
&lt;h3&gt;搭建服务器&lt;/h3&gt;
&lt;p&gt;[本地 alist 网盘如何实现公网域名远程访问？——快解析内网端口映射外网-金万维快解析 (kuaijiexi.com)](https://www.kuaijiexi.com/helpDetail380.html#:~:text=1.在alist本地任意主机电脑上，安装并登录快解析客户端。 快解析版本无忧版、钻石版、旗舰版、星耀版账号均可实现内网穿透。,2.添加映射。 通过这一步添加映射设置，将 alist 内网访问地址变成域名让外网访问。)&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://help.aliyun.com/document_detail/39834.html#h2--dns-2&quot;&gt;有哪些方法测试域名解析生效_云解析 DNS(DNS)-阿里云帮助中心 (aliyun.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/371579941&quot;&gt;全网最详细：个人网站如何备案 - 知乎 (zhihu.com)&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Linux基础</title><link>https://h1s97x.github.io/posts/notes/notes-linux%E5%9F%BA%E7%A1%80/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-linux%E5%9F%BA%E7%A1%80/</guid><description>VIM 快速入门之常用命令解析 1,VIM 复制，粘贴，剪切，替换，查找\哔哩哔哩\bilibili 符号：『』❑ ➢ 图书名称: Ubuntu Linux 应</description><pubDate>Sun, 24 Dec 2023 02:27:16 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1YY4y1n75r/?vd_source=206a091077ff783d662b49e64cc9589d&quot;&gt;VIM 快速入门之常用命令解析 1,VIM 复制，粘贴，剪切，替换，查找_哔哩哔哩_bilibili&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;符号：『』❑ ➢&lt;/p&gt;
&lt;h2&gt;参考资料&lt;/h2&gt;
&lt;h3&gt;主要参考书&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;图书名称:&lt;/th&gt;
&lt;th&gt;Ubuntu Linux 应用技术教程&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;封面图片:&lt;/td&gt;
&lt;td&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/linux-04-20261014.jpg&quot; alt=&quot;img&quot; /&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;出版印刷时间:&lt;/td&gt;
&lt;td&gt;2009-4&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;出版社:&lt;/td&gt;
&lt;td&gt;清华大学出版社&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;图书作者:&lt;/td&gt;
&lt;td&gt;陈明 编著&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ISBN:&lt;/td&gt;
&lt;td&gt;9787302193289&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;图书名称:&lt;/th&gt;
&lt;th&gt;Linux 就该这么学（第 2 版）&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;封面图片:&lt;/td&gt;
&lt;td&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/linux-03-20261014.jpg&quot; alt=&quot;Linux就该这么学（第2版）&quot; /&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;出版印刷时间:&lt;/td&gt;
&lt;td&gt;2021-9&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;出版社:&lt;/td&gt;
&lt;td&gt;人民邮电出版社&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;图书作者:&lt;/td&gt;
&lt;td&gt;刘遄 编著&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ISBN:&lt;/td&gt;
&lt;td&gt;9787115570116&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;链接&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.linuxprobe.com/chapter-00.html&quot;&gt;第 0 章 咱们先来谈谈学习方法和红帽系统。 | 《Linux 就该这么学》 (linuxprobe.com)&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;目录&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Linux 应用课程基础&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Linux 基础命令&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;文件系统&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;权限管理&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Vi 应用&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;进程管理&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SHELL 编程&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Chapter 1 Linux 概述&lt;/h2&gt;
&lt;h3&gt;1.1 Linux 操作系统简述&lt;/h3&gt;
&lt;p&gt;操作系统是计算机系统的基本系统软件。操作系统是所有软件的核心。操作系统负责控制、管理计算机的所有软件、硬件资源，是惟一直接和硬件系统打交道的软件，是整个软件系统的基础部分，同时还为计算机用户提供良好的界面。因此，操作系统直接面对所有硬件、软件和用户，它是协调计算机各组成部分之间、人机之间关系的重要软件系统。&lt;/p&gt;
&lt;h4&gt;强大好用的 Shell&lt;/h4&gt;
&lt;p&gt;一台完整的计算机是由运算器、控制器、存储器、输入/输出等多种硬件设备共同组成的，而能让各种硬件设备各司其职且又能协同运行的东西就是系统内核。Linux 系统的内核负责完成对硬件资源的分配、调度等管理任务，对系统的正常运行起着十分重要的作用。&lt;/p&gt;
&lt;p&gt;与修改 Windows 系统中的注册表类似，直接改动内核参数的难度比较大，而且一旦“手滑”还有可能导致系统直接崩溃。因此不建议同学们直接去编辑内核中的参数，而是用基于系统调用接口开发出来的程序或服务来管理计算机，以满足日常的工作需要。&lt;/p&gt;
&lt;p&gt;如图所示，人类是无法直接控制硬件的（想象一个人捧着块硬盘自言自语的滑稽场景）。硬件设备由系统内核直接管理，但由于内核的复杂性太高，在访问时存在较大的风险，因此用户不能直接访问内核。虽然通过调用系统提供的 API（应用程序编程接口）就能实现某个功能，但哪怕实现“将一条信息通过互联网传输给别人”这样简单的任务，都要手动调用几十次 API 接口，使用起来太不切实际。而最外层的服务程序则是最贴近于用户端的，这些服务程序是集成了大量 API 接口的完整软件，微信、QQ 就是这样的服务程序。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.linuxprobe.com/wp-content/uploads/2020/05/%E7%94%A8%E6%88%B7%E4%B8%8E%E7%A1%AC%E4%BB%B6-1.jpg&quot; alt=&quot;第2章 新手必须掌握的Linux命令第2章 新手必须掌握的Linux命令&quot; /&gt;&lt;/p&gt;
&lt;p&gt;讲到这里，相信大家已经能够明白服务程序对用户和硬件所能发挥的作用了。如果把整台电脑比喻成人类社会，那么服务程序就是一名翻译官，负责将用户提出的需求转换成硬件能够接收的指令代码，然后再将处理结果反馈成用户能够读懂的内容格式。这样一来一回，用户就能使用硬件资源了。&lt;/p&gt;
&lt;p&gt;看到被一层层“包裹”起来的硬件设备，大家有没有感觉像一只蜗牛的壳呢？英文中的壳叫作 Shell，我们在行业中也将用户终端程序称之为 Shell，方便好记。&lt;/p&gt;
&lt;p&gt;Shell 就是终端程序的统称，它充当了人与内核（硬件）之间的翻译官，用户把一些命令“告诉”终端程序，它就会调用相应的程序服务去完成某些工作。现在包括红帽系统在内的许多主流 Linux 系统默认使用的终端是 Bash（Bourne-Again SHell）解释器，这个 Bash 解释器主要有以下 4 项优势：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;通过上下方向键来调取执行过的 Linux 命令；&lt;/p&gt;
&lt;p&gt;命令或参数仅需输入前几位就可以用 Tab 键补全；&lt;/p&gt;
&lt;p&gt;具有强大的批处理脚本；&lt;/p&gt;
&lt;p&gt;具有实用的环境变量功能。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;1.2 发展史&lt;/h3&gt;
&lt;h3&gt;1.3 Linux 的特点与组成&lt;/h3&gt;
&lt;p&gt;Linux 是多用户、多任务、抢占式操作系统，原作者 Linus，是开源操作系统。&lt;/p&gt;
&lt;p&gt;Linux 系统基本组成:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Kernel 内核&lt;/li&gt;
&lt;li&gt;SHELL（bash）&lt;/li&gt;
&lt;li&gt;Applications 应用程序&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;§ Kernel&lt;/p&gt;
&lt;p&gt;1、 管理程序的运行，为程序分派资源，处理程序之间的通信
2、 管理对文件系统的读写
3、 把对文件系统的操作映射成对磁盘或其他设备的操作
4、 管理输入，输出，将设备映射成设备文件
5、 管理网络
§ Shell&lt;/p&gt;
&lt;p&gt;1、 Shell:是一个命令解释器，是内核和用户之间的接口。
2、 Shell:是一个编程语言。(Shell script)
3、 系统的启动过程：init-&amp;gt; login -&amp;gt; passwd -&amp;gt; shell&lt;/p&gt;
&lt;p&gt;§ Linux 应用程序&lt;/p&gt;
&lt;p&gt;• 标准的 Linux 系统都具有一整套称为应用程序的程序集，包括文本编辑器、Xwindow、办公套件、Internet 工具和数据库等。&lt;/p&gt;
&lt;h3&gt;1.4 Linux 发行版本&lt;/h3&gt;
&lt;p&gt;在介绍常见的 Linux 系统版本之前，首先需要区分 Linux 系统内核与 Linux 发行套件系统的不同。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Linux 系统内核指的是一个由 Linus Torvalds 负责维护，提供硬件抽象层、磁盘、文件系统控制及多任务功能的系统核心程序。&lt;/p&gt;
&lt;p&gt;Linux 发行套件系统是我们常说的 Linux 操作系统，也就是由 Linux 内核与各种常用软件的集合产品。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Linux 发行版是由包含 Linux 内核和软件包管理系统的软件集合组成的操作系统。Linux 用户通常通过下载其中一个 Linux 发行版来获取他们的操作系统，这些发行版可用于各种系统，从嵌入式设备（例如，OpenWrt）和个人计算机（例如，Linux Mint）到强大的超级计算机（例如，Rocks Cluster Distribution）。&lt;/p&gt;
&lt;p&gt;典型的 Linux 发行版包括一个（通常经过修改的）Linux 内核、一个初始化系统（如 systemd、OpenRC 或 runit）、GNU 工具和库、文档和许多其他软件（如 IP 网络配置实用程序和 getty TTY 安装程序等）。（可选）提供桌面体验（最常见的是 Mesa 用户空间图形驱动程序）、显示服务器（最常见的是 X.org Server，或者最近的 Wayland 合成器，如 Sway、KDE 的 KWin 或 GNOME 的 Mutter）、桌面环境、声音服务器（通常是 PulseAudio 或最近的 PipeWire）），其他相关程序可能包含在发行版中，也可以由用户安装。&lt;/p&gt;
&lt;p&gt;大多数包含的软件都是免费的开源软件，既可以作为编译的二进制文件提供，也可以以源代码形式提供，允许对原始软件进行修改。通常，Linux 发行版可以选择性地包含一些可能不以源代码形式提供的专有软件，例如某些设备驱动程序所需的二进制 Blob。&lt;/p&gt;
&lt;h4&gt;红帽企业版系统（RHEL,RedHatEnterpriseLinux）&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://www.linuxprobe.com/wp-content/uploads/2020/04/redhat.png&quot; alt=&quot;第0章 咱们先来谈谈为什么要学习Linux系统第0章 咱们先来谈谈为什么要学习Linux系统&quot; /&gt;&lt;/p&gt;
&lt;p&gt;前文在介绍 Linux 系统的发展历史时，曾提到过红帽公司。红帽公司作为全球知名的开源技术厂商，其产品值得我们放到第一位来介绍。红帽公司成立于 1993 年，于 1998 年在纳斯达克上市，自从 1999 年起陆续收购了包括 JBoss 中间件供应商、CentOS（社区企业操作系统）、Ceph 企业级存储业务等在内的数十家高科技公司及热门产品，这么做的目的当然是为了对主营业务红帽企业版 Linux 进行增强。&lt;/p&gt;
&lt;p&gt;红帽企业版 Linux 最初于 2002 年 3 月面世，当年 Dell、HP、Oracle 以及 IBM 公司便纷纷表示支持该系统平台的硬件开发，因此红帽企业版 Linux 系统的市场份额在近 20 年时间内不断猛增。红帽企业版 Linux 当时是全世界使用最广泛的 Linux 系统之一，在世界 500 强企业中，所有的航空公司、电信服务提供商、商业银行、医疗保健公司均无一例外地通过该系统向外提供服务。&lt;/p&gt;
&lt;p&gt;红帽企业版 Linux 当前的最新版本是 RHEL 8，该系统具有极强的稳定性，在全球范围内都可以获得完善的技术支持。该系统也是本书和红帽认证考试中默认使用的操作系统。&lt;/p&gt;
&lt;h4&gt;CentOS 社区企业操作系统（Community Enterprise Operating System）&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://www.linuxprobe.com/wp-content/uploads/2015/01/centos.jpg&quot; alt=&quot;第0章 咱们先来谈谈为什么要学习Linux系统第0章 咱们先来谈谈为什么要学习Linux系统&quot; /&gt;&lt;/p&gt;
&lt;p&gt;顾名思义，CentOS 是由开源社区研发和维护的一款企业级 Linux 操作系统，在 2014 年 1 月被红帽公司正式收购。CentOS 系统最为人广泛熟悉的标签就是“免费”。如果您问一个运维“老鸟”选择 CentOS 系统的理由，他绝对不会跟你说更安全或更稳定，而只是说两个字—免费！由于红帽企业版 Linux 是开源软件，任何人都有修改和创建衍生品的权利，因此 CentOS 便是将红帽企业版 Linux 中的收费功能通通去掉，然后将新系统重新编译后发布给用户免费使用的 Linux 系统。也正因为其免费的特性，CentOS 拥有了广泛的用户。&lt;/p&gt;
&lt;p&gt;从本质上来说，由于 CentOS 是针对红帽企业版 Linux 进行修改后再发布的版本，因此不会针对它单独开发新功能，CentOS 的版本号也是随红帽企业版 Linux 而变更。例如，CentOS 8.0 对应的就是 RHEL 8.0，CentOS 8.1 对应的就是 RHEL 8.1；以此类推。再就是，CentOS 系统和 RHEL 系统的软件包可以通用。也就是说，如果工作中用的是 RHEL，但是在安装某款软件时只找到了该软件的 CentOS 系统软件源，也是可以正常安装该软件的。&lt;/p&gt;
&lt;h4&gt;Fedora Linux&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://www.linuxprobe.com/wp-content/uploads/2015/01/fedora-e1422160855872.png&quot; alt=&quot;第0章 咱们先来谈谈为什么要学习Linux系统第0章 咱们先来谈谈为什么要学习Linux系统&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Fedora 翻译为中文是“浅顶软呢男帽”的意思，翻译之后跟 Linux 系统很不搭界，所以更多人干脆将其音译为“费多拉”系统。Fedora Linux 是正正经经的红帽公司自己的产品，最初是为了给红帽企业版 Linux 制作和测试第三方软件而构建的产品，孕育了最早的开源社群，固定每 6 个月发布一个新版本，当前在全球已经有几百万的用户。&lt;/p&gt;
&lt;p&gt;Fedora 是桌面版本的 Linux 系统，可以理解成是微软公司的 Windows XP 或者 Windows 10。它的目标用户是应付日常的工作需要，而不会追求稳定性的人群。用户可以在这个系统中体验到最新的技术和工具，当这些技术和工具成熟后才会被移植到红帽企业版 Linux 中，因此 Fedora 也被称为 RHEL 系统的“试验田”。运维人员如果想每天都强迫自己多学点 Linux 知识，保持自己技术的领先性，就应该多关注此类 Linux 系统的发展变化和新特征，不断调整自己的学习方向。&lt;/p&gt;
&lt;h4&gt;Debian Linux&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://www.linuxprobe.com/wp-content/uploads/2015/01/debian.png&quot; alt=&quot;第0章 咱们先来谈谈为什么要学习Linux系统第0章 咱们先来谈谈为什么要学习Linux系统&quot; /&gt;&lt;/p&gt;
&lt;p&gt;一款基于 GNU 开源许可证的 Linux 系统，历史久远，最初发布于 1993 年 9 月。Debian 的名字取自创始人 Ian Murdock 和他女朋友 Debra 的姓氏组合。在维基百科中，Debian 被翻译为“蝶变”系统，多么浪漫而富有诗意的名字。但可惜国内的用户不买账，看着 Logo 一圈一圈的形状，硬生生地将经念歪了。这么多年下来，现在反而很少有人听说过蝶变系统这个名字了。&lt;/p&gt;
&lt;p&gt;Debian 系统具有很强的稳定性和安全性，并且提供了免费的基础支持，可以良好地适应各种硬件架构，以及提供近十万种不同的开源软件，在国外拥有很高的认可度和使用率。虽然 Debian 也是基于 Linux 内核，但是在实际操作中还是跟红帽公司的产品有一些差别，例如 RHEL 7 和 RHEL 8 分别使用 Yum 和 DNF 工具来安装软件，而 Debian 使用的则是 APT 工具。&lt;/p&gt;
&lt;h4&gt;Ubuntu Linux&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://www.linuxprobe.com/wp-content/uploads/2020/04/ubuntu-e1624206289343.png&quot; alt=&quot;第0章 咱们先来谈谈为什么要学习Linux系统第0章 咱们先来谈谈为什么要学习Linux系统&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ubuntu 是一款桌面版 Linux 系统，以 Debian 为蓝本进行修改和衍生而来，发布周期为 6 个月。Ubuntu 的中文音译为“乌班图”，这个词最初来自于非洲南部部落使用的祖鲁语，意思是“我的存在是因为大家的存在”，体现了一种谦卑、感恩的价值观，寓意非常好。&lt;/p&gt;
&lt;p&gt;Ubuntu 系统的第一个版本发布于 2004 年 10 月。2005 年 7 月，Ubuntu 基金会成立，Ubuntu 后续不断增加开发分支，有了桌面版系统、服务器版系统和手机版系统。据调查，Ubuntu 最高峰时的用户达到了 10 亿人。尽管 Ubuntu 基于 Debian 系统衍生而来，但会对系统进行深度化定制，因此两者之间的软件并不一定完全兼容。Ubuntu 系统现在由 Canonical 公司提供商业技术支持，只要购买付费技术支持服务就能获得帮助，桌面版系统最长时间 3 年，服务器版系统最长时间 5 年。&lt;/p&gt;
&lt;h4&gt;Kali Linux&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://www.linuxprobe.com/wp-content/uploads/2020/04/kali.jpg&quot; alt=&quot;第0章 咱们先来谈谈为什么要学习Linux系统第0章 咱们先来谈谈为什么要学习Linux系统&quot; /&gt;&lt;/p&gt;
&lt;p&gt;跟上面的呆萌大蜥蜴相比，Kali Linux 的 Logo 似乎有点凶巴巴，一副不好惹的样子。这款系统一般是供黑客或安全人员使用的，能够以此为平台对网站进行渗透测试，通俗来讲就是能“攻击”网站。Kali Linux 系统的前身名为 BackTrack，其设计用途就是进行数字鉴识和渗透测试，内置有 600 多款网站及系统的渗透测试软件，包括大名鼎鼎的 Nmap、Wireshark、sqlmap 等。Kali Linux 能够被安装到个人电脑、公司服务器，甚至手掌大小的树莓派（一款微型电脑）上，可以让人有一种随身携带了一个武器库的感觉，有机会真应该单独写本书聊聊它。&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;开源共享精神&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;一般情况下，软件的源代码只由编写者拥有，而开源（即开放源代码，Open Source Code）是指一种更自由的软件发布模式。简单来说，开源软件的特点就是把软件程序和源代码文件一起打包提供给用户，让用户在不受限制地使用某个软件功能的基础上还可以对代码按需修改，让软件更贴合硬件环境，让功能更符合工作需求。用户还可以将其编制成衍生产品再发布出去。用户一般享有使用自由、复制自由、修改自由、创建衍生品自由，以及收费自由。是的，您没有看错，用户具备创建衍生品和收费的自由。这也就是说，可以对一个开源软件进行深度定制化加工。如果修改过的程序更加好用，或者颇具新的特色，只要符合原作者的许可要求，我们就完全可以合法地将软件进行商标注册，以商业版的形式再发布出去，只要有新用户使用您的软件并支付相应的费用，那就是您的收入。这也正好符合了黑客和极客对自由的追求，因此在合作与竞争中，国内外的开源社区慢慢生长出了强健的根基，人气也非常高。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.linuxprobe.com/wp-content/uploads/2020/04/OSI_Approved_License.png&quot; alt=&quot;第0章 咱们先来谈谈为什么要学习Linux系统第0章 咱们先来谈谈为什么要学习Linux系统&quot; /&gt;&lt;/p&gt;
&lt;p&gt;但是，如果开源软件只单纯追求“自由”而牺牲了程序员的利益，这肯定会影响程序员的创作热情。为了平衡两者的关系，截至目前，世界上已经有 100 多种被开源促进组织（OSI，Open Source Initiative）确认的开源许可证，用于保护开源工作者的权益。对于那些只知道一味抄袭、篡改、破解或者盗版他人作品的不法之徒，终归会在某一天收到法院的传票。&lt;/p&gt;
&lt;p&gt;开源许可证总览：https://opensource.org/licenses/alphabetical&lt;/p&gt;
&lt;h2&gt;Chapter 2 Linux 基本命令&lt;/h2&gt;
&lt;h3&gt;执行命令的必备知识&lt;/h3&gt;
&lt;p&gt;既然 Linux 系统中已经有了 Bash 这么好用的“翻译官”，那么接下来就有必要好好学习一下怎么跟它沟通了。要想准确、高效地完成各种任务，仅依赖于命令本身是不够的，还应该根据实际情况来灵活调整各种命令的参数。比如，我们切寿司时尽管可以用菜刀，但米粒一定会撒得满地都是，因此寿司刀上设计的用于透气的圆孔就是为了更好地适应场景而额外增加的参数。当您学完本书并具备一定的工作经验之后，一定能够领悟 Linux 命令的奥秘。&lt;/p&gt;
&lt;p&gt;常见的执行 Linux 命令的格式是下面这样的。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;命令名称 [命令参数] [命令对象]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;命令名称&lt;/strong&gt;：就是语法中的“动词”，表达的是想要做的事情，例如创建用户、查看文件、重启系统等操作。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;命令参数&lt;/strong&gt;：用于对命令进行调整，让“修改”过的命令能更好地贴合工作需求，达到事半功倍的效果。就像买衣服一样，衣服的尺码总会感觉偏大或偏小，要么只能将就着穿，要么就再裁剪修改一下，而这种对命令进行“裁剪”的行为就是加参数。例如创建一个编码为 888 的用户、仅查看文件的前 20 行、重启系统前先提醒其他用户等。参数可以用长格式（完整的选项名称），也可以用短格式（单个字母的缩写），两者分别用“--”与“-”作为前缀（示例请见表 2-1）。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;表 2-1 Linux 命令参数的长格式与短格式示例&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;长格式&lt;/td&gt;
&lt;td&gt;man --help&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;短格式&lt;/td&gt;
&lt;td&gt;man -h&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;命令对象&lt;/strong&gt;：一般指要处理的文件、目录、用户等资源名称，也就是命令执行后的“承受方”。例如创建一位叫小明的用户、查看一个叫工资表的文件、重启一个 IP 为 192.168.10.10 的系统等。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Tips&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;命令名称、命令参数与命令对象之间要用空格进行分隔，且字母严格区分大小写。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;常用的按键&lt;/h4&gt;
&lt;p&gt;Tab 键：&lt;/p&gt;
&lt;p&gt;​ 在 Bash 解释器的快捷键中，Tab 键绝对是使用频率最高的，它能够实现对命令、参数或文件的内容补全。例如，如果想执行 reboot 重启命令，但一时想不起来该命令的完整拼写，则可以这样输入：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;ctrl+c&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;​ 当同时按下键盘上的 Ctrl 和字母 C 的时候，意味着终止当前进程的运行。假如执行了一个错误命令，或者是执行某个命令后迟迟无法结束，这时就可以冷静地按下 Ctrl+C 组合键，命令行终端的控制权会立刻回到我们手中。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;Ctrl+d&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;​ 表示键盘输入结束，中断当前的通信或从文件中退出。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;ctrl+l&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;​ 清空当前终端中已有的内容（相当于清屏操作）。&lt;/p&gt;
&lt;h3&gt;2.1 常用系统工作命令&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;echo&lt;/td&gt;
&lt;td&gt;显示一串字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;passwd&lt;/td&gt;
&lt;td&gt;修改密码&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;clear&lt;/td&gt;
&lt;td&gt;清除显示器&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lpr&lt;/td&gt;
&lt;td&gt;打印&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;shutdown&lt;/td&gt;
&lt;td&gt;关机&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;reboot&lt;/td&gt;
&lt;td&gt;重启计算机&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ps&lt;/td&gt;
&lt;td&gt;查看目前程序执行情况&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;top&lt;/td&gt;
&lt;td&gt;查看目前程序执行情景和内存使用情况&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;kill&lt;/td&gt;
&lt;td&gt;终止一个进程&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;date&lt;/td&gt;
&lt;td&gt;更改或者查看目前时间&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;echo 命令&lt;/h4&gt;
&lt;p&gt;echo 命令用于在终端设备上输出字符串或变量提取后的值，语法格式为&lt;code&gt;echo [字符串] [$变量]&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这是 Linux 系统中最常用的几个命令之一，它的操作却非常简单，执行&lt;code&gt;echo 字符串&lt;/code&gt;或&lt;code&gt;echo $变量&lt;/code&gt;就行，其中$符号的意思是提取变量的实际值，以便后续的输出操作。&lt;/p&gt;
&lt;h4&gt;date 命令&lt;/h4&gt;
&lt;p&gt;date 命令用于显示或设置系统的时间与日期，语法格式为&lt;code&gt;date [+指定的格式]&lt;/code&gt;。&lt;/p&gt;
&lt;h4&gt;timedatectl 命令&lt;/h4&gt;
&lt;p&gt;timedatectl 命令用于设置系统的时间，英文全称为“time date control”，语法格式为&lt;code&gt;timedatectl [参数]&lt;/code&gt;。&lt;/p&gt;
&lt;h4&gt;reboot 命令&lt;/h4&gt;
&lt;p&gt;reboot 命令用于重启系统，输入该命令后按回车键执行即可。&lt;/p&gt;
&lt;p&gt;由于重启计算机这种操作会涉及硬件资源的管理权限，因此最好是以 root 管理员的身份来重启，普通用户在执行该命令时可能会被拒绝。&lt;/p&gt;
&lt;h4&gt;poweroff 命令&lt;/h4&gt;
&lt;p&gt;poweroff 命令用于关闭系统，输入该命令后按回车键执行即可。&lt;/p&gt;
&lt;p&gt;与上面相同，该命令也会涉及硬件资源的管理权限，因此最好还是以 root 管理员的身份来关闭电脑&lt;/p&gt;
&lt;h4&gt;wget 命令&lt;/h4&gt;
&lt;p&gt;wget 命令用于在终端命令行中下载网络文件，英文全称为“web get”，语法格式为“wget [参数] 网址”。&lt;/p&gt;
&lt;p&gt;借助于 wget 命令，可以无须打开浏览器，直接在命令行界面中就能下载文件。如果您没有 Linux 系统的管理经验，当前只需了解一下 wget 命令的参数以及作用，然后看一眼下面的演示实验就够了，切记不要急于求成。后面章节将逐步讲解 Linux 系统的配置管理方法，可以等掌握了网卡的配置方法后再来进行这个实验操作。表 2-6 所示为 wget 命令中的参数以及参数的作用。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;-b&lt;/td&gt;
&lt;td&gt;后台下载模式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-P&lt;/td&gt;
&lt;td&gt;下载到指定目录&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-t&lt;/td&gt;
&lt;td&gt;最大尝试次数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-c&lt;/td&gt;
&lt;td&gt;断点续传&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-p&lt;/td&gt;
&lt;td&gt;下载页面内所有资源，包括图片、视频等&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-r&lt;/td&gt;
&lt;td&gt;递归下载&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;history 命令&lt;/h4&gt;
&lt;p&gt;history 命令用于显示执行过的命令历史，语法格式为“history [-c]”。&lt;/p&gt;
&lt;p&gt;history 命令应该是运维人员最喜欢的命令。执行 history 命令能显示出当前用户在本地计算机中执行过的最近 1000 条命令记录。如果觉得 1000 不够用，可以自定义/etc/profile 文件中的 HISTSIZE 变量值。在使用 history 命令时，可以使用-c 参数清空所有的命令历史记录。还可以使用“!编码数字”的方式来重复执行某一次的命令。总之，history 命令有很多有趣的玩法等待您去开发。&lt;/p&gt;
&lt;h3&gt;2.2 文件管理&lt;/h3&gt;
&lt;h4&gt;管理文件和目录的命令&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;pwd&lt;/td&gt;
&lt;td&gt;显示当前工作目录&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cd&lt;/td&gt;
&lt;td&gt;将活动目录更改为指定的路径&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ls&lt;/td&gt;
&lt;td&gt;显示指定工作目录的内容&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cat&lt;/td&gt;
&lt;td&gt;显示文件的内容&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;grep&lt;/td&gt;
&lt;td&gt;在文件中查找某字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cp&lt;/td&gt;
&lt;td&gt;复制文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;touch&lt;/td&gt;
&lt;td&gt;创建文件&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;•locate 命令用于查找符合条件的文件。&lt;/p&gt;
&lt;p&gt;•whereis 命令也用于查找文件。&lt;/p&gt;
&lt;p&gt;•which 命令会在环境变量$PATH 设置的目录里查找符合条件的文件。&lt;/p&gt;
&lt;p&gt;•mkdir 是 make directory（创建目录）的缩写，用于创建新的目录。&lt;/p&gt;
&lt;p&gt;•rmdir 用来删除一个空的目录，rm 命令来删除非空目录。&lt;/p&gt;
&lt;h5&gt;pwd 命令&lt;/h5&gt;
&lt;p&gt;pwd 是 Print Working Directory（打印工作目录）的缩写，其功能是显示当前工作目录。&lt;/p&gt;
&lt;h5&gt;cd 命令&lt;/h5&gt;
&lt;p&gt;cd 是 Change Directory（更改目录）的缩写，其功能是将活动目录更改为指定的路径。&lt;/p&gt;
&lt;h5&gt;ls 命令&lt;/h5&gt;
&lt;p&gt;ls（英文全拼： list directory contents）命令用于显示指定工作目录下之内容（列出目前工作目录所含的文件及子目录)。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;-a 显示所有文件及目录 (&lt;strong&gt;.&lt;/strong&gt; 开头的隐藏文件也会列出)&lt;/li&gt;
&lt;li&gt;-d 只列出目录（不递归列出目录内的文件）。&lt;/li&gt;
&lt;li&gt;-l 以长格式显示文件和目录信息，包括权限、所有者、大小、创建时间等。&lt;/li&gt;
&lt;li&gt;-r 倒序显示文件和目录。&lt;/li&gt;
&lt;li&gt;-R 递归显示目录中的所有文件和子目录。&lt;/li&gt;
&lt;li&gt;-t 将按照修改时间排序，最新的文件在最前面。&lt;/li&gt;
&lt;li&gt;-A 同 -a ，但不列出 &quot;.&quot; (目前目录) 及 &quot;..&quot; (父目录)&lt;/li&gt;
&lt;li&gt;-F 在列出的文件名称后加一符号；例如可执行档则加 &quot;*&quot;, 目录则加 &quot;/&quot;&lt;/li&gt;
&lt;li&gt;-s 大小(size)，按文件大小排序。&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;cat 命令&lt;/h5&gt;
&lt;p&gt;cat（英文全拼：concatenate）命令用于连接文件并打印到标准输出设备上。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;重定向&lt;/strong&gt;:&lt;code&gt;&amp;gt;&lt;/code&gt;符号&lt;/p&gt;
&lt;p&gt;把 textfile1 的文档内容加上行号后输入 textfile2 这个文档里：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat -n textfile1 &amp;gt; textfile2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;把 textfile1 和 textfile2 的文档内容加上行号（空白行不加）之后将内容附加到 textfile3 文档里：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat -b textfile1 textfile2 &amp;gt;&amp;gt; textfile3
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;grep 命令&lt;/h5&gt;
&lt;p&gt;Linux grep (global regular expression) 命令用于查找文件里符合条件的字符串或正则表达式。&lt;/p&gt;
&lt;p&gt;grep 指令用于查找内容包含指定的范本样式的文件，如果发现某文件的内容符合所指定的范本样式，预设 grep 指令会把含有范本样式的那一列显示出来。若不指定任何文件名称，或是所给予的文件名为 &lt;strong&gt;-&lt;/strong&gt;，则 grep 指令会从标准输入设备读取数据。&lt;/p&gt;
&lt;p&gt;1、在文件 file.txt 中查找字符串 &quot;hello&quot;，并打印匹配的行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;grep hello file.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;cp 命令&lt;/h5&gt;
&lt;p&gt;•cp: 复制文件或目录，语法为“cp 源文件 目标文件”&lt;/p&gt;
&lt;h5&gt;touch 命令&lt;/h5&gt;
&lt;p&gt;Linux touch 命令用于修改文件或者目录的时间属性，包括存取时间和更改时间。若文件不存在，系统会建立一个新的文件。&lt;/p&gt;
&lt;p&gt;使用指令&quot;touch&quot;时，如果指定的文件不存在，则将创建一个新的空白文件。例如，在当前目录下，使用该指令创建一个空白文件&quot;file&quot;，输入如下命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;touch file            #创建一个名为“file”的新的空白文件
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;mv 命令&lt;/h5&gt;
&lt;h5&gt;rm 命令&lt;/h5&gt;
&lt;h4&gt;文件阅读命令&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;head&lt;/td&gt;
&lt;td&gt;查看文件开头部分&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tail&lt;/td&gt;
&lt;td&gt;查看文件结尾的 10 行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;less&lt;/td&gt;
&lt;td&gt;分页工具，一页一页查看信息&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;more&lt;/td&gt;
&lt;td&gt;分页工具，一页一页查看信息&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;文件备份和压缩命令&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;bzip2/bunzip2&lt;/td&gt;
&lt;td&gt;扩展文件名为 bz2 的压缩/解压工具&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gzip/gunzip&lt;/td&gt;
&lt;td&gt;扩展文件名为 gz 的压缩/解压工具&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;zip/unzip&lt;/td&gt;
&lt;td&gt;扩展文件名为 zip 的压缩/解压工具&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tar&lt;/td&gt;
&lt;td&gt;创建备份和归档&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;有关磁盘空间的命令&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;mount&lt;/td&gt;
&lt;td&gt;挂上文件系统&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;umount&lt;/td&gt;
&lt;td&gt;卸下已经挂上的文件系统&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;df&lt;/td&gt;
&lt;td&gt;检查个硬盘分区和已挂上来的文件系统的磁盘空间&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;du&lt;/td&gt;
&lt;td&gt;显示文件目录或大小&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fsck&lt;/td&gt;
&lt;td&gt;主要是检查和修复 Linux 文件系统&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;2.3 权限管理&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;chmod&lt;/td&gt;
&lt;td&gt;改变权限&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;useradd&lt;/td&gt;
&lt;td&gt;增加用户&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;su&lt;/td&gt;
&lt;td&gt;修改用户&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;2.4 进程管理&lt;/h3&gt;
&lt;h4&gt;ps 命令&lt;/h4&gt;
&lt;p&gt;ps 命令用于查看系统中的进程状态，英文全称为“processes”，语法格式为“ps [参数]”。&lt;/p&gt;
&lt;p&gt;估计读者在第一次执行这个命令时都要惊呆一下—怎么会有这么多输出值，这可怎么看得过来？其实，高手通常会将 ps 命令与第 3 章的管道符技术搭配使用，用来抓取与某个指定服务进程相对应的 PID 号码。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;-a&lt;/td&gt;
&lt;td&gt;显示所有进程（包括其他用户的进程）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-u&lt;/td&gt;
&lt;td&gt;用户以及其他详细信息&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-x&lt;/td&gt;
&lt;td&gt;显示没有控制终端的进程&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Linux 系统中时刻运行着许多进程，如果能够合理地管理它们，则可以优化系统的性能。在 Linux 系统中有 5 种常见的进程状态，分别为运行、中断、不可中断、僵死与停止，其各自含义如下所示。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;R（运行）&lt;/strong&gt;：进程正在运行或在运行队列中等待。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;S（中断）&lt;/strong&gt;：进程处于休眠中，当某个条件形成后或者接收到信号时，则脱离该 状态。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;D（不可中断）&lt;/strong&gt;：进程不响应系统异步信号，即便用 kill 命令也不能将其中断。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Z（僵死）&lt;/strong&gt;：进程已经终止，但进程描述符依然存在, 直到父进程调用 wait4()系统函数后将进程释放。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;T（停止）&lt;/strong&gt;：进程收到停止信号后停止运行。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;pstree 命令&lt;/h4&gt;
&lt;p&gt;pstree 命令用于以树状图的形式展示进程之间的关系，英文全称为“process tree”，输入该命令后按回车键执行即可。&lt;/p&gt;
&lt;p&gt;前文提到，在执行 ps 命令后，产生的信息量太大又没有规律，很难让人再想看第二眼。如果想让进程以树状图的形式，有层次地展示出进程之间的关系，则可以使用 pstree 命令。&lt;/p&gt;
&lt;h4&gt;top 命令&lt;/h4&gt;
&lt;p&gt;top 命令用于动态地监视进程活动及系统负载等信息，输入该命令后按回车键执行即可。&lt;/p&gt;
&lt;p&gt;前面介绍的命令都是静态地查看系统状态，不能实时滚动最新数据，而 top 命令能够动态地查看系统状态，因此完全可以将它看作是 Linux 中“强化版的 Windows 任务管理器”。&lt;/p&gt;
&lt;h4&gt;nice 命令&lt;/h4&gt;
&lt;p&gt;nice 命令用于调整进程的优先级，语法格式为“nice 优先级数字 服务名称”。&lt;/p&gt;
&lt;p&gt;在 top 命令输出的结果中，PR 和 NI 值代表的是进程的优先级，数字越低（取值范围是-20 ～ 19），优先级越高。在日常的生产工作中，可以将一些不重要进程的优先级调低，让紧迫的服务更多地利用 CPU 和内存资源，以达到合理分配系统资源的目的。&lt;/p&gt;
&lt;h4&gt;pidof 命令&lt;/h4&gt;
&lt;p&gt;pidof 命令用于查询某个指定服务进程的 PID 号码值，语法格式为“pidof [参数] 服务名称”。第 2 章 新手必须掌握的 Linux 命令第 2 章 新手必须掌握的 Linux 命令&lt;/p&gt;
&lt;p&gt;每个进程的进程号码值（PID）是唯一的，可以用于区分不同的进程。&lt;/p&gt;
&lt;h4&gt;kill 命令&lt;/h4&gt;
&lt;p&gt;kill 命令用于终止某个指定 PID 值的服务进程，语法格式为“kill [参数] 进程的 PID”。&lt;/p&gt;
&lt;h4&gt;killall 命令&lt;/h4&gt;
&lt;p&gt;killall 命令用于终止某个指定名称的服务所对应的全部进程，语法格式为“killall [参数] 服务名称”。&lt;/p&gt;
&lt;h3&gt;2.5 有关网络操作的命令&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FTP&lt;/td&gt;
&lt;td&gt;传送文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;telnet&lt;/td&gt;
&lt;td&gt;远程登陆&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bye&lt;/td&gt;
&lt;td&gt;结束连线并结束程序&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rlogin&lt;/td&gt;
&lt;td&gt;远端登入&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ping&lt;/td&gt;
&lt;td&gt;检查主机&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;netstat&lt;/td&gt;
&lt;td&gt;显示网络状态&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;ping 命令&lt;/h4&gt;
&lt;p&gt;ping 命令用于测试主机之间的网络连通性，语法格式为“ping [参数] 主机地址”。&lt;/p&gt;
&lt;p&gt;即便大家没有学习过 Linux 系统，相信也肯定见过别人使用 ping 命令。执行 ping 命令时，系统会使用 ICMP 向远端主机发出要求回应的信息，若连接远端主机的网络没有问题，远端主机会回应该信息。由此可见，ping 命令可用于判断远端主机是否在线并且网络是否正常。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;-c&lt;/td&gt;
&lt;td&gt;总共发送次数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-l&lt;/td&gt;
&lt;td&gt;指定网卡名称&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-i&lt;/td&gt;
&lt;td&gt;每次间隔时间（秒）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-W&lt;/td&gt;
&lt;td&gt;最长等待时间（秒）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;netstat 命令&lt;/h4&gt;
&lt;p&gt;netstat 命令用于显示如网络连接、路由表、接口状态等的网络相关信息，英文全称为“network status”，语法格式为“netstat [参数]”。&lt;/p&gt;
&lt;p&gt;只要 netstat 命令使用得当，便可以查看到网络状态的方方面面信息。我们找出一些常用的参数让大家感受一下。&lt;/p&gt;
&lt;p&gt;表 2-12 netstat 命令中的参数以及作用&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;-a&lt;/th&gt;
&lt;th&gt;显示所有连接中的 Socket&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;-p&lt;/td&gt;
&lt;td&gt;显示正在使用的 Socket 信息&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-t&lt;/td&gt;
&lt;td&gt;显示 TCP 协议的连接状态&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-u&lt;/td&gt;
&lt;td&gt;显示 UDP 协议的连接状态&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-n&lt;/td&gt;
&lt;td&gt;使用 IP 地址，不使用域名&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-l&lt;/td&gt;
&lt;td&gt;仅列出正在监听的服务状态&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-i&lt;/td&gt;
&lt;td&gt;显示网卡列表信息&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-r&lt;/td&gt;
&lt;td&gt;显示路由表信息&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;输入输出重定向&lt;/h3&gt;
&lt;p&gt;既然已经在上一章学完了几乎所有基础且常用的 Linux 命令，那么接下来的任务就是把多个 Linux 命令适当地组合到一起，使其协同工作，以便我们更加高效地处理数据。要做到这一点，就必须搞明白命令的输入重定向和输出重定向的原理。&lt;/p&gt;
&lt;p&gt;简而言之，输入重定向是指把文件导入到命令中，而输出重定向则是指把原本要输出到屏幕的数据信息写入到指定文件中。在日常的学习和工作中，相较于输入重定向，我们使用输出重定向的频率更高，所以又将输出重定向分为了标准输出重定向和错误输出重定向两种不同的技术，以及覆盖写入与追加写入两种模式。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;标准输入重定向（STDIN，文件描述符为 0）&lt;/strong&gt;：默认从键盘输入，也可从其他文件或命令中输入。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;标准输出重定向（STDOUT，文件描述符为 1）&lt;/strong&gt;：默认输出到屏幕。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;错误输出重定向（STDERR，文件描述符为 2）&lt;/strong&gt;：默认输出到屏幕。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;输入重定向中用到的符号及其作用&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;符号&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;命令 &amp;lt; 文件&lt;/td&gt;
&lt;td&gt;将文件作为命令的标准输入&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;命令 &amp;lt;&amp;lt; 分界符&lt;/td&gt;
&lt;td&gt;从标准输入中读入，直到遇见分界符才停止&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;命令 &amp;lt; 文件 1 &amp;gt; 文件 2&lt;/td&gt;
&lt;td&gt;将文件 1 作为命令的标准输入并将标准输出到文件 2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;输出重定向中用到的符号及其作用&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;符号&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;命令 &amp;gt; 文件&lt;/td&gt;
&lt;td&gt;将标准输出重定向到一个文件中（清空原有文件的数据）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;命令 2&amp;gt; 文件&lt;/td&gt;
&lt;td&gt;将错误输出重定向到一个文件中（清空原有文件的数据）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;命令 &amp;gt;&amp;gt; 文件&lt;/td&gt;
&lt;td&gt;将标准输出重定向到一个文件中（追加到原有内容的后面）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;命令 2&amp;gt;&amp;gt; 文件&lt;/td&gt;
&lt;td&gt;将错误输出重定向到一个文件中（追加到原有内容的后面）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;命令 &amp;gt;&amp;gt; 文件 2&amp;gt;&amp;amp;1 或 命令 &amp;amp;&amp;gt;&amp;gt; 文件&lt;/td&gt;
&lt;td&gt;将标准输出与错误输出共同写入到文件中（追加到原有内容的后面）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;管道命令符&lt;/h3&gt;
&lt;p&gt;同时按下键盘上的 Shift+反斜杠（\）键即可输入管道符，其执行格式为“命令 A | 命令 B”。管道命令符的作用也可以用一句话概括为“&lt;strong&gt;把前一个命令原本要输出到屏幕的信息当作后一个命令的标准输入&lt;/strong&gt;”。&lt;/p&gt;
&lt;h3&gt;常用的转义字符&lt;/h3&gt;
&lt;p&gt;为了能够更好地理解用户的表达，Shell 解释器还提供了特别丰富的转义字符来处理输入的特殊数据。刘遄老师以 10 多年的工作和培训为基础，愣是用了两周时间从数十个转义字符中提炼出了 4 个最常用的转义字符！这件事情也让我深刻反省了很长时间，原本认为图书写得越厚，作者越是大牛，现在发现这种观念完全是错误的，希望读者在读完本书后能体会到刘遄老师的用心付出。&lt;/p&gt;
&lt;p&gt;4 个最常用的转义字符如下所示。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;反斜杠（\）&lt;/strong&gt;：使反斜杠后面的一个变量变为单纯的字符。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;单引号（&apos; &apos;）&lt;/strong&gt;：转义其中所有的变量为单纯的字符串。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;双引号（&quot; &quot;）&lt;/strong&gt;：保留其中的变量属性，不进行转义处理。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;反引号（&lt;code&gt; &lt;/code&gt;）&lt;/strong&gt;：把其中的命令执行后返回结果。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Chapter 3 Linux 文件系统&lt;/h2&gt;
&lt;h3&gt;文件系统概述&lt;/h3&gt;
&lt;p&gt;文件系统是对一个存储设备上的数据和元数据进行组织的机制，其目的是易于实现数据的查询和存取。不同的操作系统具有不同的文件系统，一个操作系统可能无法兼容所有的文件系统。Linux 文件系统接口实现为分层的体系结构，从而将用户接口层、文件系统实现和操作存储设备的驱动程序分开。&lt;/p&gt;
&lt;p&gt;文件系统是操作系统中负责管理持久数据的子系统，说简单点，就是负责把用户的文件存到磁盘硬件中，因为即使计算机断电了，磁盘里的数据并不会丢失，所以可以持久化的保存文件。&lt;/p&gt;
&lt;p&gt;文件系统的基本数据单位是文件，它的目的是对磁盘上的文件进行组织管理，那组织的方式不同，就会形成不同的文件系统。&lt;/p&gt;
&lt;p&gt;Linux 最经典的一句话是：&lt;strong&gt;「一切皆文件」&lt;/strong&gt;，不仅普通的文件和目录，就连块设备、管道、socket 等，也都是统一交给文件系统管理的。&lt;/p&gt;
&lt;p&gt;要透彻理解文件系统，需要明确以下几个名词：&lt;/p&gt;
&lt;p&gt;存储介质&lt;/p&gt;
&lt;p&gt;用以存储数据的物理设备，如软盘、硬盘、光盘、磁带、网络存储设备等。&lt;/p&gt;
&lt;p&gt;磁盘的分割&lt;/p&gt;
&lt;p&gt;对于容量较大的存储介质来说，通常是硬盘。在使用时，需要合理地规划分区。常用的 Linux 磁盘分割工具有 fdisk、cfdisk、parted 等。此外，还有 PQ 等第三方工具。&lt;/p&gt;
&lt;p&gt;创建文件系统&lt;/p&gt;
&lt;p&gt;创建新的文件系统是一个过程，通常成为初始化或格式化，整个过程是针对存储介质进行的。一般情况下，操作系统都有自己相应的工具。创建文件系统是在分割磁盘空间的基础上进行的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;挂载&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在 Linux 或 Unix 系统中，没有磁盘分区的逻辑概念（如 C 盘、D 盘等），仍和一个种类的文件系统被创建后，都需要挂载到某个特定的目录才能使用，这个过程相当于激活一个文件系统，使它能够使用。&lt;/p&gt;
&lt;p&gt;Windows 的文件系统挂载使用其内部机制完成，用户基本无法探知其过程。而 Linux 使用 mount 工具对文件系统进行挂载。挂载文件系统时，需要明确挂载点。在创建文件系统后，操作系统会提示将此文件系统挂载至哪个位置，这个位置就是挂载点，通常选择“/”即根目录。挂载点的实质是一个空的目录。&lt;/p&gt;
&lt;h3&gt;文件和文件类型&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;怎样将“散落”存在磁盘等存储介质上的二进制数据，变换、映射为拿名字即可访问的“信息”是文件系统要解决的问题。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;从用户应用软件到文件系统介质示意图&lt;/p&gt;
&lt;h4&gt;定义&lt;/h4&gt;
&lt;p&gt;文件：是 Linux 用来存储信息的基本结构， 它是被命名（称为文件名）的、存储在某种媒介（如磁盘、光盘和磁带等）上的一组信息的集合。&lt;/p&gt;
&lt;p&gt;文件系统：文件系统实现数据存储的物理（介质）独立性。&lt;/p&gt;
&lt;h4&gt;文件命名规则&lt;/h4&gt;
&lt;p&gt;❑Linux 文件名的最大长度为 256 个字符&lt;/p&gt;
&lt;p&gt;❑ 通常由字母、数字、“.”（点号）、“_”（下划线）或“-”（减号）组成，文件名中不能含有“/”符号。&lt;/p&gt;
&lt;p&gt;❑ 避免使用具有特别意义的字符：? * @ # $ &amp;amp; () \ | ; ‘ “ ` &amp;lt; &amp;gt; [ ]等（大括号{}不是？）&lt;/p&gt;
&lt;p&gt;❑ 隐藏文件一般是以“.”符号开头；&lt;/p&gt;
&lt;p&gt;❑ 在文件名中的空格或制表符，在引用文件时必须用引号将其括起来；&lt;/p&gt;
&lt;p&gt;❑ 避免使用 ‘+’和‘-‘符号作为文件名的第一个字符&lt;/p&gt;
&lt;p&gt;❑ 大小写敏感&lt;/p&gt;
&lt;h4&gt;文件类型&lt;/h4&gt;
&lt;p&gt;那就先简单说说 Linux 中的文件类型，主要关注普通文件、目录文件和符号连接文件。
三种基本的文件类型&lt;/p&gt;
&lt;p&gt;普通文件（regular file）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;二进制文件&lt;/p&gt;
&lt;p&gt;以二进制形式存储在电脑中
用户一般只有通过相应的软件才能将其显示出来
一般是可执行程序、图形、图像、声音等&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;文本(txt)文件&lt;/p&gt;
&lt;p&gt;以文本的 ASCII 码形式存储在电脑中，其内容都是可读字符。
以“行”为基本结构的信息组织和存储方式&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;从本质上来说他们之间没有什么区别，因为他们在硬盘上都有一种的存放方式--二进制。
但是如果要对他们有些区分的话，那可以这样理解。
我们现在的每个字符 由一个或多个字节组成，每个字节都是用的-128—127 之间的部分数值来表示的，也就是说，-128——127 之间还有一些数据没有对应任何字符的任何字节。
如果一个文件中的每个字节的内容都是可以表示成字符的数据，我们就可以称这个文件为文本文件，可见，文本文件只是二进制文件中的一种特例，
为了与文 本文件相区别，人们又把除了文本文件以外的文件称为二进制文件，
由于很难严格区分文本文件和二进制文件的概念，所以我们可以简单地认为，如果一个文件专门 用于存储文本字符的数据，没有包含字符以外的其他数据，我们就称之为文本文件，除此之外的文件就是二进制文件&lt;/p&gt;
&lt;p&gt;目录文件（directory）&lt;/p&gt;
&lt;p&gt;是一个包含文件的容器，用于存放目录中文件列表信息。&lt;/p&gt;
&lt;p&gt;设备文件&lt;/p&gt;
&lt;p&gt;块设备（block device）：是一种具有一定结构的随机存取设备，对这种设备的读写是按块进行的&lt;/p&gt;
&lt;p&gt;◆ 以块为单位进行随机存取。
◆ 常见块设备：软盘、光盘、硬盘。&lt;/p&gt;
&lt;p&gt;字符设备（Character device）：这是一个顺序的数据流设备，对这种设备的读写是按字符进行的，而且这些字符是连续地形成一个数据流。&lt;/p&gt;
&lt;p&gt;◆ 以单个字符为单位进行顺序存取。
◆ 常见的字符设备：打印机、终端、键盘、鼠标&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;普通文件（-）
&lt;ol&gt;
&lt;li&gt;从 Linux 的角度来说，类似 mp4、pdf、html 这样应用层面上的文件类型都属于普通文件&lt;/li&gt;
&lt;li&gt;Linux 用户可以根据访问权限对普通文件进行查看、更改和删除&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;目录文件（d，directory file）
&lt;ol&gt;
&lt;li&gt;目录文件对于用惯 Windows 的用户来说不太容易理解，目录也是文件的一种&lt;/li&gt;
&lt;li&gt;目录文件包含了各自目录下的文件名和指向这些文件的指针，打开目录事实上就是打开目录文件，只要有访问权限，你就可以随意访问这些目录下的文件（普通文件的执行权限就是目录文件的访问权限），但是只有内核的进程能够修改它们&lt;/li&gt;
&lt;li&gt;虽然不能修改，但是我们能够通过 vim 去查看目录文件的内容&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;符号链接（l，symbolic link）
&lt;ol&gt;
&lt;li&gt;这种类型的文件类似 Windows 中的快捷方式，是指向另一个文件的间接指针，也就是我们常说的软链接&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;块设备文件（b，block）和字符设备文件（c，char）
&lt;ol&gt;
&lt;li&gt;这些文件一般隐藏在/dev 目录下，在进行设备读取和外设交互时会被使用到&lt;/li&gt;
&lt;li&gt;比如磁盘光驱就是块设备文件，串口设备则属于字符设备文件&lt;/li&gt;
&lt;li&gt;系统中的所有设备要么是块设备文件，要么是字符设备文件，无一例外&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;FIFO（p，pipe）
&lt;ol&gt;
&lt;li&gt;管道文件主要用于进程间通讯。比如使用 mkfifo 命令可以创建一个 FIFO 文件，启用一个进程 A 从 FIFO 文件里读数据，启动进程 B 往 FIFO 里写数据，先进先出，随写随读。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;套接字（s，socket）
&lt;ol&gt;
&lt;li&gt;用于进程间的网络通信，也可以用于本机之间的非网络通信&lt;/li&gt;
&lt;li&gt;这些文件一般隐藏在/var/run 目录下，证明着相关进程的存在&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Linux 的文件是没有所谓的扩展名的，一个 Linux 文件能不能被执行与它是否可执行的属性有关，只要你的权限中有 x ，比如[ -rwx-r-xr-x ] 就代表这个文件可以被执行，与文件名没有关系。跟在 Windows 下能被执行的文件扩展名通常是 .com .exe .bat 等不同。&lt;/p&gt;
&lt;p&gt;不过，可以被执行跟可以执行成功不一样。比如在 root 主目彔下的 install.log 是一个文本文件，修改权限成为 -rwxrwxrwx 后这个文件能够真的执行成功吗？ 当然不行，因为它的内容根本就没有可以执行的数据。所以说，这个 x 代表这个文件具有可执行的能力， 但是能不能执行成功，当然就得要看该文件的内容了。
虽然如此，不过我们仍然希望能从扩展名来了解该文件是什么东西，所以一般我们还是会以适当的扩展名来表示该文件是什么种类的。&lt;/p&gt;
&lt;p&gt;所以 Linux 系统上的文件名真的只是让你了解该文件可能的用途而已， 真正的执行与否仍然需要权限的规范才行。比如常见的/bin/ls 这个显示文件属性的指令要是权限被修改为无法执行，那么 ls 就变成不能执行了。这种问题最常发生在文件传送的过程中。例如你在网络上下载一个可执行文件，但是偏偏在你的 Linux 系统中就是无法执行，那就可能是档案的属性被改变了。而且从网络上传送到你 的 Linux 系统中，文件的属性权限确实是会被改变的&lt;/p&gt;
&lt;p&gt;Linux 文件系统会为每个文件分配两个数据结构：&lt;strong&gt;索引节点（index node）&lt;/strong&gt; 和 &lt;strong&gt;目录项（directory entry）&lt;/strong&gt;，它们主要用来记录文件的元信息和目录层次结构。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;索引节点，也就是 &lt;code&gt;inode&lt;/code&gt;，用来记录文件的元信息，比如 inode 编号、文件大小、访问权限、创建时间、修改时间、数据在磁盘的位置等等。索引节点是文件的唯一标识，它们之间一一对应，也同样都会被存储在硬盘中，所以索引节点同样占用磁盘空间。&lt;/li&gt;
&lt;li&gt;目录项，也就是 &lt;code&gt;dentry&lt;/code&gt;，用来记录文件的名字、索引节点指针以及与其他目录项的层级关联关系。多个目录项关联起来，就会形成目录结构，但它与索引节点不同的是，目录项是由内核维护的一个数据结构，不存放于磁盘，而是缓存在内存。
由于索引节点唯一标识一个文件，而目录项记录着文件的名，所以目录项和索引节点的关系是多对一，也就是说，一个文件可以有多个别字。比如，硬链接的实现就是多个目录项中的索引节点指向同一个文件。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;注意，目录也是文件，也是用索引节点唯一标识，和普通文件不同的是，普通文件在磁盘里面保存的是文件数据，而目录文件在磁盘里面保存子目录或文件。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;目录项和目录是一个东西吗？
虽然名字很相近，但是它们不是一个东西，目录是个文件，持久化存储在磁盘，而目录项是内核一个数据结构，缓存在内存。
如果查询目录频繁从磁盘读，效率会很低，所以内核会把已经读过的目录用目录项这个数据结构缓存在内存，下次再次读到相同的目录时，只需从内存读就可以，大大提高了文件系统的效率。
注意，目录项这个数据结构不只是表示目录，也是可以表示文件的。
那文件数据是如何存储在磁盘的呢？
磁盘读写的最小单位是扇区，扇区的大小只有 512B 大小，很明显，如果每次读写都以这么小为单位，那这读写的效率会非常低。
所以，文件系统把多个扇区组成了一个逻辑块，每次读写的最小单位就是逻辑块（数据块），Linux 中的逻辑块大小为 4KB，也就是一次性读写 8 个扇区，这将大大提高了磁盘的读写的效率。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;文件系统结构&lt;/h3&gt;
&lt;p&gt;文件系统指文件存在的物理空间&lt;/p&gt;
&lt;p&gt;Linux 文件系统由一组普通文件、目录文件、设备文件和链接（符号链接、硬链接）组成。&lt;/p&gt;
&lt;p&gt;在 Linux 中支持多种不同的文件系统&lt;/p&gt;
&lt;p&gt;Linux 将分属不同分区的、单独的文件系统整理形成一个系统的，总的目录层次结构，也即树状、层次结构。&lt;/p&gt;
&lt;p&gt;树状层次结构使得 Linux 核心的其它部分及系统中运行的程序会看到统一的文件系统，为用户提供了一种组织、检索和管理信息的便捷、高效的方法。&lt;/p&gt;
&lt;p&gt;文件系统-相关基本概念&lt;/p&gt;
&lt;p&gt;§当前目录即当前工作目录、环境变量 PWD&lt;/p&gt;
&lt;p&gt;§用户主目录：/home/&amp;lt;username&amp;gt;; 环境变量 HOME、~&lt;/p&gt;
&lt;p&gt;§绝对路径&lt;/p&gt;
&lt;p&gt;§相对路径&lt;/p&gt;
&lt;p&gt;§符号链接（可跨文件系统）&lt;/p&gt;
&lt;p&gt;§软链接（不可跨文件系统）&lt;/p&gt;
&lt;h4&gt;路径：绝对路径和相对路径&lt;/h4&gt;
&lt;p&gt;§linux 系统中每个文件都有一个唯一的路径名。路径名表明了文件的位置。&lt;/p&gt;
&lt;p&gt;§路径名分为两种：绝对路径和相对路径&lt;/p&gt;
&lt;p&gt;§绝对路径指出从根目录到此文件的路径，一般来说由/符号开始。&lt;/p&gt;
&lt;p&gt;§相对路径指明从当前目录到此文件的路径，相对路径名以下列方式开始.&lt;/p&gt;
&lt;p&gt;• “.”，代表当前目录&lt;/p&gt;
&lt;p&gt;• “..”，代表当前目录的父目录&lt;/p&gt;
&lt;h4&gt;软链接和硬链接&lt;/h4&gt;
&lt;p&gt;有时候我们希望给某个文件取个别名，那么在 Linux 中可以通过硬链接（Hard Link） 和软链接（Symbolic Link） 的方式来实现，它们都是比较特殊的文件，但是实现方式也是不相同的。&lt;/p&gt;
&lt;p&gt;硬链接是多个目录项中的「索引节点」指向一个文件，也就是指向同一个 inode，但是 inode 是不可能跨越文件系统的，每个文件系统都有各自的 inode 数据结构和列表，所以硬链接是不可用于跨文件系统的。由于多个目录项都是指向一个 inode，那么只有删除文件的所有硬链接以及源文件时，系统才会彻底删除该文件。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/linux-01-20261014.webp&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;软链接&lt;/h4&gt;
&lt;p&gt;当我们需要在不同的目录，用到相同的文件时，我们不需要在每一个需要的目录下都放一个必须相同的文件，我们只要在某个固定的目录，放上该文件，然后在其它的目录下用 ln 命令链接（link）它就可以，不必重复的占用磁盘空间。&lt;/p&gt;
&lt;p&gt;软连接的功能是为某一个文件在另外一个位置建立一个同步的链接，这个命令最常用的参数是-s。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;具体用法是：&lt;code&gt;ln -s 源文件 目标文件&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;例如：ln -s /bin/less /usr/local/bin/less&lt;/p&gt;
&lt;p&gt;-s 是代号（symbolic）的意思。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这里有两点要注意：第一，ln 命令会保持每一处链接文件的同步性，也就是说，不论你改动了哪一处，其它的文件都会发生相同的变化；第二，ln 的链接有软链接和硬链接两种，软链接就是 ln -s ** **,它只会在你选定的位置上生成一个文件的镜像，不会占用磁盘空间，硬链接 ln ** **,没有参数-s, 它会在你选定的位置上生成一个和源文件大小相同的文件，无论是软链接还是硬链接，文件都保持同步变化。&lt;/p&gt;
&lt;p&gt;软链接相当于重新创建一个文件，这个文件有独立的 inode，但是这个文件的内容是另外一个文件的路径，所以访问软链接的时候，实际上相当于访问到了另外一个文件，所以软链接是可以跨文件系统的，甚至目标文件被删除了，链接文件还是在的，只不过指向的文件找不到了而已。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/linux-02-20261014.webp&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/文件系统-1.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/文件系统-2.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;h2&gt;Chapter 4 Linux 权限管理&lt;/h2&gt;
&lt;p&gt;§多用户、多任务系统必须进行权限控制，权限控制就是确定用户（或者说代表用户执行的程序）对某资源有或无某种操作的权利。&lt;/p&gt;
&lt;p&gt;§直接表示会使得权限的表示、判断效率低下。&lt;/p&gt;
&lt;p&gt;q 多用户、多任务的操作系统决定了常常会有多人同时使用这部主机工作&lt;/p&gt;
&lt;p&gt;q 保证每个人的隐私权--&amp;gt;文件所有者&lt;/p&gt;
&lt;p&gt;q 资源共享，团队开发--&amp;gt;文件所属用户组&lt;/p&gt;
&lt;p&gt;Linux 中每个用户都至少属于一个用户组，以保证对一个用户组中的所有用户进行集中管理&lt;/p&gt;
&lt;h3&gt;4.1 用户身份与能力&lt;/h3&gt;
&lt;p&gt;受到 20 世纪 70 年代计算机发展的影响，Linux 系统的设计初衷之一就是为了满足多个用户同时工作的需求，因此必须具备很好的安全性，尤其是不能因为一两个服务出错而影响到整台服务器。在第 1 章学习安装 Linux 操作系统时，特别要求设置 root 管理员的密码，这个 root 管理员就是存在于所有类 UNIX 系统中的超级用户。它拥有最高的系统所有权，能够管理系统的各项功能，如添加/删除用户、启动/关闭服务进程、开启/禁用硬件设备等。虽然以 root 管理员的身份工作时不会受到系统的限制，但俗话讲“能力越大，责任就越大”，因此一旦使用这个高能的 root 管理员权限执行了错误的命令，可能会直接毁掉整个系统。使用与否，确实需要好好权衡一下。&lt;/p&gt;
&lt;p&gt;开发人员推荐使用 root 管理员权限来学习 Linux 系统，等到工作时再根据生产环境决定使用哪个用户权限；这些仅与选择相关，而非技术性问题。&lt;/p&gt;
&lt;p&gt;Linux 系统中的管理员就是 root。这其实是错误的，Linux 系统的管理员之所以是 root，并不是因为它的名字叫 root，而是因为该用户的身份号码即 UID（User IDentification）的数值为 0。在 Linux 系统中，UID 就像我们的身份证号码一样具有唯一性，因此可通过用户的 UID 值来判断用户身份。在 RHEL 8 系统中，用户身份有下面这些。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;管理员 UID 为 0&lt;/strong&gt;：系统的管理员用户。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;系统用户 UID 为 1 ～ 999&lt;/strong&gt;：Linux 系统为了避免因某个服务程序出现漏洞而被黑客提权至整台服务器，默认服务程序会由独立的系统用户负责运行，进而有效控制被破坏范围。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;普通用户 UID 从 1000 开始&lt;/strong&gt;：是由管理员创建的用于日常工作的用户。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;需要注意的是，UID 是不能冲突的，而且管理员创建的普通用户的 UID 默认是从 1000 开始的（即使前面有闲置的号码）。&lt;/p&gt;
&lt;p&gt;为了方便管理属于同一组的用户，Linux 系统中还引入了用户组的概念。通过使用用户组号码（GID，Group IDentification），可以把多个用户加入到同一个组中，从而方便为组中的用户统一规划权限或指定任务。假设一个公司中有多个部门，每个部门中又有很多员工，如果只想让员工访问本部门内的资源，则可以针对部门而非具体的员工来设置权限。例如，通过对技术部门设置权限，使得只有技术部门的员工可以访问公司的数据库信息等。&lt;/p&gt;
&lt;p&gt;另外，在 Linux 系统中创建每个用户时，将自动创建一个与其同名的基本用户组，而且这个基本用户组只有该用户一个人。如果该用户以后被归纳到其他用户组，则这个其他用户组称之为扩展用户组。一个用户只有一个基本用户组，但是可以有多个扩展用户组，从而满足日常的工作需要。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Tips&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;基本用户组就像是原生家庭，是在创建账号（出生）时就自动生成的；而扩展用户组则像工作单位，为了完成工作，需要加入到各个不同的群体中，这是需要手动添加的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt; 在设定文件权限时，用字符表示用户类型：&lt;/p&gt;
&lt;p&gt;u 文件的所有者
g 同组用户
o 其它用户
a all=ugo,即所有用户&lt;/p&gt;
&lt;h3&gt;4.2 文件权限与归属&lt;/h3&gt;
&lt;p&gt;在 Linux 系统中，每个文件都有归属的所有者和所属组，并且规定了文件的所有者、所属组以及其他人对文件所拥有的可读（r）、可写（w）、可执行（x）等权限。对于一般文件来说，权限比较容易理解：“可读”表示能够读取文件的实际内容；“可写”表示能够编辑、新增、修改、删除文件的实际内容；“可执行”则表示能够运行一个脚本程序。但是，对于目录文件来说，理解其权限设置就不那么容易了。很多资深 Linux 用户其实也没有真正搞明白。对于目录文件来说，“可读”表示能够读取目录内的文件列表；“可写”表示能够在目录内新增、删除、重命名文件；而“可执行”则表示能够进入该目录。&lt;/p&gt;
&lt;p&gt; 对文件和目录的三种权限&lt;/p&gt;
&lt;p&gt;•读（r）
•写（w）
•执行 （x）&lt;/p&gt;
&lt;p&gt; 十进制表示&lt;/p&gt;
&lt;p&gt;•用三个十进制数字表示文件权限：xyz&lt;/p&gt;
&lt;p&gt;•x、y、z 分别代表文件所有者、同组用户和其他用户的文件权限值（一个数
字对应一种人）。&lt;/p&gt;
&lt;p&gt;•文件权限值为该类型用户对文件的读、写、执行权限值的累加：
读(r)：4 写(w)：2 执行(x)：1 禁止(-)：0&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/权限管理-1.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/权限管理-2.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/权限管理-3.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;重要&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt; !IMPORTANT 命令执行对文件权限的限制&lt;/p&gt;
&lt;p&gt;➢ 在移动文件时不需要被移动文件的权限，但需其所在目录具有写权限。
➢ 在目录下增删文件、子目录时要有目录的写权限（touch,rm,mkdir,rmdir）
➢ 用 ls 列目录要有目录的读权限&lt;/p&gt;
&lt;p&gt;➢ 进入目录或将该目录作路径分量时(cd)要有目录的执行权限，故要使用任一个
文件，必须有该文件及找到该文件的路径上所有目录分量的执行权限。
➢ 仅当要打开一个文件时，即执行涉及到文件内容的操作时，才需要文件的许可。&lt;/p&gt;
&lt;p&gt;一个目录同时具有读权限和执行权限才可以打开并查看内部文件，而一个目录要有写权限才允许在其中创建其它文件，这是因为目录文件实际保存着该目录里面的文件的列表等信息&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/权限管理-4.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;h2&gt;Chapter 5 Linux 常用工具&lt;/h2&gt;
&lt;h3&gt;文本编辑器&lt;/h3&gt;
&lt;h4&gt;gedit&lt;/h4&gt;
&lt;h4&gt;vi&lt;/h4&gt;
&lt;p&gt;❑ vi 是一个较大的 Linux 命令，在启动的时候也有它自己的选项和参数&lt;/p&gt;
&lt;p&gt;❑ 基本语法：
vi [-options] [+n] [file]&lt;/p&gt;
&lt;p&gt;❑ 常用选项：&lt;/p&gt;
&lt;p&gt;-r 恢复系统突然崩溃时正在编辑的文件。&lt;/p&gt;
&lt;p&gt;-R 以只读方式打开文件&lt;/p&gt;
&lt;p&gt;+[n] 指明进入 vi 后直接位于文件的第 n 行，如果只有“+”而不指定 n，则光标位于文本的最后一行&lt;/p&gt;
&lt;p&gt;如果该文件不存在，会自动建立新文件。&lt;/p&gt;
&lt;p&gt;❑ 基本上 vi/vim 共分为三种模式:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;命令模式&lt;/strong&gt;：控制光标移动，可对文本进行复制、粘贴、删除和查找等工作。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;输入模式&lt;/strong&gt;：正常的文本录入。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;末行模式&lt;/strong&gt;：保存或退出文档，以及设置编辑环境。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;命令模式（Command Mode）&lt;/strong&gt;:光标移动，搜寻、字符和字符串删除、粘贴、取代&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;用户刚刚启动 vi/vim，便进入了命令模式。&lt;/p&gt;
&lt;p&gt;以下摘要了一些常用的命令，更详细的命令在后面可以自行查看。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;x&lt;/strong&gt; -- 删除当前光标所在处的字符。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;dd&lt;/strong&gt; -- 删除当前行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;yy&lt;/strong&gt; -- 复制当前行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;p&lt;/strong&gt;（小写） -- 粘贴剪贴板内容到光标下方。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P&lt;/strong&gt;（大写）-- 粘贴剪贴板内容到光标上方。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;u&lt;/strong&gt; -- 撤销上一次操作。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ctrl + r&lt;/strong&gt; -- 重做上一次撤销的操作。&lt;/p&gt;
&lt;p&gt;nG 跳到第 n 行&lt;/p&gt;
&lt;p&gt;0 跳到行首&lt;/p&gt;
&lt;p&gt;$ 跳到行尾&lt;/p&gt;
&lt;p&gt;ctrl+b 前移一页&lt;/p&gt;
&lt;p&gt;ctrl+f 后移一页&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;➢ 移动光标 ：最简单的方式是按键盘的上、下、左、右方向键&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;h 或 向左箭头键(←)&lt;/td&gt;
&lt;td&gt;光标向左移动一个字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;j 或 向下箭头键(↓)&lt;/td&gt;
&lt;td&gt;光标向下移动一个字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;k 或 向上箭头键(↑)&lt;/td&gt;
&lt;td&gt;光标向上移动一个字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;l 或 向右箭头键(→)&lt;/td&gt;
&lt;td&gt;光标向右移动一个字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Ctrl] + [f]&lt;/td&gt;
&lt;td&gt;屏幕『向下』移动一页，相当于 [Page Down]按键 (常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Ctrl] + [b]&lt;/td&gt;
&lt;td&gt;屏幕『向上』移动一页，相当于 [Page Up] 按键 (常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Ctrl] + [d]&lt;/td&gt;
&lt;td&gt;屏幕『向下』移动半页&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Ctrl] + [u]&lt;/td&gt;
&lt;td&gt;屏幕『向上』移动半页&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;+&lt;/td&gt;
&lt;td&gt;光标移动到非空格符的下一行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;光标移动到非空格符的上一行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;n&amp;lt;space&amp;gt;&lt;/td&gt;
&lt;td&gt;那个 n 表示『数字』，例如 20 。按下数字后再按空格键，光标会向右移动这一行的 n 个字符。例如 20&amp;lt;space&amp;gt; 则光标会向后面移动 20 个字符距离。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0 或功能键[Home]&lt;/td&gt;
&lt;td&gt;这是数字『 0 』：移动到这一行的最前面字符处 (常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$ 或功能键[End]&lt;/td&gt;
&lt;td&gt;移动到这一行的最后面字符处(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;H&lt;/td&gt;
&lt;td&gt;光标移动到这个屏幕的最上方那一行的第一个字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;M&lt;/td&gt;
&lt;td&gt;光标移动到这个屏幕的中央那一行的第一个字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;L&lt;/td&gt;
&lt;td&gt;光标移动到这个屏幕的最下方那一行的第一个字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;G&lt;/td&gt;
&lt;td&gt;移动到这个档案的最后一行(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;nG&lt;/td&gt;
&lt;td&gt;n 为数字。移动到这个档案的第 n 行。例如 20G 则会移动到这个档案的第 20 行(可配合 :set nu)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gg&lt;/td&gt;
&lt;td&gt;移动到这个档案的第一行，相当于 1G 啊！ (常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;n&amp;lt;Enter&amp;gt;&lt;/td&gt;
&lt;td&gt;n 为数字。光标向下移动 n 行(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;➢ 搜索替换 ：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;/word&lt;/td&gt;
&lt;td&gt;向光标之下寻找一个名称为 word 的字符串。例如要在档案内搜寻 vbird 这个字符串，就输入 /vbird 即可！ (常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;?word&lt;/td&gt;
&lt;td&gt;向光标之上寻找一个字符串名称为 word 的字符串。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;n&lt;/td&gt;
&lt;td&gt;这个 n 是英文按键。代表重复前一个搜寻的动作。举例来说， 如果刚刚我们执行 /vbird 去向下搜寻 vbird 这个字符串，则按下 n 后，会向下继续搜寻下一个名称为 vbird 的字符串。如果是执行 ?vbird 的话，那么按下 n 则会向上继续搜寻名称为 vbird 的字符串！&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N&lt;/td&gt;
&lt;td&gt;这个 N 是英文按键。与 n 刚好相反，为『反向』进行前一个搜寻动作。 例如 /vbird 后，按下 N 则表示『向上』搜寻 vbird 。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;:n1,n2s/word1/word2/g&lt;/td&gt;
&lt;td&gt;n1 与 n2 为数字。在第 n1 与 n2 行之间寻找 word1 这个字符串，并将该字符串取代为 word2 ！举例来说，在 100 到 200 行之间搜寻 vbird 并取代为 VBIRD 则： 『:100,200s/vbird/VBIRD/g』。(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;:1,$s/word1/word2/g&lt;/strong&gt; 或 &lt;strong&gt;:%s/word1/word2/g&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;从第一行到最后一行寻找 word1 字符串，并将该字符串取代为 word2 ！(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;:1,$s/word1/word2/gc&lt;/strong&gt; 或 &lt;strong&gt;:%s/word1/word2/gc&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;从第一行到最后一行寻找 word1 字符串，并将该字符串取代为 word2 ！且在取代前显示提示字符给用户确认 (confirm) 是否需要取代！(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;➢ 复制、删除、粘贴 ：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;x, X&lt;/td&gt;
&lt;td&gt;在一行字当中，x 为向后删除一个字符 (相当于 [del] 按键)， X 为向前删除一个字符(相当于 [backspace] 亦即是退格键) (常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;nx&lt;/td&gt;
&lt;td&gt;n 为数字，连续向后删除 n 个字符。举例来说，我要连续删除 10 个字符， 『10x』。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dd&lt;/td&gt;
&lt;td&gt;剪切游标所在的那一整行(常用)，用 p/P 可以粘贴。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ndd&lt;/td&gt;
&lt;td&gt;n 为数字。剪切光标所在的向下 n 行，例如 20dd 则是剪切 20 行(常用)，用 p/P 可以粘贴。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;d1G&lt;/td&gt;
&lt;td&gt;删除光标所在到第一行的所有数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dG&lt;/td&gt;
&lt;td&gt;删除光标所在到最后一行的所有数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;d$&lt;/td&gt;
&lt;td&gt;删除游标所在处，到该行的最后一个字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;d0&lt;/td&gt;
&lt;td&gt;那个是数字的 0 ，删除游标所在处，到该行的最前面一个字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;yy&lt;/td&gt;
&lt;td&gt;复制游标所在的那一行(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;nyy&lt;/td&gt;
&lt;td&gt;n 为数字。复制光标所在的向下 n 行，例如 20yy 则是复制 20 行(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;y1G&lt;/td&gt;
&lt;td&gt;复制游标所在行到第一行的所有数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;yG&lt;/td&gt;
&lt;td&gt;复制游标所在行到最后一行的所有数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;y0&lt;/td&gt;
&lt;td&gt;复制光标所在的那个字符到该行行首的所有数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;y$&lt;/td&gt;
&lt;td&gt;复制光标所在的那个字符到该行行尾的所有数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;p, P&lt;/td&gt;
&lt;td&gt;p 为将已复制的数据在光标下一行贴上，P 则为贴在游标上一行！ 举例来说，我目前光标在第 20 行，且已经复制了 10 行数据。则按下 p 后， 那 10 行数据会贴在原本的 20 行之后，亦即由 21 行开始贴。但如果是按下 P 呢？ 那么原本的第 20 行会被推到变成 30 行。 (常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;J&lt;/td&gt;
&lt;td&gt;将光标所在行与下一行的数据结合成同一行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;c&lt;/td&gt;
&lt;td&gt;重复删除多个数据，例如向下删除 10 行，[ 10cj ]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;u&lt;/td&gt;
&lt;td&gt;复原前一个动作。(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Ctrl]+r&lt;/td&gt;
&lt;td&gt;重做上一个动作。(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;.&lt;/td&gt;
&lt;td&gt;意思是重复前一个动作的意思。 如果你想要重复删除、重复贴上等等动作，按下小数点『.』就好了！ (常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;输入模式（Insert Mode）&lt;/strong&gt;: 编辑文本&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在命令模式下按下 i 就进入了输入模式，使用 Esc 键可以返回到命令模式。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;字符按键以及 Shift 组合&lt;/strong&gt;，输入字符&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ENTER&lt;/strong&gt;，回车键，换行&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BACK SPACE&lt;/strong&gt;，退格键，删除光标前一个字符&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DEL&lt;/strong&gt;，删除键，删除光标后一个字符&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方向键&lt;/strong&gt;，在文本中移动光标&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HOME&lt;/strong&gt;/&lt;strong&gt;END&lt;/strong&gt;，移动光标到行首/行尾&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Page Up&lt;/strong&gt;/&lt;strong&gt;Page Down&lt;/strong&gt;，上/下翻页&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Insert&lt;/strong&gt;，切换光标为输入/替换模式，光标将变成竖线/下划线&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ESC&lt;/strong&gt;，退出输入模式，切换到命令模式&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;底线命令模式（Last Line Mode）&lt;/strong&gt;: 读取、保存文件及其他额外功能&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在命令模式下按下 &lt;code&gt;:&lt;/code&gt;（英文冒号）就进入了底线命令模式。使用 Esc 键可随时退出底线命令模式。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;:w&lt;/strong&gt; -- 保存文件。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;:q&lt;/strong&gt; -- 退出 Vim 编辑器。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;:wq&lt;/strong&gt; -- 保存文件并退出 Vim 编辑器。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;:q!&lt;/strong&gt; -- 强制退出 Vim 编辑器，不保存修改。&lt;/p&gt;
&lt;p&gt;➢ 正向搜索（/）：从光标所在位置起向文件末尾方向搜索。
➢ 反向搜索（?）：从光标所在位置起向文件开头方向搜索。
➢ 搜索得到结果后，可以使用重复命令 n 或 N 沿着相同或相反的方向重复上一次的搜索&lt;/p&gt;
&lt;p&gt;➢ 字符串替换（substitute）
语法：
[n1, n2]s/str1/str2/[g][c]
在第 n1 行到 n2 行的范围内将字符串 str1 用 str2 代替:
c 每次替换都由用户确认
g 对行中搜索字符串的每次出现进行替换。
不加 g 只对行中搜索字符串的首次出现进行替换；
例子：
:s/str1/str2/
用字符串 str2 替换该行中首次出现的字符串 str1
:s/str1/str2/g
用字符串 str2 替换该行中所有出现的字符串 str1
:.,$s/str1/str2/g
用字符串 str2 替换正文当前行到末尾所有出现的字符串 str1
:1,$s/str1/str2/g
用字符串 str2 替换正文中所有出现的字符串 str1&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/vim-vi-workmodel.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;h5&gt;命令模式和输入模式切换&lt;/h5&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;i, I&lt;/td&gt;
&lt;td&gt;进入输入模式(Insert mode)：i 为『从目前光标所在处输入』， I 为『在目前所在行的第一个非空格符处开始输入』。 (常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;a, A&lt;/td&gt;
&lt;td&gt;进入输入模式(Insert mode)： a 为『从目前光标所在的下一个字符处开始输入』， A 为『从光标所在行的最后一个字符处开始输入』。(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;o, O&lt;/td&gt;
&lt;td&gt;进入输入模式(Insert mode)：o 为『在目前光标所在的下一行处输入新的一行』； O 为『在目前光标所在的上一行处输入新的一行！』(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;r, R&lt;/td&gt;
&lt;td&gt;进入取代模式(Replace mode)：r 只会取代光标所在的那一个字符一次；R 会一直取代光标所在的文字，直到按下 ESC 为止；(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[Esc]&lt;/td&gt;
&lt;td&gt;退出编辑模式，回到一般模式中(常用)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;上面这些按键中，在 vi 画面的左下角处会出现『--INSERT--』或『--REPLACE--』的字样。&lt;/p&gt;
&lt;h5&gt;命令模式和底线命令模式切换&lt;/h5&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;:&lt;/td&gt;
&lt;td&gt;切换到底线命令模式，以在最底一行输入命令。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/Vim-introduction.jpg，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;vim 环境的变更&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;:set nu&lt;/td&gt;
&lt;td&gt;显示行号，设定之后，会在每一行的前缀显示该行的行号&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;:set nonu&lt;/td&gt;
&lt;td&gt;与 set nu 相反，为取消行号！&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;▪:.+5 将光标移动到当前行往下的第 5 行。
▪:345 将光标移到第 345 行&lt;/p&gt;
&lt;p&gt;❑ Vim 和 gvim 的高级特色
• Vim 代表 Vi IMproved，如同其名称所暗示的那样，Vim 作为标准 UNIX 系统 vi 编辑器的提高版而存在。 Vim 除提供和 vi 编辑器一样强大的功能外，还提供有多级恢复、命令行历史以及命令及文件名补全等功能。
• gvim 是 vi 的 X Window 版本，该版本支持鼠标选中，一些高级的光标移动功能，并且带有菜单和工具按钮。&lt;/p&gt;
&lt;h3&gt;软件包管理&lt;/h3&gt;
&lt;h3&gt;Samba&lt;/h3&gt;
&lt;h2&gt;Chapter 6 Linux 进程管理&lt;/h2&gt;
&lt;p&gt;概念：一个进程是一个程序的一次执行的过程。&lt;/p&gt;
&lt;p&gt;❑ 操作系统通过进程来控制对 CPU 和其他系统资源的访问，并且使用进程来决定在 CPU 上运行哪个程序、运行多久。&lt;/p&gt;
&lt;p&gt;❑ Linux 系统的一个重要特点：可以同时启动多个进程。&lt;/p&gt;
&lt;p&gt;❑ 进程和程序不同：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;程序是静态的，是保存在磁盘上的可执行代码和数据的集合；&lt;/li&gt;
&lt;li&gt;进程是动态的，是 Linux 系统的基本调度单位。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;❑ 父进程和子进程&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个进程创建新进程称为创建了子进程(child process)。&lt;/li&gt;
&lt;li&gt;创建子进程的进程称为父进程。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;❑ 进程号&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PID：Process Identity number。一个 PID 唯一地标识一个进程。&lt;/li&gt;
&lt;li&gt;PPID：Parent Process ID。进程的父进程号。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Chapter 7 Linux Shell&lt;/h2&gt;
&lt;h3&gt;各种 shell&lt;/h3&gt;
&lt;p&gt;Bourne shell (sh) Unix 最初使用，且在每种 Unix 上都可以使用。在 shell 编程方面相当优秀，但在处理与用户的交互方面做得不如其他几种 shell。 sh 的全称是 Bourne shell，由 AT&amp;amp;T 公司的 Steve Bourne 开发，为了纪念他，就用他的名字命名了。&lt;/p&gt;
&lt;p&gt;C shell (csh) csh，一个语法上接近于 C 语言的 shell。 sh 之后另一个广为流传的 shell 是由柏克莱大学的 Bill Joy 设计的，这个 shell 的语法有点类似 C 语言，所以才得名为 C shell ，简称为 csh。&lt;/p&gt;
&lt;p&gt;Korn shell (ksh) 完全向上兼容 Bourne shell 并包含了 C shell 的很多特性。&lt;/p&gt;
&lt;p&gt;Bourne Again shell (Bash) 因为 Linux 操作系统缺省的 shell。即 Bash 是 Bourne shell 的扩展，与 Bourne shell 完全向后兼容。在 Bourne shell 的基础上增加、增强了很多特性。可以提供如命令补全、命令编辑和命令历史表等功能。包含了很多 C shell 和 Korn shell 中的优点，有灵活和强大的编程接口，同时又有很友好的用户界面。&lt;/p&gt;
&lt;p&gt;Debian Almquist shell (dash) 原来 bash 是 GNU/Linux 操作系统中的 /bin/sh 的符号连接，但由于 bash 过于复杂，有人把 Bash 从 NetBSD 移植到 Linux 并更名为 dash，且/bin/sh 符号连接到 dash。Dash Shell 比 Bash Shell 小的多（ubuntu16.04 上，bash 大概 1M，dash 只有 150K），符合 POSIX 标准。Ubuntu 6.10 开始默认是 Dash。&lt;/p&gt;
&lt;p&gt;Zsh zsh 配置复杂，所以很多人都不会使用。直到有一天出了 Oh My Zsh 项目（https://github.com/robbyrussell/oh-my-zsh），才让更多人发现并开始使用zsh。有人说zsh是终极shell。zsh很漂亮，很炫酷，以前是极客使用，现在小白通过oh-my-zsh可以炫耀。&lt;/p&gt;
&lt;h3&gt;Shell 脚本&lt;/h3&gt;
&lt;p&gt;可以将 Shell 终端解释器当作人与计算机硬件之间的“翻译官”，它作为用户与 Linux 系统内部的通信媒介，除了能够支持各种变量与参数外，还提供了诸如循环、分支等高级编程语言才有的控制结构特性。要想正确使用 Shell 中的这些功能特性，准确下达命令尤为重要。Shell 脚本命令的工作方式有下面两种。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;交互式（Interactive）&lt;/strong&gt;：用户每输入一条命令就立即执行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;批处理（Batch）&lt;/strong&gt;：由用户事先编写好一个完整的 Shell 脚本，Shell 会一次性执行脚本中诸多的命令。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在 Shell 脚本中不仅会用到前面学习过的很多 Linux 命令以及正则表达式、管道符、数据流重定向等语法规则，还需要把内部功能模块化后通过逻辑语句进行处理，最终形成日常所见的 Shell 脚本。&lt;/p&gt;
&lt;h3&gt;Shell 脚本练习&lt;/h3&gt;
&lt;p&gt;脚本 1:输入一个正整数，输出 0 到这个正整数的平方数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Read -p &quot;请输入一个正整数&quot; n
i=0
While [ $i -le $n ]
 do
 j= $((i*i))
 echo $j
 i=((i+1))
done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;脚本 2:输出序列的最大数、次最大数、最小数、次最小数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Read -p &quot;请输入系列正整数，0结束&quot; n
max=n; min=n; sMax= n;sMin=n
While [ $n -ne 0 ]
 do
 read n
 if [ $n -gt $max ] ;then
 sMax=max; max= $n;
 elif[$n -gt sMax];then
 sMax=$n
 fi
 if [ $n -It $min ] ;then
 sMin= $min; min= $n;
 elif[$n-ItsMin];then
 sMin= $n
 fi
done
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;linux 下普通用户变为 root 权限的三种方法&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/weixin_43886632/article/details/108244302&quot;&gt;Linux 下普通用户变为 root 权限的三种方法_19.如何从普通用户 xiaoming 切换到 root?-CSDN 博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法 3&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;修改 /etc/passwd 文件，找到 xiaoming，把用户 ID 修改为 0&lt;/p&gt;
&lt;h2&gt;Linux 一些概念&lt;/h2&gt;
&lt;p&gt;Kernel 内核：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;管理程序运行，为系统分配资源，处理程序间的通信&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;管理对文件系统的读写，把对文件系统的操作映射成对磁盘或其他设备的操作&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;管理存储器，为程序分配内存，并且管理虚拟内存&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;管理输入输出，将设备映射成设备文件&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;管理网络&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Shell&lt;/p&gt;
&lt;p&gt;是一个命令解释器，是内核和用户之间的接口&lt;/p&gt;
&lt;p&gt;是一个编程语言&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Shell 和内核的关系
Shell 是内核的保护层，对用户屏蔽内核的复杂性。
保护内核以免用户误操作造成危害。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Linux
标准的 Linux 系统都具有一套称为应用程序的程序集，包括文本编辑器、Xwindow、办公套件、Internet 工具和数据库等。&lt;/p&gt;
&lt;p&gt;文件
文件是 Linux 用来存储信息的基本结构，它是被命名的，存储在某种媒介（如磁盘、光盘和磁带等）上的一种信息集合。&lt;/p&gt;
&lt;p&gt;文件系统
Linux 文件系统实现数据存储（物理）介质独立性&lt;/p&gt;
&lt;p&gt;目录文件
目录文件存储一组相关文件的位置、大小等与文件有关的信息，但是并不包含文件的具体内容。目录文件中的每一项主要表示的是一个文件名（或子目录名）以及文件的索引节点号。&lt;/p&gt;
&lt;p&gt;索引节点
一个文件的索引节点能够指向该文件内容所在的数据块的位置，除此以外它还记录了该文件的属性。目录文件就是通过 i-node 表与文件之间建立对应关系的。&lt;/p&gt;
&lt;p&gt;块设备：
以块为单位进行随机存取。如：软盘、光盘、硬盘。&lt;/p&gt;
&lt;p&gt;字符设备：
以单个字符为单位进行顺序存取。如：打印机、终端、键盘、鼠标。&lt;/p&gt;
&lt;p&gt;文件系统：
指文件存在的物理空间。&lt;/p&gt;
&lt;p&gt;树状层次结构
Linux 将分属不同分区的、单独的文件系统整理成一个系统的，总的目录层次结构，即树状结构。&lt;/p&gt;
&lt;p&gt;挂接
Linux 通过挂接一个文件系统将该新文件系统加入它的文件系统树中，所有的文件系统，不管是什么类型，都挂接在文件系统树的一个目录上并且该文件系统之上的文件将会掩盖掉这个挂接目录中原有的内容。这个目录称为挂接目录或挂接点。&lt;/p&gt;
&lt;p&gt;链接
目录中每一对文件名称名和索引节点号称为一个链接。&lt;/p&gt;
&lt;p&gt;进程：
一个进程是一个程序的一次执行的过程。
操作系统通过进程来控制对 CPU 和其他系统资源的访问，并且使用进程来决定 CPU 上运行哪个程序、运行多久。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;程序与进程的不同：
程序：程序是静态的，是保存在磁盘上的可执行代码和数据的集合。
进程：进程是动态的，是 Linux 系统的基本调度单位&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Linux 常见目录说明&lt;/h2&gt;
&lt;h2&gt;Linux 环境变量&lt;/h2&gt;
&lt;p&gt;Linux 系统中最重要的 10 个环境变量：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;变量名称&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HOME&lt;/td&gt;
&lt;td&gt;用户的主目录（即家目录）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SHELL&lt;/td&gt;
&lt;td&gt;用户在使用的 Shell 解释器名称&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HISTSIZE&lt;/td&gt;
&lt;td&gt;输出的历史命令记录条数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HISTFILESIZE&lt;/td&gt;
&lt;td&gt;保存的历史命令记录条数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MAIL&lt;/td&gt;
&lt;td&gt;邮件保存路径&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LANG&lt;/td&gt;
&lt;td&gt;系统语言、语系名称&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RANDOM&lt;/td&gt;
&lt;td&gt;生成一个随机数字&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PS1&lt;/td&gt;
&lt;td&gt;Bash 解释器的提示符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PATH&lt;/td&gt;
&lt;td&gt;定义解释器搜索用户执行命令的路径&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EDITOR&lt;/td&gt;
&lt;td&gt;用户默认的文本编辑器&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;考试复习&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/FGRboke/article/details/122095325&quot;&gt;linux 大学期末考试题库_linux 期末考试题库-CSDN 博客&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>密码学整合</title><link>https://h1s97x.github.io/posts/notes/notes-%E5%AF%86%E7%A0%81%E5%AD%A6%E6%95%B4%E5%90%88/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-%E5%AF%86%E7%A0%81%E5%AD%A6%E6%95%B4%E5%90%88/</guid><description>目前已经学过密码相关的课程： 1.信息安全导论 2.信息安全数学基础 3.密码学原理与实践 4.Python与密码学 为什么写这样一篇整合性的文章，因为看书时突</description><pubDate>Wed, 22 Nov 2023 07:05:46 GMT</pubDate><content:encoded>&lt;p&gt;目前已经学过密码相关的课程：&lt;/p&gt;
&lt;p&gt;1.信息安全导论&lt;/p&gt;
&lt;p&gt;2.信息安全数学基础&lt;/p&gt;
&lt;p&gt;3.密码学原理与实践&lt;/p&gt;
&lt;p&gt;4.Python与密码学&lt;/p&gt;
&lt;h4&gt;前言&lt;/h4&gt;
&lt;p&gt;为什么写这样一篇整合性的文章，因为看书时突发奇想，发现之前学过的内容之间都存在不小的联系，如果能将脑中混杂的理论盒思想整合一下，即可以给自己之后的复习与重温产生极大的效益，同时也能发布到网络上，让他人受益，并在其他人的审视下纠正我的错误。&lt;/p&gt;
&lt;h2&gt;密码学综述&lt;/h2&gt;
&lt;h3&gt;密码的起源&lt;/h3&gt;
&lt;p&gt;从远古时代起，人类就能感知身边各种自然现象所隐含的信息。在狩猎和劳作的过程中，人类开发出属于自己的各种复杂系统——语言、数字、文字系统，用来表示一些抽象的概念，创造出人类最初的密码。例如，云的特征隐藏了不同的信息，远古人类通过观察云的特征来解密天气信息，以预测是否会刮风或下雨；狩猎时人类学习“解密“信息最初的实践之一，猎人在狩猎的过程中，会根据猎物遗留的足印、活动痕迹、啃食的草木等迹象，解读其中隐藏的信息，以判断猎物的种类、踪迹和活动规律等。&lt;/p&gt;
&lt;h3&gt;密码学的发展&lt;/h3&gt;
&lt;p&gt;古代岩画，将一些象征性图案以莫重方式排列组合来传达不同的信息，这是人类通过图形化符号来传递信息的最早手段之一。著名的有法国拉斯科洞窟壁画，挪威的阿尔塔岩画，我国宁夏银川的贺兰山岩画，古人类将当时人类的生活和劳动信息隐藏于岩画之中，现代才得以通过解读这些信息，还原远古人类所处的生活环境和一系列生活场景。技术与书写的抽象思维使得人类的发展史得以记载流传，玛雅数字，巴比伦数字，阿拉伯数字，罗马数字，楔形文字，还有菲斯托斯圆盘上记载的古希腊文字，这些抽象的符号表达记录着当时人类的生活。&lt;/p&gt;
&lt;p&gt;早在公元前7世纪，密码棒就在古希腊的军事重镇斯巴达得到广泛应用。这就是斯巴达密码棒。古斯巴达人用一条带子缠绕在一根木棍上，沿木棍纵轴方向写好明文，解下来的带子上就只有杂乱无章的密文字母。解密者只需找到相同直径的木棍，再把带子缠上去，沿木棍纵轴方向即可读出有意义的明文。这种方式在现代密码学中仍有体现——行列变换。&lt;/p&gt;
&lt;p&gt;密码史与人类文明发展史相生相伴，从古人类的符号标记，古文字的形成与解读，再到斯巴达密码棒等等，这些五彩斑斓的创作，体现了古人在秘密传递上的智慧，将密码学一步一步推向了以数学为基础的古典密码时代。&lt;/p&gt;
&lt;h4&gt;古典密码：&lt;/h4&gt;
&lt;p&gt;这一时期的密码学更像是一门艺术，其核心手段是代换和置换。代换是指明文中的每一个字符被替换成密文中的另一个字符，接收者对密文做反向替换便可恢复出明文；置换是密文和明文字母保持相同，但顺序被打乱。代换密码的著名例子有古罗马的凯撒密码（公元前1世纪）和法国的维吉尼亚密码（16世纪）。&lt;/p&gt;
&lt;h5&gt;凯撒密码&lt;/h5&gt;
&lt;p&gt;公元前1世纪，著名的凯撒密码，被用于高卢战争中，这是一种简单易行的单字母替代密码，这种加密方法就是将明文的字母按照字母顺序，往后依次递推相同的字母，就可以得到加密的密文，而解密的过程正好和加密的过程相反。&lt;/p&gt;
&lt;h5&gt;维吉尼亚密码&lt;/h5&gt;
&lt;p&gt;维吉尼亚密码是多表置换密码的一个典型代表，这种方法即一个明文字母可以表示为多个密文字母，多表密码加密算法结果将使得对单表置换用的简单频率分析方法失效。维吉尼亚密码使用一个词组（语句）作为密钥，词组中每一个字母都作为移位替换密码密钥确定一个替换表，维吉尼亚密码循环的使用每一个替换表完成明文字母到密文字母的变换，最后所得到的密文字母序列即为加密得到的密文。维吉尼亚是古典密码理论发展上的一个重要里程碑。&lt;/p&gt;
&lt;p&gt;置换密码也是古典密码的重要一员，通过一定的规则重新排列明文，打破明文的结构，而不改变明文的内容，仅仅是对明文字母的重新排序，就可以实现加密。虽然设计思想简单，但其贡献不可磨灭。&lt;/p&gt;
&lt;p&gt;一次一密费纳姆密码作为绝对安全密码，其保密性在理论意义上绝对安全，虽然真正实现困难，但它对现代序列密码的设计的影响意义深远。&lt;/p&gt;
&lt;p&gt;中国古代兵书《六韬》中的阴符和阴书:《六韬》又称《太公六韬》或《太公兵法》,据说是由西周的开国功臣太公望(又名吕尚或姜子牙,约公元前1128—公元前1015)所著。书中以周文王和周武王与太公问答的形式阐述军事理论,其中《龙韬•阴符》篇和《龙韬•阴书》篇,讲述了君主如何在战争中与在外的将领进行保密通信。&lt;/p&gt;
&lt;p&gt;以下是关于“阴符”使用方法对话的译文。&lt;/p&gt;
&lt;p&gt;武王问太公说:领兵深入敌国境内,军队突然遇到紧急情况,战事或有利,或失利。我要与各军远近相通,内外相应,保持密切的联系,以便及时应对战场上军队的需求,应该怎么办呢?&lt;/p&gt;
&lt;p&gt;太公回答说:国君与主将之间用阴符秘密联络。阴符共有八种:一种长一尺,表示大获全胜,摧毁敌人;一种长九寸,表示攻破敌军,杀敌主将;一种长八寸,表示守城的敌人已投降,我军已占领该城;一种长七寸,表示敌军已败退,远传捷报;一种长六寸,表示我军将誓死坚守城邑;一种长五寸,表示请拨运军粮,增派援军;一种长四寸,表示军队战败,主将阵亡;一种长三寸,表示战事失利,全军伤亡惨重。如奉命传递阴符的使者延误传递,则处死;如阴符的秘密被泄露,则无论无意泄密者或有意传告者也处死。只有国君和主将知道这八种阴符的秘密。这就是不会泄露朝廷与军队之间相互联系内容的秘密通信语言。敌人再聪明也不能识破它。&lt;/p&gt;
&lt;p&gt;在计算机时代来临之前，古典密码的破译还是一个困难问题，但随着计算机的发明，破译这些古典密码在今天看来轻而易举，但古典密码的设计思想给现代密码的设计产生了不可磨灭的影响，其工作原理——代换和置换，仍然是构造现代对称密码算法的最重要核心技术。&lt;/p&gt;
&lt;h4&gt;近代密码&lt;/h4&gt;
&lt;p&gt;近代密码是指从第一次世界大战、第二次世界大战到1976年这段时期密码的发展阶段。&lt;/p&gt;
&lt;p&gt;电报的出现第一次使远距离快速传递信息成为可能,事实上,它增强了西方各国的通讯能力;20世纪初,意大利物理学家奎里亚摩•马可尼发明了无线电报,让无线电波成为新的通讯手段,它实现了远距离通讯的即时传输,但是通过无线电波送出的每条信息不仅传给了己方,也传送给了敌方,因此这就意味着必须给每条信息加密,随着第一次世界大战的爆发,对密码和解码人员的需求急剧上升,一场秘密通讯的全球战役打响了。&lt;/p&gt;
&lt;p&gt;公元20世纪初,第一次世界大战进行到关键时刻,英国破译密码的专门机构“40号房间”利用缴获的德国密码本破译了著名的“齐默尔曼电报”,促使美国放弃中立参战,改变了战争进程。&lt;/p&gt;
&lt;p&gt;随着计算机科学的发展,快速电子计算机和现代数学方法一方面为加密技术提供了新的方法、新的概念和新的工具,另一方面也为破译者提供了有力的武器。计算机和电子时代的到来给密码设计者们带来了前所未有的自由,他们可以轻松地减少原先用铅笔和纸在做手工设计时易犯的错误,也不用再担心使用电子机械方式实现密码机的高额费用。总之,利用电子和计算机技术可以设计出更加复杂的密码体系。&lt;/p&gt;
&lt;p&gt;这一阶段真正开始源于香农在20世纪40年代末发表的一系列论文，特别是1949年的《保密系统通信理论》，把已有数千年历史的密码学推向了基于信息论的科学轨道。近代密码发展中一个重要突破是“数据加密标准”（DES）的出现。DES密码的意义在于，首先，其出现使密码学得以从政府走向民间，其设计主要由IBM公司完成，国家安全局等政府部门只是参与其中，最终经美国国家标准局公开征集遴选后，确定为联邦信息处理标准。其次，DES密码设计中的很多思想（Feistel结构、S盒等），被后来大多数分组密码所采用。再次，DES出现之后，不仅在美国联邦部门中使用，而且风行世界，并在金融等商业领域广泛使用。&lt;/p&gt;
&lt;p&gt;密码学的发展直接影响了二战的战局。Arthur Scherbius于1919年设计出了历史上最著名的密码机—德国的Enigma机,它的设计结合了机械系统与电子系统。它被证明是有史以来最为可靠的加密系统之一,从而使得二战期间德军的保密通讯技术处于当时的领先地位。随着Enigma的破译,人们意识到其实真正保证密码安全的往往不是算法,而是密钥。即使算法外泄,但只要密钥保密,密码就不会失效。&lt;/p&gt;
&lt;p&gt;荷兰密码学家Kerckhoffs于1883年在其名著作《军事密码学》中提出密码学的基本假设:密码系统中的算法即使为密码分析者所知,对推导出明文或密钥也没有帮助。也就是说,密码系统的安全性应只取决于可随时改变的密钥,而不应取决于不易被改变的事物(算法)。&lt;/p&gt;
&lt;p&gt;在二次世界大战中,印第安纳瓦霍土著语言被美军用作密码,美国二战时候特别征摹使用印第安纳瓦霍通信兵。在二次世界大战日美的太平洋战场上,美国海军军部让北墨西哥和亚历桑那印第安纳瓦霍族人使用纳瓦霍语进行情报传递。纳瓦霍语的语法、音调及词汇都极为独特,不为世人所知道,当时纳瓦霍族以外的美国人中,能听懂这种语言的也就一二十人。这是密码学和语言学的成功结合,纳瓦霍语密码成为历史上从未被破译的密码。&lt;/p&gt;
&lt;h4&gt;现代密码&lt;/h4&gt;
&lt;p&gt;现代密码学的发展与计算机技术、电子通信技术密切相关。在这一阶段,密码理论得到了蓬勃发展,密码算法的设计与分析互相促进,从而出现了大量的加密算法和各种分析方法。除此之外,密码的使用扩张到各个领域,而且出现了许多通用的加密标准,从而促进了网络和技术的发展。&lt;/p&gt;
&lt;p&gt;现代密码学有两个重要标志：一是美国制定并于1977年1月15日批准公布了公用数据加密标准DES。二是1976年，美国密码学家迪菲和赫尔曼在“密码学的新方向”的文章，首次提出公钥密码体制的构想和理论方法。即不仅加密算法本身可以公开，甚至加密用的密钥也可以公开，但这并不意味着保密程度的降低，因为加密密钥和解密密钥不一样，将解密密钥保密就可以，这就是著名的公钥密码体制。若存在这样的公钥体制，都可以将加密密钥像电话簿一样公开，任何用户想经其他用户传送一加密信息时，都可以从这本密钥薄中查到该 用户的公开密钥，用它来加密，而接收者能用只有它所具有的 解密密钥得到明文，任何第三者不能获得明文。1978年，美国麻省理工学院的里维斯特、沙米尔和阿德曼提出了RSA公钥密码体制，它是第一个成熟的、迄今为止理论上最成功的公钥密码体制。它的安全性是基于数论中的大整数因子分解。该问题是数论中的一个困难问题，至今没有有效的算法，这使得该体制具有较高的保密性。在现代密码学中，除了信息保密外，还有另一方面的要求，即信息安全体制还要能抵抗对手的主动攻击。所谓主动 攻击指的是攻击者可以在信息通道中注入自己伪造的消息，以骗取合法接收者的相信。主动攻击可能窜改信息，也可能冒名顶替，这就产生了现代密码学中的认证体制。该体制的 目的就是保证用户收到一个信息时，能验证消息是否来自 合法发送者，同时还能验证该信息是否被窜改。在许多场合中，如电子汇款，能对抗主动攻击的认证体制甚至比信息保密还重要。&lt;/p&gt;
&lt;h3&gt;编码与密码&lt;/h3&gt;
&lt;h5&gt;摩尔斯电码&lt;/h5&gt;
&lt;p&gt;&lt;a href=&quot;https://morsecode.bmcx.com/&quot;&gt;摩尔斯电码对照表&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;后续&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/qq_38324605/article/details/106861816&quot;&gt;三种古典密码的认识（置换密码，代换密码和轮换密码）-CSDN博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/weixin_52620919/article/details/119249518&quot;&gt;CTF—古典密码（凯撒密码、维吉尼亚密码、培根密码等）-CSDN博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[密码学基础_密码学有那些性质-CSDN博客](https://blog.csdn.net/qq_40843499/article/details/117813551#:~:text=密码学的特性及功能： 1 机密性 (Confidentiality)：非授权者无法获取明文内容。 2 完整性 (Integrity)：接收者能够验证消息是否在传输过程中被 增加、删除、修改,(Authenticity)：接收者能够确认 消息来源 的真实性。 4 不可抵赖性 (Nonrepudiation)：发送者无法否认其 签发 的消息。)&lt;/p&gt;
</content:encoded></item><item><title>近来的苦恼</title><link>https://h1s97x.github.io/posts/blog/%E8%BF%91%E6%9D%A5%E7%9A%84%E8%8B%A6%E6%81%BC/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/blog/%E8%BF%91%E6%9D%A5%E7%9A%84%E8%8B%A6%E6%81%BC/</guid><description>最近的烦心事不可谓不多，写这篇也是临时起意，将这些事情记录下来，也是给自己这大学两年多以来的经历进行总结教训。 总说大学大学，大不了自学。听起来很对，但真要到身</description><pubDate>Thu, 02 Nov 2023 08:26:01 GMT</pubDate><content:encoded>&lt;p&gt;最近的烦心事不可谓不多，写这篇也是临时起意，将这些事情记录下来，也是给自己这大学两年多以来的经历进行总结教训。&lt;/p&gt;
&lt;p&gt;总说大学大学，大不了自学。听起来很对，但真要到身体力行，则深感道阻且长，学习上的事，从小学以来基本都是老师讲，我听，我记，我做，不断重复然后形成知识体系，于我，上课听懂多少基本就是我学了多少；但到了大学，老师讲的东西，要么听不懂，要么听懂一部分，剩下就茫然了，尝试看书，有时会有很好的发现，可一旦落笔，则又瞬间什么又不懂了。我不知道怎么做，我不知道怎么学，我不知道怎么问，我不知道怎么解决。&lt;/p&gt;
&lt;p&gt;大二转了专业，到了软院，数据结构瞬间打回原形，大一学的只有一节课相关，但本身上课就稀里糊涂，而且还不是一个版本，数据结构用C++编程，只学了一点C，连PPT的示例代码都看不懂，更别说自己写了。于是重新走上老路，上课不听去自学C++ Prime plus，但这本书将近千页，平时还有别的课程，没时间一次看完，一次看一点的方式完全行不通。这就是第一个烦心事。&lt;/p&gt;
&lt;p&gt;接下来说说最近的，认识实习，原定第八周，推迟到了第九周，也就是本周，这周也马上过去了，明天也有web课程设计的考试，可项目又停滞了一个多月，当了团队的leader，一开始三番五次向其他成员说明，让他们要报告自己的进展，好安排任务，但是，他们完全没有团队意识，而我又很忙，9月底还找了一份实习的工作，已经做了一个多月了，该怎么办？第十四周就要提交了，本周末肯定要抓紧完成初步的项目，但还有别的任务，别的实验课，别的考试，我该怎么办？这就是第二个烦心事。&lt;/p&gt;
&lt;p&gt;认识实习这几天，学校的安排给我感觉很差劲，先是把我们班级拆成两个组，并到其他组里去，然后和我同组的另一个班，计划变更也不说明，也不能按时抵达（有时是我们班其他人），联系的时候还联系不上，不提前站队查人，导致上车后等半天，虽然这些都是小事，但给我的感受就是很不好受，行政管理低下导致的问题。这些其实都不是什么重点，重点是即将结束的大学生活，课程设计、实验、毕业设计、考研还是找工作？考研自然毫不必问，可问题是方向还有能力，就目前而言，我有很多想做的事情，可这些需要时间，还有指导！大学以来，很少在学习上有过指点，坑是自己踩的，活是自己干的。&lt;/p&gt;
</content:encoded></item><item><title>CE修改器入门</title><link>https://h1s97x.github.io/posts/notes/notes-ce%E4%BF%AE%E6%94%B9%E5%99%A8%E5%85%A5%E9%97%A8/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-ce%E4%BF%AE%E6%94%B9%E5%99%A8%E5%85%A5%E9%97%A8/</guid><description>cheat engine 官网 附加Tutorial-i386.exe进程后,我们点击教程的下一步按钮，接着继续第二关，第二关的作用还是很简单的，主要目的是遍历</description><pubDate>Thu, 19 Oct 2023 00:53:08 GMT</pubDate><content:encoded>&lt;h2&gt;新手教程&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://cheatengine.org/&quot;&gt;cheat engine 官网&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;附加&lt;code&gt;Tutorial-i386.exe进程&lt;/code&gt;后,我们点击教程的下一步按钮，接着继续第二关，第二关的作用还是很简单的，主要目的是遍历出我们想要的动态数据，比如角色的生命，人物的魔法等，都会用到精确扫描，可以说这一关是既简单又实用的东西，也是今后制作中最常用的环节，接着我们看下&lt;code&gt;Tutorial-i386.exe程序&lt;/code&gt;对这一关通关流程的描述:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;步骤 2: 精确值扫描 (密码=090453)&lt;/p&gt;
&lt;p&gt;现在你已经在 Cheat Engine 中打开了训练程序，为我们下一步的练习做好了准备。
本窗口的左下方显示的&quot;健康:XXX&quot;，在你每次点击&quot;打我&quot;按钮时，它的值便会减少。
要进入下一关，你必须找到这个数值并把它改成 1000 。
很多方法都可以找到这个数值的位置，但我将告诉你一个最简单的方法，&quot;精确数值&quot;扫描：&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;上面的简单描述的意思就是,需要将100这个数值修改为1000则本关就算通过，看下面具体的步骤&lt;/p&gt;
&lt;p&gt;1.首先游戏规则是每次我们点击&lt;code&gt;打我&lt;/code&gt;按钮则健康值则会减一,我们首先搜索这个&lt;code&gt;100&lt;/code&gt;看能不能找到些什么.&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/CE/CE-simple-scan-1.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;现在开始搜索精确数值 &lt;code&gt;100&lt;/code&gt;  数值中&lt;code&gt;输入100&lt;/code&gt; 点击 &lt;code&gt;首次扫描&lt;/code&gt; 按钮&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/CE/CE-simple-scan-2.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;默认情况下一般游戏就是4字节，这里不需要改动&lt;code&gt;扫描类型和数值类型&lt;/code&gt;，默认就好了.&lt;/p&gt;
&lt;p&gt;这次扫描我们得到 35 个结果，里面肯定有我们要找的那个血值，不过好像太多了,没关系继续往下看.&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/CE/CE-simple-scan-3.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;关键一步：为了找到更加精确的数据，我们回到 Tutorial 点击 &lt;code&gt;打我&lt;/code&gt; 按钮，此时血值已有变化了：&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/CE/CE-simple-scan-4.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;我们再输入 &lt;code&gt;95&lt;/code&gt; 点击 &lt;code&gt;再次扫描&lt;/code&gt; 按钮 结果只剩1个（这就是我们要找的），我们双击此地址将其添加到地址栏：&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/CE/CE-simple-scan-5.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;此时地址栏里面只有1个结果了，这个就是我们要找的内存地址，双击将其加入到地址栏&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/CE/CE-simple-scan-6.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;在数值95上面双击，并修改把 95 改成 1000 点击&lt;code&gt;确定&lt;/code&gt;按钮,此时通关.&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/CE/CE-simple-scan-7.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;此时回到Tutorial-i386.exe程序，会发现教程的 &lt;code&gt;下一步&lt;/code&gt; 按钮变成可用,再次点击打我按钮，数值变大了，继续点击下一步进入第三关...&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/CE/CE-simple-scan-8.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>外挂分析与实现</title><link>https://h1s97x.github.io/posts/notes/notes-%E5%A4%96%E6%8C%82%E5%88%86%E6%9E%90%E4%B8%8E%E5%AE%9E%E7%8E%B0/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-%E5%A4%96%E6%8C%82%E5%88%86%E6%9E%90%E4%B8%8E%E5%AE%9E%E7%8E%B0/</guid><description>PlantsVsZombies-Cheat: 植物大战僵尸逆向分析与辅助开发系列教程(github.com) 06.常用断点之硬件断点原理解析哔哩哔哩bilib</description><pubDate>Thu, 19 Oct 2023 00:39:59 GMT</pubDate><content:encoded>&lt;h2&gt;PVZ辅助&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/follow-the-vine-to-get-to-the-melon/PlantsVsZombies-Cheat/tree/main&quot;&gt;PlantsVsZombies-Cheat: 植物大战僵尸逆向分析与辅助开发系列教程(github.com)&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;工具使用&lt;/h3&gt;
&lt;h4&gt;OnlyDbg使用&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1cE411f7sE/?p=6&amp;amp;spm_id_from=pageDriver&amp;amp;vd_source=206a091077ff783d662b49e64cc9589d&quot;&gt;06.常用断点之硬件断点原理解析_哔哩哔哩_bilibili&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/xueyoo/articles/4938538.html&quot;&gt;OllyDBG完美教程(超强入门级) - xueyoo - 博客园 (cnblogs.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;OllyDBG界面认识&lt;/strong&gt;
&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/Network-attack/OB-desktop.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;反汇编窗口：显示被调试程序的反汇编代码。用鼠标左键点击注释标签可以切换注释显示的方式。如果需要修改代码，直接在这里在修改，比如“使用NOP填充”。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;寄存器窗口：显示当前所选线程的CPU寄存器内容。点击标签寄存器(FPU)可以切换显示寄存器的方式。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;信息窗口：显示反汇编窗口中当前选中的第一条命令的参数及一些跳转目标地址、字串等（一般动态加密解密字串等会在此出现）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;数据窗口：显示内存或文件的内容。直接点击ASCII数据可以切换编码显示方式。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;堆栈窗口：显示当前线程的堆栈数据。比如程序初始化的一些常量、变量之类的数据。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;菜单窗口：一些常用调试命令以及操作。一般把鼠标移到菜单窗口的某按钮后，界面最左下角会出现一些命令提示，比如单步步入（F7），单步步过F8，运行程序（F9）等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;命令行窗口：用来下断点等相关命令。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;调试方法&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/Network-attack/debug-shortcut-key.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;F2：设置断点。只要在光标定位的位置（上图中灰色条）按F2键即可，再按一次F2键则会删除断点。（相当于 SoftICE 中的 F9）
F8：单步跳过。每按一次这个键执行一条反汇编窗口中的一条指令，遇到 CALL 等子程序不进入其代码。（相当于 SoftICE 中的 F10）
F7：单步步入。功能同单步步过(F8)类似，区别是遇到 CALL 等子程序时会进入其中，进入后首先会停留在子程序的第一条指令上。（相当于 SoftICE 中的 F8）
F4：运行到选定位置。作用就是直接运行到光标所在位置处暂停。（相当于 SoftICE 中的 F7）
F9：运行。按下这个键如果没有设置相应断点的话，被调试的程序将直接开始运行。（相当于 SoftICE 中的 F5）
CTR + F9：执行到返回。此命令在执行到一个 ret (返回指令)指令时暂停，常用于从系统领空返回到我们调试的程序领空。（相当于 SoftICE 中的 F12）
ALT + F9：执行到用户代码。可用于从系统领空快速返回到我们调试的程序领空。（相当于 SoftICE 中的 F11）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;常用断点&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/LyShark/p/13071792.html&quot;&gt;Windows 下的常用调试API断点 - lyshark - 博客园 (cnblogs.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/qq1084283172/article/details/47733849&quot;&gt;OD调试程序常用断点大全_od 创建进程 断点-CSDN博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[&lt;a href=&quot;https://cloud.tencent.com/developer/article/1910265&quot;&gt;系统安全] 二十四.逆向分析之OD调试INT3断点、反调试、硬件断点与内存断点-腾讯云开发者社区-腾讯云 (tencent.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;汇编指令&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cmp a,b 比较a与b

mov a,b 把b的值送给a

ret 返回主程序

nop 无作用,英文“no operation”的简写，意思是“do nothing”(机器码90)
(解释:ultraedit打开编辑exe文件时你看到90,等同于汇编语句nop)

call 调用子程序

je 或jz 若相等则跳(机器码74 或0F84)

jne或jnz 若不相等则跳(机器码75或0F85)

jmp 无条件跳(机器码EB)

jb 若小于则跳

ja 无符-若大于则跳

jg 有符-若大于则跳

jge 若大于等于则跳

jl 若小于则跳

jle 若小于等于则跳

pop 出栈

push 压栈
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;1.E语言在OD里面的入口特征（OEP）
E语言的入口其实和VC++基本差不多，所以 在OD里面Ctrl+B 输入 FF 25
​2.E语言的按钮事件
FF 55 FC 5F 5E，这个就是E语言的按钮在OD中的特征码，搜索它就直接可以找到按钮事件在OD的Call的位置
3.E语言常用断点&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MessageBoxA  消息框断点&lt;/li&gt;
&lt;li&gt;ShellExecuteA/W   WinExec    弹网页&lt;/li&gt;
&lt;li&gt;CreateProcessA/W  创建进程&lt;/li&gt;
&lt;li&gt;ExitProcess       退出进程&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;脱壳&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://www.52pojie.cn/thread-1646466-1-1.html&quot;&gt;练习笔记之手撕常见压缩壳 - 『脱壳破解区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.52pojie.cn/thread-1641368-1-1.html&quot;&gt;关于压缩壳脱壳步骤详解！看这一篇足够。 - 『脱壳破解区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/hongren/p/12633232.html&quot;&gt;ESP定律脱壳——NsPack3.x脱壳 - 红刃 - 博客园 (cnblogs.com)&lt;/a&gt;
&lt;a href=&quot;https://www.52pojie.cn/thread-941671-1-1.html&quot;&gt;手脱NsPack 壳，新手看看&lt;/a&gt;
什么是壳：
壳是一种概念上的东西，人们为了保护软件不会被轻易的修改或者反编译，希望软件能够获得一种保护，能如同乌龟壳保护乌龟一般，能有一个东西保护自己，于是壳就出现了。
关于壳的作用：
壳的初始作用是保护软件，但后来发展的方向不一就出现了各种各样的壳，大致有压缩壳、加密壳、VM 壳的分类。压缩壳故名思意，主要作用是用于压缩方面，可以有效的减小软件的大小；加密壳，其主要作用是保护软件；VM 壳是一种很特殊的壳，它利用了虚拟机技术，可以很有效的保护指定地址代码，但很大的牺牲了效率，所以一般只在关键代码处使用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ESP定律法 （80%的压缩壳可脱）&lt;/strong&gt;
ESP 定律法是脱壳的利器，是应用频率最高的脱壳方法之一。ESP 定律的原理在于程序中堆栈平衡的合理利用。由于在程序自解密或者自解压过程中，不少壳会先将当前寄存器内容压栈，如使用 pushad，在解压结束后，会将之前的寄存器值出栈，如使用 popad。因此在寄存器出栈时，往往程序代码被自动恢复，此时硬件断点触发。然后在程序当前位置，只需要少许单步跟踪，就很容易到达正确的 OEP 位置。&lt;/p&gt;
&lt;p&gt;具体操作步骤：pushad开头，F8一次以后，8个寄存器只有ESP是红色时候，就可以使用该定律。
右键ESP 选择数据窗口跟随，数据窗口下硬件访问断点，F9运行，此时已经接近OEP。
&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/Network-attack/pvz-shell.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;h3&gt;逆向分析&lt;/h3&gt;
&lt;p&gt;可以看到这个辅助的功能有很多，接下来就跟我一起解密这些按钮在点击后发生了什么。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;之前分析的时候一直没开游戏只对辅助od，结果破壳破了好久也没成功，但就算破壳也无法直接获取到源码，只能看汇编，破解还是要在游戏中去进行，辅助修改游戏的内存，只要对写内存操作打断点就可以观察具体的修改。踩了这样一个坑，希望引以为戒。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/Network-attack/PVZ-analysis-1.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;h4&gt;阳光修改&lt;/h4&gt;
&lt;p&gt;1.首先我们找出阳光的动态地址，找到之后我们双击这个地址，将其加入到地址列表中，阳光的查找技巧相信你已经能够掌握了，这里就不再罗嗦了。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/game-hack-analysis-10-20261014.png&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/game-hack-analysis-10-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;2.接着我们选择最下方的地址列表，然后按下&lt;code&gt;F6键&lt;/code&gt;，也就是下一个硬件写入断点，然后回到游戏等待阳光出现并点击阳光，此时CE会出现以下代码，我们只需要记下&lt;code&gt;00430A11&lt;/code&gt;这个内存地址，然后直接关闭CE吧，短期内用不到了。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/game-hack-analysis-08-20261014.png&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/game-hack-analysis-08-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;3.打开OD，然后我们直接附加植物大战僵尸的游戏进程，点击&lt;code&gt;文件&lt;/code&gt;然后点击&lt;code&gt;附加&lt;/code&gt;选择植物大战僵尸进程就好。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/game-hack-analysis-06-20261014.png&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/game-hack-analysis-06-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;4.附加完成后，我们的游戏会被OD暂停运行，此时我们直接按下&lt;code&gt;F12&lt;/code&gt;让游戏正常的运行，默认情况下会停在系统领空，我们直接按下&lt;code&gt;Ctrl + G&lt;/code&gt;输入&lt;code&gt;00401000&lt;/code&gt;来到程序领空。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/game-hack-analysis-07-20261014.png&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/game-hack-analysis-07-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;5.接着我们按下&lt;code&gt;Ctrl + G&lt;/code&gt;输入&lt;code&gt;00430A11&lt;/code&gt;跳转到刚才找到的代码位置，过去以后直接&lt;code&gt;F2&lt;/code&gt;下一个断点&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/game-hack-analysis-11-20261014.png&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/game-hack-analysis-11-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;6.紧接着我们回到游戏，然后等待阳光的出现，当阳光出现后我们点击阳光，OD会自动断在00430A11的位置，其中ecx就是要增加的阳光数。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/game-hack-analysis-03-20261014.png&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/game-hack-analysis-03-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;上图并没有CALL调用，应该是在外部，我们直接回朔到上一层&lt;code&gt;Ctrl+F9&lt;/code&gt;执行到返回，&lt;code&gt;F8&lt;/code&gt;直接出CALL.&lt;/p&gt;
&lt;p&gt;7.我们在JNZ和CALL的两处位置下断点，然后运行程序，当我们点击阳光时会断在JNZ的位置，如果把JNZ用NOP填充的话，会发现当我们再次点击阳光的时候阳光会迅速的被回收没有了动画。&lt;/p&gt;
&lt;p&gt;也就是说，JNZ如果成立的话则会有回收阳光的动画，当不成立的时候则会执行&lt;code&gt;call 004309d0&lt;/code&gt;显然这就是回收阳光的关键CALL，但我们可猜测，再回收阳光的时候一定会判断是否回收，所以回收的判断应该在上一层。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/game-hack-analysis-02-20261014.png&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/game-hack-analysis-02-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;8.首先我们回到游戏，等待出现阳光然后点击阳光，OD会断下，断下后我们取消这两处的断点，然后跟着OD单步&lt;code&gt;F8&lt;/code&gt;向下走，直接出这个CALL，出CALL后会看到如下代码，我们直接下三个断点。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/game-hack-analysis-09-20261014.png&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/game-hack-analysis-09-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;9.下好断点，回到游戏等待阳光下落我们回收，此时OD会断下，我们运行OD发现会一直被断在&lt;code&gt;0043158F&lt;/code&gt;只要阳光没有收回就会一直断下，此处的跳转并没有实现，因为我们没有去点击阳光，这里可以得出如果点击了阳光JNZ则会跳转成立，否则不成立等待用户点击回收阳光。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/game-hack-analysis-05-20261014.png&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/game-hack-analysis-05-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;10.我们取消所有的断点，然后尝试把JNZ跳转改成JMP无条件跳转，让其只要一出现阳光就自动的触发回收的CALL试试。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/game-hack-analysis-01-20261014.png&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/game-hack-analysis-01-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;改完以后我们回到游戏，会发现只要阳光出现了会被立即回收。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/game-hack-analysis-04-20261014.png&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/game-hack-analysis-04-20261014.png&quot; alt=&quot;img&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>UML统一建模语言</title><link>https://h1s97x.github.io/posts/notes/notes-uml%E7%BB%9F%E4%B8%80%E5%BB%BA%E6%A8%A1%E8%AF%AD%E8%A8%80/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-uml%E7%BB%9F%E4%B8%80%E5%BB%BA%E6%A8%A1%E8%AF%AD%E8%A8%80/</guid><description>以下简要地介绍了这 13 个 UML 图表。UML 图表可大致分为结构性图表和行为性图表两种。 ! 结构性图表显示了系统在不同抽象层次和实现层次上的静态结构以及</description><pubDate>Mon, 16 Oct 2023 02:44:42 GMT</pubDate><content:encoded>&lt;h2&gt;UML词汇表和术语&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;抽象类&lt;/strong&gt;：一个永远不会被实例化的类。这个类的一个实例永远不会存在。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Actor（演员）&lt;/strong&gt;：发起系统参与的事件的对象或人物。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;活动&lt;/strong&gt;：活动图中的步骤或行动。表示系统或演员采取的行动。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;活动图&lt;/strong&gt;：一个美化的流程图，显示了流程中的步骤和决定以及并行操作，如算法或业务流程。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;聚合&lt;/strong&gt;：是另一类的一部分。图中的包含类旁边有一个空心钻石。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;工件&lt;/strong&gt;：描述设计过程中某个步骤输出的文档。描述是图形，文字或其他组合。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;关联&lt;/strong&gt;：一个模型的两个元素之间的联系。这可能代表代码中的成员变量，或者人员记录与其所代表的人之间的关联，或者两类工作人员之间的关系，或者任何类似的关系。默认情况下，一个关联中的两个元素是相等的，并且通过该关联知道对方。一个协会也可以是一个可导航的协会，这意味着协会的来源端知道目标端，但反之亦然。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;关联类&lt;/strong&gt;：表示两个其他类之间的关联信息的类。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;属性&lt;/strong&gt;：可用于引用其他对象或保存对象状态信息的对象的特征。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;基类&lt;/strong&gt;：定义由子类通过泛化关系继承的属性和操作的类。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;分支&lt;/strong&gt;：活动图中的决策点。分支出现多个转变，每个都有一个保护条件。当控制到达分支时，恰好一个保护条件必须为真; 并且控制遵循相应的转换。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;类&lt;/strong&gt;：类似对象的类别，全部由相同的属性和操作描述，并且所有的赋值兼容。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;类图&lt;/strong&gt;：显示系统类和它们之间的关系。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;分类器&lt;/strong&gt; ：具有属性和操作的UML元素。具体来说，Actor，Classes和Interfaces。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;协作&lt;/strong&gt;：通信图中两个对象之间的关系，指示消息可以在对象之间来回传递。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;通信图&lt;/strong&gt;：显示如何在强调对象角色的同时完成操作的图表。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;组件&lt;/strong&gt;：系统中可部署的代码单元。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;组件图&lt;/strong&gt;：显示各种组件和接口之间关系的图表。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;概念&lt;/strong&gt;：要包含在域模型中的名词或抽象概念。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;构建阶段&lt;/strong&gt;：Rational统一过程的第三阶段，在这个阶段中，正在构建的系统中内置了多个功能迭代。这是主要工作完成的地方。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;依赖关系&lt;/strong&gt;：指示一个分类器的关系知道另一个分类器的属性和操作，但不直接连接到第二个分类器的任何实例。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;部署图&lt;/strong&gt;：显示各种处理器之间关系的图表。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;域&lt;/strong&gt;：系统所涉及的宇宙的一部分。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;精化阶段&lt;/strong&gt;：Rational统一过程的第二阶段，允许额外的项目计划，包括构建阶段的迭代。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;元素&lt;/strong&gt;：出现在模型中的任何项目。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;封装&lt;/strong&gt;：对象中的数据是私有的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;泛化&lt;/strong&gt;：指示一个类是另一个类（超类）的子类。一个空心箭头指向超类。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;事件&lt;/strong&gt;：在状态图中，这表示导致系统采取行动或切换状态的信号或事件或输入。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;最终状态&lt;/strong&gt; ：在状态图或活动图中，这表示图完成的点。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;叉&lt;/strong&gt;：活动图中的一个点，多个并行控制线程开始。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;泛化&lt;/strong&gt;：继承关系，其中一个子类继承并添加到基类的属性和操作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GoF&lt;/strong&gt;：四人帮设计模式。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;高凝聚力&lt;/strong&gt;：GRASP评估模式，确保课程不是太复杂，做不相关的功能。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;低耦合&lt;/strong&gt;：GRASP评估模式，衡量一个类别依赖于另一个类别或与另一个类别相关联。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;启动阶段&lt;/strong&gt;：Rational统一过程的第一阶段，处理原始概念化和项目开始阶段。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;继承&lt;/strong&gt;：子类继承父类（超类）类的属性或特性。这些属性可以在子类中重写。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;初始状态&lt;/strong&gt;：在状态图或活动图中，这表示图开始的点。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;实例&lt;/strong&gt;：一个类像模板一样用来创建一个对象。这个对象被称为类的一个实例。可以创建任何数量的该类的实例。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;接口&lt;/strong&gt;：定义形成行为契约的属性和操作的分类器。提供者类或组件可以选择实现接口（即实现其属性和操作）。客户端类或组件可以依赖于接口，并因此使用提供者而不提供提供者的真实类别的任何细节。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;迭代&lt;/strong&gt;：一个小项目部分，在这个小项目中，一小部分功能被添加到项目中。包括分析，设计和编码的开发循环。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;加入&lt;/strong&gt;：活动图中的一个点，多个并行控制线程同步并重新加入。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;成员&lt;/strong&gt;：分类器中的属性或操作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;合并&lt;/strong&gt;：活动图中的一个点，不同的控制路径汇集在一起。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;消息&lt;/strong&gt;：从一个对象到另一个对象的请求，要求接收消息的对象执行某些操作。这基本上是对接收对象中的方法的调用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;方法&lt;/strong&gt;：对象中的函数或过程。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模型&lt;/strong&gt;：中央UML神器。由各个元素组成的层次结构，以及元素之间的关系。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多重性&lt;/strong&gt;：显示在领域模型中，表示外部概念框，表示与其他对象的分位数的对象数量关系。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可导航性&lt;/strong&gt;：指示关系的哪一端知道另一端。关系可以具有双向可导航性（每一端意识到另一端）或单向导航（一端意识到另一端，但反之亦然）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;符号&lt;/strong&gt;：创建分析和设计方法的图形化文档。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;对象&lt;/strong&gt;：对象：在活动图中，从活动接收信息或向活动提供信息的对象。在“协作图”或“序列图”中，参与图中所示场景的对象。通常：给定分类器（Actor，Class或Interface）的一个实例或示例。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;包&lt;/strong&gt;：逻辑上应该组合在一起的一组UML元素。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;包图&lt;/strong&gt;：所有元素都是包和依赖关系的类图。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模式&lt;/strong&gt;：用于确定交互对象责任分配的解决方案。这是一个成功解决众所周知的常见问题的名称。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数&lt;/strong&gt;：一个操作的参数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多态性&lt;/strong&gt;：相同的消息，不同的方法。也用作模式。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;私有&lt;/strong&gt;：应用于属性或操作的可见性级别，指示只有包含该成员的分类器的代码才可以访问该成员。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;处理器&lt;/strong&gt;：在部署图中，代表可以部署代码的计算机或其他可编程设备。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;受保护&lt;/strong&gt;：应用于属性或操作的可见性级别，指示只有包含该成员或其子类的分类器的代码才能访问该成员。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;公开&lt;/strong&gt;：应用于属性或操作的可见性级别，指示任何代码都可以访问该成员。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;读取方向箭头&lt;/strong&gt;：指示领域模型中关系的方向。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;实现&lt;/strong&gt;：表示组件或类提供给定的接口。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;角色&lt;/strong&gt;：用于域模型，是关于角色角色的可选描述。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;顺序图&lt;/strong&gt;：一个图表，显示随着时间的推移对象的存在，以及随着时间的推移在这些对象之间传递的消息来执行一些行为。状态图图 - 显示所有可能的对象状态的图。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;状态&lt;/strong&gt;：在状态图中，这表示系统或子系统的一种状态：它在某个时间点所做的事情，以及其数据的值。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;状态图&lt;/strong&gt;：显示系统或子系统的状态，状态之间的转换以及导致转换的事件的图表。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;静态&lt;/strong&gt;：一个属性的修饰符，用于指示分类器的所有实例之间共享的属性只有一个副本。“操作”的修饰符，用于指示“操作”独立运行并且不在分类器的一个特定实例上操作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;刻板印象（Stereotype）&lt;/strong&gt;：一个应用于Model元素的修饰符，用于指示它通常不能用UML表示的东西。从本质上讲，定型允许你定义你自己的UML“方言”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;子类&lt;/strong&gt;：继承由子类通过泛化关系定义的属性和操作的类。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;互動區&lt;/strong&gt;：活动图的一个元素，指示系统或域的哪些部分执行特定的活动。泳道内的所有活动都由泳道所代表的对象，组件或者演员负责。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;时间拳击&lt;/strong&gt;：每次迭代都会有特定目标的时间限制。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;过渡&lt;/strong&gt;：在活动图中，表示从一个活动或分支或合并或分叉或连接到另一个的控制流程。在国家图中，代表着一个国家向另一个国家的转变。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;过渡阶段&lt;/strong&gt; - Rational 统一过程的最后一个阶段，在这个阶段，用户接受使用新系统和系统的培训，用户可以使用这个阶段。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;统一建模语言（ UML）&lt;/strong&gt;：统一建模语言（ UML）利用文本和图形文档，通过在对象之间建立更紧密的关系，来增强软件项目的分析和设计&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用例&lt;/strong&gt;：在用例图中，表示系统响应来自Actor的某个请求所采取的操作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用例图&lt;/strong&gt;：显示参与者和用例之间关系的图表。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可见性&lt;/strong&gt;：对属性或操作的修饰符，指示哪些代码可以访问成员。可见性级别包括公共，受保护和私有。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;工作流程&lt;/strong&gt;：一组产生特定结果的活动。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;UML - 概述&lt;/h2&gt;
&lt;p&gt;以下简要地介绍了这 13 个 UML 图表。UML 图表可大致分为结构性图表和行为性图表两种。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/uml-01-20261014.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;结构性图表显示了系统在不同抽象层次和实现层次上的静态结构以及它们之间的相互关系。结构性图表中的元素表示系统中具意义的概念，可能包括抽象的、现实的和實作的概念。结构性图表有七种类型：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.visual-paradigm.com/cn/guide/uml-unified-modeling-language/what-is-uml/#class-diagram&quot;&gt;类图 (Class Diagram)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.visual-paradigm.com/cn/guide/uml-unified-modeling-language/what-is-uml/#component-diagram&quot;&gt;组件图 (Component Diagram)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.visual-paradigm.com/cn/guide/uml-unified-modeling-language/what-is-uml/#deployment-diagram&quot;&gt;部署图 (Deployment Diagram)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.visual-paradigm.com/cn/guide/uml-unified-modeling-language/what-is-uml/#object-diagram&quot;&gt;对象图 (Object Diagram)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.visual-paradigm.com/cn/guide/uml-unified-modeling-language/what-is-uml/#package-diagram&quot;&gt;包图 (Package Diagram)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.visual-paradigm.com/cn/guide/uml-unified-modeling-language/what-is-uml/#composite-structure-diagram&quot;&gt;复合结构图 (Composite Structure Diagram)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.visual-paradigm.com/cn/guide/uml-unified-modeling-language/what-is-uml/#profile-diagram&quot;&gt;轮廓图 (Profile Diagram)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;行为性图表显示了系统中对象的动态行为 ，可用以表达系统随时间的变化。行为性图表有七种类型：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.visual-paradigm.com/cn/guide/uml-unified-modeling-language/what-is-uml/#use-case-diagram&quot;&gt;用例图 (Use Case Diagram)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.visual-paradigm.com/cn/guide/uml-unified-modeling-language/what-is-uml/#activity-diagram&quot;&gt;活动图 (Activity Diagram)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.visual-paradigm.com/cn/guide/uml-unified-modeling-language/what-is-uml/#state-machine-diagram&quot;&gt;状态机图 (State Machine Diagram)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.visual-paradigm.com/cn/guide/uml-unified-modeling-language/what-is-uml/#sequence-diagram&quot;&gt;序列图 (Sequence Diagram)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.visual-paradigm.com/cn/guide/uml-unified-modeling-language/what-is-uml/#communication-diagram&quot;&gt;通訊圖 (Communication Diagram)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.visual-paradigm.com/cn/guide/uml-unified-modeling-language/what-is-uml/#interaction-overview-diagram&quot;&gt;交互概述图 (Interaction Overview Diagram)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.visual-paradigm.com/cn/guide/uml-unified-modeling-language/what-is-uml/#timing-diagram&quot;&gt;时序图 (Timing Diagram)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;类图 (Class Diagram)&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/ibukikonoha/article/details/80643959?utm_source=app&amp;amp;app_version=4.9.1&quot;&gt;UML类图总结（浅显易懂实用）-CSDN博客&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;类的基本表示方式&lt;/h4&gt;
&lt;p&gt;在UML类图中，每一个矩形代表一个类。&lt;/p&gt;
&lt;h4&gt;关系&lt;/h4&gt;
&lt;p&gt;类之间的关系有继承关系，实现关系，依赖关系，关联关系，聚合关系，组合关系。&lt;/p&gt;
&lt;p&gt;有三种主要的关系：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;关联 - 代表类型之间的关系（一个人为公司工作，一间公司有多个办事处）。&lt;/li&gt;
&lt;li&gt;继承 - 专为将实例关系图 (ERD) 应用于面向对象设计而设的一种关系。它在面向对象设计中的继承概念互相呼应。&quot;is-a&quot; relationship&lt;/li&gt;
&lt;li&gt;聚合 - 面向对象设计中的一种对象组合的形式，是另一类的一部分。“has-a” relationship&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/software-engineering/class-diagram-example.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;h3&gt;序列图 (Sequence Diagram)&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/fly_zxy/article/details/80911942?utm_source=app&amp;amp;app_version=4.9.1&quot;&gt;UML时序图(Sequence Diagram)学习笔记_-CSDN博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Definition&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;时序图(Sequence Diagram)，又名序列图、循序图，是一种UML交互图。它通过描述对象之间发送消息的时间顺序显示多个对象之间的动态协作。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Element&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;角色(Actor)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;系统角色，可以是人或者其他系统，子系统。以一个小人图标表示。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;对象(Object)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;对象位于时序图的顶部,以一个矩形表示。对象的命名方式一般有三种：
1 对象名和类名。例如：华为手机:手机、loginServiceObject:LoginService。
2 只显示类名，不显示对象，即为一个匿名类。例如：:手机、:LoginSservice。
3 只显示对象名，不显示类名。例如：华为手机:、loginServiceObject:。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;生命线(LifeLine)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;时序图中每个对象和底部中心都有一条垂直的虚线，这就是对象的生命线(对象的时间线)。以一条垂直的虚线表。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;控制焦点(Activation)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;控制焦点代表时序图中在对象时间线上某段时期执行的操作。以一个很窄的矩形表示。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;消息(Message)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;表现代表对象之间发送的信息。消息分为三种类型。
同步消息(Synchronous Message)
消息的发送者把控制传递给消息的接收者，然后停止活动，等待消息的接收者放弃或者返回控制。用来表示同步的意义。以一条实线+实心箭头表示。
异步消息(Asynchronous Message)
消息发送者通过消息把信号传递给消息的接收者，然后继续自己的活动，不等待接受者返回消息或者控制。异步消息的接收者和发送者是并发工作的。以一条实线+大于号表示。
返回消息(Return Message)
返回消息表示从过程调用返回。以小于号+虚线表示。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;关联消息&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;表示方法的自身调用或者一个对象内的一个方法调用另外一个方法。以一个半闭合的长方形+下方实心剪头表示。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;时序图示例：&lt;/strong&gt;
1，用户输入手机密码
2，打开手机
3，打开微信扫一扫
4，返回微信扫一扫界面
5.1 扫描商家收款码
5.2 商家生成收款二维码
5.3 返回收款二维码
5.4 识别商家收款码
6，提示用户输入微信支付密码
7.1 输入微信支付密码
7.2 微信验证用户输入密码正确
7.3 向商家汇款&lt;/p&gt;
&lt;p&gt;7.4 汇款成功
8，提示用户支付成功
&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/software-engineering/UML-sequence-diagram.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;h3&gt;状态机图 (State Machine Diagram)&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/gstrong298/article/details/25166425?utm_source=app&amp;amp;app_version=4.9.1&quot;&gt;【学习小记】UML——状态机图_uml状态机图怎么画-CSDN博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Definition&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;UML状态机图同活动图一样，是动态视图的一种。&lt;/p&gt;
&lt;p&gt;UML状态机图描述的是某个对象的状态和感兴趣的事件，以及对象响应该事件的行为。它描述的是“某个对象”，因此，一个状态机图中只能表示一个对象，而不是多个对象。状态图显示了该对象的生命周期，也即，对象经历的事件，对象的转换和对象在这些事件之间的状态。
&lt;strong&gt;Element&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;1、状态&lt;/p&gt;
&lt;p&gt;UML状态机图中的状态，以圆角方框表示，通常有状态名称，进入/退出活动，内部转换，子状态，延迟事件组成（后面介绍）。&lt;/p&gt;
&lt;p&gt;状态还包括初始状态（由实心黑圆点表示）和终止状态（由实心黑圆点外包一个圆圈表示）。&lt;/p&gt;
&lt;p&gt;2、事件&lt;/p&gt;
&lt;p&gt;事件包含在转换中.&lt;/p&gt;
&lt;p&gt;3、转换&lt;/p&gt;
&lt;p&gt;转换涉及的内容包括：源状态、目标状态、触发事件（事件）、监护条件、动作，具体可以参看下图的例子。&lt;/p&gt;
&lt;p&gt;如何绘制状态机图&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  绘制状态机图的基本步骤为：

  1、寻找主要状态

  2、确定状态间的转换，可借助状态转换分析表

  3、细化状态内部的活动和转换

        根据需要添加内部转换、进入转换、退出转换以及相关的活动等。

  4、用复合状态来展开细节

  下面用一个例子来进行绘制过程的演示。
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;数据流图 (Data-flow diagrams(DFD))&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/I_r_o_n_M_a_n/article/details/121309525&quot;&gt;【精选】软件工程 -- 数据流图的画法_CodeJiao的博客-CSDN博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Definition&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;数据流图(Data Flow Diagram，DFD)是一种图形化技术，它描绘信息流和数据从输入移动到输出的过程中所经受的变换。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;数据流图的优点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在数据流图中没有任何具体的物理元素，它只是描绘信息在软件中流动和被处理的情况。&lt;/li&gt;
&lt;li&gt;因为数据流图是系统逻辑功能的图形表示，即使不是专业的计算机技术人员也容易理解它，所以是分析员与用户之间极好的沟通工具。&lt;/li&gt;
&lt;li&gt;设计数据流图时只需考虑系统必须完成的基本逻辑功能，完全不需考虑怎样具体地实现这些功能，因此，它也是今后进行软件设计很好的出发点。&lt;/li&gt;
&lt;li&gt;数据流图可以在任何抽象层次上被用来表示系统或软件。事实上，数据流图可以被分层次地画，层次越低（在数据流图中1层比0层的层次更等）表现出的信息流细节和功能细节也越多。数据流图既提供了功能建模机制、也提供了信息流建模机制。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Element&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;数据流图有4种基本符号：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;正方形（或立方体）表示数据的源点或终点；&lt;/li&gt;
&lt;li&gt;圆角矩形（或圆形）代表变换数据的处理；&lt;/li&gt;
&lt;li&gt;开口矩形（或两条平行横线）代表数据存储；&lt;/li&gt;
&lt;li&gt;箭头表示数据流，即特定数据的流动方向。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;参考&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.visual-paradigm.com/cn/guide/uml-unified-modeling-language/what-is-uml/&quot;&gt;什么是统一建模语言（UML）？ (visual-paradigm.com)&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>观星指南</title><link>https://h1s97x.github.io/posts/blog/%E8%A7%82%E6%98%9F%E6%8C%87%E5%8D%97/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/blog/%E8%A7%82%E6%98%9F%E6%8C%87%E5%8D%97/</guid><description>Hello Sky “夏季大三角”：这是由三颗亮星——织女星、牛郎星、天津四组成的三角形，在夏季星空里显而易见。 夏季三角 - wikipedia</description><pubDate>Sun, 01 Oct 2023 03:34:09 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.cdstm.cn/theme/hellosky/&quot;&gt;Hello Sky&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;夏季大三角&lt;/h3&gt;
&lt;p&gt;“夏季大三角”：这是由三颗亮星——织女星、牛郎星、天津四组成的三角形，在夏季星空里显而易见。
&lt;a href=&quot;https://en.wikipedia.org/wiki/Summer_Triangle&quot;&gt;夏季三角 - wikipedia&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>实习内容</title><link>https://h1s97x.github.io/posts/notes/notes-%E5%AE%9E%E4%B9%A0%E5%86%85%E5%AE%B9/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-%E5%AE%9E%E4%B9%A0%E5%86%85%E5%AE%B9/</guid><description>前几天在淘宝群看到有招前端开发的实习工作，就答应下来，目前已经开始实习了，这里记录一下实习的内容。 实习的薪资说是一个月3k，之后转正会涨到1w，感觉挺不错的，</description><pubDate>Wed, 27 Sep 2023 06:11:53 GMT</pubDate><content:encoded>&lt;h2&gt;概要&lt;/h2&gt;
&lt;p&gt;前几天在淘宝群看到有招前端开发的实习工作，就答应下来，目前已经开始实习了，这里记录一下实习的内容。
实习的薪资说是一个月3k，之后转正会涨到1w，感觉挺不错的，重点是不会耽误课程，而且还能学到东西，跟我商量了上班时间，我根据课表安排周一、周四、周五、周六这四天去，但都是下午，因为早上都有课，最近刚好碰上国庆，公司也不上班，主要要用内网，只能在公司的时候才去写，明天去一趟把页面的框架搭好，之后就到10月7再去了。&lt;/p&gt;
&lt;h2&gt;iVX无编程前端可视化开发平台&lt;/h2&gt;
&lt;p&gt;公网地址：http://192.168.10.114/zdhome/?Mqtt=74EF0#/login?redirect=/home
http://192.168.10.6/
企业主账号：org
子用户名：yjq
密码：12345678&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1H4411e73x/?p=2&amp;amp;spm_id_from=pageDriver&amp;amp;vd_source=206a091077ff783d662b49e64cc9589d&quot;&gt;基础篇|4.0改版基本介绍_哔哩哔哩_bilibili&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1t5411t7s4/?p=2&amp;amp;spm_id_from=pageDriver&amp;amp;vd_source=206a091077ff783d662b49e64cc9589d&quot;&gt;【iVX精通】02-iVX工具结构_哔哩哔哩_bilibili&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;通用前台顶部&lt;/h3&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/Internship\gen-top.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;h4&gt;顶部左右两侧：&lt;/h4&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/Internship\gen-top-side.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;h4&gt;菜单按钮&lt;/h4&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/Internship\gen-top-button.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;image-20230928152926282&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/Internship\gen-top-button2.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;h3&gt;时间显示&lt;/h3&gt;
&lt;p&gt;字体设置：
文字颜色：#168FFF
文字字号：16
文本字体：全局变量-&amp;gt;数字字体-&amp;gt;下标0
排版设置：
右外边距：（选择前台时间参数）时间==true?&quot;136px&quot;:&quot;&quot;
事件：创建触发器&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/Internship\time.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;h3&gt;mqtt&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;重点参考&lt;/strong&gt;
&lt;a href=&quot;https://blog.csdn.net/qq_45173404/article/details/121687489&quot;&gt;RabbitMQ超详细学习笔记&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.aliyun.com/article/769883&quot;&gt;超详细的RabbitMQ入门，看这篇就够了！&lt;/a&gt;
&lt;a href=&quot;https://blog.csdn.net/lzx1991610/article/details/102970854&quot;&gt;RabbitMQ使用教程(超详细)&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;自定义图表 Echats&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://demo.ivx.cn/%E4%BB%BFEcharts%E5%9B%BE%E8%A1%A8%E7%BD%91%E9%A1%B5&quot;&gt;仿Echarts图表网页 |iVX教学Demo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Echats官网：
&lt;a href=&quot;https://echarts.apache.org/zh/index.html&quot;&gt;echarts自定义图表&lt;/a&gt;&lt;/p&gt;
&lt;h5&gt;自定义图表组件&lt;/h5&gt;
&lt;p&gt;进入官网后，选择需要的图表示例&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/Internship\自定义图表教程-1.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/Internship\自定义图表教程-2.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;在IVX中选择通用变量将json串导入进去，然后让自定义图表的数据选择到刚才设置的变量对象。&lt;/p&gt;
&lt;h5&gt;函数法引入echats图表&lt;/h5&gt;
&lt;h3&gt;API使用&lt;/h3&gt;
&lt;h2&gt;&lt;a href=&quot;https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Introduction#%E4%BB%80%E4%B9%88%E6%98%AF_api&quot;&gt;什么是 API?&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;应用程序接口（API，Application Programming Interface）是基于编程语言构建的结构，使开发人员更容易地创建复杂的功能。它们抽象了复杂的代码，并提供一些简单的接口规则直接使用。&lt;/p&gt;
&lt;p&gt;客户端 JavaScript 中有很多可用的 API — 他们本身并不是 JavaScript 语言的一部分，却建立在 JavaScript 语言核心的顶部，为使用 JavaScript 代码提供额外的超强能力。他们通常分为两类：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;浏览器 API&lt;/strong&gt;内置于 Web 浏览器中，能从浏览器和电脑周边环境中提取数据，并用来做有用的复杂的事情。例如&lt;a href=&quot;https://developer.mozilla.org/zh-CN/docs/Web/API/Geolocation_API&quot;&gt;Geolocation API&lt;/a&gt;提供了一些简单的 JavaScript 结构以获得位置数据，因此你可以在 Google 地图上标示你的位置。在后台，浏览器确实使用一些复杂的低级代码（例如 C++）与设备的 GPS 硬件（或可以决定位置数据的任何设施）通信来获取位置数据并把这些数据返回给你的代码中使用浏览器环境；但是，这种复杂性通过 API 抽象出来，因而与你无关。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第三方 API&lt;/strong&gt;缺省情况下不会内置于浏览器中，通常必须在 Web 中的某个地方获取代码和信息。例如&lt;a href=&quot;https://dev.twitter.com/overview/documentation&quot;&gt;Twitter API&lt;/a&gt; 使你能做一些显示最新推文这样的事情，它提供一系列特殊的结构，可以用来请求 Twitter 服务并返回特殊的信息。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;使用别人的API，通常需要以下步骤：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;准备工作：首先，你需要在API提供商的网站上注册账号，并申请API Key。API Key是一个字符串，可以用于标识你的身份和&lt;a href=&quot;https://www.zhihu.com/search?q=%E8%AE%BF%E9%97%AE%E6%9D%83%E9%99%90&amp;amp;search_source=Entity&amp;amp;hybrid_search_source=Entity&amp;amp;hybrid_search_extra=%7B%22sourceType%22%3A%22answer%22%2C%22sourceId%22%3A2819227535%7D&quot;&gt;访问权限&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;查看API文档：其次，你需要查看API的文档，了解API的功能、参数和&lt;a href=&quot;https://www.zhihu.com/search?q=%E8%BF%94%E5%9B%9E%E5%80%BC&amp;amp;search_source=Entity&amp;amp;hybrid_search_source=Entity&amp;amp;hybrid_search_extra=%7B%22sourceType%22%3A%22answer%22%2C%22sourceId%22%3A2819227535%7D&quot;&gt;返回值&lt;/a&gt;。一般来说，API文档会包含以下内容：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;API的地址（URL）&lt;/li&gt;
&lt;li&gt;API的请求方式（GET、POST、PUT、DELETE等）&lt;/li&gt;
&lt;li&gt;API的请求头（Headers）&lt;/li&gt;
&lt;li&gt;API的请求参数（Query String / Request Body）&lt;/li&gt;
&lt;li&gt;API的响应状态码（Status Code）&lt;/li&gt;
&lt;li&gt;API的响应格式（Content-Type）&lt;/li&gt;
&lt;li&gt;API的响应数据（Response Body）&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;发送请求：然后，你可以使用你喜欢的&lt;a href=&quot;https://www.zhihu.com/search?q=%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80&amp;amp;search_source=Entity&amp;amp;hybrid_search_source=Entity&amp;amp;hybrid_search_extra=%7B%22sourceType%22%3A%22answer%22%2C%22sourceId%22%3A2819227535%7D&quot;&gt;编程语言&lt;/a&gt;，通过HTTP协议向API发送请求。例如，你可以使用Python的requests库，发送GET请求：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;import requests

# 设置API地址、请求头和请求参数
url = &apos;https://api.example.com/users&apos;
headers = {&apos;Authorization&apos;: &apos;Bearer YOUR_API_KEY&apos;}
params = {&apos;page&apos;: 2, &apos;per_page&apos;: 10}

# 发送GET请求
response = requests.get(url, headers=headers, params=params)

# 打印响应状态码
print(response.status_code)

# 打印响应数据
print(response.text)
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;处理响应：最后，你可以根据API的文档，处理响应数据。根据响应状态码和响应格式，你可以判断请求是否成功，并进行相应的处理。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;例如，假设API返回的是JSON格式的数据，你可以使用Python的json库将响应&lt;a href=&quot;https://www.zhihu.com/search?q=%E6%95%B0%E6%8D%AE%E8%BD%AC%E6%8D%A2&amp;amp;search_source=Entity&amp;amp;hybrid_search_source=Entity&amp;amp;hybrid_search_extra=%7B%22sourceType%22%3A%22answer%22%2C%22sourceId%22%3A2819227535%7D&quot;&gt;数据转换&lt;/a&gt;为字典：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import requests
import json

# 设置API地址、请求头和请求参数
url = &apos;https://api.example.com/users&apos;
headers = {&apos;Authorization&apos;: &apos;Bearer YOUR_API_KEY&apos;}
params = {&apos;page&apos;: 2, &apos;per_page&apos;: 10}

# 发送GET请求
response = requests.get(url, headers=headers, params=params)

# 判断响应状态码
if response.status_code == 200:
    # 将响应数据转换为字典
    data = json.loads(response.text)
    # 打印响应数据
    print(data)
else:
    print(&apos;Request failed!&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意，使用API时，你需要遵守API的规则和限制，如请求频率限制、访问权限限制等。你也应该对API的响应进行&lt;a href=&quot;https://www.zhihu.com/search?q=%E9%94%99%E8%AF%AF%E5%A4%84%E7%90%86&amp;amp;search_source=Entity&amp;amp;hybrid_search_source=Entity&amp;amp;hybrid_search_extra=%7B%22sourceType%22%3A%22answer%22%2C%22sourceId%22%3A2819227535%7D&quot;&gt;错误处理&lt;/a&gt;，避免程序因为API的问题而崩溃。&lt;/p&gt;
&lt;h3&gt;学习任务&lt;/h3&gt;
&lt;p&gt;1.先做书上的示例，理解组件及其属性&lt;/p&gt;
&lt;h3&gt;任务一：换肤功能实现&lt;/h3&gt;
&lt;p&gt;1.IVX（已完成）
2.vue+bootstrap&lt;/p&gt;
</content:encoded></item><item><title>git版本控制</title><link>https://h1s97x.github.io/posts/notes/notes-git%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-git%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/</guid><description>一个可以管理和追踪软件代码或其他类似内容的不同版本的工具。 出于相同的目的：开发以及维护开发出来的代码、方便读取代码的历史版本、记录所有的修改、方便团队协作开发</description><pubDate>Sun, 17 Sep 2023 23:40:40 GMT</pubDate><content:encoded>&lt;h2&gt;版本控制系统 VCS (Version Control System)&lt;/h2&gt;
&lt;p&gt;一个可以管理和追踪软件代码或其他类似内容的不同版本的工具。
出于相同的目的：开发以及维护开发出来的代码、方便读取代码的历史版本、记录所有的修改、方便团队协作开发。&lt;/p&gt;
&lt;h2&gt;Git&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.liaoxuefeng.com/wiki/896043488029600/896067008724000&quot;&gt;Git 简介 - 廖雪峰的官方网站 (liaoxuefeng.com)&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Git 安装&lt;/h3&gt;
&lt;p&gt;官方下载地址：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://git-scm.com/book/zh/v2/%E8%B5%B7%E6%AD%A5-%E5%AE%89%E8%A3%85-Git&quot;&gt;Git - 安装 Git (git-scm.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;因为 Git 有很多新的配置项，而且之前也不懂，没有概念，之后可以考虑把这一块补充上，下面是一些参考链接，翻译可以用有道截图翻译（非常推荐）。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.aliyun.com/article/1060387&quot;&gt;2022 新版 Git 教程 从入门到实战（一）安装 Git（一）-阿里云开发者社区 (aliyun.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;配置项：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/boyi6573/article/details/126897945&quot;&gt;Git 安装教程_git 默认编辑器选什么好-CSDN 博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://juejin.cn/post/7200627917307428923&quot;&gt;在 git 上和 github 上修改默认分支 - 掘金 (juejin.cn)&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;常用 Git 命令&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/qtiao/article/details/97783243?ydreferer=aHR0cHM6Ly9jbi5iaW5nLmNvbS8%3D&quot;&gt;Git 常用基本命令使用详细大全&lt;/a&gt;
&lt;a href=&quot;https://www.liaoxuefeng.com/wiki/896043488029600&quot;&gt;Git 教程 - 廖雪峰&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git remote prune origin      删除本地有但在远程库已经不存在的分支 &amp;lt;br&amp;gt;
git clone -b   分支名 地址 &amp;lt;br&amp;gt;
git remote add origin git@github.com:XXXXX/demo.git   新增远程仓库&amp;lt;br&amp;gt;
git remote set-url origin git@github.com:XXXXX/demo.git  修改远程仓库&amp;lt;br&amp;gt;
git remote set-url --add origin git@github.com:XXXXX/demo.git  添加多个远程仓库&amp;lt;br&amp;gt;
git stash    不提交工作区内容 &amp;lt;br&amp;gt;
git stash pop 恢复工作区&amp;lt;br&amp;gt;
git fetch   拉取远程分支&amp;lt;br&amp;gt;
git reset --hard &amp;lt;commit ID号&amp;gt; 回滚&amp;lt;br&amp;gt;

git push -f origin master 推送到远程分支 &amp;lt;br&amp;gt;
git diff    查看工作区和暂存区的区别 &amp;lt;br&amp;gt;
git diff –cached    查看暂存区和版本库之间的区别 &amp;lt;br&amp;gt;
git diff HEAD   查看工作区和版本库之间的区别 &amp;lt;br&amp;gt;
git status   查看当前的工作状态&amp;lt;br&amp;gt;

git push --set-upstream origin online_help_20191024   推远程分支&amp;lt;br&amp;gt;
git push origin --delete dev20181018    删除远程分支&amp;lt;br&amp;gt;
git branch -d dev20181018   删除本地分支&amp;lt;br&amp;gt;
git cherry-pick 285c9477a759abdee94099e25e75e39e8c77d46b 合并指定版本
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;git init
初始化一个git仓库

git add .
添加所有修改到暂存区

git clone
将项目克隆到本地

git pull
将项目更新到本地

git add
将项目添加到本地仓库

git commit
将项目提交到本地仓库

git push
将项目推送到远程仓库

git branch
查看分支

git checkout
切换分支

git merge
合并分支

git remote
查看远程仓库

git status
查看项目状态

git log
查看提交日志

git reset
回退版本

git reflog
查看命令历史

git diff
查看修改内容

git rm
删除文件

git mv
移动文件

git tag
打标签

git stash
暂存工作区

git stash pop
恢复暂存区

git stash list
查看暂存区
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;命令流程图&lt;/h5&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/git/命令流程图.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;h3&gt;Bug&amp;amp;Solution&lt;/h3&gt;
&lt;p&gt;####SSH -T git@github.com&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.github.com/zh/authentication/connecting-to-github-with-ssh/testing-your-ssh-connection&quot;&gt;测试 SSH 连接&lt;/a&gt;
&lt;a href=&quot;https://blog.csdn.net/qq_33934427/article/details/109004052&quot;&gt;Git push origin master 报错的解决方法 &amp;amp; 常见 git 命令（待更新）&lt;/a&gt;
&lt;a href=&quot;https://www.cnblogs.com/liliuyu/articles/14015522.html&quot;&gt;Git 提交出现 Updates were rejected 解决方案&lt;/a&gt;
&lt;a href=&quot;https://github.com/git-lfs/git-lfs/issues/3400&quot;&gt;Remote &quot;origin&quot; does not support the LFS locking API&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;git 项目 push 错分支的解决方法&lt;/h4&gt;
&lt;p&gt;好的解决方案：
&lt;a href=&quot;https://blog.csdn.net/gongdamrgao/article/details/115032436&quot;&gt;main 与 master 分支合并&lt;/a&gt;
未成功的方案：
&lt;a href=&quot;https://blog.csdn.net/weixin_41529708/article/details/106691359?spm=1001.2101.3001.6661.1&amp;amp;utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-106691359-blog-109001509.235%5Ev38%5Epc_relevant_sort_base2&amp;amp;depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-106691359-blog-109001509.235%5Ev38%5Epc_relevant_sort_base2&amp;amp;utm_relevant_index=1&quot;&gt;GIT 提交错分支，push 错分支怎么办&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;解决 Git reset --soft HEAD^撤销 commit 时报错&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/weixin_51063895/article/details/129646819?ydreferer=aHR0cHM6Ly9jbi5iaW5nLmNvbS8%3D&quot;&gt;fatal: ambiguous argument &apos;HEAD^&apos;: unknown revision or path not in the working tree.&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Git push 报错&lt;/h4&gt;
&lt;p&gt;Updates were rejected because the tip of your current branch is behind (更新被拒绝，因为当前分支的落后与远程分支)
&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/git/git-push失败.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git push -u origin main -f
强制提交
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/422024766&quot;&gt;Git push 报错:Updates were rejected&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;进行 Git push 操作时报错：fatal: The current branch master has no upstream branch.
原因：没有将本地的分支与远程仓库的分支进行关联&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git push -u origin main
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.aliyun.com/article/764975&quot;&gt;Git push 报错:The current branch master has no upstream branch&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;项目分支提交错误&lt;/h4&gt;
&lt;p&gt;已经 push 如何取消：
Git reset --hard HEAD^
Git push origin HEAD --force&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/gongdamrgao/article/details/115032436&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;文件超 100M 无法上传&lt;/h4&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/git/文件超100M上传失败.png，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git lfs install

git lfs track &quot;*.psd&quot;
.psd是需要上传的文件后缀名，可以是其他格式

git add .gitattributes

git add [file.psd]
[]内是文件名，可以是其他文件名

git commit -m &quot;add psd file&quot;

git push origin main

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/qq_39564555/article/details/100051051&quot;&gt;error: GH001: Large files detected&lt;/a&gt;
&lt;a href=&quot;https://github.com/git-lfs/git-lfs/issues/3400&quot;&gt;Remote &quot;origin&quot; does not support the LFS locking API&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;git 提交或克隆报错 fatal: unable to access git 仓库: Failed to connec&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;git config --global --unset http.proxy

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/good_good_xiu/article/details/118567249&quot;&gt;Failed to connec -CSDN&lt;/a&gt;
&lt;a href=&quot;https://stackoverflow.com/questions/27087483/how-to-resolve-git-pull-fatal-unable-to-access-https-github-com-empty#:~:text=Go%20to%20Win%20-%3E%20Control%20Panel%20-%3E%20Credential,again%2C%20put%20the%20right%20ones%20and%20that%27s%20it.&quot;&gt;Failed to connec -Stackoverflow&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;“warning: LF will be replaced by CRLF”警告&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/lgq2016/article/details/104745963&quot;&gt;关于 git 提示“warning: LF will be replaced by CRLF”终极解答&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;删除本地 git 仓库&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;rm -fr .git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;标志 -f 用于在没有提示的情况下强行删除不存在的文件和参数（小心使用）。
标志 -r 用于递归删除每个目录及其内容。&lt;/p&gt;
&lt;h4&gt;删除与远程仓库的连接&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;git remote remove origin
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Git add . 无响应&lt;/h4&gt;
&lt;p&gt;昨天登录平台把项目的 vue 文件下载下来后，放到当前的 teach 文件夹里，结果之后发现之前提交成功后的 github 仓库居然是空的，因此重新提交，但在 Git add 的时候却出现无响应问题，最后发现原因可能是有两个 example 文件夹，里面有很多文件，导致 Git add . 无响应，将这两个文件夹删除后，重新提交就成功了。&lt;/p&gt;
&lt;p&gt;还有就是文件太多导致锁住，删除.lock 文件即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rm -f ./.git/index.lock
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/zhang_pengfei/article/details/50460261?spm=1001.2101.3001.6650.1&amp;amp;utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-50460261-blog-128531463.235%5Ev38%5Epc_relevant_sort_base2&amp;amp;depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-50460261-blog-128531463.235%5Ev38%5Epc_relevant_sort_base2&amp;amp;utm_relevant_index=2&quot;&gt;Git add . 无响应&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Git push 无响应&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/qq_43431735/article/details/106031021&quot;&gt;Connection closed by remote host&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;You‘ve successfully authenticated, but GitHub does not provide shell access.&lt;/h4&gt;
&lt;p&gt;在要推送的项目的本地文件夹下，右键 - Git Bash Here，执行命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; git remote set-url origin git@github.com:名字/仓库名.git
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Git 克隆并覆盖本地仓库&lt;/h4&gt;
&lt;p&gt;项目其他人下载到本地时，发现本地已经有了一个同名的文件夹，但是里面的内容不一样，这时候就需要覆盖本地仓库了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone &amp;lt;远程仓库地址&amp;gt; &amp;lt;本地仓库名&amp;gt;
git clone git@github.com:h1s97x/Web.git

git fetch --all　
用于获取远程仓库的最新代码
git reset --hard origin/master
用于将本地仓库重置为与远程仓库的master分支完全一致。请确保在运行此命令之前，所有本地修改都已保存，并且不再需要。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://geek-docs.com/git/git-questions/237_git_git_clone_and_overwrite_local_repository.html&quot;&gt;Git 克隆并覆盖本地仓库&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;与远程主机关闭 github.com 的连接&lt;/h4&gt;
&lt;p&gt;Connection to github.com closed by remote host.
&lt;a href=&quot;https://unix.stackexchange.com/questions/717583/connection-to-github-com-closed-by-remote-host&quot;&gt;与远程主机关闭 github.com 的连接&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>对时事的一些看法</title><link>https://h1s97x.github.io/posts/blog/%E5%AF%B9%E6%97%B6%E4%BA%8B%E7%9A%84%E4%B8%80%E4%BA%9B%E7%9C%8B%E6%B3%95/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/blog/%E5%AF%B9%E6%97%B6%E4%BA%8B%E7%9A%84%E4%B8%80%E4%BA%9B%E7%9C%8B%E6%B3%95/</guid><description>女性要求平权的声音历来已久了，过去的封建时代压迫女性的现象确实是事实，可站在当下的角度，我们是否能够去批判古人的价值观还是个未知，拿本朝的剑去斩前朝的官也过于滑</description><pubDate>Sun, 17 Sep 2023 06:44:06 GMT</pubDate><content:encoded>&lt;h3&gt;男女对立 “女拳” 与 “男拳”&lt;/h3&gt;
&lt;p&gt;女性要求平权的声音历来已久了，过去的封建时代压迫女性的现象确实是事实，可站在当下的角度，我们是否能够去批判古人的价值观还是个未知，拿本朝的剑去斩前朝的官也过于滑稽，更何况当下的女性其实又并未经历过那般压迫，那么有什么理由打着这样的旗号？要说受过压迫的站起来反抗自然是被认可的，但是现在的女性又有什么压迫呢？
就我的19年人生路而言，女性的地位分明在男生之上，除开隶属的阶级不谈，中国的目前的教育是贯穿小孩的童年到成人的，这样的占比非常之高了，因此我下面谈论的就是校园生活里的男女。上小学时，便被老师教导要让着女生，不要欺负女生，吃饭玩耍都是女士优先，干活做事的时候却反而不提女士优先的准则，只因为是男性所以就要处处礼让。这样的教育一直延续到了高中，甚至大学，这样的教育是不是有些过于偏激了呢？到现在我甚至怀疑男女性在某些方面是否真的有足够大的差距以至于每每提及时总要把女生个体的“差劲”“弱小”上升到物种层面。因为我并非研究医学，因此这方面的医学文献基本不了解，我只从自我的角度出发谈一谈，究竟是不是真的有这样的差距。
我从小到大的生活经历中，尤其是小学时女生的体质并不比男生差，甚至有些方面还要强，比如说速度，我记得小时候上体育课跑步，有些女生跑的比男生还要快。到了初中后，似乎突然就差距变大了，女生跑步被男生远远的落下，很多女生800m的成绩才勉强追上男生1000m的成绩，甚至有些还要差的多。令人匪夷所思，可这真的是突然发生的变化吗？我想不是，小学时男女生一起玩耍时，男生会打篮球，跑步，女生也会跑步，玩跳皮筋什么的，总之很少看到体育课一节课都只是坐在地上围一圈说话的，到了初中，这就是常见的事情了，女生们一起坐在地上聊天，男生们则很少有这种情况，体育课是最期待的，操场是消耗无处发泄的精力的地方，打篮球，跑步，差距自然而然就产生了，但这时的差距我认为并不大，远远到不了碾压的地步，但为什么最后差距变得非常大呢？我认为还是更多的心理原因，她们觉得自己跑不过男生，打不过男生，所以就不愿意去做了。缺乏了动力，因此就更加不愿意去做，这样的恶性循环，最后导致了巨大的差距。
我看到国外有很多女性运动员，她们的成绩跟男性的差别并不如我们国家现在这样的大，这种普遍存在的教育保护，到头来反而成全了“歧视”，让本来没有多大差距的男女性产生了巨大的差距。进一步来说，也正是这样的教育促使了当下男女对立的发展，在我的信息茧房里我看到的大多数女性主动挑起对立，为财、为权比比皆是，拜金思潮的影响远未结束。当然我也看到过很多男性的恶劣行径，但当下不管是网络上女性施暴，还是在现实生活中的校园暴力，都是女性为主导，这很难让人不去说什么。
毛主席说过，妇女能顶半边天，这是毫无疑问的。但现在的情况实在令人愧怍，女性在要求平权的道路上越走越崎岖，原因也大多是自找的，我不知道这样的女性是不是真的想要平权，还是只是想要更多的特权，但我知道的是，这样的女性是不会得到平权的，因为她们不配。&lt;/p&gt;
&lt;p&gt;女性平权运动的发展要从工业革命说起，自从19世纪欧洲英国工业革命开始，世界的局势发生了巨大的变化，开启了充满战火，不平等贸易，黑奴等等的100多年，工人阶级出现，一大批手工业者的失业，工厂不断兴起，女性就是在这一阶段突破了家庭的封锁，站到了台前，工业的不断发展使得国家产能的不断提高，但也使得失业人口不断增加，资本家获得的财产不尽其数，同时工人阶级的人数不断壮大，新的阶级斗争的序幕慢慢拉开，在战争中工人阶级起到巨大的作用，因而力量不断壮大，男性上战场，更多的女性就离开家庭到工厂成为工人&lt;/p&gt;
&lt;p&gt;未完待续&lt;/p&gt;
&lt;h3&gt;交通事故&lt;/h3&gt;
&lt;p&gt;会上B站刷交通事故的视频也差不多就是今年的事，每次都会看好久，交通事故的惨烈程度让我触目惊心，18年那会回老家的路上也出过一次车祸，不过不是很严重，但就是这样，我还记得那时的突然，“强烈的推背感”让我吓了一跳，我坐在副驾驶系好了安全带，但仍然感受到背部以上到脖子传来的痛楚。或许因此我更能感受到车祸发生的惨痛，看到很多的案例，有的是因为酒驾，强硬冲卡害人害己；有的是因为超速，在斑马线、十字路口往往是赶绿灯的最后几秒；有的是因为违章，到处乱停的，变道不打转向灯的，别车的，故意加塞的，比比皆是，素质极其低下；有的是因为路况、天气，雨雪天气还开得那么快，路上有积雪、水坑也不注意，轮胎打滑就溜出去了；有的是因为路人，有些路人过马路“脖子焊死”，慢慢悠悠的过的有，突然加速跑的有，把马路当行人道的有，还有“暴走族”强占车道；有的是因为司机睡驾；有的是因为乘客，女乘客态度恶劣、甚至用刀砍人的都有，令人恐惧。
种种案例，每天都在发生，死伤无数，但是这些案例的背后，都是一个个活生生的人，都是一个个家庭，人死不能复生，家庭破碎不能重建。悲剧的发生是谁都不想看到的，但仍然会发生，会出现，现在很流行的一个梗叫“防御性驾驶”，就是对自己的生命负责，在很多场合下慢一点，不超车，不抢道，不酒驾，不疲劳驾驶，不违章，不强行冲卡，不强占车道，不强行变道，不强行加塞，做到了这些，车祸离你还会近吗？
one more thing，远离大车！&lt;/p&gt;
&lt;h3&gt;动物保护&lt;/h3&gt;
&lt;p&gt;最近看到一个关于要把动物保护提上立法议程的视频，&lt;a href=&quot;https://www.bilibili.com/video/BV17w411i7x5/?spm_id_from=333.880.my_history.page.click&quot;&gt;反虐待动物法真的该立吗？如此正义为何立不出来？&lt;/a&gt;,看完之后感觉对立法有了更深的了解。
同时对于动物保护我也有话要说，首先的问题是我们要保护什么？保护什么动物？保护到什么程度？要我说，我只关心猫猫狗狗，所以我只保护这少数的几种动物，至于其他的动物，除了国家保护动物和我可能会喜欢的个体外，我完全不在意其死活，要是因为意外死掉，我只会说关我屁事，要是被其主人杀了吃了，我也no cares。但如果是故意去虐待，那我只能在网络上口诛笔伐一下，没有制裁的权利和能力，但这也只对少数的几种可能喜欢的动物罢了，我也不是没看过杀红火蚁、拔蚊子口器、五毒互吃的视频，这些视频，按照性质其实同虐待并无太大差别，但是于我而言我不仅不感觉到残忍，反而还很高兴，是否有些奇怪？但我想要传达的是，大多数人只关心自己的利益罢了，倘若触犯不到自己，其实是懒得管的，甚至还会觉得很有趣，之前重看《钢铁是怎样炼成的》的时候，对于书中的许多细节深感惊讶，作者把人类互相之间的打架、暴力冲突、语言暴力等都解释为“无聊”，大多害行皆因无聊而起。
至于立法的需要，我认为这几乎是不可能也是完全无必要的，当下的社会还是人类的社会，那么人就应该是第一位的，或许未来不是了，那也是未来的事；眼下因为养宠物的需求越来越多，其实也是社会的诸多问题彰显出来，人们迫切需要转移压力的对象和地点，对于这部分人而言，自然利益相关，他们想要给与自己的宠物极大的特权，让别人没法伤害到他们，但很多事情不能光从自己的角度出发，这些养宠物的很多也不会体谅他人的辛苦，遛狗不拴绳、宠物随地大小便、宠物咬人、宠物乱叫等等，这些都是很常见的，但这些人却不会去想想别人的感受，他们只会觉得自己的宠物是最可爱的，至于其他人，同我什么干系？这种连自己的同胞都能漠视的人，如果真的对他们的诉求一呼百应，那么最终遭殃的将会是这社会的每一个人，因为这些人的诉求是不会停止的，权力会催生欲望，而欲望会加深对权力的渴望。
我的判断是当下更加急需的是对养宠物的人的教育，让他们知道自己的宠物是自己的责任。&lt;/p&gt;
</content:encoded></item><item><title>DIY设计知识</title><link>https://h1s97x.github.io/posts/notes/notes-diy%E8%AE%BE%E8%AE%A1%E7%9F%A5%E8%AF%86/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-diy%E8%AE%BE%E8%AE%A1%E7%9F%A5%E8%AF%86/</guid><description>所谓smt钢网：钢网（stencils），也就是SMT模板（SMT Stencil），是一种SMT专用模具。就是SMT贴片焊接前，给PCB印刷锡膏或者红胶的治具</description><pubDate>Thu, 14 Sep 2023 23:37:00 GMT</pubDate><content:encoded>&lt;h3&gt;钢网&lt;/h3&gt;
&lt;p&gt;所谓smt钢网：钢网（stencils），也就是SMT模板（SMT Stencil），是一种SMT专用模具。就是SMT贴片焊接前，给PCB印刷锡膏或者红胶的治具。
作用是：帮助锡膏的沉积，将准确数量的锡膏或红胶转移到空PCB上的准确位置。
&lt;a href=&quot;https://www.bilibili.com/video/BV1VY4y1t7Tt/?spm_id_from=333.788.recommend_more_video.-1&amp;amp;vd_source=206a091077ff783d662b49e64cc9589d&quot;&gt;自制钢网教程&lt;/a&gt;
&lt;a href=&quot;https://zhuanlan.zhihu.com/p/326300191&quot;&gt;钢网是什么&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>terms-新名词</title><link>https://h1s97x.github.io/posts/notes/notes-terms-%E6%96%B0%E5%90%8D%E8%AF%8D/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-terms-%E6%96%B0%E5%90%8D%E8%AF%8D/</guid><description>联邦机器学习(Federated machine learning/Federated Learning)，又名联邦学习，联合学习，联盟学习。联邦机器学习是一个</description><pubDate>Wed, 13 Sep 2023 06:09:10 GMT</pubDate><content:encoded>&lt;h3&gt;联邦学习&lt;/h3&gt;
&lt;p&gt;联邦机器学习(Federated machine learning/Federated Learning)，又名联邦学习，联合学习，联盟学习。联邦机器学习是一个机器学习框架，能有效帮助多个机构在满足用户隐私保护、数据安全和政府法规的要求下进行数据使用和机器学习建模。联邦学习作为分布式的机器学习范式,可以有效解决数据孤岛问题,让参与方在不共享数据的基础上联合建模,能从技术上打破数据孤岛,实现AI协作。&lt;/p&gt;
&lt;p&gt;谷歌在2016年提出了针对手机终端的联邦学习,微众银行AI团队则从金融行业实践出发,关注跨机构跨组织的大数据合作场景，首次提出“联邦迁移学习”的解决方案，将迁移学习和联邦学习结合起来。据杨强教授在“联邦学习研讨会”上介绍,联邦迁移学习让联邦学习更加通用化,可以在不同数据结构、不同机构间发挥作用，没有领域和算法限制,同时具有模型质量无损、保护隐私、确保数据安全的优势。&lt;/p&gt;
&lt;p&gt;联邦学习定义了机器学习框架，在此框架下通过设计虚拟模型解决不同数据拥有方在不交换数据的情况下进行协作的问题。虚拟模型是各方将数据聚合在一起的最优模型，各自区域依据模型为本地目标服务。联邦学习要求此建模结果应当无限接近传统模式，即将多个数据拥有方的数据汇聚到一处进行建模的结果。在联邦机制下，各参与者的身份和地位相同，可建立共享数据策略。由于数据不发生转移，因此不会泄露用户隐私或影响数据规范。为了保护数据隐私、满足合法合规的要求。&lt;/p&gt;
&lt;p&gt;联邦学习有三大构成要素：数据源、联邦学习系统、用户。三者间关系如图所示，在联邦学习系统下，各个数据源方进行数据预处理，共同建立及其学习模型，并将输出结果反馈给用户。
&amp;lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/python-crypto-01-20261014.webp&quot; style=&quot;&quot;&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我的想法
每个设备作为一个终端，每个终端都有自己的数据，这些数据都是不同的，但是都有一个共同的目标，就是训练一个模型，这个模型可以用来预测，可以用来分类，可以用来识别等等。但是这些数据都是不同的，所以不能直接合并，但是可以通过联邦学习的方式，将这些数据进行训练，最后得到一个模型，这个模型可以用来预测，可以用来分类，可以用来识别等等。
具体的方式就是算法，譬如说最初的算法就是不断的将各个终端的数据data进行加权平均，然后不断重复训练一个模型，最后将这个模型发送给每个终端使用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/100688371&quot;&gt;什么是联邦学习&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;OTG&lt;/h3&gt;
&lt;p&gt;USB On-The-Go通常缩写为USB OTG，是USB 2.0规格的补充标准。它可使USB设备，例如播放器或手机，从USB周边设备变为USB主机，与其他USB设备连接通信。在正常情况下，这些支持OTG的USB设备和USB主机（如台式机或者笔记本电脑），仍然作为USB周边设备使用。&lt;/p&gt;
&lt;p&gt;支持OTG的设备，如USB打印机，可以不连接电脑的情况下直接接入U盘来读取设备内文件进行打印；或平板电脑可以直接接入USB存储碟、键盘或鼠标来扩充外界硬件功能。
&lt;a href=&quot;https://zh.wikipedia.org/wiki/USB_On-The-Go&quot;&gt;OTG - WIKI&lt;/a&gt;
&lt;a href=&quot;https://web.archive.org/web/20130410140447/http://www.usblab.idv.tw/usbotg.html&quot;&gt;USB OTG（On-The-Go）简介&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Alist本地部署</title><link>https://h1s97x.github.io/posts/notes/notes-alist%E6%9C%AC%E5%9C%B0%E9%83%A8%E7%BD%B2/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-alist%E6%9C%AC%E5%9C%B0%E9%83%A8%E7%BD%B2/</guid><description>1.&lt;a href=&quot;#Alist手动下载&quot;&gt;Alist 下载&lt;/a&gt; 2.&lt;a href=&quot;#添加存储&quot;&gt;&lt;/a&gt;添加存储 【Alist】Alist V3 &quot;</description><pubDate>Tue, 12 Sep 2023 13:38:55 GMT</pubDate><content:encoded>&lt;h3&gt;目录&lt;/h3&gt;
&lt;p&gt;1.&amp;lt;a href=&quot;#Alist手动下载&quot;&amp;gt;Alist 下载&amp;lt;/a&amp;gt; 2.&amp;lt;a href=&quot;#添加存储&quot;&amp;gt;&amp;lt;/a&amp;gt;添加存储&lt;/p&gt;
&lt;h3&gt;Alist 手动下载&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1BP411G7q9/?spm_id_from=333.788&amp;amp;vd_source=206a091077ff783d662b49e64cc9589d&quot;&gt;【Alist】Alist V3 &quot;全新版本&quot; 使用 安装/启动 教程！&lt;/a&gt;&lt;/strong&gt;
&lt;a href=&quot;https://github.com/alist-org/alist/releases&quot;&gt;官网下载地址&lt;/a&gt;
当你看到 &lt;code&gt;start server@0.0.0.0:5244&lt;/code&gt; 的输出，之后没有报错，说明操作成功。 第一次运行时会输出初始密码。程序默认监听 5244 端口。现在打开&lt;code&gt;http://ip:5244&lt;/code&gt; 可以看到登录页面&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 安装
scoop install alist
# 运行
alist server
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;安装 SCOOP&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/ScoopInstaller/Install&quot;&gt;勺子安装程序/安装：📥 下一代勺子（取消）安装程序 (github.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/qq_40302536/article/details/116097541&quot;&gt;#Scoop windows 下的包管理器-CSDN 博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://gitee.com/squallliu/scoop&quot;&gt;scoop: 国内镜像版 (gitee.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/128955118&quot;&gt;搭建 Windows 统一开发环境（Scoop） - 知乎 (zhihu.com)&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 通过设置环境变量来配置自定义目录
$env:SCOOP=&quot;D:\Program Files (x86)\Scoop&quot;
[Environment]::SetEnvironmentVariable(&apos;SCOOP&apos;, $env:SCOOP, &apos;User&apos;)
$env:SCOOP_GLOBAL=&apos;D:\Program Files (x86)\Scoop\GlobalScoopApps&apos;
[Environment]::SetEnvironmentVariable(&apos;SCOOP_GLOBAL&apos;, $env:SCOOP_GLOBAL, &apos;Machine&apos;)
# -RunAsAdmin 以管理员身份运行 （不用管理员下载的话每次都连不上网页）
iex &quot;&amp;amp; {$(irm get.scoop.sh)} -RunAsAdmin&quot;

iwr -useb https://gitee.com/glsnames/scoop-installer/raw/master/bin/install.ps1 | iex
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;守护进程&lt;/strong&gt;
用 .VBS 脚本启动和停止，分别创建两个脚本 分别是 启动.vbs 和 停止.vbs&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Dim ws
Set ws = Wscript.CreateObject(&quot;Wscript.Shell&quot;)
ws.run &quot;alist.exe server&quot;,vbhide
Wscript.quit

&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Dim ws
Set ws = Wscript.CreateObject(&quot;Wscript.Shell&quot;)
ws.run &quot;taskkill /f /im alist.exe&quot;,0
Wscript.quitx

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;添加存储&lt;/h3&gt;
&lt;h4&gt;挂载路径&lt;/h4&gt;
&lt;p&gt;唯一标识，即要挂载到的位置，如果要挂载到根目录，就是 &lt;code&gt;/&lt;/code&gt;&lt;/p&gt;
&lt;h4&gt;启用签名&lt;/h4&gt;
&lt;p&gt;对文件进行签名加密(不会需要密码)，仅对本驱动生效，如果别的没启用签名也没设置签名全部和元信息加密其他的不会进行签名&lt;/p&gt;
&lt;p&gt;使用场景：不想开启全部签名，也不想设置元信息加密，只想对某驱动进行签名加密防止被扫&lt;/p&gt;
&lt;p&gt;影响范围：设置--&amp;gt;全局--&amp;gt;签名所有 &amp;gt; 元信息目录加密 &amp;gt; 单驱动签名&lt;/p&gt;
&lt;h3&gt;Alist 美化&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1Wg41187Bf/?spm_id_from=333.788&amp;amp;vd_source=206a091077ff783d662b49e64cc9589d&quot;&gt;【Alist】Alist v3 美化教程（1）- bilibili &lt;/a&gt;&lt;/strong&gt;
&lt;a href=&quot;https://blog.imoeq.com/alist-v3-ui-modify/&quot;&gt;AList V3 前端 UI 自定义美化教程&lt;/a&gt;
&lt;a href=&quot;https://www.moepan.cf/?page=1&quot;&gt;萌站&lt;/a&gt;
&lt;a href=&quot;https://icp.gov.moe/join.php&quot;&gt;萌国 ICP 备案申请&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &amp;lt;a href=&quot;https://icp.gov.moe/?keyword=20232014&quot; target=&quot;_blank&quot;&amp;gt;萌ICP备20232014号&amp;lt;/a&amp;gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://console.leancloud.cn/apps/NL7uAvKyvA7yraOpwgN9irQf-gzGzoHsz/&quot;&gt;LeanCloud - 评论区&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;style&amp;gt;
&amp;lt;!-- 右上菜单按钮改透明(不分亮色或暗色背景) --&amp;gt;
.hope-c-ivMHWx-hZistB-cv.hope-icon-button{background-color: rgba(255, 255, 255,0.3) !important;}
&amp;lt;!-- 侧边选单改透明后(不分亮色或暗色背景) --&amp;gt;
.hope-c-PJLV-ijgzmFG-css{background-color: rgba(255, 255, 255, 0.5)!important;}
&amp;lt;!-- 使用背景图(亮色背景) --&amp;gt;
.hope-ui-light{background-image: url(&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/alist-deploy-01-20261014.jpg&quot;)!important;background-repeat:no-repeat;background-size:cover;background-attachment:fixed;background-position-x:center;}
&amp;lt;!-- 使用背景图(暗色背景) --&amp;gt;
.hope-ui-dark {background-image: url(&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/alist-deploy-01-20261014.jpg&quot;) !important;background-repeat:no-repeat;background-size:cover;background-attachment:fixed;background-position-x:center;}
&amp;lt;!-- 透明(非毛玻璃)(亮色背景) --&amp;gt;
.hope-ui-light .hope-c-PJLV-ikEIIxw-css{background:rgba(255,255,255,0.2)!important;backdrop-filter:blur(10px)!important;}
.obj-box.hope-stack.hope-c-dhzjXW.hope-c-PJLV.hope-c-PJLV-igScBhH-css{background-color: rgba(255, 255, 255, 0.5) !important;}
.hope-c-PJLV.hope-c-PJLV-ikSuVsl-css{background-color: rgba(255, 255, 255, 0.5)!important;}
.hope-ui-light pre{background-color: rgba(255, 255, 255, 0.1)!important;}
&amp;lt;!-- 透明(非毛玻璃)(暗色背景) --&amp;gt;
.hope-ui-dark .hope-c-PJLV-ikEIIxw-css{background: rgb(0 0 0 / 10%) !important;}
.obj-box.hope-stack.hope-c-dhzjXW.hope-c-PJLV.hope-c-PJLV-iigjoxS-css{background-color:rgb(0 0 0 / 50%) !important;}
.hope-c-PJLV.hope-c-PJLV-iiuDLME-css{background-color:rgb(0 0 0 / 50%) !important;}
.hope-ui-dark pre{background-color: rgb(0 0 0 / 10%) !important;}
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Alist 设置&lt;/h3&gt;
&lt;h4&gt;搜索/索引&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://alist.nn.ci/zh/guide/advanced/search.html&quot;&gt;搜索/索引 - alist 文档&lt;/a&gt;&lt;/p&gt;
&lt;h5&gt;如何使用&lt;/h5&gt;
&lt;p&gt;按照以下步骤开启搜索:
转到索引页，选择一个搜索索引，并单击保存;
保存索引后，单击构建索引来构建索引;
现在你可以通过点击页面右上角的搜索块或使用快捷键 Ctrl + K 来搜索文件&lt;/p&gt;
&lt;h5&gt;忽略路径&lt;/h5&gt;
&lt;p&gt;构建索引期间跳过填写的路径，一行一个路径，可多行填写&lt;/p&gt;
&lt;h4&gt;用户&lt;/h4&gt;
&lt;h5&gt;忘记密码怎么办？&lt;/h5&gt;
&lt;p&gt;如果您是站点的所有者，您可以通过在终端中运行 ./alist admin 来获取管理员账号信息。
否则，您可以要求站点所有者重置密码。&lt;/p&gt;
&lt;p&gt;3.25.0 以上版本将密码改成加密方式存储的 hash 值，无法直接反算出密码，如果忘记了密码只能通过重新 &lt;code&gt;随机生成&lt;/code&gt; 或者 &lt;code&gt;手动设置&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 随机生成一个密码
./alist admin random
# 手动设置一个密码,`NEW_PASSWORD`是指你需要设置的密码
./alist admin set NEW_PASSWORD

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Aria2 离线下载&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/qq_55058006/article/details/115570993&quot;&gt;Aria2 安装和使用全教程&lt;/a&gt;
&lt;a href=&quot;https://zhuanlan.zhihu.com/p/37021947&quot;&gt;抛弃迅雷，Aria2 新手入门&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Alist 部署到服务器&lt;/h3&gt;
&lt;h3&gt;反向代理&lt;/h3&gt;
&lt;h2&gt;nginx&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/94151597&quot;&gt;教你快速使用 nginx 部署网站 - 知乎 (zhihu.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/hyfsbxg/article/details/122322125&quot;&gt;Nginx 详解（一文带你搞懂 Nginx）-CSDN 博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;在网站配置文件的 server 字段中添加&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;location / {
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Proto $scheme;
  proxy_set_header Host $http_host;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header Range $http_range;
	proxy_set_header If-Range $http_if_range;
  proxy_redirect off;
  proxy_pass http://127.0.0.1:5244;
  # the max size of file to upload
  client_max_body_size 20000m;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后重载 Nginx 配置&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ngingx -s reload
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最新进展 2023.10.05
失败，以下是报错内容&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/603165779&quot;&gt;nginx worker_connections are not enough - 知乎 (zhihu.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;设置后仍未解决，应该是其他问题&lt;/p&gt;
&lt;h4&gt;内网穿透&lt;/h4&gt;
&lt;p&gt;什么是内网穿透？
众所周知，得益于 NAT（Network Address Translator 网络地址转换）技术的存在，使得全球互联网可连接的计算机数能够远远超过 43 亿台。其原理就是在本地网络中使用私有地址（内网 IP），在连接互联网时使转而使用全局 IP 地址（公网 IP），这样，多个计算机就可以共用一个 IP 地址，从而达到减缓 IPv4 地址损耗的目的。&lt;/p&gt;
&lt;p&gt;大伙可以打开命令行输入 &lt;code&gt;ifconfig | grep inet&lt;/code&gt; 找到自己的内网 IP 地址，你的 IP 一定是在 192.168.0.0 ～ 192.168.255.255 这个区间里面，这是因为 TCP/IP 协议专门设定了内网 IP 地址的范围：&lt;/p&gt;
&lt;p&gt;A 类 - 10.0.0.0/8：10.0.0.0 ～ 10.255.255.255
B 类 - 172.16.0.0/12：172.16.0.0 ～ 172.31.255.255
C 类 - 192.168.0.0/16：192.168.0.0 ～ 192.168.255.255
仅靠这个内网 IP 地址是无法上网的，需要通过 NAT 路由器转成公网 IP 才行，可以通过 &lt;code&gt;curl cip.cc&lt;/code&gt; 来查看自己电脑的出口公网 IP 地址。&lt;/p&gt;
&lt;p&gt;回忆一下，当我们在本地进行开发的时候，是不是都是通过 127.0.0.1:端口号 来访问服务的？其实也可以通过 内网 IP:端口号 进行访问，当然别人是没法通过这个地址正常访问的。&lt;/p&gt;
&lt;p&gt;那这时候可能有小伙伴要问了，NAT 路由器不是会将内网 IP 转成公网 IP 吗？直接通过个 公网 IP:端口号 难道不能访问这个本地服务吗？&lt;/p&gt;
&lt;p&gt;很遗憾，不能。&lt;/p&gt;
&lt;p&gt;使用 NAT 绑定的公网 IP 直接进行访问，就意味着所有请求的 IP 报文中的目的 IP 地址都是这个公网 IP，那么 NAT 路由器无法区分这个请求到底要转发到内网的哪一台机器，这会导致端口冲突问题。&lt;/p&gt;
&lt;p&gt;因此，如果这个时候你想要把这个服务开放给互联网上的其他人进行访问，你有两种方案：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将该服务部署到云服务器&lt;/li&gt;
&lt;li&gt;内网穿透，将当前的内网 IP 和一个可访问的公网 IP 建立联系，使得别人可以通过公网 IP 访问你的本地主机
本地服务开放给公网访问这个需求在做微信开发的时候还是非常常见的，因为微信的各种回调函数都要求是公网可以访问的地址，总不能每在本地改一次代码就部署一次云服务器吧，不说费时间了，调试起来也很麻烦，所以内网穿透还是非常必要的。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;说了这么多，总结下什么是内网穿透？&lt;/p&gt;
&lt;p&gt;一般情况下，私有网络中的计算机无法被公网访问，因为它们的 IP 地址是内网 IP，不能直接被公网访问。内网穿透技术通过将公网上的请求转发到内网中的计算机上，从而实现了内网计算机与外网之间的数据通信。内网穿透技术主要应用于需要远程控制、远程访问、文件共享等场景，以便在安全性、便捷性和实用性方面取得平衡。&lt;/p&gt;
&lt;p&gt;内网穿透实现方案
实现内网穿透的方法有很多，下面介绍四种常见的实现：&lt;/p&gt;
&lt;p&gt;端口映射（Port forwarding）
反向代理（Reverse proxy）
VPN（Virtual Private Network，虚拟专用网络）
NAT 穿透（NAT Traversal）
&lt;a href=&quot;https://www.cnblogs.com/liyangit-bky/articles/13993007.html&quot;&gt;五种永久免费 内网穿透傻瓜式使用 - z-7 - 博客园 (cnblogs.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Cloudflare 使用：
&lt;a href=&quot;https://developers.cloudflare.com/learning-paths/get-started/#live_website&quot;&gt;Get started with Cloudflare · Cloudflare Docs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.imoeq.com/cloudflare-argo-tunnel/&quot;&gt;Cloudflare tunnel 详细介绍 —— 面向 cloudflare 的网站搭建,1 分钟快速启动一个有 ssl 证书的 wordpress 博客 - 私は萌い Q(˘•ω•˘) (imoeq.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.outv.im/2021/cloudflared-tunnel/&quot;&gt;用 Cloudflare Tunnel 进行内网穿透 | Re:Linked (outv.im)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cloudflare 添加域名的常见问题&lt;/strong&gt;
1、域名记录怎么填？&lt;/p&gt;
&lt;p&gt;我们一般设置两条分别指向服务器 IP 地址的 A 记录即可，示例如下：&lt;/p&gt;
&lt;p&gt;第一条：Name：@，IPv4 address：服务器 IP
第二条：Name：www，IPv4 address：服务器 IP
这样就把 example.com 和 www.example.com 两个域名都指向服务器了，待解析生效后就可以在网络上访问到了。&lt;/p&gt;
&lt;p&gt;更详细的内容可以参考《Namesilo DNS 域名解析教程和常见问题解决方法汇总》。&lt;/p&gt;
&lt;p&gt;2、如何修改域名服务器？&lt;/p&gt;
&lt;p&gt;域名服务器需要在购买域名的商家的管理后台修改，由于不同商家的后台界面不同，没法统一说明。&lt;/p&gt;
&lt;p&gt;一般可以看下 DNS 设置、Change Nameservers 之类的选项里有没有。&lt;/p&gt;
&lt;p&gt;以 Namesilo 为例，可以参考《Namesilo 域名购买及使用教程》里的第四步 “设置 DNS 服务器” 部分。&lt;/p&gt;
&lt;p&gt;3、修改域名服务器后，Cloudflare 检测不到&lt;/p&gt;
&lt;p&gt;DNS 生效需要一定时间，确认自己修改无误后，耐心等待一段时间，再点击 “重新检测”。&lt;/p&gt;
&lt;p&gt;4、如何查看自己的域名服务器？&lt;/p&gt;
&lt;p&gt;我们可以自己查询下域名服务器，以确定是否设置生效。CMD 运行 nslookup -qt=ns 域名 -&amp;gt; 回车，nameserver 后面的就是域名服务器地址。&lt;/p&gt;
&lt;p&gt;5、开启 Cloudflare CDN 后网站访问速度反而变慢了？&lt;/p&gt;
&lt;p&gt;Cloudflare 的 CDN 节点大多在国外（免费版），国内用户访问速度不稳定，国外访问速度很快。&lt;/p&gt;
&lt;p&gt;比较适合主要面向国外访客的网站（如外贸站点等）；或者不在意速度，想节省源站资源的情况；或者主要想使用它的保护功能的用户。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.\cloudflared.exe login
.\cloudflared tunnel create h1s97x
.\cloudflared tunnel token h1s97x
.\cloudflared tunnel --no-autoupdate run --token eyJhIjoiYWNiYzYxYjNmMTY1ZmYwOTc2NDFlYTg4Nzk5YzllYjEiLCJzIjoieUtnZG5IUEJSc1RkVHNWTFBmaWJ3Sy9pakVlZ2dRTkNCTThuODFHcXpQOD0iLCJ0IjoiNGJlMTgyNmYtYzU4Yi00MTg0LTliNmQtMTc4OWQwMzk1NzE1In0= --url http://127.0.0.1:5200
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最新进展 2023.10.05
失败，以下是报错内容&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;2023-10-05T08:23:57Z INF Starting tunnel tunnelID=4be1826f-c58b-4184-9b6d-1789d0395715
2023-10-05T08:23:57Z INF Version 2023.8.2
2023-10-05T08:23:57Z INF GOOS: windows, GOVersion: go1.20.6, GoArch: amd64
2023-10-05T08:23:57Z INF Settings: map[no-autoupdate:true token:***** url:http://127.0.0.1:5200]
2023-10-05T08:23:57Z INF cloudflared will not automatically update on Windows systems.
2023-10-05T08:23:57Z INF Generated Connector ID: cceee48e-8705-4429-a886-7170dc75b683
2023-10-05T08:23:57Z INF Initial protocol quic
2023-10-05T08:23:57Z INF ICMP proxy will use 10.27.214.21 as source for IPv4
2023-10-05T08:23:58Z INF ICMP proxy will use 2001:da8:7001:2000::7a8b in zone WLAN as source for IPv6
2023-10-05T08:23:58Z INF cloudflared does not support loading the system root certificate pool on Windows. Please use --origin-ca-pool &amp;lt;PATH&amp;gt; to specify the path to the certificate pool
2023-10-05T08:23:58Z INF Starting metrics server on 127.0.0.1:53456/metrics
2023-10-05T08:24:03Z INF

===================================================================================
You are hitting an error while using the experimental post-quantum tunnels feature.

Please check:

   https://pqtunnels.cloudflareresearch.com

for known problems.
===================================================================================


2023-10-05T08:24:03Z ERR Failed to create new quic connection error=&quot;failed to dial to edge with quic: timeout: no recent network activity&quot; connIndex=0 event=0 ip=198.41.200.73
2023-10-05T08:24:03Z INF Retrying connection in up to 2s connIndex=0 event=0 ip=198.41.200.73
2023-10-05T08:24:09Z ERR Failed to create new quic connection error=&quot;failed to dial to edge with quic: timeout: no recent network activity&quot; connIndex=0 event=0 ip=198.41.192.77
2023-10-05T08:24:09Z INF Retrying connection in up to 4s connIndex=0 event=0 ip=198.41.192.77
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;sunny-ngrok&lt;/h5&gt;
&lt;p&gt;&lt;a href=&quot;https://www.ngrok.cc/user.html&quot;&gt;Sunny-Ngrok 内网穿透服务&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cloud.tencent.com/developer/article/2073189&quot;&gt;五分钟免费获得自己的永久域名，免费！永久！-腾讯云开发者社区-腾讯云 (tencent.com)&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>RSS订阅</title><link>https://h1s97x.github.io/posts/notes/notes-rss%E8%AE%A2%E9%98%85/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-rss%E8%AE%A2%E9%98%85/</guid><description>使用RSS订阅B站UP主的方法</description><pubDate>Tue, 29 Aug 2023 07:26:53 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.moerats.com/archives/567/&quot;&gt;使用RSS订阅B站UP主的方法&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>‘种子’下载与相关内容</title><link>https://h1s97x.github.io/posts/notes/notes-%E7%A7%8D%E5%AD%90%E4%B8%8B%E8%BD%BD%E4%B8%8E%E7%9B%B8%E5%85%B3%E5%86%85%E5%AE%B9/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-%E7%A7%8D%E5%AD%90%E4%B8%8B%E8%BD%BD%E4%B8%8E%E7%9B%B8%E5%85%B3%E5%86%85%E5%AE%B9/</guid><description>&lt;style type=&quot;text/css&quot;&gt; rb[data-id=&quot;template-ruby&quot;]::before,.template-ruby-hidde</description><pubDate>Tue, 29 Aug 2023 06:51:07 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;style type=&quot;text/css&quot;&amp;gt;
rb[data-id=&quot;template-ruby&quot;]::before,.template-ruby-hidden  {
content: &quot;&quot;;
display: inline-block;
width: 0;
font-size: 0;
}
&amp;lt;/style&amp;gt;&lt;/p&gt;
&lt;h3&gt;图种&lt;/h3&gt;
&lt;p&gt;&amp;lt;div class=&quot;mw-parser-output&quot;&amp;gt;
&amp;lt;table border=&quot;1&quot; class=&quot;moe-infobox infoboxSpecial&quot; style=&quot;float: right; width: 260px; font-size: 90%; margin: 1em 0 1em 1em; background: #f9f9f9; border: 1px #999999 solid; border-collapse: collapse; clear: right;&quot; cellpadding=&quot;2&quot; cellspacing=&quot;0&quot;&amp;gt;
&amp;lt;tbody&amp;gt;&amp;lt;tr class=&quot;infobox-image-container&quot;&amp;gt;
&amp;lt;td colspan=&quot;2&quot; align=&quot;center&quot;&amp;gt;&amp;lt;a href=&quot;/File:31056785.jpg&quot; class=&quot;image&quot;&amp;gt;&amp;lt;img alt=&quot;31056785.jpg&quot; width=&quot;280&quot; height=&quot;452&quot; class=&quot;infobox-image lazyload multimediaViewerScrollSet&quot; data-file-width=&quot;808&quot; data-file-height=&quot;1305&quot; data-lazy-src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/torrent-01-20261014.jpg&quot; data-lazy-srcset=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/torrent-03-20261014.jpg 1.5x, https://cnb.cool/h1s97x/Pixy/-/raw/main/images/torrent-02-20261014.jpg 2x&quot; data-lazy-state=&quot;done&quot; style=&quot;&quot; src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/torrent-01-20261014.jpg&quot; srcset=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/torrent-03-20261014.jpg 1.5x, https://cnb.cool/h1s97x/Pixy/-/raw/main/images/torrent-02-20261014.jpg 2x&quot;&amp;gt;&amp;lt;/a&amp;gt;
&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;
&amp;lt;tr class=&quot;infobox-title&quot;&amp;gt;
&amp;lt;td colspan=&quot;2&quot; align=&quot;center&quot; bgcolor=&quot;#E0FFFF&quot;&amp;gt;&amp;lt;b&amp;gt;基本资料&amp;lt;/b&amp;gt;
&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th width=&quot;80px&quot;&amp;gt;用语名称
&amp;lt;/th&amp;gt;
&amp;lt;td align=&quot;center&quot;&amp;gt;图种
&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th&amp;gt;其他表述
&amp;lt;/th&amp;gt;
&amp;lt;td align=&quot;center&quot;&amp;gt;&amp;lt;s&amp;gt;铃木社大合集720P.torrent&amp;lt;/s&amp;gt;
&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th&amp;gt;相关条目
&amp;lt;/th&amp;gt;
&amp;lt;td align=&quot;center&quot;&amp;gt;&amp;lt;a href=&quot;&quot; class=&quot;mw-redirect&quot; title=&quot;绅士&quot;&amp;gt;绅士&amp;lt;/a&amp;gt;、&amp;lt;a href=&quot;&quot; title=&quot;Oo.avi&quot;&amp;gt;oo.avi&amp;lt;/a&amp;gt;、&amp;lt;a href=&quot;&quot; title=&quot;神秘代码&quot;&amp;gt;神秘代码&amp;lt;/a&amp;gt;
&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/tbody&amp;gt;&amp;lt;/table&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;图种&amp;lt;/b&amp;gt;是一种采用特殊方式将jpg图片文件和rar压缩文件合并起来，一般打开来看只是正常的图片，修改扩展名为rar后解压，得到隐藏的文件。经常被用来&amp;lt;ruby&amp;gt;&amp;lt;rb data-id=&quot;template-ruby&quot;&amp;gt;绅士&amp;lt;span class=&quot;template-ruby-hidden&quot;&amp;gt;（&amp;lt;/span&amp;gt;&amp;lt;/rb&amp;gt;&amp;lt;rt style=&quot;font-size:0.75em&quot;&amp;gt;hentai&amp;lt;/rt&amp;gt;&amp;lt;span class=&quot;template-ruby-hidden&quot;&amp;gt;）&amp;lt;/span&amp;gt;&amp;lt;/ruby&amp;gt;之间的一种隐蔽的传播文件种子的途径之一。
&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;h2&amp;gt;Windows系统下图种的制作方法&amp;lt;/h3&amp;gt;&lt;/p&gt;
&lt;h4&gt;方法1&lt;/h4&gt;
&lt;p&gt;&amp;lt;ol data-last-margin-left-max-length=&quot;3&quot; style=&quot;margin-left: 1.7em;&quot;&amp;gt;
&amp;lt;li&amp;gt;把下载链接写在txt里或者保存为一个种子&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;用winrar创建你选择的图片和种子的压缩文件，【压缩方式】处选择【储存】（先图片，再种子，顺序不能错）
&amp;lt;ul class=&quot;margin-left-set&quot; style=&quot;margin-left: 1.2em;&quot;&amp;gt;&amp;lt;li&amp;gt;一些同学由于理解力或者技术上有困难无法排序，可以先“压缩”图片，然后再把种子文件拖入&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;把生成的文件后缀从rar改为先前图片对应的后缀名（jpg,gif,png…………）&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/p&gt;
&lt;h4&gt;方法1.5&lt;/h4&gt;
&lt;p&gt;&amp;lt;p&amp;gt;直接将磁链放入用记事本打开的图片末尾然后保存(大小只会增加60字节)
&amp;lt;/p&amp;gt;
&amp;lt;h4&amp;gt;&amp;lt;span id=&quot;.E6.96.B9.E6.B3.952&quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&quot;mw-headline&quot; id=&quot;方法2&quot;&amp;gt;方法2&amp;lt;/span&amp;gt;&amp;lt;/h4&amp;gt;
&amp;lt;ol data-last-margin-left-max-length=&quot;4&quot; class=&quot;margin-left-set&quot; style=&quot;margin-left: 1.7em;&quot;&amp;gt;&amp;lt;li&amp;gt;把一个“种子”或其他任何&amp;lt;span title=&quot;你知道的太多了&quot; class=&quot;heimu&quot;&amp;gt;.avi或类型文件&amp;lt;/span&amp;gt;压缩成压缩包（取名为a.rar）放在D盘&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;把压缩包和一张你想显示的图片(取名为a.jpg)放到一起&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;新建一个文本文档(txt)在文档中输入“&amp;lt;code&amp;gt;copy /b D:\a.jpg + D:\a.rar D:\b.jpg&amp;lt;/code&amp;gt;”&amp;lt;span title=&quot;你知道的太多了&quot; class=&quot;heimu&quot;&amp;gt;当然你可以直接用cmd&amp;lt;/span&amp;gt;（此处是放在D盘所以地址写的是D）保存（这里注意一定要把图片放在前面，压缩文件放后面）&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;把新建的文本文档的扩展名改为.bat。双击新建的文本文档.bat。产生的b.jpg文件就是图种。
&amp;lt;ul class=&quot;margin-left-set&quot; style=&quot;margin-left: 1.2em;&quot;&amp;gt;&amp;lt;li&amp;gt;【或者可以直接进入cmd，在命令行上输入“&amp;lt;code&amp;gt;copy/b D:\a.jpg + D:\a.rar D:\b.jpg&amp;lt;/code&amp;gt;”。然后回车执行】&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;
&amp;lt;h4&amp;gt;&amp;lt;span id=&quot;.E6.96.B9.E6.B3.953&quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&quot;mw-headline&quot; id=&quot;方法3&quot;&amp;gt;方法3&amp;lt;/span&amp;gt;&amp;lt;/h4&amp;gt;
&amp;lt;ul class=&quot;margin-left-set&quot; style=&quot;margin-left: 1.2em;&quot;&amp;gt;&amp;lt;li&amp;gt;使用“渗透”软件。&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;原理：利用FLASH SWF、24bit BMP、JPG文件的冗余空间，把一批文件插入到这类文件中去，而不破坏原文件的结构和显示效果，以达到隐藏用户重要文件的目的。&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;优点：因为是利用冗余空间，所以生成文件的体积比上两种方法要小的多，更方便。&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;h2&amp;gt;&amp;lt;span id=&quot;.E6.89.93.E5.BC.80.E5.9B.BE.E7.A7.8D.E7.9A.84.E6.96.B9.E5.BC.8F&quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&quot;mw-headline&quot; id=&quot;打开图种的方式&quot;&amp;gt;打开图种的方式&amp;lt;/span&amp;gt;&amp;lt;/h2&amp;gt;
&amp;lt;p&amp;gt;打开方式直接用压缩软件打开, 或将图种的文件的扩展名改为“.rar”然后双击解压。
&amp;lt;/p&amp;gt;
&amp;lt;h2&amp;gt;&amp;lt;span id=&quot;.E5.8E.9F.E7.90.86&quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&quot;mw-headline&quot; id=&quot;原理&quot;&amp;gt;原理&amp;lt;/span&amp;gt;&amp;lt;/h2&amp;gt;
&amp;lt;p&amp;gt;简单的说就是把图片与压缩文件用二进制字节流的方式合并起来。
&amp;lt;/p&amp;gt;
&amp;lt;ul class=&quot;margin-left-set&quot; style=&quot;margin-left: 1.2em;&quot;&amp;gt;&amp;lt;li&amp;gt;大部分图片格式都是元数据格式文件，数据是按照特定格式的字节流（即：区块）来分区保存，图片解释程序只会处理符合格式的区块字节流读取和处理图像数据，不符合的会被视为损坏的区块处理。&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Rar压缩文件以rar特有的魔术字节流（ASCII码为Rar!）/(ZIP压缩文件为PK)作为其文件格式的读取开头，&amp;lt;b&amp;gt;这个魔术字并不需要一定在文件字节流的起始。&amp;lt;/b&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;
&amp;lt;p&amp;gt;所以：
&amp;lt;/p&amp;gt;
&amp;lt;ul class=&quot;margin-left-set&quot; style=&quot;margin-left: 1.2em;&quot;&amp;gt;&amp;lt;li&amp;gt;如果以图片查看程序打开的话，只会处理和读取合并后整个文件前面图片部分，后面rar文件的字节流会被作为损坏的图片文件区块丢弃不处理。&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;如果以rar处理程序打开的话，会忽略处理前面的图像文件格式的字节流，直到读取到其文件格式读取头魔术字才开始作为rar文件格式的处理，识别为rar压缩文件。&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;
&amp;lt;p&amp;gt;由于缩图会重新生成图片格式文件的数据流，包括后面“隐藏”的rar文件字节流，所以缩图文件的图种会没有了，因为rar的数据被破坏掉了。
&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;同样，只能rar字节流放后面，否则图片解析程序会认为该图像损坏了。
&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;qBittorrent&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/64254201&quot;&gt;专业磁力种子下载工具 qBittorrent 使用教程&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>皇帝成长计划2h5 攻略</title><link>https://h1s97x.github.io/posts/notes/notes-%E7%9A%87%E5%B8%9D%E6%88%90%E9%95%BF%E8%AE%A1%E5%88%922h5-%E6%94%BB%E7%95%A5/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-%E7%9A%87%E5%B8%9D%E6%88%90%E9%95%BF%E8%AE%A1%E5%88%922h5-%E6%94%BB%E7%95%A5/</guid><description>4:4 4:56 4:2 进存档后 群组搜索（代表八股取士这张卡，冒号前的4是4字节，第二个是卡的代号，第三个数字是属性或者数量等） 2代表名臣人数（➗2即真正</description><pubDate>Tue, 29 Aug 2023 06:40:17 GMT</pubDate><content:encoded>&lt;h3&gt;CE修改&lt;/h3&gt;
&lt;p&gt;4:4 4:56 4:2 进存档后 群组搜索（代表八股取士这张卡，冒号前的4是4字节，第二个是卡的代号，第三个数字是属性或者数量等）&lt;/p&gt;
&lt;p&gt;2代表名臣人数（➗2即真正数量）&lt;/p&gt;
&lt;p&gt;注意：
1.游戏内所有数字搜索都要*2
2.修改策卡前要先把影响回合数的变量搜索出来、不然效果只有一回合&lt;/p&gt;
&lt;p&gt;重要的卡代码：
18 修改税率
778 修改军团攻击力
1836 丹药数——   紫宸殿——浩荡寻游
624 珍宝数量——   郊外——围猎场——自动狩猎
58 三千佳丽
1394 修改招募随从名臣率&lt;/p&gt;
&lt;p&gt;4:10 4:778 4:0 4:0 4:0 4:6&lt;/p&gt;
&lt;h3&gt;参考链接&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.bilibili.com/read/cv16660275&quot;&gt;联合搜索常用代码&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;攻略地址：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.taptap.cn/moment/15228796399520212&quot;&gt;【攻略】速刷本纪的通用方法 - 皇帝成长计划2攻略 - TapTap 皇帝成长计划2论坛&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.taptap.cn/group/162112/group-label/162232&quot;&gt;皇帝成长计划2攻略 - TapTap 皇帝成长计划2论坛&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>稚晖君开源——ElectronBot:Mini Desktop Robot</title><link>https://h1s97x.github.io/posts/notes/notes-%E7%A8%9A%E6%99%96%E5%90%9B%E5%BC%80%E6%BA%90-electronbot-mini-desktop-robot/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-%E7%A8%9A%E6%99%96%E5%90%9B%E5%BC%80%E6%BA%90-electronbot-mini-desktop-robot/</guid><description>GitHub 项目详细说明 B站 复刻项目教程 购买电子元件花了120￥ ：头部主控和传感器版的元件 打板一共9块，已经白嫖4块，如果下个月还能免费打板就能只用</description><pubDate>Mon, 28 Aug 2023 11:51:10 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://github.com/peng-zhihui/ElectronBot&quot;&gt;GitHub 项目详细说明&lt;/a&gt;
&lt;a href=&quot;https://www.bilibili.com/video/BV1CM411K7bL/?spm_id_from=333.880.my_history.page.click&amp;amp;vd_source=206a091077ff783d662b49e64cc9589d&quot;&gt;B站 复刻项目教程&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;当前情况&lt;/h3&gt;
&lt;p&gt;购买电子元件花了120￥ ：头部主控和传感器版的元件
打板一共9块，已经白嫖4块，如果下个月还能免费打板就能只用20块打一个板
热风枪：鹿仙子（德国品牌好像）858 109￥
吸锡线：9￥
锡膏：15g 带针管 8￥
镊子：2￥
洗板水＋刷子+酒精瓶：22￥
万用表：UT136B 110￥
工作垫：15￥
简易焊台：20￥（淘宝群二手）
3D打印支出：
损坏支出：
练习支出：
合计：140&lt;/p&gt;
&lt;p&gt;预计项目总花费在600往上&lt;/p&gt;
</content:encoded></item><item><title>黑暗之魂3攻略</title><link>https://h1s97x.github.io/posts/notes/notes-%E6%94%BB%E7%95%A5-%E9%BB%91%E6%9A%97%E4%B9%8B%E9%AD%823/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-%E6%94%BB%E7%95%A5-%E9%BB%91%E6%9A%97%E4%B9%8B%E9%AD%823/</guid><description>后续工作： 人物画像 bug操作 《黑暗之魂3》游戏介绍《黑暗之魂3》攻略资料中心 - VG百科 (vgbaike.com) 黑暗之魂三已汉化中文地图 面向速通</description><pubDate>Mon, 28 Aug 2023 11:32:39 GMT</pubDate><content:encoded>&lt;p&gt;后续工作：
人物画像&lt;/p&gt;
&lt;p&gt;bug操作&lt;/p&gt;
&lt;h2&gt;游戏系统说明&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://www.vgbaike.com/dark_souls_3&quot;&gt;《黑暗之魂3》游戏介绍_《黑暗之魂3》攻略资料中心 - VG百科 (vgbaike.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://tieba.baidu.com/p/5904924370?pn=1&quot;&gt;黑暗之魂三已汉化中文地图&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;职业系统&lt;/h3&gt;
&lt;p&gt;面向速通和一周目体验玩家。魂系游戏的一个很重要特征就是职业系统并不与DNF以及其他一众RPG的职业路线相同，黑魂的职业就只是单纯的开局装备和初始等级与加点不同，后续并没有对每个职业做细分，例如职业独有技能等，因此对于普通玩家来说，选什么开局只会影响到最初的体验。虽然说初期的体验往往意味对游戏留存率有很大的影响，但魂系本身在对职业的理解上跟其他类型的游戏就不同，就我的看法而言，实在影响比较小。（毕竟后期都是水桶号）&lt;/p&gt;
&lt;p&gt;喜欢自由配置加属性的，那就选择魂系经典的无用之人开局，木棒的各种属性都比较平衡，削韧不算太高但能打断活尸的动作，距离不算太长，伤害也中等，战技怒吼加攻。但无用之人虽然自由，但属性其实也不算好，全10的加点很多属性基本开局用不上，算是一种浪费。&lt;/p&gt;
&lt;p&gt;新手一般开局选择骑士，有长剑和物防100盾，在前期对游戏不熟悉的情况下，只要弱智的举盾等对面打然后反击就能碾压普通小怪。&lt;/p&gt;
&lt;p&gt;游戏开局有10个职业选择，分别是：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1：骑士（Knight）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这是一个适合穿装甲的职业，专注于生命力、力量和活力。初始拥有盾牌、长剑和不错的盔甲，使得初期战斗变得简单。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2：佣兵（Mercenary）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这个职业专注于敏捷的战斗风格，拥有不错的耐力。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3：战士（Warrior）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这个职业拥有强大的攻击力，甚至比骑士更加威猛，但是他的防御力较低。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4：传令者（Herald）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这个职业和战士相似，拥有盾牌和盔甲，并且拥有恢复治愈能力，他比较适合新手。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5：小偷（Thief）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;新手不推荐使用这个职业，他的生命值和耐力都很低。他拥有较高的机动性，能够从敌人的背后突袭。当然，他需要精通躲闪技能。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6：刺客（Assassin）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这是一个结合了小偷和符咒师特点的职业。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7：魔法师（Sorcerer）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这是一个适用于老手的职业，专注于各种攻击性魔法的运用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8：咒术师（Pyromancer）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这是结合了战士和魔法师特点的职业，他比纯粹的魔法师要弱，但可以用剑。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9：圣职（Cleric）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这个职业混合了战斗技能、治愈和防御性魔法。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10：一无所有者（Deprived）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这个职业只推荐给那些玩过前两作、拥有丰富经验的老手。在初期这个职业相当的薄弱，但给玩家相当的自由度，在开始让玩家自由的分配你的属性。&lt;/p&gt;
&lt;h3&gt;人物系统&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;角色的基本属性和能力&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/dark-souls-3-05-20261014.jpg&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/dark-souls-3-02-20261014.jpg&quot; alt=&quot;游民星空&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1：生命力（Vigor）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;决定角色的血量上限高低，也会影响对寒气的抵抗力。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2：集中力（Attunement）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;决定专注值的高低，也会影响法术的记忆空格数。集中力越高，允许你使用更多的法术。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3：持久力（Endurance）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;决定精力的高低，也会影响雷属性的防御力和出血低抗力。精力决定着攻击、格挡和躲避的运用，它还影响着出血和闪电属性的抵抗力。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4：体力（Vitality）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;它决定着物理防御力的高低，也会影响装备重量的上限值，以及对毒素的抵抗力。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5：力气（Strength）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;使用强力重量级武器的必需能力值，有些武器会有力气值的限制。它会提高使用武器的攻击力，也会影响火属性防御力。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6：敏捷（Dexterity）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;和力气相同，它是使用难以操作武器的必需能力值，一些武器会有敏捷属性的要求。它可提升武器的伤害、法术的咏唱速度，以及减轻从高处坠落时的伤害。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7：智力（Intelligence）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;它是使用魔法或咒术的必需能力值。它能提高魔法和咒术的威力，也会影响魔力属性的防御能力。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8：信仰（Faith）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;它是使用奇迹或咒术的必需能力值。它会提高使用奇迹、咒术的威力，也会影响暗属性的防御力。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9：运气（Luck）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;它决定寻宝能力高低的能力值。会提高敌人出血、中毒的威力，也会影响对咒死的抵抗力。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;基本能力&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/dark-souls-3-04-20261014.jpg&quot;&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/dark-souls-3-01-20261014.jpg&quot; alt=&quot;游民星空&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1：血量&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;生存的力量和生气。若是归零则死亡。使用原素瓶，或是营火处休息可恢复。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2：专注值&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;使用法术、战技，集中精神时不可或缺的力量。使用原素灰瓶，或在营火处休息可恢复。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3：精力&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;进行各种行动时的力量。行动时会减少，但会自动恢复。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4：装备重量&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;可装备的武器、防具的重量上限值。重量越重，动作会越迟钝。若是超过上限值，行动会变得极度迟钝。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5：强韧度&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;受到攻击时，能承受的冲击度。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6：寻宝能力&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;打倒敌人后，从尸体上发现道具的能力。&lt;/p&gt;
&lt;h3&gt;全成就&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1E54y1i77f/?spm_id_from=333.999.0.0&amp;amp;vd_source=206a091077ff783d662b49e64cc9589d&quot;&gt;【黑暗之魂3】全npc支线任务剧情攻略索引&lt;/a&gt;
&lt;a href=&quot;https://www.bilibili.com/video/BV1tJ411y7SD/?spm_id_from=333.999.0.0&amp;amp;vd_source=206a091077ff783d662b49e64cc9589d&quot;&gt;黑暗之魂3-全誓约道具の离线刷法&lt;/a&gt;
&lt;a href=&quot;https://www.bilibili.com/video/BV19Q4y1N7Re/?spm_id_from=333.999.0.0&quot;&gt;【黑暗之魂3】楔形石原盘全收集&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;质变系统&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.zhihu.com/question/486525599&quot;&gt;黑魂3武器质变推荐&lt;/a&gt;
所有的质变种类是厚重、锋利、熟练、粗制、结晶、愚人、火焰、混沌、雷电、祝福、幽邃、暗、血、毒、游魂&lt;/p&gt;
&lt;p&gt;游魂宝石吃运气加成，运气越高加成越高，祝福好像吃的是信仰加成，祝福质变会每秒回血，游魂质变等级上去的话有些武器是要比熟练质变和锋利质变还要高的
一周目等级低的情况下，粗制、火焰、幽邃质变都是固定加成，只看武器的等级，所以可以用
到之后等级比较高就可以考虑换其他质变了，不过一般推荐熟练质变，加点力量66，敏捷80，一周目可以点30，30，30力量可以拿起绝大部分武器
双刀的锋利质变要比熟练质变面板更高一些，不用属性质变是因为可以擦纸或者法术上buff&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我推荐的质变与相关武器：
锋利质变：佣兵对刀，守墓人对刀，丛云；
熟练质变：阔剑，洛直，小镰刀等绝大多武器；匕首可以每种属性做一个质变，因为主要打背刺，上buff浪费时间，而且换成其他质变加成并不高。
结晶/雷电质变：两种加成在相同加点的情况下是相同的，结晶质变吃智力补正，雷电质变吃信仰补正。但是魂3 没有特别弱魔力的boss，所以不推荐质变结晶，雷电可以雷电洛特大，非常强；洛特大还得是祝福质变，雷质变会固定洛特大的雷伤，祝福会让洛特大的雷伤有信仰补正，所以祝福洛特大神中神
愚人/祝福：愚人吃智力补正，祝福吃信仰补正。愚人可以缓慢回蓝，祝福可以缓慢回血，推荐可以质变一些盾牌。祝福推荐：护手带，洛特大也可以，面板同样很高。
混沌/暗：同时吃智力和信仰两种补正，不爱擦纸或者玩法爷，亦或者高周目水桶可以包里多搞几把爱用的武器质变成混沌或者暗，伤害都不错，由于很多敌人弱火或者暗，所以这两种质变非常推荐。武器就不推荐了，自己喜欢的都可以质变一下
血/毒：魂3机制里毒比较拉跨，所以一般都不怎么用毒，PVP能玩的也就一把咕噜腐败短刀，出血的话还能用用，打一些boss都很舒服。血质变适合给盗贼短刀，毒质变适合给咕噜腐败短刀。
游魂：游魂质变吃幸运补正，叠加到物理面板。
最后厚重质变没说，是因为确实不怎么用，厚重会减敏捷补正加力量补正，一般等级高了都会点，不如熟练质变，如果只是高力量可以玩玩厚重质变，同理锋利质变就是高敏捷号&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;太阳战士&lt;/h3&gt;
&lt;h5&gt;加点1&lt;/h5&gt;
&lt;p&gt;&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;389&quot; height=&quot;532&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=7647270e4cdfa9ecfd2e561f52d1f754/ec6175adcbef7609cfa2dc8d73dda3cc7dd99e89.jpg?tbpicau=2023-08-30-05_3a3443a75c75fe23342938ad92ce4baf&quot; size=&quot;259855&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;不加智力、体力、运气，14集中力和15信仰恰好可以使用惜别&lt;/p&gt;
&lt;p&gt;39生命力、40持久力是软上限，40力量和敏捷是第一个软上限。元素瓶配置推荐带两瓶蓝（因为灵体元素瓶减半），保证能够补充一次惜别&lt;/p&gt;
&lt;h5&gt;加点2&lt;/h5&gt;
&lt;p&gt;&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;362&quot; height=&quot;499&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=02724e9c06a98226b8c12b2fba80b97a/16b12f600c338744024ee395140fd9f9d52aa0e3.jpg?tbpicau=2023-08-30-05_016ce63cce935168501effd2e8c58b6c&quot; size=&quot;219139&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;集中力升到28，信仰升到30。25信仰可以使用黑剑和大恢复，28信仰可以使用金石之誓，但是要占两个记忆格子，30信仰可以使用雷电剑和暗月光剑。此时我们可以携带的奇迹配置为惜别+大恢复+一个属性附魔，28的集中力刚好可以放这三个奇迹一次。元素瓶建议带4瓶蓝。至此低配版太阳战士完成，打低周目是没问题的了。&lt;/p&gt;
&lt;h5&gt;加点3&lt;/h5&gt;
&lt;p&gt;&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;367&quot; height=&quot;498&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=b76fdd6b61c6a7efb926a82ecdfbafe9/ee615fd5ad6eddc401a4cc4f2edbb6fd5366335c.jpg?tbpicau=2023-08-30-05_194e0f00264910d66025413dfe06b25f&quot; size=&quot;224747&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;60力量、敏捷是第二个软上限，但是双持武器可以吃到1.5倍的力量补正，故力量升到66。45信仰可以使用阳光疗愈30集中力恰好可以使用一次惜别+附魔+阳光疗愈。至于先升力量敏捷还是集中力和信仰，可以自行选择。&lt;/p&gt;
&lt;p&gt;达成这个加点之后我们就需要注意升体力，因为高周目的boss需要我们有足够的体力穿重甲。&lt;/p&gt;
&lt;h5&gt;加点4&lt;/h5&gt;
&lt;p&gt;&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;362&quot; height=&quot;491&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=489c43700cd5ad6eaaf964e2b1ca39a3/ac9d2a061d950a7b988f36701dd162d9f3d3c966.jpg?tbpicau=2023-08-30-05_63d27e19a436080a35d3110059de6216&quot; size=&quot;219440&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;集中力升到35，信仰升到60。元素瓶建议多带点蓝瓶，大概8瓶。集中力升到35之后对蓝条的增益迅速下降，如果记忆格子够的话不建议再往上升&lt;/p&gt;
&lt;h5&gt;加点5&lt;/h5&gt;
&lt;p&gt;&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;358&quot; height=&quot;493&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=285afac154a98226b8c12b2fba83b97a/16b12f600c338744286657c8460fd9f9d62aa07d.jpg?tbpicau=2023-08-30-05_934cd7550cd6c87a2aff4ff00eee3165&quot; size=&quot;216917&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;其实体力不用升到99，够用就行，敏捷可以考虑继续升到80&lt;/p&gt;
&lt;h5&gt;配装1&lt;/h5&gt;
&lt;p&gt;通用武器和戒指配置
锋利佣兵对刀+狼曲
&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;185&quot; height=&quot;136&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=de0fd106c658ccbf1bbcb53229d9bcd4/449a0ffbfbedab646e9e3574e036afc378311e2f.jpg?tbpicau=2023-08-30-05_62361164b7af4f41339af7df1b9555ed&quot; size=&quot;42047&quot;&amp;gt;
&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;259&quot; height=&quot;71&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=32834237182442a7ae0efdade142ad95/e86f153d269759ee91bf187fa5fb43166c22df29.jpg?tbpicau=2023-08-30-05_8b870c7d3f25243c8528c74367c5dcd4&quot; size=&quot;35189&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;大部分情况下都适用，佣兵对刀攻速快，狼曲当你连续攻击时增加攻击力并回血，再搭配教宗右眼（也是连续攻击增加攻击力），输出很不错，一定要记得给佣兵对刀附魔或者抹上松脂，并且双持右键攻击&lt;/p&gt;
&lt;h5&gt;配装2&lt;/h5&gt;
&lt;p&gt;熟练阔剑+盾
&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;192&quot; height=&quot;145&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=42b0a16c8cea15ce41eee00186013a25/a9c14d23dd54564ed3d32bd0f6de9c82d0584f3f.jpg?tbpicau=2023-08-30-05_751e38b1778a296af675e89710308731&quot; size=&quot;48719&quot;&amp;gt;
&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;260&quot; height=&quot;65&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=f4bfa35bf7b7d0a27bc90495fbee760d/755f95c8a786c91726b59dd08c3d70cf3ac7573f.jpg?tbpicau=2023-08-30-05_4de790b1aef0b8f33524dcf1a0c8ef11&quot; size=&quot;31967&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;也许很多人都抛弃了最初的剑盾流，但是你不得不承认，直剑的输出效率非常优秀，而盾牌能够大大提高容错，因此剑盾非常适合新手太阳熟悉业务，老手不带盾可以换成老狼曲剑，伤害一样很高。阔剑作为面板最高的直剑，是当仍不让的首选。盾牌则需要根据不同的boss选择不同的盾牌，如果你等级够，也可以直接选择哈维尔盾。&lt;/p&gt;
&lt;h5&gt;戒指选择&lt;/h5&gt;
&lt;p&gt;&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;56&quot; height=&quot;68&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=eeca89b7df95d143da76e42b43f18296/b97b796d55fbb2fbef7b3da6584a20a44723dc4c.jpg?tbpicau=2023-08-30-05_b29bb4a76a354e5eb445994bf73bcfb1&quot; size=&quot;2251&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;宠爱+3，带上就摘不下来&lt;/p&gt;
&lt;p&gt;&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;50&quot; height=&quot;63&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=91491dc8d5ef76c6d0d2fb23ad17fdf6/328e4af41bd5ad6ea950ab03c4cb39dbb4fd3cc0.jpg?tbpicau=2023-08-30-05_f6de91497e53853c7d0833fe35115a44&quot; size=&quot;2203&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;绿花+3，可以常带，增加10/s的精力恢复。40持久力时，精力条总量为179&lt;/p&gt;
&lt;p&gt;&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;64&quot; height=&quot;70&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=d35b60fdf4ec8a13141a57e8c7029157/d1388fef76094b3628d85ffffecc7cd98c109df4.jpg?tbpicau=2023-08-30-05_3d11bc6b9e87ad38244ca7a4153fb114&quot; size=&quot;2302&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;钢铁庇护+3，可以常带，非常优秀的减伤戒指，即使身穿哈维尔一套也能增加10%左右的物理减伤率，如果你穿轻甲，则可以增加14%左右，不穿衣服加17%盾徽，同样非常优秀的减伤戒指，不穿衣服加20%全部减伤，穿哈维尔的话跟钢铁庇护效果差不多，最适合用在高周目和灵车防止被一刀秒杀（点名盖尔），或者你想穿轻甲，那么配合钢铁庇护+3可以达到40%左右的物理减伤，这已经比不戴戒指穿哈维尔高了。缺点是在受到伤害后效果会消失，需要有一定技术（至少不能被boss抓滚吧），尽量配合阳光疗愈（受法术伤害加成影响，最好能一下奶满）使用&lt;/p&gt;
&lt;p&gt;&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;60&quot; height=&quot;65&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=60806df2910735fa91f04eb1ae500f9f/6a86272762d0f7035a175a354dfa513d2497c5cd.jpg?tbpicau=2023-08-30-05_d4d5382945ca3e119d7049bd9b5a1f04&quot; size=&quot;2291&quot;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;64&quot; height=&quot;62&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=d76c1c00124a20a4311e3ccfa0539847/cfbbfd389b504fc24118f417b8dde71191ef6df7.jpg?tbpicau=2023-08-30-05_2ec210f17b99d4071ff704bac62a824f&quot; size=&quot;2299&quot;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;65&quot; height=&quot;63&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=746df00827d9f2d3201124e799ed8a53/b917f6b7d0a20cf43e34313633094b36aeaf99cd.jpg?tbpicau=2023-08-30-05_ef7003aacccd8dafd1f5a070a888c5cf&quot; size=&quot;2307&quot;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;65&quot; height=&quot;64&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=41b22b0d89510fb37819779fe932c893/711ae4ec08fa513d81e8096a2a6d55fbb3fbd95e.jpg?tbpicau=2023-08-30-05_69dfcf5588763f1d353f65a7c7c74bdf&quot; size=&quot;2320&quot;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;66&quot; height=&quot;67&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=b1dd11215f4c510faec4e21250582528/108a7e292df5e0feafbc46e8196034a85cdf72cd.jpg?tbpicau=2023-08-30-05_740038d934ba7d72161a00bd1b1b52d3&quot; size=&quot;2382&quot;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;各种方石戒指+2（最高加20%单属性减伤率）以及斑方石戒指+1，可以选择带。我有时会把绿花戒指换下来带这个，或者当你觉得很难保持满血，就用这个代替盾徽戒指。觉得扛得住可以不带&lt;/p&gt;
&lt;p&gt;&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;65&quot; height=&quot;73&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=91471dc8d5ef76c6d0d2fb23ad17fdf6/328e4af41bd5ad6ea95eab03c4cb39dbb4fd3cce.jpg?tbpicau=2023-08-30-05_bc69f8c62d418e3c4c40562147091673&quot; size=&quot;2727&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;伫立龙徽戒指+2，增加法术（奇迹）持续时间，好像是35%，我用来增加附魔的时间（比较懒），大概加了21秒，原本是60秒，个人感觉还行&lt;/p&gt;
&lt;p&gt;&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;70&quot; height=&quot;68&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=719159a0bc18972ba33a00c2d6cc7b9d/f14aedec8a136327c357a787868fa0ec09fac766.jpg?tbpicau=2023-08-30-05_f3b14f5d8b59d14852645cb0cf302b72&quot; size=&quot;2110&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;红泪石戒指，残血时提高攻击力20%（攻击力用乘法计算，相当于原来的攻击力乘1.2），用的最多的场景是打黑龙灵车&lt;/p&gt;
&lt;p&gt;&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;63&quot; height=&quot;64&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=fd55f9689d13632715edc23ba18ea056/2b7a32094b36acafc257f3486bd98d1000e99c61.jpg?tbpicau=2023-08-30-05_9b94303c8c743f211e3e1270640e91a1&quot; size=&quot;1896&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;教宗右眼，可带，跟老狼曲剑效果差不多，这两个具体加多少攻击力忘了好像是分三层，攻击次数越多加的越多。但是毕竟占了一个戒指位，我的话更喜欢带减伤戒指&lt;/p&gt;
&lt;p&gt;&amp;lt;div id=&quot;post_content_139956522595&quot; class=&quot;d_post_content j_d_post_content &quot; style=&quot;display:;&quot;&amp;gt;                    奇迹的选择&amp;lt;img class=&quot;BDE_Smiley&quot; pic_type=&quot;1&quot; width=&quot;30&quot; height=&quot;30&quot; src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/dark-souls-3-03-20261014.png&quot;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;65&quot; height=&quot;70&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=d3362a4f1d46f21fc9345e5bc6256b31/477a8a1b9d16fdfaabada25aa38f8c5496ee7bd2.jpg?tbpicau=2023-08-30-05_809e420038b21e1dac43f9343489fe92&quot; size=&quot;2653&quot;&amp;gt;&amp;lt;br&amp;gt;惜别，受到致命伤害会保留1生命值，苟命神器&amp;lt;br&amp;gt;&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;64&quot; height=&quot;66&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=97f8e609890735fa91f04eb1ae500f9f/6a86272762d0f703ad6fd1ce55fa513d2797c55d.jpg?tbpicau=2023-08-30-05_b01755710d1d88c12a6f9f7bcb21068a&quot; size=&quot;2426&quot;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;66&quot; height=&quot;72&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=44d3568c3f381f309e198da199004c67/b3d6bff2b21193136a829b0472380cd793238dd3.jpg?tbpicau=2023-08-30-05_839828b260a4f197906ee4192a472b05&quot; size=&quot;2393&quot;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;61&quot; height=&quot;61&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=5cf2c239ed51f819f1250342eab54a76/c8a1709b033b5bb5ac0a64ec6bd3d539b700bc5d.jpg?tbpicau=2023-08-30-05_0b922830cf46d612798f1b3054132292&quot; size=&quot;2253&quot;&amp;gt;&amp;lt;br&amp;gt;属性附魔，大幅度提升输出，持续时间60s。因为附魔的存在，不推荐去给武器属性质变，信仰不低于40的话，武器属性质变的伤害比附魔差很多&amp;lt;br&amp;gt;&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;68&quot; height=&quot;71&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=19e9f3fdee18972ba33a00c2d6cf7b9d/f14aedec8a136327ab2f0ddad48fa0ec0afac7b4.jpg?tbpicau=2023-08-30-05_96ec3ab95e6b0abc0be4a3ad814189ab&quot; size=&quot;2399&quot;&amp;gt;&amp;lt;br&amp;gt;阳光疗愈，恢复量受圣铃的法术威力加成影响，60信仰龙铃能从1滴血直接奶满&amp;lt;br&amp;gt;&amp;lt;img class=&quot;BDE_Image&quot; pic_type=&quot;0&quot; width=&quot;62&quot; height=&quot;62&quot; src=&quot;http://tiebapic.baidu.com/forum/w%3D580/sign=dd2dac9b5634970a47731027a5cbd1c0/57d007a98226cffc6be9db06ae014a90f403eadd.jpg?tbpicau=2023-08-30-05_ef83a1e90e9e85596185c4083f2e5346&quot; size=&quot;2150&quot;&amp;gt;&amp;lt;br&amp;gt;金石之誓，可以考虑带，增加攻击力和减伤率，攻击力加10%，减伤不穿衣服加10%，可以加给队友，但是占了两个格子&amp;lt;br&amp;gt;如果不打灵车，带4瓶蓝应该就够用了，灵车建议带8瓶，另外尽量不要被打破惜别，惜别耗蓝100，而阳光疗愈耗蓝80&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;参考攻略&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.gamersky.com/handbook/201604/739818.shtml&quot;&gt;《黑暗之魂3》图文流程攻略 全收集攻略及BOSS战解析_操作键位及职业介绍-游民星空 GamerSky.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://tieba.baidu.com/p/7416825187&quot;&gt;太阳战士经验贴&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;跑图&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1XK411V7Bv/?spm_id_from=333.999.0.0&amp;amp;vd_source=206a091077ff783d662b49e64cc9589d&quot;&gt;【黑暗之魂3】黑暗之魂3新手跑图流程导航向(本体+dlc1+dlc2)&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;黑桐谷歌&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1XK411V7Bv/?spm_id_from=333.999.0.0&amp;amp;vd_source=206a091077ff783d662b49e64cc9589d&quot;&gt;【黑暗之魂3】黑桐谷歌视频攻略解说 01&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Mod推荐&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV185411J79M/?spm_id_from=333.999.0.0&amp;amp;vd_source=206a091077ff783d662b49e64cc9589d&quot;&gt;【黑暗之魂3】黑桐谷歌视频攻略解说 01&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;其他&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;神攻略，中间有对黑魂系统的讲解，不枯燥、冷幽默、最详细的并且最硬核的，有隐藏机制、数值说明&lt;/strong&gt;
&lt;a href=&quot;https://www.bilibili.com/video/BV1Jp411R71k/?spm_id_from=333.999.0.0&amp;amp;vd_source=206a091077ff783d662b49e64cc9589d&quot;&gt;黑暗之魂重制版 (Dark Souls Remastered) 流程解说攻略&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://tieba.baidu.com/p/7434431701&quot;&gt;太阳战士联机贴&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>破解相关</title><link>https://h1s97x.github.io/posts/notes/notes-%E7%A0%B4%E8%A7%A3%E7%9B%B8%E5%85%B3/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-%E7%A0%B4%E8%A7%A3%E7%9B%B8%E5%85%B3/</guid><description>吾爱破解论坛 零基础新手破解学习导航帖 破解工具包 新建文本文件(.txt),修改文件后缀为（.reg）,运行即可 Windows Registry Edito</description><pubDate>Mon, 28 Aug 2023 10:57:54 GMT</pubDate><content:encoded>&lt;h3&gt;破解论坛&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.52pojie.cn/&quot;&gt;吾爱破解论坛&lt;/a&gt;
&lt;a href=&quot;https://www.52pojie.cn/thread-582852-1-1.html&quot;&gt;零基础新手破解学习导航帖&lt;/a&gt;
&lt;a href=&quot;https://down.52pojie.cn/Tools/&quot;&gt;破解工具包&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;免费用MathType&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Windows Registry Editor Version 5.00
​
[-HKEY_CURRENT_USER\Software\JavaSoft\Prefs\com\wiris\editor\license\]
[HKEY_CURRENT_USER\Software\JavaSoft\Prefs\com\wiris\editor\license\]
&quot;/Cv/E/C81+j/Rw/U/E6ws/Znt/Os/K/Q==&quot;=&quot;11/M/P/Ir/Y/B/S/P/Pr/Q/L/Ny/T9s4/Iw==&quot;
&quot;/Mz/Bhef/D/Q/Hs30/U6/F/Pdl/R/Xsg==&quot;=&quot;/S/Zd8c/Ai/Z/A83028s6/Kn/Gf/M/Q==&quot;
&quot;vi/L/M7/K/Hb/C/O/A/K/X9uuis/O1/J/A==&quot;=&quot;/S/Zd8c/Ai/Z/A80\\/O/S/C/Vgp\\0/P/Q==&quot;
&quot;wm/U/C/Y/T/Nz5/Tw=&quot;=&quot;/Wna/F/W3q/I/Yp/V\\lj/Dedai56/Ur/Wpf/P/Kpl/Soh/A/Z/Qe\\6hit3ym\\6m5sp/B/B/Q==&quot;
&quot;x/W/Yrj/M/Db/Bds=&quot;=&quot;104z/W8rbqpw5\\/Qz0/C/Q/Opu/Dj/B5b/Dwsy77&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/yangpengcheng/p/13525939.html&quot;&gt;MathType 破解&lt;/a&gt;
成功破解&lt;/p&gt;
&lt;h3&gt;苹果越狱&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.bilibili.com/read/cv9948721&quot;&gt;苹果iPhone/iPad/iPod touch越狱全指南&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;未尝试&lt;/p&gt;
&lt;h3&gt;B站地区限制破解&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/shaoziqi/p/17032698.html&quot;&gt;B站地区限制破解方法&lt;/a&gt;
&lt;a href=&quot;https://greasyfork.org/zh-CN/scripts/25718-%E8%A7%A3%E9%99%A4b%E7%AB%99%E5%8C%BA%E5%9F%9F%E9%99%90%E5%88%B6&quot;&gt;解除B站区域限制——脚本&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;破解无法复制&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/344419634&quot;&gt;解除网页禁止复制&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;typora破解&lt;/h3&gt;
&lt;p&gt;使用方法一破解成功
&lt;a href=&quot;https://zhuanlan.zhihu.com/p/649901662#:~:text=%E6%94%BE%E5%85%A5%20typora%20%E7%9A%84%E5%AE%89%E8%A3%85%E7%9B%AE%E5%BD%95%EF%BC%9A%20%E8%BF%90%E8%A1%8C%20cmd%20%E8%B7%B3%E8%BD%AC%E5%88%B0%E5%AE%89%E8%A3%85%E7%9B%AE%E5%BD%95%EF%BC%8C%E8%BE%93%E5%85%A5%20node_inject.exe,%E5%9B%9E%E8%BD%A6%E5%8D%B3%E5%8F%AF%E8%87%AA%E5%8A%A8%E5%A4%84%E7%90%86%EF%BC%8C%E7%84%B6%E5%90%8E%E8%BE%93%E5%85%A5%20license-gen.exe%20%E5%9B%9E%E8%BD%A6%E5%8F%AF%E5%BE%97%E5%88%B0%E5%BA%8F%E5%88%97%E5%8F%B7%20%23%20%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AE%E7%9B%B8%E5%85%B3%E6%96%87%E4%BB%B6.node_inject.exe%20%23%20%E5%BA%8F%E5%88%97%E5%8F%B7%E7%94%9F%E6%88%90.license-gen.exe%20Email%E5%8F%AF%E4%BB%A5%E9%9A%8F%E6%84%8F%E8%BE%93%E5%85%A5%E4%B8%80%E4%B8%AA%EF%BC%8C%E5%B9%B6%E5%A4%8D%E5%88%B6%E4%B8%8A%E8%BF%B0%E7%94%9F%E6%88%90%E7%9A%84%E5%BA%8F%E5%88%97%E5%8F%B7%EF%BC%8C%E7%82%B9%E5%87%BB%E6%BF%80%E6%B4%BB%E5%8D%B3%E5%8F%AF&quot;&gt;如何激活使用Typora（2023年最新版）&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;VMware破解&lt;/h3&gt;
&lt;p&gt;激活码：
17：JU090-6039P-08409-8J0QH-2YR7F&lt;/p&gt;
&lt;h3&gt;Intellij IDEA U破解&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.quanxiaoha.com/idea-pojie/idea-pojie-202323.html&quot;&gt;IDEA 2023.2.3 最新破解版安装教程（附激活码，亲测好用） - 犬小哈教程 (quanxiaoha.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;6G5NXCPJZB-eyJsaWNlbnNlSWQiOiI2RzVOWENQSlpCIiwibGljZW5zZWVOYW1lIjoic2lnbnVwIHNjb290ZXIiLCJhc3NpZ25lZU5hbWUiOiIiLCJhc3NpZ25lZUVtYWlsIjoiIiwibGljZW5zZVJlc3RyaWN0aW9uIjoiIiwiY2hlY2tDb25jdXJyZW50VXNlIjpmYWxzZSwicHJvZHVjdHMiOlt7ImNvZGUiOiJQU0kiLCJmYWxsYmFja0RhdGUiOiIyMDI1LTA4LTAxIiwicGFpZFVwVG8iOiIyMDI1LTA4LTAxIiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IlBEQiIsImZhbGxiYWNrRGF0ZSI6IjIwMjUtMDgtMDEiLCJwYWlkVXBUbyI6IjIwMjUtMDgtMDEiLCJleHRlbmRlZCI6dHJ1ZX0seyJjb2RlIjoiSUkiLCJmYWxsYmFja0RhdGUiOiIyMDI1LTA4LTAxIiwicGFpZFVwVG8iOiIyMDI1LTA4LTAxIiwiZXh0ZW5kZWQiOmZhbHNlfSx7ImNvZGUiOiJQUEMiLCJmYWxsYmFja0RhdGUiOiIyMDI1LTA4LTAxIiwicGFpZFVwVG8iOiIyMDI1LTA4LTAxIiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IlBHTyIsImZhbGxiYWNrRGF0ZSI6IjIwMjUtMDgtMDEiLCJwYWlkVXBUbyI6IjIwMjUtMDgtMDEiLCJleHRlbmRlZCI6dHJ1ZX0seyJjb2RlIjoiUFNXIiwiZmFsbGJhY2tEYXRlIjoiMjAyNS0wOC0wMSIsInBhaWRVcFRvIjoiMjAyNS0wOC0wMSIsImV4dGVuZGVkIjp0cnVlfSx7ImNvZGUiOiJQV1MiLCJmYWxsYmFja0RhdGUiOiIyMDI1LTA4LTAxIiwicGFpZFVwVG8iOiIyMDI1LTA4LTAxIiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IlBQUyIsImZhbGxiYWNrRGF0ZSI6IjIwMjUtMDgtMDEiLCJwYWlkVXBUbyI6IjIwMjUtMDgtMDEiLCJleHRlbmRlZCI6dHJ1ZX0seyJjb2RlIjoiUFJCIiwiZmFsbGJhY2tEYXRlIjoiMjAyNS0wOC0wMSIsInBhaWRVcFRvIjoiMjAyNS0wOC0wMSIsImV4dGVuZGVkIjp0cnVlfSx7ImNvZGUiOiJQQ1dNUCIsImZhbGxiYWNrRGF0ZSI6IjIwMjUtMDgtMDEiLCJwYWlkVXBUbyI6IjIwMjUtMDgtMDEiLCJleHRlbmRlZCI6dHJ1ZX1dLCJtZXRhZGF0YSI6IjAxMjAyMjA5MDJQU0FOMDAwMDA1IiwiaGFzaCI6IlRSSUFMOi0xMDc4MzkwNTY4IiwiZ3JhY2VQZXJpb2REYXlzIjo3LCJhdXRvUHJvbG9uZ2F0ZWQiOmZhbHNlLCJpc0F1dG9Qcm9sb25nYXRlZCI6ZmFsc2V9-SnRVlQQR1/9nxZ2AXsQ0seYwU5OjaiUMXrnQIIdNRvykzqQ0Q+vjXlmO7iAUwhwlsyfoMrLuvmLYwoD7fV8Mpz9Gs2gsTR8DfSHuAdvZlFENlIuFoIqyO8BneM9paD0yLxiqxy/WWuOqW6c1v9ubbfdT6z9UnzSUjPKlsjXfq9J2gcDALrv9E0RPTOZqKfnsg7PF0wNQ0/d00dy1k3zI+zJyTRpDxkCaGgijlY/LZ/wqd/kRfcbQuRzdJ/JXa3nj26rACqykKXaBH5thuvkTyySOpZwZMJVJyW7B7ro/hkFCljZug3K+bTw5VwySzJtDcQ9tDYuu0zSAeXrcv2qrOg==-MIIETDCCAjSgAwIBAgIBDTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBMB4XDTIwMTAxOTA5MDU1M1oXDTIyMTAyMTA5MDU1M1owHzEdMBsGA1UEAwwUcHJvZDJ5LWZyb20tMjAyMDEwMTkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCUlaUFc1wf+CfY9wzFWEL2euKQ5nswqb57V8QZG7d7RoR6rwYUIXseTOAFq210oMEe++LCjzKDuqwDfsyhgDNTgZBPAaC4vUU2oy+XR+Fq8nBixWIsH668HeOnRK6RRhsr0rJzRB95aZ3EAPzBuQ2qPaNGm17pAX0Rd6MPRgjp75IWwI9eA6aMEdPQEVN7uyOtM5zSsjoj79Lbu1fjShOnQZuJcsV8tqnayeFkNzv2LTOlofU/Tbx502Ro073gGjoeRzNvrynAP03pL486P3KCAyiNPhDs2z8/COMrxRlZW5mfzo0xsK0dQGNH3UoG/9RVwHG4eS8LFpMTR9oetHZBAgMBAAGjgZkwgZYwCQYDVR0TBAIwADAdBgNVHQ4EFgQUJNoRIpb1hUHAk0foMSNM9MCEAv8wSAYDVR0jBEEwP4AUo562SGdCEjZBvW3gubSgUouX8bOhHKQaMBgxFjAUBgNVBAMMDUpldFByb2ZpbGUgQ0GCCQDSbLGDsoN54TATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQELBQADggIBABqRoNGxAQct9dQUFK8xqhiZaYPd30TlmCmSAaGJ0eBpvkVeqA2jGYhAQRqFiAlFC63JKvWvRZO1iRuWCEfUMkdqQ9VQPXziE/BlsOIgrL6RlJfuFcEZ8TK3syIfIGQZNCxYhLLUuet2HE6LJYPQ5c0jH4kDooRpcVZ4rBxNwddpctUO2te9UU5/FjhioZQsPvd92qOTsV+8Cyl2fvNhNKD1Uu9ff5AkVIQn4JU23ozdB/R5oUlebwaTE6WZNBs+TA/qPj+5/we9NH71WRB0hqUoLI2AKKyiPw++FtN4Su1vsdDlrAzDj9ILjpjJKA1ImuVcG329/WTYIKysZ1CWK3zATg9BeCUPAV1pQy8ToXOq+RSYen6winZ2OO93eyHv2Iw5kbn1dqfBw1BuTE29V2FJKicJSu8iEOpfoafwJISXmz1wnnWL3V/0NxTulfWsXugOoLfv0ZIBP1xH9kmf22jjQ2JiHhQZP7ZDsreRrOeIQ/c4yR8IQvMLfC0WKQqrHu5ZzXTH4NO3CwGWSlTY74kE91zXB5mwWAx1jig+UXYc2w4RkVhy0//lOmVya/PEepuuTTI4+UJwC7qbVlh5zfhj8oTNUXgN0AOc+Q0/WFPl1aw5VV/VrO8FCoB15lFVlpKaQ1Yh+DVU8ke+rt9Th0BCHXe0uZOEmH0nOnH/0onD&lt;/p&gt;
&lt;h3&gt;neat reader v8.1.4破解&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.52pojie.cn/thread-1715250-1-1.html&quot;&gt;neat reader v8.1.4破解&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;PyCharm 2023.2.1破解&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.quanxiaoha.com/pycharm-pojie/pycharm-pojie-202321.html&quot;&gt;Pycharm 2023.2.1最新激活码&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Navicat 16.2.11破解&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/ssmushui/p/17556189.html&quot;&gt;Navicat最新162激活破解&lt;/a&gt;
&lt;a href=&quot;https://github.com/LiJunYi2/navicat-keygen-16V&quot;&gt;其他的一些破解工具&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;初识破解&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.52pojie.cn/thread-1358649-1-1.html&quot;&gt;玩玩破解，写给新人看（第一集） - 『脱壳破解区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.52pojie.cn/thread-1629315-1-1.html&quot;&gt;玩玩破解之逆向工具篇 - 『脱壳破解区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;破解，无非就是一个注册与未注册的过程。&lt;/p&gt;
&lt;p&gt;jnz 改 jz，74 改 75 这么一个过程。&lt;/p&gt;
</content:encoded></item><item><title>访问外网 梯子</title><link>https://h1s97x.github.io/posts/notes/notes-%E8%AE%BF%E9%97%AE%E5%A4%96%E7%BD%91-%E6%A2%AF%E5%AD%90/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-%E8%AE%BF%E9%97%AE%E5%A4%96%E7%BD%91-%E6%A2%AF%E5%AD%90/</guid><description>自用梯子： 魔戒 黑洞——安卓手机，一天免费3小时 梯子直接全局代理，配置什么的基本不用自己管，很方便 SSTap 全称 SOCKSTap, 是一款利用虚拟网卡</description><pubDate>Mon, 28 Aug 2023 10:38:50 GMT</pubDate><content:encoded>&lt;p&gt;自用梯子：
&lt;a href=&quot;https://www.mojie.me/?ref=www.mjjloc.com#/dashboard&quot;&gt;魔戒&lt;/a&gt;
&lt;a href=&quot;https://www.heidongvpn.com/&quot;&gt;黑洞&lt;/a&gt;——安卓手机，一天免费3小时
梯子直接全局代理，配置什么的基本不用自己管，很方便&lt;/p&gt;
&lt;h3&gt;如何访问外网&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;简介&lt;/strong&gt;
SSTap 全称 SOCKSTap, 是一款利用虚拟网卡技术在网络层实现的代理工具。SSTap 能在网络层拦截所有连接并转发给 HTTP, SOCKS4/5, SHADOWSOCKS(R) 代理，而无需对被代理的应用程序做任何修改或设置。它能同时转发 TCP, UDP 数据包，非常适合于游戏玩家使用。
这是一个免费的工具，仅有应用内常驻非弹出式广告，并不会影响使用体验。（也有第三方去广告版本）
&lt;a href=&quot;https://wiki.kache.moe/2019/11/19/Windows-SSTap/&quot;&gt;SSTap教程&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;VPN使用
&lt;a href=&quot;https://zh.wikihow.com/%E4%BD%BF%E7%94%A8VPN&quot;&gt;使用VPN&lt;/a&gt;
&lt;a href=&quot;https://www.vsneko.com/article/24/&quot;&gt;如何访问外网|向日葵&lt;/a&gt;——链接已失效&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>CDN介绍与使用</title><link>https://h1s97x.github.io/posts/notes/notes-cdn%E4%BB%8B%E7%BB%8D%E4%B8%8E%E4%BD%BF%E7%94%A8/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-cdn%E4%BB%8B%E7%BB%8D%E4%B8%8E%E4%BD%BF%E7%94%A8/</guid><description>在线CDN工具 More Info：详解 某视频APP实现1千万用户同时在线观看。 如果大家观看的是1080p清晰度的视频（理论上需要4Mbps带宽），那么，累</description><pubDate>Mon, 28 Aug 2023 07:50:42 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.jsdelivr.com/&quot;&gt;在线CDN工具&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;CDN介绍&lt;/h2&gt;
&lt;p&gt;More Info：&lt;a href=&quot;https://zhuanlan.zhihu.com/p/28940451&quot;&gt;详解&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;内容导引&lt;/h3&gt;
&lt;p&gt;某视频APP实现1千万用户同时在线观看。&lt;/p&gt;
&lt;p&gt;如果大家观看的是1080p清晰度的视频（理论上需要4Mbps带宽），那么，累计需要的流量带宽是10,000,000×4Mbps=40,000,000Mbps=40Tbps。&lt;/p&gt;
&lt;p&gt;对于优酷、爱奇艺这样的互联网视频内容提供商来说，这无疑是非常巨大的流量压力。
我们普通计算机的网卡，是1Gbps的带宽。如果优酷有一台超级服务器，那么，这台超级服务器就需要4万块网卡，而且必须百分之百跑满速度，才能够实现这1千万用户的流畅观看。
对于一些实力不够的服务商，或者突发流量陡增的情况，就会造成拥塞，从而导致卡顿和延时。
有这么一个说法：当用户打开一个页面，等待超过4秒，他就会关闭这个页面。也就是说，这个用户就会流失。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;控制时延&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;无疑是现代信息科技的重要指标，CDN的意图就是尽可能的减少资源在转发、传输、链路抖动等情况下顺利保障信息的连贯性&lt;/p&gt;
&lt;p&gt;因为这项技术是把内容进行了分发，所以，它的名字就叫做:Content Delivery Network或Content Ddistribute Network，即&lt;strong&gt;内容分发网络&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CDN=更智能的镜像+缓存+流量导流。&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;原理&lt;/h3&gt;
&lt;p&gt;CDN这个技术其实说起来并不复杂，最初的核心理念，就是将内容缓存在终端用户附近。
内容源不是远么？那么，我们就在靠近用户的地方，建一个缓存服务器，把远端的内容，复制一份，放在这里，不就OK了？&lt;/p&gt;
&lt;p&gt;&amp;lt;img class=&quot;ztext-gif&quot; width=&quot;640&quot; role=&quot;presentation&quot; src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/cdn-intro-02-20261014.webp&quot; data-thumbnail=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/cdn-intro-01-20261014.jpg&quot; data-size=&quot;normal&quot; alt=&quot;动图&quot; style=&quot;display: block;&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/cdn-intro-03-20261014.webp&quot; data-rawwidth=&quot;600&quot; data-rawheight=&quot;344&quot; width=&quot;600&quot; data-original=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/cdn-intro-04-20261014.jpg&quot; data-actualsrc=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/cdn-intro-04-20261014.jpg&quot; data-original-token=&quot;v2-5793aec83fc645e002a1cd70ab7209a3&quot; height=&quot;344&quot; data-lazy-status=&quot;ok&quot;&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;基础架构：最简单的CDN网络由一个DNS服务器和几台缓存服务器组成：
&amp;lt;ol&amp;gt;
&amp;lt;li data-pid=&quot;dSiR9k7k&quot;&amp;gt;当用户点击网站页面上的内容URL，经过本地DNS系统解析，DNS系统会最终将域名的解析权交给CNAME指向的CDN专用DNS服务器。&amp;lt;/li&amp;gt;&amp;lt;br&amp;gt;
&amp;lt;li data-pid=&quot;YLuTZzbQ&quot;&amp;gt;CDN的DNS服务器将CDN的全局负载均衡设备IP地址返回用户。&amp;lt;/li&amp;gt;&amp;lt;br&amp;gt;
&amp;lt;li data-pid=&quot;mDHYloMm&quot;&amp;gt;用户向CDN的全局负载均衡设备发起内容URL访问请求。&amp;lt;/li&amp;gt;&amp;lt;br&amp;gt;
&amp;lt;li data-pid=&quot;RgkWzDfb&quot;&amp;gt;CDN全局负载均衡设备根据用户IP地址，以及用户请求的内容URL，选择一台用户所属区域的区域负载均衡设备，告诉用户向这台设备发起请求。&amp;lt;/li&amp;gt;&amp;lt;br&amp;gt;
&amp;lt;li data-pid=&quot;zk1INIt4&quot;&amp;gt;区域负载均衡设备会为用户选择一台合适的缓存服务器提供服务，选择的依据包括：根据用户IP地址，判断哪一台服务器距用户最近；根据用户所请求的URL中携带的内容名称，判断哪一台服务器上有用户所需内容；查询各个服务器当前的负载情况，判断哪一台服务器尚有服务能力。基于以上这些条件的综合分析之后，区域负载均衡设备会向全局负载均衡设备返回一台缓存服务器的IP地址。&amp;lt;/li&amp;gt;&amp;lt;br&amp;gt;
&amp;lt;li data-pid=&quot;pHzVNgJr&quot;&amp;gt;全局负载均衡设备把服务器的IP地址返回给用户。&amp;lt;/li&amp;gt;&amp;lt;br&amp;gt;
&amp;lt;li data-pid=&quot;H9lyFGfp&quot;&amp;gt;用户向缓存服务器发起请求，缓存服务器响应用户请求，将用户所需内容传送到用户终端。如果这台缓存服务器上并没有用户想要的内容，而区域均衡设备依然将它分配给了用户，那么这台服务器就要向它的上一级缓存服务器请求内容，直至追溯到网站的源服务器将内容拉到本地。&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/52362950&quot;&gt;通俗介绍——非常推荐&lt;/a&gt;
&lt;a href=&quot;https://www.zhihu.com/question/491891705&quot;&gt;简要介绍&lt;/a&gt;
&lt;a href=&quot;https://zhuanlan.zhihu.com/p/28940451&quot;&gt;较全面介绍——不太好懂&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>一些想法和解决方案</title><link>https://h1s97x.github.io/posts/blog/%E4%B8%80%E4%BA%9B%E6%83%B3%E6%B3%95%E5%92%8C%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/blog/%E4%B8%80%E4%BA%9B%E6%83%B3%E6%B3%95%E5%92%8C%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/</guid><description>https://www.jianshu.com/p/abe4ab1f62ee win+shift+S 快捷键 alt+shift+F VS 格式化（自动对齐）快</description><pubDate>Mon, 28 Aug 2023 03:27:18 GMT</pubDate><content:encoded>&lt;h3&gt;Sublime text 自动排版&lt;/h3&gt;
&lt;p&gt;https://www.jianshu.com/p/abe4ab1f62ee&lt;/p&gt;
&lt;h3&gt;Windows11 截屏快捷键：&lt;/h3&gt;
&lt;p&gt;win+shift+S&lt;/p&gt;
&lt;h3&gt;Visual Studio Code 自动排版&lt;/h3&gt;
&lt;p&gt;快捷键 alt+shift+F&lt;/p&gt;
&lt;p&gt;VS 格式化（自动对齐）快捷键
部分对齐（对齐光标所在行或对齐选中代码行）：（按两次组合键）
　　 1.Ctrl+K
　　 2.Ctrl+F
全部代码对齐：
法一：
　　 Ctrl+K+D（三个键同时按下）
法二：
　　 1.Ctrl+K
　　 2.Ctrl+D
法三：
　　 1.Ctrl+A（全选）
　　 2.Ctrl+K
　　 3.Ctrl+F&lt;/p&gt;
&lt;h3&gt;win10 子系统 Ubuntu 与 win 文件互访&lt;/h3&gt;
&lt;p&gt;windows 访问 Ubuntu 文件 1.直接定位 Ubuntu 文件夹，使用&lt;code&gt;everything&lt;/code&gt;搜索&lt;/p&gt;
&lt;p&gt;2.可以直接复制文件到所需文件夹&lt;/p&gt;
&lt;p&gt;Ubuntu 访问 windows 文件 #确认 win10 文件所在盘，比如：到 e 盘
cd /mnt/e #查看文件，并以到到 Ubuntu 文件夹内
ls
mv file file_dir&lt;/p&gt;
&lt;h3&gt;Visual Studio Code HTML 预览&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;open in browers

使用：htm文件中右键，点击右键菜单的view in browser，即可打开浏览器运行；

alt+b，运行；

alt+shift+b，切换不同浏览器运行；

优点：可以运行多个界面

Live Server Preview
这个插件不用另外打开浏览器；它是直接在Vscode里面预览HTML的，并且是自动实时更新的，不用不停地Ctrl+S保存查看
使用：F1 -&amp;gt; 弹出一个窗口输入live，然后右边出现预览窗口
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;More Info:&lt;a href=&quot;https://blog.csdn.net/baidu_38607919/article/details/102506792?ydreferer=aHR0cHM6Ly9jbi5iaW5nLmNvbS8%3D&quot;&gt;VS HTML 预览&lt;/a&gt;
&lt;a href=&quot;https://www.cnblogs.com/cuianbing/p/13730793.HTML&quot;&gt;插件介绍&lt;/a&gt;
&lt;a href=&quot;https://blog.csdn.net/L7810_mq/article/details/103472576&quot;&gt;live server error&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;已解决&lt;/p&gt;
&lt;h3&gt;高效查找之布隆过滤算法&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;problem：1.哈希函数的具体算法（如何保证不同输入的值不同）
2.删除：
相同值碰撞——假如选择加1存储（其他也行），下面是一种场景：
如果两本书的哈希值都是2，那么根据算法一个存到哈希表中2的位置，一个存放到3的位置，这时有人借走了位置2的那本书（可以查找成功），此时有人想借位置3的那本书，如果只将2的位置关闭，那么就查不到这本书了。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;More Info:&lt;a href=&quot;https://www.bilibili.com/video/BV1uN411a7f6/?spm_id_from=333.788.top_right_bar_window_history.content.click&amp;amp;vd_source=206a091077ff783d662b49e64cc9589d&quot;&gt;B 站视频介绍&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;未解决&lt;/p&gt;
&lt;h3&gt;图片压缩&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/385452484&quot;&gt;图片压缩&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;已解决&lt;/p&gt;
&lt;h3&gt;HTML 点击按钮 button 跳转页面&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/weixin_45844049/article/details/103165560&quot;&gt;HTML 点击按钮 button 跳转页面的几种实现方法&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;未使用&lt;/p&gt;
&lt;h3&gt;页面设置标签栏（叠放设计，点击后在本页面内切换，左侧设计标签栏）&lt;/h3&gt;
&lt;p&gt;有想法，未实现
（以前有见过，魔兽/DND 的网站似乎，用在攻略设计里）&lt;/p&gt;
&lt;h3&gt;手机电脑共享文件夹&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/430863401&quot;&gt;手机电脑共享文件夹&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;将开发软件设置到右键菜单&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/u012520952/article/details/121683828#:~:text=Typora%E5%A6%82%E4%BD%95%E6%B7%BB%E5%8A%A0%E5%88%B0%E5%8F%B3%E9%94%AE%E8%8F%9C%E5%8D%95%E4%B8%AD%E5%8F%AF%E4%BB%A5%E6%89%93%E5%BC%80%E6%96%87%E4%BB%B6%E5%A4%B9%201%20%E6%93%8D%E4%BD%9C%201.win%2Br%20%E8%B0%83%E5%87%BA%E8%BF%90%E8%A1%8C%E7%AA%97%E5%8F%A3%202.%E8%BE%93%E5%85%A5regedit%E6%89%93%E5%BC%80%E6%B3%A8%E5%86%8C%E8%A1%A8%20%E4%B8%80%E3%80%81%E5%AF%B9%E6%96%87%E4%BB%B6%E5%A2%9E%E5%8A%A0Typora%E5%8F%B3%E9%94%AE%E6%89%93%E5%BC%80%E9%A1%B9%201.%E9%A6%96%E5%85%88%E6%89%BE%E5%88%B0%E8%B7%AF%E5%BE%84,%E7%94%A8%E8%AE%B0%E4%BA%8B%E6%9C%AC%E5%88%86%E5%88%AB%E5%BD%95%E5%85%A5%E4%B8%A4%E4%B8%AA%E5%86%85%E5%AE%B9%20%E7%84%B6%E5%90%8E%E5%88%86%E5%88%AB%E5%8F%8C%E5%87%BB%E4%B8%A4%E4%B8%AA%E6%96%87%E4%BB%B6%E4%BC%9A%EF%BC%8C%E6%B3%A8%E5%86%8C%E8%A1%A8%E4%BC%9A%E8%87%AA%E5%8A%A8%E8%AE%BE%E7%BD%AE%20Windows%20Registry%20Editor%20Version%205.00%20&quot;&gt;Typora 如何添加到右键菜单&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;写的很好，很实用，参考价值很高&lt;/p&gt;
&lt;h3&gt;网站建站&lt;/h3&gt;
&lt;p&gt;[windows 环境搭建 http 服务器_windows http 服务-CSDN 博客](https://blog.csdn.net/qq_41646032/article/details/126243156#:~:text=搭建http服务器 1.打开 internet,Information Server（IIS）管理器，在开始菜单里面找到 IIS 管理器管理控制台或者搜索 IIS 打开 2.进入页面，左边网站右击新建网站，主机名可不用输，然后直接点击立即启动网站确认。)&lt;/p&gt;
</content:encoded></item><item><title>jack-hf2.2 游戏攻略</title><link>https://h1s97x.github.io/posts/notes/notes-jack-hf2-2-%E6%B8%B8%E6%88%8F%E6%94%BB%E7%95%A5/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-jack-hf2-2-%E6%B8%B8%E6%88%8F%E6%94%BB%E7%95%A5/</guid><description>&lt;style type=&quot;text/css&quot;&gt; .heimu { transition: color 0.13s linear; color: #252525;</description><pubDate>Mon, 28 Aug 2023 00:26:21 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;style type=&quot;text/css&quot;&amp;gt;
.heimu {
transition: color 0.13s linear;
color: #252525;
text-shadow: none;
background-color: #252525;
}
span.heimu:hover, span.heimu:active {
color: white;
}
&amp;lt;/style&amp;gt;&lt;/p&gt;
&lt;h1&gt;jack-hfv2.21 游戏攻略&lt;/h1&gt;
&lt;h2&gt;目录&lt;/h2&gt;
&lt;p&gt;&amp;lt;a href=&quot;#介绍&quot;&amp;gt;介绍&amp;lt;/a&amp;gt;
&amp;lt;a href=&quot;#属性篇&quot;&amp;gt;属性篇&amp;lt;/a&amp;gt;
&amp;lt;a href=&quot;#装备篇&quot;&amp;gt;装备篇&amp;lt;/a&amp;gt;
&amp;lt;a href=&quot;#技能篇&quot;&amp;gt;技能篇&amp;lt;/a&amp;gt;
&amp;lt;a href=&quot;#法术篇&quot;&amp;gt;法术篇&amp;lt;/a&amp;gt;
&amp;lt;a href=&quot;#任务篇&quot;&amp;gt;任务篇&amp;lt;/a&amp;gt;
&amp;lt;a href=&quot;#等级篇&quot;&amp;gt;等级篇&amp;lt;/a&amp;gt;
&amp;lt;a href=&quot;#训练篇&quot;&amp;gt;训练篇&amp;lt;/a&amp;gt;
&amp;lt;a href=&quot;#预兆篇&quot;&amp;gt;预兆篇&amp;lt;/a&amp;gt;
&amp;lt;a href=&quot;#战斗篇&quot;&amp;gt;战斗篇&amp;lt;/a&amp;gt;
&amp;lt;a href=&quot;#新手攻略&quot;&amp;gt;新手攻略&amp;lt;/a&amp;gt;
&amp;lt;a href=&quot;#作弊代码&quot;&amp;gt;作弊代码&amp;lt;/a&amp;gt;
&amp;lt;a href=&quot;#ce修改&quot;&amp;gt;ce修改&amp;lt;/a&amp;gt;
&amp;lt;a href=&quot;#参考链接&quot;&amp;gt;参考链接&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;h2&gt;介绍&lt;/h2&gt;
&lt;p&gt;本篇大部分基于网络上已知攻略整合而来，少部分经过个人测试得出，本人自己测试过的会标记出来，用到的攻略会在末尾贴出&lt;/p&gt;
&lt;p&gt;本人游玩时长大约在50h左右 （目前暂未通关 8.28）
为了记录也是第一次尝试写攻略，做的不好请见谅&lt;/p&gt;
&lt;h2&gt;属性篇&lt;/h2&gt;
&lt;h3&gt;男主属性&lt;/h3&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/jack-hf/男主属性.jpg，待你提供后重新引用 --&amp;gt;
&amp;lt;!-- &amp;lt;img src=&quot;https://easylink.cc/wfgxho&quot; title=&quot;男主属性&quot;&amp;gt; --&amp;gt;&lt;/p&gt;
&lt;h3&gt;精灵&amp;lt;span title=&quot;你知道的太多了&quot; class=&quot;heimu&quot;&amp;gt;slave&amp;lt;/span&amp;gt;属性&lt;/h3&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/jack-hf/精灵属性1.jpg，待你提供后重新引用 --&amp;gt;
&amp;lt;!-- &amp;lt;img src=&quot;https://easylink.cc/hhfcm&quot; title=&quot;精灵属性1&quot;&amp;gt; --&amp;gt;
&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/jack-hf/精灵属性2.jpg，待你提供后重新引用 --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &amp;lt;img src=&quot;https://easylink.cc/x6wkut&quot; title=&quot;精灵属性2&quot;&amp;gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;More info: &lt;a href=&quot;https://www.andlaou.com/archives/152525&quot;&gt;详细攻略&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;装备篇&lt;/h2&gt;
&lt;h3&gt;男主装备&lt;/h3&gt;
&lt;h4&gt;铠甲武器：打竞技场，雾的边缘狩猎，以及女主反抗时增加攻防&lt;/h4&gt;
&lt;p&gt;默认男主出门就有一副护甲和一把重剑，再去武器店买一把砍肉刀（20火花&amp;lt;span title=&quot;你知道的太多了&quot; class=&quot;heimu&quot;&amp;gt;火花==金钱&amp;lt;/span&amp;gt;）就基本够用了。龙火杖要凑齐广场四小强装备后去雾的边缘狩猎，随机掉落。&lt;/p&gt;
&lt;h4&gt;服装：部分服装可以提高主角右侧的技能等级。&lt;/h4&gt;
&lt;p&gt;正式西装——艺术等级+1，对应女主技能：礼仪课，舞蹈课，声乐课，乐器课，宠物训练，马匹训练。&lt;/p&gt;
&lt;p&gt;软甲：格斗等级+1，对应女主技能：角斗士。&lt;/p&gt;
&lt;p&gt;制服：家务等级+1，对应女主技能：家务课，烹饪课，礼仪（言辞）培训。&lt;/p&gt;
&lt;p&gt;医生长袍：医术等级+1，对应女主技能：护理培训，巫术（炼金术）课。&lt;/p&gt;
&lt;p&gt;巫师长袍：魔法等级+1，与女主技能无关。&lt;/p&gt;
&lt;p&gt;注：&amp;lt;span title=&quot;你知道的太多了&quot; class=&quot;heimu&quot;&amp;gt;魔法等级S++后释放魔法合欢术与警示箴言可以增加4点忠诚/服从经验，S+为3点。&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;渡鸦之冠：广场法师任务奖励（给一只利维坦），双神器之一，装备后可查看女主服从等数值（其实就是预兆里各项参数的具体数值化，装备后预兆可以免费查看，不过没必要了）。

获取条件：白金城——中央广场——大法师

要求：一只完全体恶魔——利维坦（每次生产48单位的恶魔精液）&amp;lt;span title=&quot;你知道的太多了&quot; class=&quot;heimu&quot;&amp;gt;不同汉化版翻译不同，只要是完全体恶魔就行&amp;lt;/span&amp;gt;

奖励：渡鸦之冠

效果：佩戴后无需释放占卜术就能看到奴隶的服从恐惧等情报，并可以了解更具体的数值（i）【调试信息】，并强化光环。（四个神器任务中最简单的一个，属于奴隶主必备装备之一，就问你要不要把。）
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/jack-hf/调试信息.jpg，待你提供后重新引用 --&amp;gt;
&amp;lt;!-- &amp;lt;img src=&quot;https://easylink.cc/9p6wnq&quot; title=&quot;调试信息&quot;&amp;gt; --&amp;gt;
&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/jack-hf/调试面板.jpg，待你提供后重新引用 --&amp;gt;
&amp;lt;!-- &amp;lt;img src=&quot;https://easylink.cc/iyx4we&quot; title=&quot;调试面板&quot;&amp;gt; --&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;奇美拉宝石：广场输精员任务获得。男主时刻处于种马状态。战斗中增加生命，释放魔法烙印时会发生爆炸效果（女主主动服务，体力消耗至2格，兴奋度5）。

获取条件：白金城——中央广场——输精员

要求：一个性技全S+的奴隶（无其他要求）

奖励：奇美拉宝石

效果：佩戴后性欲直接变种马，提升性交效果，强化光环。战斗中提升红色数值（体力）。
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;蛇之护符：广场智者任务。战斗中增加耐力，平时效果未知。

获取条件：白金城——中央广场——智者

要求：一个赛马冠军（只要有赛马冠军称号就能提交任务，不用弄到总冠军）

奖励：蛇之护符

效果：佩戴后战斗中提升紫色数值（勇气），并强化光环，佩戴后使用魔法烙印技能会触发隐藏效果。

&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;赛车比赛
其实很简单。钢铁玫瑰的宠物用品买齐，穿上，这些装备会提供赛车的加成。当然，驯马技能练满。进入比赛，全选最轻。存个档，一路全按中，如果中途失败，就读档，升级重车轮，再不行就升级重栏杆，基本就肯定没问题了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;公牛之戒：广场军需官任务（一个角斗冠军，需打赢角斗比赛5次），神器之二，主角体力最小为1，不会再减。

获取条件：白金城——中央广场——军需官

要求：一个竞技场冠军

奖励：公牛之戒

效果：佩戴后体力最少变成1，且不会再降低。（神器，必入）战斗中提升绿色数值（耐力），强化光环。
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;竞技比赛
每次都去打，体力只要两格就行，装备用火焰刀（200$），加耐力、自尊、脾气、骄傲 PS：&amp;lt;span title=&quot;你知道的太多了&quot; class=&quot;heimu&quot;&amp;gt;忘了要说什么&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;幽灵领主的复仇

触发条件：身上的星火超过15000，且需要一定声望，去中央广场特殊任务里找幽灵领主。

要求：S+奴隶，赛马总冠军，全纹身，全穿孔，全性技 等（游戏里这个任务目标写的很详细，根本不用猜）

奖励：秘银链甲

效果：本作最强铠甲，强化光环。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;More info（隐藏要求）: &lt;a href=&quot;https://tieba.baidu.com/p/7322259572&quot;&gt;幽灵领主的复仇&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;龙火杖：凑齐广场四小强装备后去雾的边缘狩猎，随机掉落。
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;恶魔培养&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;1.先在贸易广场——地厂商办公室买房
2.拓展服务：租一个恶魔围栏（10$ 10days）
3.到迷雾边境——走私犯老巢处购买迷雾恶魔（100$)
4.刷一个特殊助理，练性技能——恶魔X到S级
5.点击助理头像——进入助理面板后选择恶魔榨精
6.每天只要助理精力不为负即可（最好空精力）
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;特殊助理&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;1.有奴隶情况下，迷雾边境处进出场景可刷
2.同上（具体条件未知）
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;精灵装备&lt;/h3&gt;
&lt;p&gt;家政：女仆套装 +2、橡胶手套 +1、穆斯林头巾 +1
厨艺：花边围裙 +2，穆斯林头巾 +1
秘书：女生校服 +2，时尚眼镜 +1
演讲：和服浴袍 +2
护理：护士套装 +2，橡胶手套 +1
炼金：法师长袍 +3
魔法：法师长袍 +3
体能：锁甲比基尼 +2，长筒皮靴 +2（运动服装运动鞋大失败）
角斗：锁甲比基尼 +3，纤维手套 +2，塔比短袜 +2，尖刺项圈 +1
舞蹈：异国服装 +3，芭蕾舞鞋 +2
音乐：无
艺术：无
宠物：宠物套装 +3/gang塞马尾 +2，牵宠皮具 +1，毛绒爪套 +1，猫耳 +1
（宠物套装对其他训练的debuff太重，一旦忘了脱下来血亏）
马术：gang塞马尾 +2，小马笼头 +3，马蹄靴 +3，马具胸衣 +4
奶牛：奶牛设备 +2
.
xing训练：皮革胸衣 +4，长筒皮靴 +2，乳链 +1
.
降低傲慢：钢圈 -2，gang塞马尾 -1，小马笼头 -3，马蹄靴 -1，马具胸衣 -2
项圈+马术四件套在服装方面又提供了降低slave傲慢的途径，只需要第一天释放一次心灵操控，然后穿上即可，配合规则来降低傲慢，效率可以说已经很高了&lt;/p&gt;
&lt;p&gt;时装影响魅力：一般穿异国服装就能到蓝&lt;/p&gt;
&lt;h2&gt;法术篇&lt;/h2&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/jack-hf/法术.jpg，待你提供后重新引用 --&amp;gt;
&amp;lt;!-- &amp;lt;img src=&quot;https://easylink.cc/ewyktk&quot; title=&quot;法术&quot;&amp;gt; --&amp;gt;
&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/jack-hf/法术-不同汉化.jpg，待你提供后重新引用 --&amp;gt;
&amp;lt;!-- &amp;lt;img src=&quot;https://easylink.cc/2sgb44&quot; title=&quot;法术-其他汉化&quot;&amp;gt; --&amp;gt;
第一项法术书：就是解释各个魔法作用的，无视。&lt;/p&gt;
&lt;p&gt;第二项占卜术：没有渡鸦之冠时查看预兆相关内容。&lt;/p&gt;
&lt;p&gt;第三项合欢术：消耗女主兴奋值，增加忠诚经验。&lt;/p&gt;
&lt;p&gt;第四项钻心咒：惩罚魔法，代替物理惩罚手段消除女主抵抗值，不消耗体力。&lt;/p&gt;
&lt;p&gt;第五项警示箴言：每天只能释放一次，增加女主驯化经验，降低心情度1级。&lt;/p&gt;
&lt;p&gt;第六项失心咒：增加主角影响力（语言影响力，红框中两项）&lt;/p&gt;
&lt;p&gt;第七项恐惧术：提高女主恐惧值。&lt;/p&gt;
&lt;p&gt;第八项心灵操控：让女主服从所有指令（第一次使用增加忠诚经验）。&lt;/p&gt;
&lt;p&gt;第九项魔法烙印：给女主上标记（逃跑必失败），同时恢复女主体力至当前体力上限并增加6点体力值经验。&lt;/p&gt;
&lt;h2&gt;任务篇&lt;/h2&gt;
&lt;h3&gt;墓地:【（身体改良、性虐道具、宠物道具）】&lt;/h3&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/jack-hf/墓地.jpg，待你提供后重新引用 --&amp;gt;
&amp;lt;!-- &amp;lt;img src=&quot;https://easylink.cc/p8zj09&quot; title=&quot;墓地&quot;&amp;gt; --&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;大主教要的巫师：
等级A+
属性：智力B
技能：女仆A+，护士S+，女巫S+，秘书B+
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;王子助手要的舞伴：
等级A+
属性：健康B，自我A
技能：礼仪S，舞蹈A
性技能：群xA
特殊要求：处女
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;执政官要的间谍：
等级A+
属性：自我B，智力A
技能：礼仪S，秘书B
姓技能：暴露A
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;王子要的艺术家：
等级S+
属性：情绪A
技能：礼仪A，舞蹈S，乐器S，歌唱S
特殊要求：处女
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;遗忘废墟：【（纹身、化妆、药品）】&lt;/h3&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/jack-hf/遗忘废墟.jpg，待你提供后重新引用 --&amp;gt;
&amp;lt;!-- &amp;lt;img src=&quot;https://easylink.cc/d758rp&quot; title=&quot;遗忘废墟&quot;&amp;gt; --&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;大主教要的巫女：
等级A+
技能：护士S，女巫S，舞蹈A，乐器A，歌唱A
特殊要求：处女，萝莉
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;将军要的棋手：
等级A+
属性：智力S
技能：秘书A
姓技能：扩张B，口技B，异种A
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;女王儿子的教师：
等级S+
属性：情绪S
技能：宠物S，护士A，女巫A，女仆A，歌唱A
姓技能：异种A
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;蛇纹城：【科技区（整容、治疗、冷冻）】&lt;/h3&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/jack-hf/蛇纹城.jpg，待你提供后重新引用 --&amp;gt;
&amp;lt;!-- &amp;lt;img src=&quot;https://easylink.cc/l6dhxq&quot; title=&quot;蛇纹城&quot;&amp;gt; --&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;大主教要的替身：等级A+
属性：智力B，自我B
技能：礼仪S
性技能：抚慰A，口技A
特殊要求：处女，非萝莉
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;将军要的士兵：
等级A+
属性：性情A
技能：角斗士S
姓技能：群S，姓虐B，异种B
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;执政官要的护卫：
等级A+
属性：健康A，风情A
技能：角斗士S，礼仪B，护士B，女巫B
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;总统要的老婆：
等级S+
属性：情绪B
技能：礼仪B
姓技能：抚慰A，扩张S，口技A
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;金牛城：【slave俱乐部】&lt;/h3&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/jack-hf/金牛城.jpg，待你提供后重新引用 --&amp;gt;
&amp;lt;!-- &amp;lt;img src=&quot;https://easylink.cc/zgq0zi&quot; title=&quot;金牛城&quot;&amp;gt; --&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;大主教要的护士：
等级A+
属性：体型A
技能：护士S，巫女S，女仆A
姓技能：姓虐A
特殊要求：处女
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;将军给儿子要的青梅竹马：
等级A+
属性：智力A，自我A，美貌S
技能：礼仪B，秘书B，乐器B
特殊要求：处女，萝莉
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;执政官要的母马：
等级A+
属性：健康B，性情B
技能：马驹A，女仆A
性技能：异种S
特殊要求：熟女
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;国王要的聊天对象：
等级S+
属性：智力B，自我B，风情B，体型A
技能：礼仪A
性技能：口技S，扩张S，抚慰S
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;白金城：【竞技场、贸易广场、slave公会、地产】&lt;/h3&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/jack-hf/白金城1.jpg，待你提供后重新引用 --&amp;gt;
&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/jack-hf/白金城2.jpg，待你提供后重新引用 --&amp;gt;
&amp;lt;!-- &amp;lt;img src=&quot;https://easylink.cc/m56dgx&quot; title=&quot;白金城1&quot;&amp;gt; --&amp;gt;
&amp;lt;!-- &amp;lt;img src=&quot;https://easylink.cc/6djltf&quot; title=&quot;白金城2&quot;&amp;gt; --&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;红衣主教要的秘书：
等级A+
属性：智力A
技能：秘书S
姓技能：口技B，扩张A，抚慰B
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;教皇要的修女：等级S+
属性：自我S
技能：角斗士S
特殊要求：处女
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;迷雾边境&lt;/h3&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/jack-hf/迷雾.jpg，待你提供后重新引用 --&amp;gt;
&amp;lt;!-- &amp;lt;img src=&quot;https://easylink.cc/i3yb5e&quot; title=&quot;迷雾边境&quot;&amp;gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;PS：迷雾边境无法巡视&amp;lt;span title=&quot;你知道的太多了&quot; class=&quot;heimu&quot;&amp;gt;需要消耗3格精力&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;h4&gt;交任务：&lt;/h4&gt;
&lt;p&gt;1.奴隶不能有疤痕！
2.不能在负精力情况下交！
3.有魅力要求：
魅力 = 美貌 + 异国风情 + 风格
风格：该属性影响交任务，普通任务达到蓝色即可，特殊任务看任务具体要求。提升方式为清洁，化妆（放逐区波叔纹身店永久化妆20火花），怀特区精灵沙龙美甲，理发，打蜡，服装搭配。
4.交货前记得存档，经常会有同样的操作以后第一次不收但是第二次就收的情况出现，但是也有第一次拒收以后读档前怎么补救都不收，读档以后马上就可以的情况，所以记得一定要先存档，一定要先存档，先存档。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1.再确认任务要求，以及确认有没有伤疤、红肿、恢复期、怀孕、受伤和生病之类的情况。
2.读档再试几遍。
3.去洗澡美容。
4.去整容。
5.重练一个 更漂亮的。
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;技能篇&lt;/h2&gt;
&lt;h3&gt;男主技能&lt;/h3&gt;
&lt;p&gt;PS:惩罚中的鞭挞第四、五项显示没有鞭子&amp;lt;span title=&quot;你知道的太多了&quot; class=&quot;heimu&quot;&amp;gt;到武器店去买：副手——短鞭和单手——长鞭&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;h3&gt;精灵技能&lt;/h3&gt;
&lt;p&gt;常用技能：在没有对应特性的情况下，除了角斗士以外，智力是聪慧，（男主亲自教）F-D需要3次，D-C3次，C-B6-7次，B-A14-16次，A-S24-26次；角斗士打竞技场（空手的对手），F-D2次，D-C2次，C-B4次，B-A5次，A-S14次。&lt;/p&gt;
&lt;p&gt;影响训练次数的属性主要是智慧（绿色情况下F-D D-C C-B跟拥有特性时差不多）【自测】
拥有对应特性的情况下F-D1次，D-C1次，C-B2次，B-A3次，A-S5次。&lt;/p&gt;
&lt;p&gt;X技能：基本一次升一级，没升级是服从不够产生抵抗。&lt;/p&gt;
&lt;p&gt;能够同时升级的技能：&lt;/p&gt;
&lt;p&gt;常用技能：
宠物训练—宠物服从训练
坐骑操控—坐骑训练&lt;/p&gt;
&lt;p&gt;性技能：&lt;br /&gt;
抚慰：乳X+按摩+足X—手X（到A）
口活：深喉—口X+颜色（到B），舔肛，深喉—舔舌（到A）
群交：群交—二重+三重+扩张项全部（拳X和后X到A）【最容易练到S的技能，要服从高一点】
暴露：器具—自X，羞耻+诱惑—露X（到A）
性虐：自虐—受虐，答辩——喝尿&lt;/p&gt;
&lt;h2&gt;等级篇&lt;/h2&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/jack-hf/等级判定.jpg，待你提供后重新引用 --&amp;gt;
&amp;lt;!-- &amp;lt;img src=&quot;https://easylink.cc/x8jh37&quot; title=&quot;等级判定&quot;&amp;gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;等级要求：（可能与评级排名有关）
（意外情况：掌握==s+）
一共12个常用技能：女仆、烹饪、医护、秘书、战斗、施法、礼仪、跳舞、绘画、乐器、宠物和马驹&lt;/p&gt;
&lt;p&gt;D:1 C:2 B:3 A:4 S:5&lt;/p&gt;
&lt;p&gt;D- 要求：五个常用技能（女仆、烹饪、医护、秘书）全为D+ [level 5]
D+  要求：五个常用技能全为为C+ [level 10]
C- 要求：五个常用技能B+ 且要有两种性技能为D- [level 15 +level 2]
C+ 要求：常用技能五个B+和3个D-（或者3个A+和两个B+) 四种D-性技能或者两种C-性技能 [level 18 + level 4]
B- 要求：常用技能两个S+ 性技能一个S+ 且[level 20 + level 8]
B+ 要求：[level 23 + level 10]
A- 要求：[level 25 +  level 15]
A+ 要求：[level 26 + level 18]
S- 要求：[level 28 + level 20]&lt;/p&gt;
&lt;p&gt;测试 评级排名为D+的：&lt;/p&gt;
&lt;p&gt;test2 艺术家 （舞蹈、绘画、音乐全B+）：
满足艺术家条件后初始为D-
D+ 任一常用技能D-后
C- 要求：常用技能两个为C- 其他为D- 且要有两种性技能为D-
C+ 要求：常用技能至少一个为B+ 其余为C- 四种D-性技能或者两种C-性技能
B- 要求：常用技能两个S+ 性技能一个S+ 一个C+ 一个D+
B+ 要求：性技能一个S+ 两个C+ 一个D-&lt;/p&gt;
&lt;h2&gt;训练篇&lt;/h2&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/jack-hf/rule.jpg，待你提供后重新引用 --&amp;gt;
&amp;lt;!-- &amp;lt;img src=&quot;https://easylink.cc/v6x3xd&quot; title=&quot;规则作用&quot;&amp;gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;练技能的顺序为：&lt;/p&gt;
&lt;p&gt;{让她清理或做饭(-1体力)→奖励温泉(-1体力+2情绪)→练蜘蛛或触手技能(-1体力)→奖励温泉(-1体力+2情绪)→服待我洗澡或按摩（-1体力）}【选择性】→奖励按摩（+1体力+2情绪）→练蜘蛛或触手技能(-1体力)&lt;/p&gt;
&lt;p&gt;自尊、脾气、骄傲：角斗场&lt;/p&gt;
&lt;p&gt;脾气最简单的就是靠礼仪，长筒靴，钉刺项圈，黄金囚笼，金酒
一次礼仪课＋2点
智力奴隶靠教医护、巫术加，助手靠记账加
一次秘书课+2点&lt;/p&gt;
&lt;h2&gt;预兆篇&lt;/h2&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/jack-hf/预兆面板.jpg，待你提供后重新引用 --&amp;gt;
&amp;lt;!-- &amp;lt;img src=&quot;https://easylink.cc/ckjl0d&quot; title=&quot;预兆面板&quot;&amp;gt; --&amp;gt;
介绍【之后补充】&lt;/p&gt;
&lt;h2&gt;战斗篇&lt;/h2&gt;
&lt;p&gt;红代表力量，绿代表速度，紫代表精确
增加红绿紫也就是血量：学战技，穿装备，+技能
举例：如果主要是造成力量伤害，那就买一把火焰剑，前期可以买个软甲才10$武术+1等级。
学战吼，野蛮冲刺，回旋踢（回旋踢前期钱不够可以先不学），开局先放战吼，获得增益力量&lt;em&gt;2；
再放野蛮冲刺，敌方减益受到力量伤害&lt;/em&gt;2；（紫高可以先放野蛮）
然后选择武器普攻，因为主要是力量伤害，所以有愤怒增益就无脑红，如果buff愤怒没了就选绿色，有了再选红色。敌方受到红伤害就是：力量&lt;em&gt;2减益&lt;/em&gt;2，最少增加四倍伤害，如果触发武器双倍伤害或回旋踢，力量&lt;em&gt;2武器&lt;/em&gt;2减益*2,8倍6倍？
火焰剑buff：绿色增益是愤怒，减益是震惊；红色普攻增益是加速，减益是不稳定。&lt;/p&gt;
&lt;h2&gt;新手攻略&lt;/h2&gt;
&lt;p&gt;&amp;lt;!-- TODO(IMG): 缺失原图，原路径 /images/jack-hf/开局攻略.jpg，待你提供后重新引用 --&amp;gt;
&amp;lt;!-- &amp;lt;img src=&quot;https://easylink.cc/ml87yl&quot; title=&quot;开局攻略&quot;&amp;gt; --&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一种思路
默认男主初期有2600火花，一副铠甲，一把重剑，经济充裕。游戏开始直接在家点女主位，去拍卖行，20起拍，不是傲慢的随便来一个，最终价尽量不要超过50，然后放逐区打品牌纹身，怀特区精灵沙龙给双方洗澡，武器店买屠夫的砍肉刀，服装店买一件巫师长袍，地产商租金牛区房子，回家魔法挨个打上，解释处境，训练（最初训练就是家务和烹饪，其他容易反抗）。第二天继续，女主没体力了就去怀特区给双方洗澡，顺道买点食物。前期男主体力消耗肯定多于女主，体力为负也没关系，只要不晕就行。因为角斗场开始比赛的日子，从家里直接点角斗场再进怀特区是不耗体力的，而且女主打角斗场男主不耗体力，负的体力可以利用这个日子回复。但是女主体力不要负数，服从低的情况下体力负数会降情绪和服从。忠诚等级3，评价D+之后变成助手，然后再去拍卖行随便找个20的，这个是专门打竞技场做公牛之戒任务的，只要忠诚提到3级以上，其他可以无视（我最后一个档服从-7带去都要了）。得到公牛之戒后，限制男主行动的障碍没有了，再去拍卖行弄个20的，不要傲慢（出货慢），再把所有区客户跑一遍，记住要D+的都是什么，然后根据属性进行最优方向培训，到D+交货。这期间可以租个恶魔栏，买只恶魔培养到利维坦去交任务换渡鸦之冠。等到火花快花完时，女主应该在B+甚至A+，去拍卖行可得2600+到3800+（最高），如果火花快没了，差一点到A+，可以去雾的边缘找债主借500,20天内还600即可。有4000+火花时（算上可借的），去拍卖行（去前存档，或者有底气去雾的边缘狩猎也行），刷个样貌精致的（其他属性好美丽也行，最后再整容，依靠名誉提升的话，需要打完挑战冠军，团队战，挑战恶魔，总共需要参加7场，用时49天以上，而一旦出bug少打一场就彻底凉了，马术比赛不加荣誉），一直培训到评价S+，拍卖行最低5850，最高8125，去之前存档，拍到7750即可，然后继续，拍出1个A+，2个S+之后，品牌等级就应该到传奇了，这时候随便送去一个都能拍到5000+，相当于每10天进账最低5000，再破产就去医院看脑科吧。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;个人想法
这游戏时间比较长了，又是国外的游戏，国内没什么讨论环境，一开始什么都不懂玩起来很折磨，我也试了n次开局才勉强懂了些，加上save/load很慢&amp;lt;span title=&quot;你知道的太多了&quot; class=&quot;heimu&quot;&amp;gt;这点真的很想吐槽！&amp;lt;/span&amp;gt;
新人上手最好是能够玩一下游戏的新手模式，要是大神的话直接就能上手了（笑）
沙盒模式可以自己测试，游戏有内置的作弊系统，&lt;strong&gt;注意&lt;/strong&gt;记得把钱搞满，不然用两下会显示钱不够不让用了（每次使用都会扣钱）
剧情模式就选第一个简单的上手，前期先去白金区买房：金牛区-地牢，恶魔围栏，实验室（别怕钱不够，都有用） 买装备：一把火焰刀（200$）, 或者自己用就再买屠夫刀（20$）&amp;lt;span title=&quot;你知道的太多了&quot; class=&quot;heimu&quot;&amp;gt;记得存档S/L！&amp;lt;/span&amp;gt;给自己买服装：巫师长袍，制服。回家换上衣服（简单剧情开场好像送神器渡鸦之冠,记得装上，之后就不用占卜了）之后是slave公会选D-的培养项目,前期推荐养个耐力满的，神智低下也没关系，不建议要傲慢满的这样会容易些。D-只需要常用技能累积到5级就行了，很容易练，一次500块。累积到一定钱数，自己也有经验了就可以做任务了，推荐先做广场军需官任务,拿公牛之戒，这样就可以锁一点精力随便想去哪个城就去。
精灵服从低（小于0）的时候就在说服界面选解释处境和威胁（威胁少用，会加绝望，绝望到3点就难救了），服从差不多到0（&amp;gt;-3就差不多）就可以选择鼓励，其他两个就别点了，每次奖励或者惩罚时要根据具体点数选择，（选项左下角会有数值提醒）不然会加溺爱，溺爱如果上来了，就选主仆贵贱，会减溺爱。
训练最好先练耐力，耐力一次加3点，竞技一次加的非常多，一定要去！其他在别的篇章都有说，懒得敲字了~&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;作弊代码&lt;/h3&gt;
&lt;p&gt;打开游戏的核心文件，然后去搜索那些作弊码，就可以找到作弊码所在的文本。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;在沙盒或自定义模式里取自己名字的时候输入就行了
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;JeanGrey（低作弊）：
包括CrushBoss，Thor，Hulk，Isabella，Rogue&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;BlackWidow（中等作弊）（好像和上面一样的。Enchantress没用）：
包括CrushBoss，Thor，Hulk，Isabella，Rogue，Enchantress&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;ScarletWitch（高作弊）：
包括CrushBoss，Thor，Hulk，Elektra，Isabella，Rogue&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;BlackCat（最大作弊）：
包括CrushBoss，Thor，Hulk，Isabella，Deathstrike，
无视派系争斗，奖杯全开，贵族，名声+，魔法印记价值+&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;CrushBoss：
所有能力最大&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Thor：
开始时住宅位置=怀特镇（所有房间）
在怀特镇的名誉（印记状态）=4
+25000星火
有你魔法印记的奴隶价格+&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Hulk：
开始时住宅位置=怀特镇
力量= 5，性欲= 5，支配= 5
所有物品（武器，盔甲，衣服，食品，药品，特殊训练用品）
特殊宝物（渡鸦之冠、奇美拉宝石等等）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;loli：
所有奴隶都是萝莉&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;young：
所有奴隶都是少女&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;mature（或者milf）：
所有的奴隶都是熟女&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Elektra：
奴隶有武器，盔甲，衣服
奴隶：体型= 4或5; 名誉=1-3，口技+，群jiao+，抚慰+，暴露+，属性+，技能+，光环属性+，完整穿孔，如果不是萝莉就纹身，衣服齐全&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Isabella：
Isabella有：完整的穿孔，纹身，所有的衣服，武器和盔甲，珠宝，一些名誉，魔法印记，无法生育
如果你有恶魔栏的话，在得到Isabella的同时会得到一个恶魔&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Deathstrike：
奴隶有武器，盔甲，衣服
覆盖Elektra
获得完全训练好的奴隶&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Rogue：
奴隶有武器，盔甲，衣服
奴隶有完整的穿孔，纹身，衣服，盔甲，武器和配件&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;CE修改&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;有具体数值的很简单：如星火、耐力、智力
女仆等技能在S+传授情况下，搜未知初始，再次扫描数值加5
选择精准搜索，4字节 —&amp;gt; 输入数值然后首次搜索，数值变化再次搜索 -&amp;gt; 一直到只剩一个或者几个数值 —&amp;gt; 双击选中后会加入到下面地址栏里，改名并修改数值
想锁定就直接在左边的框里选中为X的状态即可&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;没有具体数值：
女仆等技能在S+传授情况下，搜未知初始，再次扫描数值加5
对于单独的一个角色而言，每个技能的地址在升级技能后不变，但基础值归0.
也就是说，到b+以后搜加5可快速修改到S+.
升级后是从0开始的。如果不知道初始只能搜未知初始再一次次升级搜增加的值
学院服去上课满状态一次技能值+7.
150左右从A升到S&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://tieba.baidu.com/p/7390138859?pid=139683092790&amp;amp;cid=0#139683092790&quot;&gt;CE修改贴参考——不具体&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;参考链接：&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://tieba.baidu.com/p/8512426825?pn=1&quot;&gt;攻略图&lt;/a&gt;
&lt;a href=&quot;https://tieba.baidu.com/p/5703877808&quot;&gt;特殊任务&lt;/a&gt;
&lt;a href=&quot;https://tieba.baidu.com/p/6050078854?pn=1#135461895181l&quot;&gt;任务攻略&lt;/a&gt;
&lt;a href=&quot;https://www.andlaou.com/archives/152525&quot;&gt;详细攻略&lt;/a&gt;
&lt;a href=&quot;https://www.vgc.cn/news/92923.html&quot;&gt;新手攻略&lt;/a&gt;
&lt;a href=&quot;https://tieba.baidu.com/p/6692746556&quot;&gt;脾气、智力如何提升&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>markdown使用</title><link>https://h1s97x.github.io/posts/notes/notes-markdown%E4%BD%BF%E7%94%A8/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-markdown%E4%BD%BF%E7%94%A8/</guid><description>[TOC] Markdown语法 &lt;table class=&quot;table table-bordered&quot;&gt; &lt;thead class=&quot;thead-light&quot;</description><pubDate>Sun, 27 Aug 2023 14:34:19 GMT</pubDate><content:encoded>&lt;p&gt;[TOC]&lt;/p&gt;
&lt;h2&gt;参考链接&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://markdown.com.cn&quot;&gt;Markdown语法&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Markdown语法&lt;/h2&gt;
&lt;p&gt;&amp;lt;table class=&quot;table table-bordered&quot;&amp;gt;
&amp;lt;thead class=&quot;thead-light&quot;&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;th&amp;gt;元素&amp;lt;/th&amp;gt;
&amp;lt;th&amp;gt;Markdown 语法&amp;lt;/th&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;
&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;a href=&quot;&quot;&amp;gt;标题（Heading）&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;code&amp;gt;# H1&amp;lt;br&amp;gt;## H2&amp;lt;br&amp;gt;### H3&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;a href=&quot;&quot;&amp;gt;粗体（Bold）&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt; &amp;lt;td&amp;gt;&amp;lt;code&amp;gt;&lt;strong&gt;bold text&lt;/strong&gt;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;a href=&quot;&quot;&amp;gt;斜体（Italic）&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;code&amp;gt;&lt;em&gt;italicized text&lt;/em&gt;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;a href=&quot;&quot;&amp;gt;引用块（Blockquote）&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;code&amp;gt;&amp;gt; blockquote&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;a href=&quot;&quot;&amp;gt;有序列表（Ordered List）&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;code&amp;gt;1. First item&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt; &amp;lt;code&amp;gt;2. Second item&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt; &amp;lt;code&amp;gt;3. Third item&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;a href=&quot;&quot;&amp;gt;无序列表（Unordered List）&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;code&amp;gt;
- First item&amp;lt;br&amp;gt;
- Second item&amp;lt;br&amp;gt;
- Third item&amp;lt;br&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;a href=&quot;&quot;&amp;gt;代码（Code）&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt; &amp;lt;td&amp;gt;&amp;lt;code&amp;gt;&lt;code&gt;code&lt;/code&gt;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;a href=&quot;&quot;&amp;gt;分隔线（Horizontal Rule）&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;code&amp;gt;---&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;a href=&quot;&quot;&amp;gt;链接（Link）&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt; &amp;lt;td&amp;gt;&amp;lt;code&amp;gt;&lt;a href=&quot;https://www.example.com&quot;&gt;title&lt;/a&gt;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;a href=&quot;&quot;&amp;gt;图片（Image）&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt; &amp;lt;td&amp;gt;&amp;lt;code&amp;gt;&lt;img src=&quot;image.jpg&quot; alt=&quot;alt text&quot; /&gt;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;添加图片
&lt;code&gt;![图片alt](图片链接 &quot;图片title&quot;)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;超链接
&lt;code&gt;[超链接显示名](超链接地址 &quot;超链接title&quot;)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;完成标记
&lt;code&gt;- [x] content&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;改变字体颜色
&lt;code&gt;$\color{设置颜色} {文本内容} $&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;设置颜色：有三种表现形式
1.直接用对应颜色的英文表示，如Blue（纯蓝）、Red（纯红）、Pink（粉红）等（首字母大小写都行）。
2.rgb三原色(红绿蓝)：rgb(0,0,0)（黑色） 每一项0-255变化，全0为黑，全255为白。
3.十六进制表示法：如#000000(黑色)、#ffffff(白色)、#008000（绿色）
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一句话多个颜色，则用\隔开，其中，颜色代码可以随意更换&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;注意：在 HTML 块级标签内不能使用 Markdown 语法。例如 &lt;code&gt;&amp;lt;p&amp;gt;italic and **bold**&amp;lt;/p&amp;gt;&lt;/code&gt; 将不起作用。&lt;/li&gt;
&lt;li&gt;&amp;lt;p&amp;gt;italic and &lt;strong&gt;bold&lt;/strong&gt;&amp;lt;/p&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>网站设计 效果实现与代码</title><link>https://h1s97x.github.io/posts/notes/notes-%E7%BD%91%E7%AB%99%E8%AE%BE%E8%AE%A1-%E6%95%88%E6%9E%9C%E5%AE%9E%E7%8E%B0%E4%B8%8E%E4%BB%A3%E7%A0%81/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-%E7%BD%91%E7%AB%99%E8%AE%BE%E8%AE%A1-%E6%95%88%E6%9E%9C%E5%AE%9E%E7%8E%B0%E4%B8%8E%E4%BB%A3%E7%A0%81/</guid><description>&lt;style type=&quot;text/css&quot;&gt; .shell { width:100%; height: 162px; display: flex; justi</description><pubDate>Mon, 21 Aug 2023 12:29:31 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;style type=&quot;text/css&quot;&amp;gt;
.shell {
width:100%;
height: 162px;
display: flex;
justify-content: center;
}
&amp;lt;/style&amp;gt;
&amp;lt;div class=&quot;shell&quot;&amp;gt;
&amp;lt;video loop autoplay src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/web-design-02-20261014.webp&quot; style=&quot;height:100%;transform:translate(36px,-8px);&quot;&amp;gt;&amp;lt;/video&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;script&amp;gt;
const shell = document.querySelect(&quot;.shell&quot;)
const video = document.querySelect(&quot;.video&quot;)
shell.addEventListener(&apos;mouseenter&apos;,function(e){
this.x=e.clientX
video.style.transition = &apos;none&apos;
})
shell.addEventListener(&apos;mousemove&apos;,function(e){
this.x=e.clientX
const disx = this._x + this.x
const move = 36 - disx/-20
video.style.transform = &apos;translate(${move}px,-8px)&apos;
})
shell.addEventListener(&apos;mouseleave&apos;,function(e){
video.style.transition = .3 +&apos;s&apos;
video.style.transform = &apos;translate(36px,-8px)&apos;
})
&amp;lt;/script&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root 是颜色变量
:root {
    --theme-accent-color: rgb(142, 212, 149);
    --theme-accent-color--rgb: 142, 212, 149;
    --theme-secondary-color: rgb(24, 127, 245);
    --theme-secondary-color--rgb: 24, 127, 245;
    --theme-text-color: rgb(44, 62, 80);
    --theme-text-color--rgb: 44, 62, 80;
    --theme-success-color: rgb(35, 165, 94);
    --theme-info-color: rgb(122, 157, 255);
    --theme-warn-color: rgb(255, 180, 4);
    --theme-danger-color: rgb(255, 114, 114);
    --theme-empty-color: rgb(185, 185, 185);
    --theme-link-color: rgb(63, 81, 181);
    --theme-accent-link-color: rgb(255, 255, 255);
    --theme-background-color: rgb(248, 248, 248);
    --theme-border-color: rgb(218, 218, 218);
    --theme-button-color: rgb(37, 180, 73);
    --theme-button-color--hover: #2bd155;
    --theme-button-color--active: #219e40;
    --theme-card-background-color: rgba(255, 255, 255);
    --theme-card-background-color--rgb: 255, 255, 255;
    --theme-card-box-shadow: 0 0 .5rem rgba(0, 0, 0, .1);
    --theme-card-box-shadow--hover: 0 0 .85rem rgba(0, 0, 0, .2);
    --theme-card-border-color: rgb(230, 230, 238);
    --theme-header-background-color: rgb(255, 255, 255);
    --theme-header-box-shadow-color: rgb(238, 239, 241);
    --theme-footer-top-color: rgb(251, 251, 252);
    --theme-footer-bottom-color: #e7e7ed;
    --theme-input-background-color: rgb(245, 245, 245);
    --theme-input-border-color: rgb(222, 222, 222);
    --theme-input-border-color--hover: #e1e1e1
}
h2:before {
    width: .25em;
    height: 70%;
    background-color: rgb(142, 212, 149);
}
h3:before {
    width: .2em;
    height: 60%;
    background-color: rgba(var(--theme-accent-color--rgb),.85);
}
h1:before,h2:before,h3:before,h4:before,h5:before,h6:before {
    content: &quot;&quot;;
    display: block;
    position: absolute;
    top: 50%;
    left: 0;
    transform: translateY(-50%);
    border-radius: 1em;
}
h2,h3{
    font-weight: 700;
    padding: .2rem;
    color: var(--theme-text-color);
    position: relative;/* 重要元素 */
}
h2 {
    border: 0;
    font: inherit;
    font-size: 1.6em;
    margin: 1rem 0;
    padding-left: 1rem;
    vertical-align: baseline;
}
h3 {
    font-size: 1.3rem;
    margin: .5em 0;
    padding-left: .8rem;
}

&amp;lt;h2&amp;gt;标题&amp;lt;/h2&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;一：文字前面用一个标签，将标签设置成小方块
二、使用边框属性
三、使用伪元素:before
四、border-left: 2px solid red;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://www.zhihu.com/question/395120933&quot;&gt;CSS如何在文字面前设置一个小方块？&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;style type=&quot;text/css&quot;&amp;gt;
  .heimu {
    transition: color 0.13s linear;
    color: #252525;
    text-shadow: none;
    background-color: #252525;
  }
  span.heimu:hover, span.heimu:active {
    color: white;
}
&amp;lt;/style&amp;gt;

&amp;lt;span title=&quot;你知道的太多了&quot; class=&quot;heimu&quot;&amp;gt;黑幕&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;https://zditect.com/article/21283930.html#:~:text=%E2%80%9C%E6%AD%A3%E7%A1%AE%E2%80%9D%E7%9A%84%E9%93%BE%E6%8E%A5%E6%A0%BC%E5%BC%8F%E6%98%AF%20file%3A%2F%2F%2F%20%E5%90%8E%E8%B7%9F%E6%96%87%E4%BB%B6%E7%9A%84%E8%B7%AF%E5%BE%84%EF%BC%8C%E5%9C%A8%E6%95%B4%E4%B8%AA%E8%BF%87%E7%A8%8B%E4%B8%AD%E4%BD%BF%E7%94%A8%E6%AD%A3%E6%96%9C%E6%9D%A0%EF%BC%9A,%5B%20%5Bfile%3A%2F%2F%2Fhome%2Fuser%2Fsomefile%5D%5D%20%E3%80%82

&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;加前缀 file:///
空格用 %20
斜杠用正斜杠/ 别用反斜杠\
例如： file:///E:/Thunder%20download/Thunder%20download
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;https://www.cnblogs.com/tqtsanshi/p/16631636.html
使用云链转换：https://easylink.cc/
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;同页面
示例：
首先，使用 id 属性创建书签：
&amp;lt;h2 id=&quot;C4&quot;&amp;gt;第 4 章&amp;lt;/h2&amp;gt;

然后，在同一页中添加指向书签的链接跳到 (&quot;Chapter 4&quot;):
&amp;lt;a href=&quot;#C4&quot;&amp;gt;跳转到第 4 章&amp;lt;/a&amp;gt;

不同页面：
&amp;lt;a href=&quot;html_demo.html#C4&quot;&amp;gt;跳转到第 4 章&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;More info: &lt;a href=&quot;https://www.w3schools.cn/html/html_links_bookmarks.asp&quot;&gt;页面书签&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;More info: &lt;a href=&quot;https://www.zhihu.com/question/432499743&quot;&gt;HTML+CSS制作如图所示的网页导航栏&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;什么是图片懒加载
当访问一个页面的时候，先把img元素或是其他元素的背景图片路径替换成一张大小为1*1px图片的路径（这样就只需请求一次），当图片出现在浏览器的可视区域内时，才设置图片真正的路径，让图片显示出来。这就是图片懒加载。

通俗一点：
1、就是创建一个自定义属性data-src存放真正需要显示的图片路径，而img自带的src放一张大小为1 * 1px的图片路径。
2、当页面滚动直至此图片出现在可视区域时，用js取到该图片的data-src的值赋给src。
ps：自定义属性可以取任何名字

需要了解的问题
1、如何加载图片？
2、如何判断一个元素出现在视野中？

solution1: 如何加载图片
**只需要把data-src中的地址放到src的里面就好了**
code:

function loadImg($img){
//.attr(值)
//.attr(属性名称,值)
$img.attr(&apos;src&apos;, $img.attr(&apos;data-src&apos;)) //把data-src的值 赋值给src
$img.attr(&apos;data-isLoaded&apos;, 1) //已加载过的图片做标记
}
solution2:如何判断一个元素出现在视野中？
**元素相对顶点的距离(文字) &amp;lt;= 窗口高度 + 滚动的距离**
代码：

function isShow($node){
return $node.offset().top &amp;lt;= $(window).height() + $(window).scrollTop()
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://easylink.cc/iso5pz&quot; &amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;a href=&quot;https://www.cnblogs.com/moxiaowohuwei/p/7908877.html&quot;&amp;gt;参考文章&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;data-*
data-* 全局属性 是一类被称为自定义数据属性的属性，它赋予我们在所有 HTML 元素上嵌入自定义数据属性的能力，并可以通过脚本在 HTML 与 DOM 表现之间进行专有数据的交换。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/zh-CN/docs/Web/HTML/Global_attributes/data-*&quot;&gt;mdn data-*&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
&amp;lt;style type=&quot;text/css&quot;&amp;gt;
.developer-code-block {
    position: relative; /* 重要 */
    }
.developer-code-block .copy {
    color: #fff;
    background-color: #697191;
    font-size: .8em;
    padding: 0 0.5em;
    border-radius: 0.5em;
    z-index: 10;
    top: 0.3em;
    right: 0.2em;
    transition: opacity 0.3s ease-in-out;
    opacity: 0;
    position: absolute;/*重要*/
}
button {
    cursor: pointer;
    border: none;
    outline: 0;
}

&amp;lt;/style&amp;gt;

&amp;lt;div class=&quot;developer-code-block&quot;&amp;gt;&amp;lt;pre class=&quot;prism-token token line-numbers language-javascript&quot; tabindex=&quot;0&quot;&amp;gt;&amp;lt;code class=&quot;language-javascript&quot; style=&quot;margin-left: 0px;&quot;&amp;gt;hexo clean
&amp;lt;span aria-hidden=&quot;true&quot; class=&quot;line-numbers-rows&quot;&amp;gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;button class=&quot;copy&quot;&amp;gt;复制&amp;lt;/button&amp;gt;&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;	&amp;lt;style type =&quot;text/css&quot;&amp;gt;
	.prettyprint {
    margin: 0 0 24px;
    padding: 8px 16px 6px 56px;
    background-color: #f6f8fa;
    border: none;
	position: relative;
    overflow-y: hidden;
    overflow-x: auto;
}
	pre {
    position: relative;
	font-family: Consolas,Inconsolata,Courier,monospace;
    font-size: 14px;
    line-height: 22px;
    color: #000;
}	
.prettyprint .pre-numbering {
    position: absolute;
    width: 48px;
    background-color: #eef0f4;
    top: 0;
    left: 0;
    margin: 0;
    padding: 8px 0;
    list-style: none;
    text-align: center;
}
	&amp;lt;/style&amp;gt;

&amp;lt;pre class=&quot;prettyprint&quot; data-index=&quot;1&quot; name=&quot;code&quot;&amp;gt;&amp;lt;code class=&quot;hljs python has-numbering&quot; onclick=&quot;mdcp.copyCode(event)&quot; style=&quot;position: unset;&quot;&amp;gt;    &amp;lt;span class=&quot;hljs-function&quot;&amp;gt;&amp;lt;span class=&quot;hljs-keyword&quot;&amp;gt;def&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;hljs-title&quot;&amp;gt;parse&amp;lt;/span&amp;gt;&amp;lt;span class=&quot;hljs-params&quot;&amp;gt;(self , response)&amp;lt;/span&amp;gt;:&amp;lt;/span&amp;gt;
        &amp;lt;span class=&quot;hljs-keyword&quot;&amp;gt;for&amp;lt;/span&amp;gt; item &amp;lt;span class=&quot;hljs-keyword&quot;&amp;gt;in&amp;lt;/span&amp;gt; response.css(&amp;lt;span class=&quot;hljs-string&quot;&amp;gt;&apos;.con_list_item *&apos;&amp;lt;/span&amp;gt;):
            jobMessage = item.css(&amp;lt;span class=&quot;hljs-string&quot;&amp;gt;&apos;::text&apos;&amp;lt;/span&amp;gt;).extract()
            print(jobMessage)&amp;lt;div class=&quot;hljs-button {2}&quot; data-title=&quot;复制&quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;ul class=&quot;pre-numbering&quot; style=&quot;&quot;&amp;gt;&amp;lt;li style=&quot;color: rgb(153, 153, 153);&quot;&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li style=&quot;color: rgb(153, 153, 153);&quot;&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li style=&quot;color: rgb(153, 153, 153);&quot;&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;li style=&quot;color: rgb(153, 153, 153);&quot;&amp;gt;4&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;
&amp;lt;/pre&amp;gt;


&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;style type=&quot;text/css&quot;&amp;gt;
rb[data-id=&quot;template-ruby&quot;]::before,.template-ruby-hidden  {
    content: &quot;&quot;;
    display: inline-block;
    width: 0;
    font-size: 0;
}
&amp;lt;/style&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;ruby&amp;gt;&amp;lt;rb data-id=&quot;template-ruby&quot;&amp;gt;绅士&amp;lt;span class=&quot;template-ruby-hidden&quot;&amp;gt;（&amp;lt;/span&amp;gt;&amp;lt;/rb&amp;gt;&amp;lt;rt style=&quot;font-size:0.75em&quot;&amp;gt;hentai&amp;lt;/rt&amp;gt;&amp;lt;span class=&quot;template-ruby-hidden&quot;&amp;gt;）&amp;lt;/span&amp;gt;&amp;lt;/ruby&amp;gt;
&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;code {
    font-family: &quot;Courier New&quot;,sans-serif;
    font-size: 12px;
    padding: 0 5px;
    line-height: 1.8;
    margin: 0 3px;
    display: inline-block;
    overflow-x: auto;
    vertical-align: middle;
    border-radius: 3px;
    background-color: #f6f6f6;
    color: #e83e8c;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script&amp;gt;
&quot;use strict&quot;;
window.RLQ = window.RLQ || [];
window.RLQ.push(() =&amp;gt; {
    $(() =&amp;gt; {
        $(&quot;.navbox.largeNavbox:not(.largeNavboxExeced)&quot;).each((_, ele) =&amp;gt; {
            const self = $(ele);
            self.addClass(&quot;largeNavboxExeced&quot;).find(&quot;.navbox-title&quot;).first().prepend(`&amp;lt;${&apos;span&apos;} class=&quot;largeNavbox-toggle&quot;&amp;gt;&amp;lt;/${&apos;span&apos;}&amp;gt;`);
            const button = self.find(&quot;.largeNavbox-toggle&quot;);
            button.append(`[&amp;lt;${&apos;a&apos;}&amp;gt;全隐/全显&amp;lt;/${&apos;a&apos;}&amp;gt;]`);
            button.find(&quot;a&quot;).on(&quot;click&quot;, () =&amp;gt; {
                const buttons = self.find(&quot;.navbox-list .mw-collapsible-toggle&quot;);
                if (buttons.filter(&quot;.mw-collapsible-toggle-collapsed&quot;).length * 2 &amp;gt; buttons.length) {
                    buttons.filter(&quot;.mw-collapsible-toggle-collapsed&quot;).click();
                } else {
                    buttons.filter(&quot;:not(.mw-collapsible-toggle-collapsed)&quot;).click();
                }
            });
        });
        $(&quot;.largeNavbox-toggle&quot;).css({
            position: &quot;absolute&quot;,
            &quot;user-select&quot;: &quot;none&quot;,
        }).find(&quot;a&quot;).css(&quot;cursor&quot;, &quot;pointer&quot;);
    });
});
&amp;lt;/script&amp;gt;&amp;lt;style&amp;gt;
.largeNavbox-toggle {
    right: 6em
}
.skin-moeskin .largeNavbox-toggle {
    right: 4.5em
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;a {
  color: var(--text-link);

  &amp;amp;.external:after {
    background-color: #696969;
    content: &quot;&quot;;
    display: inline-flex;
    height: 10px;
    margin-left: 4px;
    mask-image: url(&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/web-design-01-20261014.svg&quot;);
    mask-size: cover;
    width: 10px;
  }
}


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://cdnjs.com/&quot;&gt;cndjs&lt;/a&gt;
&lt;a href=&quot;https://tympanus.net/codrops/&quot;&gt;Codrops 前端效果网站&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>latex公式</title><link>https://h1s97x.github.io/posts/notes/notes-latex%E5%85%AC%E5%BC%8F/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-latex%E5%85%AC%E5%BC%8F/</guid><description>LaTeX，作为广义上的计算机标记语言（比如HTML），它继承了计算机语言的光荣传统，通过一些简单的代码表达出精确的含义，具有不二义性。其文章排版的结果可以完全</description><pubDate>Mon, 21 Aug 2023 09:33:39 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Introduction&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;LaTeX，作为广义上的计算机标记语言（比如HTML），它继承了计算机语言的光荣传统，通过一些简单的代码表达出精确的含义，具有不二义性。其文章排版的结果可以完全按照你的想法来，不仅解决了玄学问题，渲染出来的文章优美；同时，其还可以通过简单的语法写出优雅高贵的数学公式，目前Markdown也已经支持LaTeX语法的公式&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;数学模式&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;行内公式：嵌入在行内，适用于简单短小的公式
&lt;code&gt;$ f(x) = a+b $&lt;/code&gt;
$ f(x) = a+b $&lt;/li&gt;
&lt;li&gt;块级公式：居中独占一行，适用于比较长或重要的公式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;$$ f(x) = a+b $$&lt;/code&gt;
$$ f(x) = a+b $$
&lt;code&gt;$$ f(x) = a - b \tag{1.1} $$&lt;/code&gt;
$$ f(x) = a - b \tag{1.1} $$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;公式中的空格均会被忽略，可以使用命令\quad或\qquad实现&lt;/li&gt;
&lt;li&gt;在行间公式中，命令\tag{n}可以进行手动编号&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;数学结构&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;简单运算&lt;/h3&gt;
&lt;p&gt;拉丁字母、阿拉伯数字和 +-*/= 运算符均可以直接输入获得
命令:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;\cdot 表示乘法的圆点&lt;/li&gt;
&lt;li&gt;\neq 表示不等号&lt;/li&gt;
&lt;li&gt;\equiv 表示恒等号&lt;/li&gt;
&lt;li&gt;\bmod 表示取模
&lt;code&gt;$$ x+2-3*4/6=4/y + x\cdot y $$&lt;/code&gt;
$$ x+2-3*4/6=4/y + x\cdot y $$
&lt;code&gt;$$ 0 \neq 1 \quad x \equiv x \quad 1 = 9 \bmod 2 $$&lt;/code&gt;
$$ 0 \neq 1 \quad x \equiv x \quad 1 = 9 \bmod 2 $$&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;根号、分式&lt;/h3&gt;
&lt;p&gt;命令：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;\sqrt 表示平方根&lt;/li&gt;
&lt;li&gt;\sqrt[n] 表示n次方根&lt;/li&gt;
&lt;li&gt;\frac 表示分式
&lt;code&gt;$$\sqrt{x} + \sqrt{x^{2}+\sqrt{y}} = \sqrt[3]{k_{i}} - \frac{x}{m}$$&lt;/code&gt;
$$\sqrt{x} + \sqrt{x^{2}+\sqrt{y}} = \sqrt[3]{k_{i}} - \frac{x}{m}$$&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;向量&lt;/h3&gt;
&lt;p&gt;命令：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;\vec表示向量&lt;/li&gt;
&lt;li&gt;\overrightarrow 表示箭头向右的向量&lt;/li&gt;
&lt;li&gt;\overleftarrow 表示箭头向左的向量
&lt;code&gt;$$\vec{a} + \overrightarrow{AB} + \overleftarrow{DE}$$&lt;/code&gt;
$$\vec{a} + \overrightarrow{AB} + \overleftarrow{DE}$$&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;积分、极限、求和、乘积&lt;/h3&gt;
&lt;p&gt;命令：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;\int 表示积分&lt;/li&gt;
&lt;li&gt;\lim 表示极限&lt;/li&gt;
&lt;li&gt;\sum 表示求和&lt;/li&gt;
&lt;li&gt;\prod 表示乘积&lt;/li&gt;
&lt;li&gt;^与_ 表示上、下限
&lt;code&gt;$$  \lim_{x \to \infty} x^2_{22} - \int_{1}^{5}x\mathrm{d}x + \sum_{n=1}^{20} n^{2} = \prod_{j=1}^{3} y_{j}  + \lim_{x \to -2} \frac{x-2}{x} $$&lt;/code&gt;
$$  \lim_{x \to \infty} x^2_{22} - \int_{1}^{5}x\mathrm{d}x + \sum_{n=1}^{20} n^{2} = \prod_{j=1}^{3} y_{j}  + \lim_{x \to -2} \frac{x-2}{x} $$&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;间隔符号&lt;/h3&gt;
&lt;p&gt;命令：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;+空格&lt;/li&gt;
&lt;li&gt;\quad&lt;/li&gt;
&lt;li&gt;\qquad
这三种写入方式所呈现的效果是——&lt;strong&gt;间隔依次增大&lt;/strong&gt;
&lt;code&gt;$x\ y$&lt;/code&gt;
$x\ y$
&lt;code&gt;$x\quad y$&lt;/code&gt;
$x\quad y$
&lt;code&gt;$x\qquad y$&lt;/code&gt;
$x\qquad y$&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;上下标&lt;/h3&gt;
&lt;p&gt;命令：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;_ 表示下标&lt;/li&gt;
&lt;li&gt;^ 表示上标&lt;/li&gt;
&lt;li&gt;&apos; 表示求导
上下标内容不止一个字符时，需用大括号括起来。
&lt;code&gt;$$ a_{ij}^{2} + b^3_{2}=x^{t} + y&apos; + x&apos;&apos;_{12} $$&lt;/code&gt;
$$ a_{ij}^{2} + b^3_{2}=x^{t} + y&apos; + x&apos;&apos;_{12} $$&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;上下标记&lt;/h3&gt;
&lt;p&gt;命令：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;\overline 在表达式上方画出水平线&lt;/li&gt;
&lt;li&gt;\underline 在表达式下方画出水平线&lt;/li&gt;
&lt;li&gt;\overbrace 在表达式上方给出一个水平的大括号&lt;/li&gt;
&lt;li&gt;\underbrace 在表达式下方给出一个水平的大括号
&lt;code&gt;$$\overline{x+y} \qquad \underline{a+b}$$&lt;/code&gt;
$$\overline{x+y} \qquad \underline{a+b}$$
&lt;code&gt;$$\overbrace{1+2+\cdots+n}^{n个} \qquad \underbrace{a+b+\cdots+z}_{26}$$&lt;/code&gt;
$$\overbrace{1+2+\cdots+n}^{n个} \qquad \underbrace{a+b+\cdots+z}_{26}$$&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;三圆点&lt;/h3&gt;
&lt;p&gt;命令：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;\ldots 点位于基线上&lt;/li&gt;
&lt;li&gt;\cdots 点设置为居中&lt;/li&gt;
&lt;li&gt;\vdots 使其垂直&lt;/li&gt;
&lt;li&gt;\ddots 对角线排列
&lt;code&gt;$$ x_{1},x_{2},\ldots,x_{5}  \quad x_{1} + x_{2} + \cdots + x_{n} $$&lt;/code&gt;
$$ x_{1},x_{2},\ldots,x_{5}  \quad x_{1} + x_{2} + \cdots + x_{n} $$&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;重音符号&lt;/h3&gt;
&lt;p&gt;命令：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;\hat&lt;/li&gt;
&lt;li&gt;\bar&lt;/li&gt;
&lt;li&gt;\tilde
&lt;code&gt;$ \hat{x} $&lt;/code&gt;
$ \hat{x} $
&lt;code&gt;$ \bar{x} $&lt;/code&gt;
$ \bar{x} $
&lt;code&gt;$ \tilde{x} $&lt;/code&gt;
$ \tilde{x} $&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;运算符&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://cnb.cool/h1s97x/Pixy/-/raw/main/images/latex-formula-01-20261014.webp&quot; alt=&quot;&quot; title=&quot;希腊字母&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;在Markdown里插入希腊字母&lt;/h3&gt;
&lt;p&gt;如果直接在公式块中是可以直接显示的。$$
\phi
$$&lt;/p&gt;
&lt;p&gt;如果想在文字中嵌入，输入 $\phi$。&lt;/p&gt;
&lt;p&gt;typora，设置 文件—&amp;gt;偏好设置—&amp;gt;Markdown，勾上 “内联公式”&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/u012028275/article/details/115057245&quot;&gt;【Markdown笔记】Markdown中添加和使用希腊字母_dadalaohua的博客-CSDN博客&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;参考&lt;/h3&gt;
&lt;p&gt;&amp;lt;a href=&quot;https://blog.csdn.net/CaptainD_W/article/details/116570636?ydreferer=aHR0cHM6Ly9jbi5iaW5nLmNvbS8%3D&quot;&amp;gt;在 Markdown(.md)文件中写入各种漂亮的 LaTex公式&amp;lt;/a&amp;gt;
&amp;lt;a href=&quot;https://zhuanlan.zhihu.com/p/450465546&quot;&amp;gt;Markdown/LaTeX 数学公式和符号表&amp;lt;/a&amp;gt;
&amp;lt;a href=&quot;https://www.overleaf.com/learn&quot;&amp;gt;权威参考&amp;lt;/a&amp;gt;
&amp;lt;a href=&quot;https://zhuanlan.zhihu.com/p/110756681&quot;&amp;gt;LaTeX 公式篇&amp;lt;/a&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Scrapy爬虫</title><link>https://h1s97x.github.io/posts/notes/notes-scrapy%E7%88%AC%E8%99%AB/</link><guid isPermaLink="true">https://h1s97x.github.io/posts/notes/notes-scrapy%E7%88%AC%E8%99%AB/</guid><description>&lt;!-- 注意在符号与内容之间的空格，不加空格会认为都是文本内容 --&gt; &lt;head&gt; &lt;meta charset=&quot;utf-8&quot;&gt; &lt;meta name=&quot;v</description><pubDate>Sun, 23 Jul 2023 07:54:11 GMT</pubDate><content:encoded>&lt;h1&gt;Scrapy 爬取静态HTML页面&lt;/h1&gt;
&lt;h3&gt;第一次写个人Blog,作为学习生涯的一点小小记录，也是不断摸索前进的一点慰藉。&lt;/h3&gt;
&lt;p&gt;&amp;lt;!-- 注意在符号与内容之间的空格，不加空格会认为都是文本内容 --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;head&amp;gt;
&amp;lt;meta charset=&quot;utf-8&quot;&amp;gt;
&amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&amp;gt;
&amp;lt;title&amp;gt;&amp;lt;/title&amp;gt;
&amp;lt;style type =&quot;text/css&quot;&amp;gt;
.prettyprint {
margin: 0 0 24px;
padding: 8px 16px 6px 56px;
background-color: #f6f8fa;
border: none;
position: relative;
overflow-y: hidden;
overflow-x: auto;
}
pre {
position: relative;
font-family: Consolas,Inconsolata,Courier,monospace;
font-size: 14px;
line-height: 22px;
color: #000;
}	
.prettyprint .pre-numbering {
position: absolute;
width: 48px;
background-color: #eef0f4;
top: 0;
left: 0;
margin: 0;
padding: 8px 0;
list-style: none;
text-align: center;
}
&amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;&lt;/p&gt;
&lt;p&gt;Upadate 8.29
&lt;a href=&quot;https://blog.csdn.net/dangsh_/article/details/78617178&quot;&gt;css选择器(Selectors) 用法&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;#container 选择id为container的元素&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;.container 选择所有class包含container的元素&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;* 选择所有元素&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;div a 选取所有div下所有a元素&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;ul + p 选取ul后面的第一个p元素&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;ul ~p 选取与ul相邻的所有p元素&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;a:nth-child(2) 选取下面第二个标签，如果是a的话则选取，不是则不取&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;a:nth-child(2n) 选取第偶数个a元素&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;a:nth-child(2n+1) 选取第奇数个a元素&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;li.multi-chosen &amp;gt; a 选取class为multi-chosen的li的所有a元素&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;a[title] 选取所有拥有title属性的a元素&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;a[href=”https://www.lagou.com/jobs/3537439.html”] 选取所有href属性为&amp;lt;a href=&quot;https://www.lagou.com/jobs/3537439.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&amp;gt;https://www.lagou.com/jobs/3537439.html&amp;lt;/a&amp;gt;的a元素&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;a[href*=”www.lagou.com”] 选取所有href属性值中包含www.lagou.com的a元素&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;a[href^=”http”] 选取所有href属性值中以http开头的a元素&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;div:not(#content-container) 选取所有id为非content-container 的div&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import scrapy
from First.items import FirstItem

class Lagou(scrapy.Spider):
    name = &quot;forth&quot;
    start_urls = [
        &quot;https://www.lagou.com/zhaopin/Java/&quot;
    ]


    def parse(self , response):
        pass
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;    def parse(self , response):
        for item in response.css(&apos;#lg_tnav h1&apos;):
            jobMessage = item.css(&apos;::text&apos;).extract()
            print(jobMessage)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;参考 笔记暂未写&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/printf123scanf/article/details/105911511?ydreferer=aHR0cHM6Ly9jbi5iaW5nLmNvbS8%3D?ydreferer=aHR0cHM6Ly9jbi5iaW5nLmNvbS8%3D?ydreferer=aHR0cHM6Ly9jbi5iaW5nLmNvbS8%3D?ydreferer=aHR0cHM6Ly9jbi5iaW5nLmNvbS8%3D&quot;&gt;scrapy爬取script标签中某个var变量的值&lt;/a&gt;
&lt;a href=&quot;https://www.cnblogs.com/syq816/p/12612263.html&quot;&gt;Python中使用正则表达式获取两个字符中间部分&lt;/a&gt;
&lt;a href=&quot;https://github.com/airingursb/bilibili-user&quot;&gt;Bilibili用户爬虫&lt;/a&gt;
&lt;a href=&quot;https://zhuanlan.zhihu.com/p/357392015&quot;&gt;这才是B站爬虫的正确姿势，视频、评论、弹幕全部拿下&lt;/a&gt;
&lt;a href=&quot;https://blog.csdn.net/Java_ZZZZZ/article/details/131630415&quot;&gt;【python爬虫实战】：不同验证码的自动识别&lt;/a&gt;
&lt;a href=&quot;https://blog.csdn.net/gets_s/article/details/115839687&quot;&gt;爬虫之模拟登录、自动获取cookie值、验证码识别&lt;/a&gt;
&lt;a href=&quot;https://www.cnblogs.com/angelyan/p/12207835.html#:~:text=1.%E6%89%93%E5%BC%80tasker%E7%95%8C%E9%9D%A2%EF%BC%8C%E7%82%B9%E5%87%BB%E5%8F%B3%E4%B8%8B%E5%8A%A0%E5%8F%B7%202.%E7%82%B9%E5%87%BB%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E7%B1%BB%E5%9E%8B%EF%BC%8C%E9%80%89%E6%8B%A9%E4%BA%8B%E4%BB%B6,3.%E9%80%89%E6%8B%A9%E4%BA%8B%E4%BB%B6%E7%B1%BB%E5%9E%8B%EF%BC%8C%E7%82%B9%E5%87%BB%E7%94%B5%E8%AF%9D%204.%E7%82%B9%E5%87%BB%E6%94%B6%E5%88%B0%E7%9F%AD%E4%BF%A1%205.%E5%9C%A8%E5%8F%91%E4%BB%B6%E4%BA%BA%E5%92%8C%E5%86%85%E5%AE%B9%E4%B8%AD%E5%A1%AB%E5%86%99%E8%BF%87%E6%BB%A4%E6%9D%A1%E4%BB%B6%EF%BC%8C%E6%AF%94%E5%A6%82%EF%BC%9A10086%E5%8F%91%E6%9D%A5%E7%9A%84%EF%BC%8C%E5%86%85%E5%AE%B9%E6%98%AF%E9%AA%8C%E8%AF%81%E7%A0%81%EF%BC%8C%E7%84%B6%E5%90%8E%E7%9B%B4%E6%8E%A5%E7%82%B9%E5%87%BB%E5%B7%A6%E4%B8%8A%E8%A7%92%E7%9A%84%E8%BF%94%E5%9B%9E%E6%8C%89%E9%92%AE%E8%87%AA%E5%8A%A8%E4%BF%9D%E5%AD%98&quot;&gt;python爬虫解决手机验证码问题 &lt;/a&gt;
&lt;a href=&quot;https://zhuanlan.zhihu.com/p/154055263&quot;&gt;网络爬虫|网页中嵌套iframe框架内容爬取的两种思路&lt;/a&gt;
&lt;a href=&quot;https://blog.csdn.net/lihaian/article/details/126104447&quot;&gt;爬虫---scrapy爬虫框架（详细+实战）&lt;/a&gt;
&lt;a href=&quot;https://cloud.tencent.com/developer/article/1826626&quot;&gt;浅谈Python两大爬虫库——urllib库和requests库区别&lt;/a&gt;
&lt;a href=&quot;https://blog.csdn.net/qq_43546676/article/details/88777227&quot;&gt;urllib.request详细介绍&lt;/a&gt;
&lt;a href=&quot;https://www.cnblogs.com/litufu/articles/8721207.html&quot;&gt;pandas read_html使用详解（一）&lt;/a&gt;
&lt;a href=&quot;https://cloud.tencent.com/developer/article/1559913?from=15425&quot;&gt;js爬虫，正则&lt;/a&gt;
&lt;a href=&quot;https://blog.csdn.net/yang731227/article/details/83900422&quot;&gt;go语言模拟网站登录并爬虫&lt;/a&gt;
&lt;a href=&quot;https://cloud.tencent.com/developer/article/1406499?from=15425&quot;&gt;python爬虫的重定向问题&lt;/a&gt;
&lt;a href=&quot;https://www.cnblogs.com/LXP-Never/p/11382216.html&quot;&gt;爬虫——控制台抓包和requests.post()发送请求&lt;/a&gt;
&lt;a href=&quot;https://blog.csdn.net/qq_28072715/article/details/79387422&quot;&gt;python ：codecs模块简介&lt;/a&gt;
&lt;a href=&quot;https://juejin.cn/post/6991828558096105485&quot;&gt;嵌入的iframe&lt;/a&gt;
&lt;a href=&quot;https://www.cnblogs.com/taolusi/p/9282565.html&quot;&gt;Python爬取javascript(js)动态网页&lt;/a&gt;
&lt;a href=&quot;https://blog.csdn.net/qq_32623363/article/details/78768636&quot;&gt;python3（urlopen）获取网页的坑&lt;/a&gt;
&lt;a href=&quot;https://juejin.cn/post/7135036717966786573&quot;&gt;正则表达式-如何简单匹配HTML中a标签的href&lt;/a&gt;
&lt;a href=&quot;https://blog.csdn.net/weixin_43452467/article/details/113884408&quot;&gt;正则表达式匹配指定字符串中间内容&lt;/a&gt;
&lt;a href=&quot;https://www.cnblogs.com/zhongyehai/p/9170366.html&quot;&gt;python+selenium四：iframe查看、定位、切换&lt;/a&gt;&lt;/p&gt;
</content:encoded></item></channel></rss>