-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.json
1 lines (1 loc) · 390 KB
/
index.json
1
[{"categories":["编程-ღ-技术"],"content":"参考资料: https://techdiylife.github.io/blog/topic.html?category2=t07\u0026blogid=0039 https://blog.csdn.net/qq_46106285/article/details/137430941 LangChain|Ollama结合LangChain使用的速通版(包含代码以及切换各种模型的方式) https://cloud.tencent.com/developer/article/2324375 示例选择器 1. 安装anaconda https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 下载Anaconda3-4.1.1-Windows-x86_64.exe。 安装完成后,创建虚拟环境 conda create -n clj python==3.10 conda activate clj 2.安装langChain https://python.langchain.com/v0.1/docs/get_started/installation/#langchain-cli 在虚拟环境中执行 conda install langchain -c conda-forge langchain还有一些依赖可以安装,暂时不安装跳过这步 LangServe助力开发者以REST API的形式部署LangChain可运行文件和链条。通过LangChain CLI安装时,LangServe会自动安装。如果您不通过LangChain CLI安装,可以使用以下命令分别安装客户端和服务器所需的依赖: 安装所有依赖项: pip install “langserve[all]” 仅安装客户端依赖项: pip install “langserve[client]” 仅安装服务器依赖项: pip install “langserve[server]” LangChain CLI LangChain CLI是一个非常实用的工具,它可以帮助您管理LangChain模板和其他LangServe项目。安装命令如下: pip install langchain-cli 3. 安装jupyternotebook 安装命令:conda install -y jupyter 并不必须,但是便于学习,建议使用。 安装完后,执行jupyter notebook,可指定端口号,默认端口8888,在浏览器中打开http://localhost:8888/,即可进入交互界面。 4. 安装ollama https://ollama.com/download/windows 安装完成后,执行ollama run codellama,将模型(3.8G)pull到本地。 用langchain调用ollama的模型(官方教程使用的是llama2) 5. LLM Chain链条 为什么用LangChain LangChain 包的主要价值主张是: 组件:用于处理语言模型的可组合工具和集成。无论你是否使用 LangChain 框架的其余部分,组件都是模块化的,易于使用 现成的链:用于完成高级任务的组件的内置组合 LLM Chain的一个例子,下面链条中包括了模型、提示、输出解析。 6. 提示工程 p.s. 内容参考LangChain(https://cookbook.langchain.com.cn/docs/langchain-prompt-templates/)。 提示工程领域,一个典型的提示结构包括以下组件(并非所有的提示都使用这些组件,但是一个好的提示通常会使用两个或更多组件) 指令(instruction) :告诉模型该怎么做,如何使用外部信息(如果提供),如何处理查询并构建 Out。 外部信息 或 上下文 (contexts):充当模型的附加知识来源。这些可以手动插入到提示中,通过向量数据库 (Vector Database) 检索(检索增强)获得,或通过其他方式(API、计算等)引入。 用户输入或 查询 :通常(但不总是)是由人类用户(即提示者)输入到系统中的查询。 输出指示器(output indicator) :标记要生成的文本的 开头。(如果生成 Python 代码,我们可以使用import来指示模型必须开始编写 Python 代码(因为大多数 Python 脚本以import开头)。 ) 下面的例子中包括了指令、上下文、用户输入。 实际上,不太可能硬编码上下文和用户问题。我们会通过一个 模板 PromptTemplate 将它们输入。 在LangChain里,像介绍LLM Chain链条的例子里,提示(prompt)用到了ChatPromptTemplate,是一种模板。那么什么是模板: 我们在使用AI时,AI回复的效果通常取决于我们的提问方式,例如: 1.你期望AI回答物理问题时,会先和AI约定说”你现在是一个物理学教授“; 2.你不希望AI自由发挥无中生有,你会在问题最后补上一句“如果你不知道,就说不知道,不要创造答案”(If you don't know the answer, just say that you don’t know, don’t try to make up an answer) 而这些成规律的对话内容就形成了所谓对话的“模板”。Langchain 中的提示模板类旨在简化使用动态输入构建提示的过程。 构建一个模板prompt_template: 使用这个模板的时候,print(openai( prompt_template.format( query = “Which libraries and model providers offer LLMs?” ))) 看上去只是将原本的输入query替换掉了,python可以用f-string轻松实现这个过程,但是 Langchain 的 PromptTemplate 对象,可以规范化这个过程,添加多个参数,并以面向对象的方式构建提示。 除了可以自己创造模板并调试外,LangChain也内置了很多种的模板。langchain官网对ChatPromptTemplate的封装:https://api.python.langchain.com/en/latest/prompts/langchain_core.prompts.chat.ChatPromptTemplate.html,下面是一个使用ChatPromptTemplate模板的例子。 7. 少样本提示 少样本提示(ref. https://www.promptingguide.ai/zh/techniques/fewshot . https://arxiv.org/pdf/2001.08361),当模型足够大时,仅需要给出少量提示,模型就能从少量样本中学习到如何执行认为。 在LangChain中,少样本提示适合使用FewShotPromptTemplate。 from langchain import FewShotPromptTemplate llm = Ollama(model=\"llama2\") from langchain import FewShotPromptTemplate # create our examples examples = [ { \"query\": \"How are you?\", \"answer\": \"I can't complain but sometimes I still do.\" }, { \"query\": \"What time is it?\", \"answer\": \"It's time to get a watch.\" } ] # create a example template example_template = \"\"\" User: {query} AI: {answer} \"\"\" # create a prompt example from above template example_prompt = PromptTemplate( input_variables = [\"query\", \"answer\"], template = example_template ) # now break our previous prompt into a prefix and suffix # the prefix is our instructions prefix = \"\"\" The following are exerpts from conversations with an AI assistant. The assistant is typically sarcastic and witty, producing creative and funny responses to the users questions. Here are some examples: input \"\"\" # and the suffix our user input and output indicator suffix = \"\"\" User: {query} AI: \"\"\" # now create the few shot prompt template few_shot_prompt_template = FewShotPromptTemplate( examples = examples, examp","date":"2024-06-05","objectID":"https://seacj.github.io/posts/clj%E4%B8%B4%E6%97%B6/:0:0","tags":["C++"],"title":"Clj临时","uri":"https://seacj.github.io/posts/clj%E4%B8%B4%E6%97%B6/"},{"categories":["编程-ღ-技术"],"content":"复习Complex类的实现过程 构建复数Complex类的思考路程: #ifndef __COMPLEX__ #define __COMPLEX__ #include \u003ciostream\u003e //实际上include不一定要写在前面,只要在函数外面就行using std::ostream; class complex { public: complex (double r = 0, double i = 0) : re(r), im(i){} complex\u0026 operator += (const complex\u0026); double real() const {return re;} double imag() const {return im;} private: double re, im; friend complex\u0026 __doapl(complex*, const complex); }; #endif //do assignment-plus,函数中想直接取得re和im,所以声明成友元函数 complex\u0026 __doapl(complex* ths, const complex r){ ths-\u003ere += r.re; ths-\u003eim += r.im; return *ths; } inline complex\u0026 complex::operator += (const complex\u0026 r){ return __doapl(this, r); } //非成员函数 //把+不设计为成员函数是因为不只是复数加复数,还可以是实数加复数 inline complex operator + (const complex\u0026 x, const complex\u0026 y){ return complex(x.real() + y.real(), x.imag() + y.imag()); } inline complex operator + (const complex\u0026 x, double y){ return complex(x.real() + y, x.imag()); } //操作符重载只能用在左边的变量上 inline complex operator + (double x, const complex\u0026 y){ return complex(x + y.real(), y.imag()); } //由于希望能够连用,如cout \u003c\u003c c1 \u003c\u003c endl; 所以有返回值 ostream\u0026 operator \u003c\u003c (ostream\u0026 os, const complex\u0026 x){ return os \u003c\u003c '(' \u003c\u003c x.real() \u003c\u003c x.imag() \u003c\u003c ')'; } 知识点 防卫式声明 构造函数初值列 类成员函数后面加上const关键字后,这个函数就能够被const对象和非const对象调用 三大函数:拷贝构造,拷贝复制,析构 拷贝构造,拷贝复制,析构函数被称为三大函数(Big Three)。 class String { public: String(const char* cstr = 0); // 基础构造函数 String(const String\u0026 str); // 1.拷贝构造函数 String\u0026 operator=(const String\u0026 str); // 2.拷贝赋值 重载= ~String(); // 3.析构函数 char* get_c_str() const { return m_data; } // 不改data,const private: char* m_data; }; class with pointer member 必须有 copy ctor 和 copy op=,否则会导致下面1. 指向同一个地方,改a,b就跟着改了 。2. 原本b的指向的地方没有清掉。 // 拷贝构造函数 inline String::String(const String\u0026 str) { auto len = strlen(str.m_data); // 直接取另一个的data,因为兄弟之间互为friend m_data = new char[len + 1]; memset(m_data, 0, len + 1); strcpy(m_data, str.m_data); } // 拷贝赋值函数 inline String\u0026 String::operator=(const String\u0026 str) { if (this == \u0026str) { // 0.自我赋值(self assignment)检测,否则直接delete,没有数据了 return *this; } delete[] m_data; // 1.清空被赋值的内容 m_data = new char[strlen(str.m_data) + 1]; // 2.申请新的内存 strcpy(m_data, str.m_data); // 3.拷贝 return *this; } 扩展补充:类模板,函数模板,及其他 class Account { public: static double m_rate; // 只是声明 static void set_rate(const double\u0026 x) { m_rate = x; } }; double Account::m_rate = 8.0; // 定义:使其获得内存,在类外写 int main() { // 调用static函数的两个方式: Account::set_rate(5.0); // 1.通过class name 调用 Account a; a.set_rate(7.0); // 2.通过object调用 } 使用static,并把构造函数写到private中,实现单例模式。 class A { public: static A\u0026 getInstance() { static A a; // 放在getInstance函数中,而不是在private的好处:只有当有人调用getInstance的时候,才会分配空间,但是离开函数以后,依然存在。 return a; } setup() {...} private: A(); A(const A\u0026 rhs); }; A::getInstance().setup(); 但是该成员函数处理的哪个对象,靠this pointer来确定 加了static的成员变量:和对象就脱离了;所有对象的该数据都一样,就弄成静态的; 加了static的成员函数:静态函数没有this pointer,所以不可以处理对象中的非静态的成员变量;只能处理静态成员变量。 组合与继承 queue所有的功能在deque中已经完成,所以调用deque来完成。 这是adapter设计模式(adapter有两种实现方法,复合和继承),用于填补“现有的程序”和“所需的程序”之间差异的设计模式。 虽然实现的时候是用指针,但是也叫composition by reference。 和Composition相比,Delegation有一个指针指向实现了所有功能的类(pointer to implementation),外界只看到Handle,左边的Handle不变,只需要改右边,所以被称为编译防火墙。 C++有三种继承(public、private、protected,但是最常用的就是public,java里的继承都是public继承),继承就表明类之间是is-a关系。 虚函数 考虑到继承的时候,需要搭配父类的虚函数。 虚函数:让子类能够重写(override)。其中一类是纯虚函数(pure virtual),父类不实现,子类一定要实现。 设计模式 Template Method:父类中的一个重要部分延缓到子类中去实现(如下Serialize() ),子类可能要一年或两年后才把它的具体内容写出来。这里Template不是c++中的模板,只是套用概念。 #include\u003ciostream\u003e//------ FrameWork class CDocument { public: void OnFileOpen(); //这里 Serialize() 为 纯虚函数 或 空函数 都可以 virtual void Serialize() = 0; //virtual void Serialize(){ } }; void CDocument::OnFileOpen() { //如下 每个 std::cout 表示一个 实际动作 std::cout \u003c\u003c \"dialog...\" \u003c\u003c std::endl; std::cout \u003c\u003c \"check file status...\" \u003c\u003c std::endl; std::cout \u003c\u003c \"Open file...\" \u003c\u003c std::endl; //(3) this-\u003eSerialize() // 父类 non-virtual func 中 用 pointer this 调 // 父类 pure virtual func =\u003e // this -\u003e 运行时 obj/myDoc 的 vptr -\u003e vtbl // -\u003e 子类中 override 的 vf Serialize(); std::cout \u003c\u003c \"close file...\" \u003c\u003c std::endl; std::cout \u003c\u003c \"update all views...\" \u003c\u003c std::endl; } //------ ","date":"2022-05-10","objectID":"https://seacj.github.io/posts/houjiecpp1/:0:0","tags":["C++"],"title":"C++面向对象高级编程(上)","uri":"https://seacj.github.io/posts/houjiecpp1/"},{"categories":["算法"],"content":"题记 滑动窗口每次做完,过一段时间又忘了,所以还是需要在一起总结一下。 模板 参考资料:https://leetcode-cn.com/problems/longest-substring-with-at-most-two-distinct-characters/solution/hua-dong-chuang-kou-zhen-di-jian-dan-yi-73bii/ 上面链接中的内容很多,感觉有点冗余,这里我认为只需要记住写法: class Solution: def problemName(self, s: str) -\u003e int: # Step 1. 初始化 res = 0 i = 0 window = {} for j in range(len(s)): # Step 2: 更新需要维护的变量, 有的变量需要一个if语句来维护 (比如最大最小长度) x = new_x if condition: y = new_y ''' ------------- 下面是两种情况,读者请根据题意二选1 ------------- ''' # Step 3 - 情况1 题目要求滑动框固定 if 窗口长度大于限定值: # 更新 (部分或所有) 维护变量 # 窗口左指针前移一个单位保证窗口长度固定 # Step 3 - 情况2 维护一个可变长的合法的滑动窗 while 不合法: window[i] = ... # 更新 (部分或所有) 维护变量 i += 1 return res 滑动窗变长 ","date":"2021-08-21","objectID":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/:0:0","tags":["算法","leetcode","滑动窗口"],"title":"Leetcode滑动窗口","uri":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/"},{"categories":["算法"],"content":"159. 至多包含两个不同字符的最长字串 class Solution: def lengthOfLongestSubstringTwoDistinct(self, s: str) -\u003e int: i, j = 0, 0 window = {} # 元素和它最后一个的位置 kind = 0 res = 0 for j in range(len(s)): if s[j] not in window or window[s[j]] == 0: window[s[j]] = 1 kind += 1 else: window[s[j]] += 1 while kind \u003e= 3: window[s[i]] -= 1 if window[s[i]] == 0: kind -= 1 i += 1 res = max(res, j-i+1) return res ","date":"2021-08-21","objectID":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/:1:0","tags":["算法","leetcode","滑动窗口"],"title":"Leetcode滑动窗口","uri":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/"},{"categories":["算法"],"content":"感想 这种有元素种类限制的题目,用一个变量kind存,比用len(window)要好,因为滑动窗口左边弹出元素的临界值是window[x]==0,这个时候还要弹出这个元素,容易出错 注意不要忽略条件window[s[j]] == 0,这种情况虽然元素还在window里,但是相当于没有。 ","date":"2021-08-21","objectID":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/:1:1","tags":["算法","leetcode","滑动窗口"],"title":"Leetcode滑动窗口","uri":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/"},{"categories":["算法"],"content":"209. 长度最小的子数组(≥target) class Solution: def minSubArrayLen(self, s: int, nums: List[int]) -\u003e int: i = 0 res = float('inf') cur_sum = 0 for j in range(len(nums)): cur_sum += nums[j] while cur_sum \u003e= s: cur_sum -= nums[i] res = min(res, j-i+1) i += 1 if res == float('inf'): return 0 return res ","date":"2021-08-21","objectID":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/:2:0","tags":["算法","leetcode","滑动窗口"],"title":"Leetcode滑动窗口","uri":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/"},{"categories":["算法"],"content":"感想 和模板不太一样,因为要求的是最短的长度,所以res的更新在while循环中。 ","date":"2021-08-21","objectID":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/:3:0","tags":["算法","leetcode","滑动窗口"],"title":"Leetcode滑动窗口","uri":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/"},{"categories":["算法"],"content":"487. 最大连续1的个数 II(滑动窗口内最多可以有1个0) class Solution: def findMaxConsecutiveOnes(self, nums: List[int]) -\u003e int: zero_count = 0 res = 0 i = 0 for j in range(len(nums)): if nums[j] == 0: zero_count += 1 while zero_count == 2: if nums[i] == 0: zero_count -= 1 i += 1 res = max(res, j-i+1) return res 这题的变形是1004. 最大连续1的个数 III,基本上一摸一样的写法,就是最多1个0,变成最多k个0. ","date":"2021-08-21","objectID":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/:4:0","tags":["算法","leetcode","滑动窗口"],"title":"Leetcode滑动窗口","uri":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/"},{"categories":["算法"],"content":"感想 和159类似,但是这里想到了其他更新i的方法,结果并不对,变长滑动窗口还是要老老实实用while循环一步步更新i的值直到条件满足,这样不容易出错。 ","date":"2021-08-21","objectID":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/:5:0","tags":["算法","leetcode","滑动窗口"],"title":"Leetcode滑动窗口","uri":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/"},{"categories":["算法"],"content":"3. 无重复字符的最长子串 class Solution: def lengthOfLongestSubstring(self, s: str) -\u003e int: i = 0 res = 0 window = set() for j in range(len(s)): while s[j] in window: window.remove(s[i]) i += 1 window.add(s[j]) res = max(res, j-i+1) return res ","date":"2021-08-21","objectID":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/:6:0","tags":["算法","leetcode","滑动窗口"],"title":"Leetcode滑动窗口","uri":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/"},{"categories":["算法"],"content":"感想 这道题在滑动窗口中出现的频率最高,不过代码其实不太套路化,可以看到前面的所有题目,都是对window 操作之后,再做while。代码上这道题是相反的,但是逻辑上,同样也是先把num[j]放进去,但是因为这里用的是set,所以要在后面放入num[j]。 滑动窗固定 ","date":"2021-08-21","objectID":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/:7:0","tags":["算法","leetcode","滑动窗口"],"title":"Leetcode滑动窗口","uri":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/"},{"categories":["算法"],"content":"643. 子数组最大平均数 I class Solution: def findMaxAverage(self, nums: List[int], k: int) -\u003e float: i = 0 cur_sum = sum(nums[:k]) res = cur_sum for j in range(k, len(nums)): cur_sum -= nums[i] cur_sum += nums[j] res = max(cur_sum, res) i += 1 return res / k ","date":"2021-08-21","objectID":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/:8:0","tags":["算法","leetcode","滑动窗口"],"title":"Leetcode滑动窗口","uri":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/"},{"categories":["算法"],"content":"感想 一道简单题,但是要注意这里滑动窗口一开始就初始化了前k个。 ","date":"2021-08-21","objectID":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/:9:0","tags":["算法","leetcode","滑动窗口"],"title":"Leetcode滑动窗口","uri":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/"},{"categories":["算法"],"content":"1208. 尽可能使字符串相等 class Solution: def equalSubstring(self, s: str, t: str, maxCost: int) -\u003e int: res = 0 i = 0 cur_sum = 0 for j in range(len(s)): cur_sum += abs(ord(s[j]) - ord(t[j])) while cur_sum \u003e maxCost: cur_sum -= abs(ord(s[i]) - ord(t[i])) i += 1 res = max(res, j-i+1) return res ","date":"2021-08-21","objectID":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/:10:0","tags":["算法","leetcode","滑动窗口"],"title":"Leetcode滑动窗口","uri":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/"},{"categories":["算法"],"content":"感想 看到子字符串,要想到可能可以用到滑动窗。如果不知道能用滑动窗,感觉还是挺难写出来的。 ","date":"2021-08-21","objectID":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/:11:0","tags":["算法","leetcode","滑动窗口"],"title":"Leetcode滑动窗口","uri":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/"},{"categories":["算法"],"content":"剑指 Offer 59 - I. 滑动窗口的最大值 典型的固定长度的滑动窗,并且是出题频率最高的。与其说是滑动窗口,其实是单调队列。 class Solution: def maxSlidingWindow(self, nums: List[int], k: int) -\u003e List[int]: res = [] i = 0 window = collections.deque() for j in range(len(nums)): if window and j - window[0] \u003e= k: window.popleft() while window and nums[window[-1]] \u003c= nums[j]: window.pop() window.append(j) if j \u003e= k - 1: res.append(nums[window[0]]) return res ","date":"2021-08-21","objectID":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/:12:0","tags":["算法","leetcode","滑动窗口"],"title":"Leetcode滑动窗口","uri":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/"},{"categories":["算法"],"content":"感想 记得构建的是一个左大右小的队列,答案是队列的最大值。 ","date":"2021-08-21","objectID":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/:13:0","tags":["算法","leetcode","滑动窗口"],"title":"Leetcode滑动窗口","uri":"https://seacj.github.io/posts/leetcode%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/"},{"categories":["算法"],"content":"不从算法层面讨论,而看看python实现上怎么写。 ","date":"2021-05-27","objectID":"https://seacj.github.io/posts/%E6%8E%92%E5%BA%8F/:0:0","tags":["算法","leetcode","python"],"title":"python中自定义排序函数","uri":"https://seacj.github.io/posts/%E6%8E%92%E5%BA%8F/"},{"categories":["算法"],"content":"现有字典 d= {‘a’:24,‘g’:52,‘i’:12,‘k’:33}请按value值进行排序? sorted(d.items(),key=lambda x:x[1]) ","date":"2021-05-27","objectID":"https://seacj.github.io/posts/%E6%8E%92%E5%BA%8F/:1:0","tags":["算法","leetcode","python"],"title":"python中自定义排序函数","uri":"https://seacj.github.io/posts/%E6%8E%92%E5%BA%8F/"},{"categories":["算法"],"content":"前K个高频单词 (规则1下比较相同时,按照规则2排序) 给一非空的单词列表,返回前 k 个出现次数最多的单词。 返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率,按字母顺序排序。 class Solution: def topKFrequent(self, words: List[str], k: int) -\u003e List[str]: count = {} for w in words: if w in count: count[w] += 1 else: count[w] = 1 count = sorted(count.items(), key = lambda kv:( - kv[1], kv[0])) # 排序的关键代码 return [x[0] for x in count[:k]] ","date":"2021-05-27","objectID":"https://seacj.github.io/posts/%E6%8E%92%E5%BA%8F/:2:0","tags":["算法","leetcode","python"],"title":"python中自定义排序函数","uri":"https://seacj.github.io/posts/%E6%8E%92%E5%BA%8F/"},{"categories":["算法"],"content":"函数cmp_to_key 另外如果不是像数字排序或者字符串排序时,应自己定义cmp_to_key,如下: from collections import Counter from functools import cmp_to_key class Solution: def topKFrequent(self, words, k): d = Counter(words) def cmp(x, y): if d[x] \u003e d[y] or (d[x] == d[y] and x \u003c y): return -1 else: return 1 return sorted(d.keys(), key=cmp_to_key(cmp))[:k] 作者:qingfengpython 链接:https://leetcode-cn.com/problems/top-k-frequent-words/solution/692qian-kge-gao-pin-dan-ci-pythonshuang-eqc94/ 还有一道cmp_to_key的题目是179. 最大数。 给定一组非负整数 nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数。 # 输入:nums = [10,2] # 输出:\"210\" class Solution: def largestNumber(self, nums: List[int]) -\u003e str: str_list = list(map(str, nums)) compare = lambda x, y : 1 if x + y \u003c y + x else -1 str_list.sort(key = functools.cmp_to_key(compare)) res = ''.join(str_list) if res[0] == '0': return '0' return res ","date":"2021-05-27","objectID":"https://seacj.github.io/posts/%E6%8E%92%E5%BA%8F/:2:1","tags":["算法","leetcode","python"],"title":"python中自定义排序函数","uri":"https://seacj.github.io/posts/%E6%8E%92%E5%BA%8F/"},{"categories":["算法"],"content":"重写堆排序规则 题目是前k个,所以可以用堆来优化。 import heapq, collections class Word: def __init__(self, word, fre): self.word = word self.fre = fre def __lt__(self, other): if self.fre != other.fre: return self.fre \u003c other.fre return self.word \u003e other.word class Solution: def topKFrequent(self, words: List[str], k: int) -\u003e List[str]: cnt = collections.Counter(words) heap = [] for word, fre in cnt.items(): heapq.heappush(heap, Word(word, fre)) if len(heap) \u003e k: heapq.heappop(heap) heap.sort(reverse=True) return [x.word for x in heap] 作者:luo-bi-da-quan 链接:https://leetcode-cn.com/problems/top-k-frequent-words/solution/pythonyou-xian-dui-lie-by-luo-bi-da-quan-w8me/ ","date":"2021-05-27","objectID":"https://seacj.github.io/posts/%E6%8E%92%E5%BA%8F/:2:2","tags":["算法","leetcode","python"],"title":"python中自定义排序函数","uri":"https://seacj.github.io/posts/%E6%8E%92%E5%BA%8F/"},{"categories":["算法"],"content":"排序链表 ref. https://leetcode-cn.com/problems/sort-list/solution/pai-xu-lian-biao-by-leetcode-solution/ ","date":"2021-05-27","objectID":"https://seacj.github.io/posts/%E6%8E%92%E5%BA%8F/:2:3","tags":["算法","leetcode","python"],"title":"python中自定义排序函数","uri":"https://seacj.github.io/posts/%E6%8E%92%E5%BA%8F/"},{"categories":["编程-ღ-技术"],"content":"LMDB数据库 感觉最近训练模型的时候,GPU利用率经常是间断出现0%,试了很多方法比如dataloader的多个worker,prefetch,感觉速度没达到预期,而且依然会出现0%的情况,所以使用LMDB试试能不能提升效率。 最后的效果如下: 比直接一个个文件读取快了至少3倍,并且GPU利用率再也没出现0%的情况(看到有issue说读取多个大文件的时候可能没有提升,所以主要应用场景应该是大量读取小文件,我这里的文件大小基本上每个在50到400KB)。 Step 1. 统计数据大小 使用du -sh命令统计当前文件夹的总大小,统计结果有343G,将344(343+1)转化成B,有2954937499648B。 Step 2. LMDB数据的添加、修改、删除 # -*- coding: utf-8 -*- import lmdb # map_size定义最大储存容量,单位是B,以下定义344G容量 env = lmdb.open(\"./train\", map_size=2954937499648) txn = env.begin(write=True) # 添加数据和键值 txn.put(key='1'.encode(), value=np.array([1,2,3])) txn.put(key='2'.encode(), value=np.array([1,2,3])) txn.put(key='3'.encode(), value=np.array([1,2,3])) # 通过键值删除数据 txn.delete(key='1'.encode()) # 修改数据 txn.put(key='3'.encode(), value='ddd'.encode()) # 通过commit()函数提交更改 txn.commit() env.close() 此时会创建目录,里面有2个文件data.mdb和lock.mdb. 其中map_size是指定能够存放的最大的大小,当数据超过map_size时,会报lmdb.MapFullError: mdb_put: MDB_MAP_FULL: Environment mapsize limit reached。 Step 3. LMDB数据的查询 #查询 env = lmdb.Environment('./train') #env = lmdb.open(\"./train\") txn = env.begin() #write=False # get函数通过键值查询数据 print('打印2的值:',txn.get('2'.encode())) print('打印3的值:',txn.get('3'.encode())) # 通过cursor()遍历所有数据和键值 for key, value in txn.cursor(): print (key, value) print('样本数量:', txn.stat()['entries']) #读取LMDB文件的样本数量 # close env.close() 输出: 打印2的值: b'\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x00\\x00' 打印3的值: b'33333' b'2' b'\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x00\\x00' b'3' b'33333' 样本数量: 2 可以看到保存的格式都是byte,对于numpy可以用np.frombuffer(image_bin, dtype=int),需要注意dtype类型和保存的numpy类型有关。 参考资料 PyTorch 60.读写LMDB数据库 https://zhuanlan.zhihu.com/p/266136706 python之lmdb https://blog.csdn.net/zhayushui/article/details/103896519 ","date":"2021-04-28","objectID":"https://seacj.github.io/posts/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E7%94%A8lmdb%E7%BB%99%E6%95%B0%E6%8D%AE%E8%AF%BB%E5%8F%96%E5%8A%A0%E9%80%9F/:0:0","tags":["机器学习"],"title":"机器学习用lmdb给数据读取加速","uri":"https://seacj.github.io/posts/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E7%94%A8lmdb%E7%BB%99%E6%95%B0%E6%8D%AE%E8%AF%BB%E5%8F%96%E5%8A%A0%E9%80%9F/"},{"categories":["算法"],"content":"俄罗斯套娃题题目 给定一些标记了宽度和高度的信封,宽度和高度以整数对形式 (w, h) 出现。当另一个信封的宽度和高度都比这个信封大的时候,这个信封就可以放进另一个信封里,如同俄罗斯套娃一样。 请计算最多能有多少个信封能组成一组“俄罗斯套娃”信封(即可以把一个信封放到另一个信封里面)。 说明: 不允许旋转信封。 示例: 输入: envelopes = [[5,4],[6,4],[6,7],[2,3]] 输出: 3 解释: 最多信封的个数为 3, 组合为: [2,3] =\u003e [5,4] =\u003e [6,7]。 ","date":"2021-03-04","objectID":"https://seacj.github.io/posts/leetcode354%E4%BF%84%E7%BD%97%E6%96%AF%E5%A5%97%E5%A8%83/:1:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode354俄罗斯套娃","uri":"https://seacj.github.io/posts/leetcode354%E4%BF%84%E7%BD%97%E6%96%AF%E5%A5%97%E5%A8%83/"},{"categories":["算法"],"content":"最长递增子序列题目 俄罗斯套娃信封是道困难题,其前置题目是300. 最长递增子序列,所以先以这个前置题目入手,其关键是知道状态转移方程。 问题描述: 给定一个无序的整数数组,找到其中最长上升子序列(LIS)的长度。 示例: 输入: [10,9,2,5,3,7,101,18] 输出: 4 解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。 ","date":"2021-03-04","objectID":"https://seacj.github.io/posts/leetcode354%E4%BF%84%E7%BD%97%E6%96%AF%E5%A5%97%E5%A8%83/:2:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode354俄罗斯套娃","uri":"https://seacj.github.io/posts/leetcode354%E4%BF%84%E7%BD%97%E6%96%AF%E5%A5%97%E5%A8%83/"},{"categories":["算法"],"content":"最长递增子序列题解1 点击查看题解 问题的关键是状态转移方程为 : dp[i]=max(dp[j])+1,其中0≤j\u003ci且num[j]\u003cnum[i]。 需要注意的是这里的状态dp[j]指的是以第j个数字结尾的子序列(注意是子序列以num[i]结尾)的最长的长度。 根据状态转移方程,代码就出来了: class Solution: def lengthOfLIS(self, nums: List[int]) -\u003e int: if not nums: return 0 dp = [1] * len(nums) # 最小是1,也就是子串只包含自己 res = 0 for i in range(len(nums)): for j in range(i): if nums[i] \u003e nums[j]: dp[i] = max(dp[i], dp[j] + 1) return max(dp) 时间复杂度是$O(n^2)$, 空间复杂度是O(n). (有复杂度更低的解法,但是先回到俄罗斯套娃题) ","date":"2021-03-04","objectID":"https://seacj.github.io/posts/leetcode354%E4%BF%84%E7%BD%97%E6%96%AF%E5%A5%97%E5%A8%83/:3:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode354俄罗斯套娃","uri":"https://seacj.github.io/posts/leetcode354%E4%BF%84%E7%BD%97%E6%96%AF%E5%A5%97%E5%A8%83/"},{"categories":["算法"],"content":"俄罗斯套娃题题解1 找到同时满足envelopes[0]递增、envelopes[1]也递增的序列。 首先第一步是排序,将信封的envelopes[0]小到大排序, 比如有[6,7], [7,9], [8,8], [9,9]. 由于envelopes[0]递增(这里先不考虑envelopes[0]相等的情况),只需要考虑envelopes[1]的最长递增子序列就可以了。 但是如果出现了envelopes[0]相等的情况,比如[9, 9], [1, 2], [1, 3], [1, 1], [1, 1]怎么处理呢。 其实很简单,就是再对envelopes[1]做降序处理,变成[9, 9], [1, 3], [1, 2], [1, 1], [1, 1],也就是envelops[0]相等的时候,envelops[1]一定递减,使得后一个信封一定不会套住前一个信封,否则[1, 2], [1, 3]由于3大于2,将会算出[1, 3]信封套进[1, 2]。 现在来模拟对envelopes[1] ``[9,3,2,1(a),1(b)]最长递增子序列过程: dp[0] 9 1 dp[1] 9, 3 2 dp[2] 9, 2 2 dp[3] 9, 1(a) 2 dp[4] 9, 1(a) 因为envelopes[4][1] \u003e envelops[3][1]不成立 2 class Solution: def maxEnvelopes(self, envelopes: List[List[int]]) -\u003e int: if not envelopes: return 0 envelopes.sort(key= lambda x: (x[0], -x[1])) dp = [1] * len(envelopes) for i in range(len(envelopes)): for j in range(i): if envelopes[i][1] \u003e envelopes[j][1]: dp[i] = max(dp[i], dp[j] + 1) return max(dp) 时间复杂度是$O(n^2)$, 空间复杂度是O(n). ","date":"2021-03-04","objectID":"https://seacj.github.io/posts/leetcode354%E4%BF%84%E7%BD%97%E6%96%AF%E5%A5%97%E5%A8%83/:4:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode354俄罗斯套娃","uri":"https://seacj.github.io/posts/leetcode354%E4%BF%84%E7%BD%97%E6%96%AF%E5%A5%97%E5%A8%83/"},{"categories":["算法"],"content":"最长递增子序列题解2 点击查看题解 上面的题解链接有图解。这里的关键是dp[i]用来存放长度为i的子序列末尾的最小的数字。 e..g. [10, 9, 2, 5, 3, 7, 21, 18] nums 10 9 2 5 3 7 21 18 tails 10 0 0 0 0 0 0 0 tails 9 0 0 0 0 0 0 0 tails 2 0 0 0 0 0 0 0 tails 2 5 0 0 0 0 0 0 tails 2 3 0 0 0 0 0 0 tails 2 3 7 0 0 0 0 0 tails 2 3 7 21 0 0 0 0 tails 2 3 7 18 0 0 0 0 # Dynamic programming + Dichotomy. class Solution: def lengthOfLIS(self, nums: [int]) -\u003e int: tails, res = [0] * len(nums), 0 for num in nums: i, j = 0, res while i \u003c j: m = (i + j) // 2 if tails[m] \u003c num: i = m + 1 # 如果要求非严格递增,将此行 '\u003c' 改为 '\u003c=' 即可。 else: j = m tails[i] = num if j == res: res += 1 return res ","date":"2021-03-04","objectID":"https://seacj.github.io/posts/leetcode354%E4%BF%84%E7%BD%97%E6%96%AF%E5%A5%97%E5%A8%83/:5:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode354俄罗斯套娃","uri":"https://seacj.github.io/posts/leetcode354%E4%BF%84%E7%BD%97%E6%96%AF%E5%A5%97%E5%A8%83/"},{"categories":["算法"],"content":"俄罗斯套娃题题解2 class Solution: def maxEnvelopes(self, envelopes: List[List[int]]) -\u003e int: if not envelopes: return 0 envelopes.sort(key=lambda x:(x[0], -x[1])) res = 0 tails = [None] * len(envelopes) for x, y in envelopes: i, j = 0, res while i \u003c j: mid = i + (j-i) // 2 if tails[mid] \u003c y: i = mid + 1 else: j = mid tails[j] = y if j == res: res += 1 return res ","date":"2021-03-04","objectID":"https://seacj.github.io/posts/leetcode354%E4%BF%84%E7%BD%97%E6%96%AF%E5%A5%97%E5%A8%83/:6:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode354俄罗斯套娃","uri":"https://seacj.github.io/posts/leetcode354%E4%BF%84%E7%BD%97%E6%96%AF%E5%A5%97%E5%A8%83/"},{"categories":["随笔-ღ-心得"],"content":"归根结底,之所以熬夜,只是不再期待明天而已 但是现在,我想明天早上,去买一份肠粉当作早餐,如果有豆浆或者绿豆沙,我要买一杯 现在天气很好,我要出门,买两斤15块一斤的油炸鸡锁骨了 ","date":"2021-02-03","objectID":"https://seacj.github.io/posts/%E9%9A%8F%E7%AC%9420210203/:0:0","tags":["随笔"],"title":"随笔20210203","uri":"https://seacj.github.io/posts/%E9%9A%8F%E7%AC%9420210203/"},{"categories":["算法"],"content":"题记 一年半之前写了一篇回溯算法的博客, 不过只是简单的了解了一下,真要写题还是写不出来(过了一年半,不会写的题还是不会写),所以再集中刷一下题. 回溯法通常应用在树形问题上. 入门题 先看一到题,用来复习回溯算法的写法,和所谓树形问题的特点. ","date":"2020-12-10","objectID":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E9%A2%98/:1:0","tags":["算法","leetcode"],"title":"回溯算法题","uri":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E9%A2%98/"},{"categories":["算法"],"content":"17. 电话号码的字母组合 s( digits[0…n-1] ) = letter(digits[0]) + s( digits[1…n-1] ) = letter(digits[0]) + letter(digits[1]) + s( digits[2…n-1] ) class Solution: def letterCombinations(self, digits: str) -\u003e List[str]: if digits == \"\": return list() # 如果不做这个判断,结果会返回错误答案[\"\"] phoneMap = { \"2\": \"abc\", \"3\": \"def\", \"4\": \"ghi\", \"5\": \"jkl\", \"6\": \"mno\", \"7\": \"pqrs\", \"8\": \"tuv\", \"9\": \"wxyz\", } res = [] def backtrack(index, s): if index == len(digits): res.append(s) return for d in phoneMap[digits[index]]: backtrack(index + 1, s + d) # 如果s用list表示,上面一句的代码应分成下面的三行 # s.append(d) # backtrack(index + 1, s) # s.pop() backtrack(0, \"\") return res 排列问题 ","date":"2020-12-10","objectID":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E9%A2%98/:2:0","tags":["算法","leetcode"],"title":"回溯算法题","uri":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E9%A2%98/"},{"categories":["算法"],"content":"46. 全排列 Perms(nums[0…n-1]) = {取出一个数字} + Perms(nums[{0…n-1} - 取出的数字]). 与前面的17题的区别是,这里一旦取出了一个数字,后面就不能再取. class Solution: def permute(self, nums: List[int]) -\u003e List[List[int]]: if not nums: return [] # used = [False] * len(nums) res = [] def backtrack(index, p): if index == len(nums): res.append(p[:]) # 在python中需要复制一次p,否则后续p改变了,会影响res中已经压入的p return for i, n in enumerate(nums): if n in p: # 判断是否这个数字已经使用,也可以通过一个bool的数组实现,从而用空间换时间 continue #if used[i]: # continue p.append(n) # used[i] = True backtrack(index+1, p) p.pop() # used[i] = False backtrack(0, []) return res ","date":"2020-12-10","objectID":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E9%A2%98/:3:0","tags":["算法","leetcode"],"title":"回溯算法题","uri":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E9%A2%98/"},{"categories":["算法"],"content":"47. 全排列 II 与上题的区别是,数组会有相同的元素,如输入[1,1,2],输出为[1,1,2],[1,2,1],[2,1,1]. class Solution: def permuteUnique(self, nums: List[int]) -\u003e List[List[int]]: if not nums: return [] nums.sort() # 先排序 res = [] used = [False] * len(nums) def backtrack(index, p): if index == len(nums): res.append(p[:]) return for i, n in enumerate(nums): if used[i]: continue if i \u003e 0 and n == nums[i-1] and not used[i-1]: # not used[i-1]比较难想到 continue p.append(n) used[i] = True backtrack(index+1, p) p.pop() used[i] = False backtrack(0, []) return res 组合问题 ","date":"2020-12-10","objectID":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E9%A2%98/:4:0","tags":["算法","leetcode"],"title":"回溯算法题","uri":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E9%A2%98/"},{"categories":["算法"],"content":"77. 组合 输入n=4,k=2; 输出[[1,2], [1,3], [1,4], [2,3], [2,4], [3,4]], 注意[1,2]和[2,1]是一个组合. class Solution: def combine(self, n: int, k: int) -\u003e List[List[int]]: if n\u003c=0 or k\u003c=0: return [] res = [] def backtrack(index, p): if len(p) == k: res.append(p[:]) return for i in range(index, n+1): p.append(i) backtrack(i+1, p) p.pop() backtrack(1, []) return res 回溯法解决组合问题的优化——剪枝 还是上面的77题,如图示我们发现, 实际上只需要取1,2,3就可以了,因为取4的话剩下的数的数量不够,所以可以跳过4.也就是可以剪枝. 假设我们最终要遍历到索引x的位置, 即i会便利[start,… x] (注意两边都是闭区间, 写到代码range函数时,里面的参数为x+1). 当前我们已经寻找到 len(p) 个数字, 还需要再寻找 k - len(p) 个数, 所以有 x 至少要大于等于 n - (k-len(p)) + 1. 比如, 之前的图示, 还要找2(k-len(p) == 2 - 0)个数, i需要遍历到取3的位置,也就是 4 - 2 + 1. class Solution: def combine(self, n: int, k: int) -\u003e List[List[int]]: if n\u003c=0 or k\u003c=0: return [] res = [] def backtrack(start, p): if len(p) == k: res.append(p[:]) return for i in range(start, n - (k - len(p)) + 1 + 1): # 剪枝,不需要遍历到i==n的地方 p.append(i) backtrack(i+1, p) p.pop() backtrack(1, []) return res 组合题: Combination Sum Combination Sum2 Combination Sum III Subsets Subsets II Binary Watch 二维平面上使用回溯法 ","date":"2020-12-10","objectID":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E9%A2%98/:5:0","tags":["算法","leetcode"],"title":"回溯算法题","uri":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E9%A2%98/"},{"categories":["算法"],"content":"79. Word Search board = [ [‘A’,‘B’,‘C’,‘E’], [‘S’,‘F’,‘C’,‘S’], [‘A’,‘D’,‘E’,‘E’] ] 给定 word = “ABCCED”, 返回 true 给定 word = “SEE”, 返回 true 给定 word = “ABCB”, 返回 false class Solution: def exist(self, board: List[List[str]], word: str) -\u003e bool: m, n = len(board), len(board[0]) if len(word) == 0: return True used = [[False]*n for _ in range(m)] def searchWord(index, startx, starty): if index == len(word)-1 and word[index] == board[startx][starty]: return True if word[index] == board[startx][starty]: used[startx][starty] = True # 四个方向可以用一个数组[[-1,0], [0,1], [1,0], [0,-1]]和一个for循环的方式替换下面的代码 # 是否超过边界,可以定义一个函数inArea(x,y) # 上 if startx \u003e= 1 and not used[startx-1][starty]: if searchWord(index+1, startx-1, starty): return True # 下 if startx \u003c m - 1 and not used[startx+1][starty]: if searchWord(index+1, startx+1, starty): return True # 左 if starty \u003e= 1 and not used[startx][starty-1]: if searchWord(index+1, startx, starty-1): return True # 右 if starty \u003c n - 1 and not used[startx][starty+1]: if searchWord(index+1, startx, starty+1): return True used[startx][starty] = False return False for i in range(m): for j in range(n): if searchWord(0, i, j): return True return False 二维平面中的经典算法——floodfill算法 这个算法的本质就是深度优先遍历. ","date":"2020-12-10","objectID":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E9%A2%98/:6:0","tags":["算法","leetcode"],"title":"回溯算法题","uri":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E9%A2%98/"},{"categories":["算法"],"content":"200. Number of Islands 0代表水,1代表陆地,求陆地组成的岛屿的数量. 输入:grid = [ [“1”,“1”,“0”,“0”,“0”], [“1”,“1”,“0”,“0”,“0”], [“0”,“0”,“1”,“0”,“0”], [“0”,“0”,“0”,“1”,“1”] ] class Solution: def numIslands(self, grid: List[List[str]]) -\u003e int: m, n = len(grid), len(grid[0]) if n == 0: return 0 d = [[0,1], [1,0], [-1,0], [0,-1]] res = 0 visited = [[False] * n for _ in range(m)] def isArea(x, y): return x \u003e= 0 and y \u003e=0 and x \u003c m and y \u003c n # 从grid[x][y]的位置开始进行floodfill def __dfs(x, y): visited[x][y] = True # 由于我们需要把和岛屿相连接的所有岛屿标记成True,而没有反过来标记成False的过程,所以有些地方不把这个归为回溯,不过这个方法可以很明确的叫做floodfill # 向四个方向进行搜索遍历 for i in range(4): new_x = x + d[i][0] new_y = y + d[i][1] # 由于递归保证x,y合法, 且grid[x][y]是没有访问过的陆地,所以实际上递归的终止条件隐含在这里了,终止条件就是走到一个地方四个方向都是非法的 if isArea(new_x, new_y) and not visited[new_x][new_y] and grid[new_x][new_y] == '1': __dfs(new_x, new_y) for i in range(m): for j in range(n): if not visited[i][j] and grid[i][j] == '1': res += 1 __dfs(i,j) return res ","date":"2020-12-10","objectID":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E9%A2%98/:7:0","tags":["算法","leetcode"],"title":"回溯算法题","uri":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E9%A2%98/"},{"categories":["算法"],"content":"130 Surrounded Regions ","date":"2020-12-10","objectID":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E9%A2%98/:8:0","tags":["算法","leetcode"],"title":"回溯算法题","uri":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E9%A2%98/"},{"categories":["算法"],"content":"417 Pacific Atlantic Water Flow 找到同时能够流向太平洋和大西洋的地方,如下图括号处的结果. 回溯法是经典人工智能的基础 因为经典(传统)人工智能很多是基于搜索的,所以经常使用回溯. ","date":"2020-12-10","objectID":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E9%A2%98/:9:0","tags":["算法","leetcode"],"title":"回溯算法题","uri":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E9%A2%98/"},{"categories":["算法"],"content":"51 N-Queens n皇后问题研究的是如何将 n个皇后放置在n×n的棋盘上,并且使皇后彼此之间不能相互攻击, 即竖线横线2条斜线4条线不同时出现2个或以上皇后。 输入:4 输出:[ [\".Q..\", // 解法 1 “…Q”, “Q…\", “..Q.\"], [\"..Q.\", // 解法 2 “Q…\", “…Q”, “.Q..\"] ] class Solution: def solveNQueens(self, n: int) -\u003e List[List[str]]: if n \u003c= 0: return [] res = [] col = [False] * n # 用于判断列是否冲突 dia1 = [False] * (2*n-1) dia2 = [False] * (2*n-1) def gernerateBoard(row): # [1,3,0,2] -\u003e # [\".Q..\", // 解法 1 # \"...Q\", # \"Q...\", # \"..Q.\"] assert len(row) == n board = [] for x in row: tmp = [\".\"]*n tmp[x] = 'Q' board.append(''.join(tmp)) return board # 尝试在n皇后问题中,摆放第index行的皇后,将摆放结果放在row中 def putQueen(index, row): if index == n: res.append(gernerateBoard(row)) return # 能否将第index行的皇后放在第i列 for i in range(n): # 如果纵方向和对角线方向都不冲突就可以放 if not col[i] and not dia1[index + i] and not dia2[index-i+n-1]: row.append(i) col[i] = True dia1[index + i] = True dia2[index-i+n-1] = True putQueen(index+1, row) col[i] = False dia1[index + i] = False dia2[index-i+n-1] = False row.pop() putQueen(0, []) return res 52 N-Queens 37 Sudoku Solver 参考资料 慕课的一个付费算法课程 https://coding.imooc.com/learn/list/82.html ","date":"2020-12-10","objectID":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E9%A2%98/:10:0","tags":["算法","leetcode"],"title":"回溯算法题","uri":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E9%A2%98/"},{"categories":["算法"],"content":"192. 统计词频 写一个 bash 脚本以统计一个文本文件 words.txt 中每个单词出现的频率。 cat words.txt | xargs -n 1 | sort | uniq -c | sort -nr | awk '{print $2, $1}' xargs 分割字符串 -n 1表示每行输出一个 可以加-d指定分割符 要使用uniq统计词频需要被统计文本相同字符前后在一起,所以先排序 uniq -c 表示同时输出出现次数 sort -nr 其中-n表示把数字当做真正的数字处理(当数字被当做字符串处理,会出现11比2小的情况) words.txt: the day is sunny the the the sunny is is a a a a a a a a a a a a a a b b b b 结果: a 14 the 4 b 4 is 3 sunny 2 day 1 193. 有效电话号码 给定一个包含电话号码列表(一行一个电话号码)的文本文件 file.txt,写一个 bash 脚本输出所有有效的电话号码。 你可以假设一个有效的电话号码必须满足以下两种格式: (xxx) xxx-xxxx 或 xxx-xxx-xxxx。(x 表示一个数字) 你也可以假设每行前后没有多余的空格字符。 awk '/^([0-9]{3}-|\\([0-9]{3}\\) )[0-9]{3}-[0-9]{4}$/' file.txt ","date":"2020-11-27","objectID":"https://seacj.github.io/posts/%E5%8A%9B%E6%89%A3shell%E9%A2%98/:0:0","tags":["“leetcode\"","linux"],"title":"力扣shell题","uri":"https://seacj.github.io/posts/%E5%8A%9B%E6%89%A3shell%E9%A2%98/"},{"categories":["算法"],"content":"题记 看到一个说法,面试考数组、字符串题的频率最高,所以来刷一下这类题。这次根据leetocde提供的教程的顺序来学习。 数组简介 ","date":"2020-11-02","objectID":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/:0:0","tags":["算法","leetcode"],"title":"Leetcode数组和字符串","uri":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/"},{"categories":["算法"],"content":"寻找数组的中心索引 定义数组 中心索引 的:数组中心索引的左侧所有元素相加的和等于右侧所有元素相加的和,若没有中心索引返回-1。 输入:nums = [1, 7, 3, 6, 5, 6] 输出:3 解释:索引 3 (nums[3] = 6) 的左侧数之和 (1 + 7 + 3 = 11),与右侧数之和 (5 + 6 = 11) 相等。同时, 3 也是第一个符合要求的中心索引。 class Solution(object): def pivotIndex(self, nums): S = sum(nums) leftsum = 0 for i, x in enumerate(nums): if leftsum == (S - leftsum - x): return i leftsum += x return -1 要点是找到左右节点之和的关系:leftsum==S-nums[i]-leftsum ","date":"2020-11-02","objectID":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/:1:0","tags":["算法","leetcode"],"title":"Leetcode数组和字符串","uri":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/"},{"categories":["算法"],"content":"搜索插入位置 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 你可以假设数组中无重复元素。 输入: [1,3,5,6], 5 输出: 2 一开始用一个for循环顺序遍历实现了O(n)的代码,但是这种排序数组寻找目标值首先应该想到O(logN)的二分法。 二分法找第一个 def lower_bound(array, first, last, value): while first \u003c last: # 搜索区间[first, last)不为空 mid = first + (last - first)//2 # 防溢出 if array[mid] \u003c value: first = mid + 1 else: last = mid return first # last也行,因为此时重合 class Solution: def searchInsert(self, nums: List[int], target: int) -\u003e int: first, last =0, len(nums) while first \u003c last: # 搜索区间[first, last)不为空 mid = first + (last - first)//2 # 防溢出 if nums[mid] \u003c target: first = mid + 1 else: last = mid return first # last也行,因为此时重合 二维数组 ","date":"2020-11-02","objectID":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/:2:0","tags":["算法","leetcode"],"title":"Leetcode数组和字符串","uri":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/"},{"categories":["算法"],"content":"旋转矩阵 给你一幅由 N × N 矩阵表示的图像,其中每个像素的大小为 4 字节。请你设计一种算法,将图像旋转 90 度。 不占用额外内存空间能否做到? 一道中等难度题,tag里面只有数组,我觉得应该加上数学的tag,顺带一提这是剑指offer里的题,微软面试也出过。 旋转矩阵就是先上下翻转,再对角线翻转。 1 2 3 | 7 8 9 | 7 4 1 4 5 6 | 4 5 6 | 8 5 2 7 8 9 | 1 2 3 | 9 6 3 class Solution: def rotate(self, matrix: List[List[int]]) -\u003e None: n = len(matrix) # 水平翻转 for i in range(n // 2): for j in range(n): matrix[i][j], matrix[n - i - 1][j] = matrix[n - i - 1][j], matrix[i][j] # 主对角线翻转 for i in range(n): for j in range(i): matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j] ","date":"2020-11-02","objectID":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/:3:0","tags":["算法","leetcode"],"title":"Leetcode数组和字符串","uri":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/"},{"categories":["算法"],"content":"零矩阵 编写一种算法,若M × N矩阵中某个元素为0,则将其所在的行与列清零。 一道非常简单的题,一开始用两个set记录实现了,不过教科书式的写法是灵活使用原矩阵的第一行和第一列,即用第一行和第一列代替2个set,并用2个变量记录第一行和第一列的状态。 代码就略了,因为很简单,就是用第一行、第一列记录的思路不容易马上想。 ","date":"2020-11-02","objectID":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/:4:0","tags":["算法","leetcode"],"title":"Leetcode数组和字符串","uri":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/"},{"categories":["算法"],"content":"对角线遍历 给定一个含有 M x N 个元素的矩阵(M 行,N 列),请以对角线遍历的顺序返回这个矩阵中的所有元素,对角线遍历如下图所示。 可以分成2种情况:从左下到右上、从右上到左下,总共遍历m+n-1次。 class Solution: def findDiagonalOrder(self, matrix: List[List[int]]) -\u003e List[int]: if not matrix: return [] m,n = len(matrix), len(matrix[0]) res = [] for ii in range(m+n-1): if ii%2 == 0: # 从左下向右上 if ii \u003c m: # 开始点为最左边(ii,0) i, j = ii, 0 else: # 开始点为最下行 i, j = m - 1, ii - m + 1 while i \u003e= 0 and j \u003c n: res.append(matrix[i][j]) i -= 1 j += 1 else: # 从右上到左下 if ii \u003c n: i, j = 0, ii else: i, j = ii - n + 1, n - 1 while i \u003c m and j \u003e= 0: res.append(matrix[i][j]) i += 1 j -= 1 return res 字符串简介 ","date":"2020-11-02","objectID":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/:5:0","tags":["算法","leetcode"],"title":"Leetcode数组和字符串","uri":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/"},{"categories":["算法"],"content":"最长回文子串 给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。 示例 1: 输入: “babad” 输出: “bab” 注意: “aba” 也是一个有效答案。 示例 2: 输入: “cbbd” 输出: “bb” 这题很早就见过,不过一直放着了,其实也挺简单的,下面是别人的题解。 class Solution: def longestPalindrome(self, s: str) -\u003e str: size = len(s) if size \u003c 2: return s # 至少是 1 max_len = 1 res = s[0] for i in range(size): palindrome_odd, odd_len = self.__center_spread(s, size, i, i) palindrome_even, even_len = self.__center_spread(s, size, i, i + 1) # 当前找到的最长回文子串 cur_max_sub = palindrome_odd if odd_len \u003e= even_len else palindrome_even if len(cur_max_sub) \u003e max_len: max_len = len(cur_max_sub) res = cur_max_sub return res def __center_spread(self, s, size, left, right): \"\"\" left = right 的时候,此时回文中心是一个字符,回文串的长度是奇数 right = left + 1 的时候,此时回文中心是一个空隙,回文串的长度是偶数 \"\"\" i = left j = right while i \u003e= 0 and j \u003c size and s[i] == s[j]: i -= 1 j += 1 return s[i + 1:j], j - i - 1 ","date":"2020-11-02","objectID":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/:6:0","tags":["算法","leetcode"],"title":"Leetcode数组和字符串","uri":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/"},{"categories":["算法"],"content":"(选修)字符串匹配算法:KMP Knuth–Morris–Pratt(KMP)算法是一种改进的字符串匹配算法,它的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。它的时间复杂度是 O(m+n)。KMP看文字贼难搞,看动画就很容易学了,视频教程。 28. 实现 strStr() 这道题被归到简单题,大概因为暴力法真的很容易想到,复杂度是O(m*n)。 class Solution: def strStr(self, haystack: str, needle: str) -\u003e int: for i in range(len(haystack)-len(needle)+1): match = True for j in range(len(needle)): if needle[j] != haystack[i+j]: match = False break if match: return i return -1 不过有时候好像会问用KMP解这道题,貌似一般不要求手撕,但是需要知道KMP算法的思路。 通过视频教程转化成代码深入理解一下: 如下图示,找最长公共前后缀,比如ABBAB的最长公共前后缀是AB。 如下图示(这里下标从1开始),定义主串的当前位置是出现不匹配的位置: 模式串(黄色数组)一号位不匹配时,下一次比较是模式串1号位与主串的下一位比较。 模式串二号位不匹配时,下一次比较是模式串1号位(A没有最长公共前后缀)与主串的当前位比较。 模式串三号位不匹配时,下一次比较是模式串1号位(AB没有最长公共前后缀)与主串的当前位比较。 模式串四号位不匹配时,下一次比较是模式串2号位(ABA最长公共前后缀是A)与主串的当前位比较。 模式串五号位不匹配时,下一次比较是模式串3号位(ABAB最长公共前后缀是AB)与主串的当前位比较。 模式串六号位不匹配时,下一次比较是模式串4号位(ABABA最长公共前后缀是ABA)与主串的当前位比较。(即下图所示) 模式串n号位不匹配时,下一次比较是模式串前n-1数组的最长公共前后缀中,公共前缀结束的后一位(等于最大公共前后缀长度+1)与主串的当前位比较。 我自己再举个例子: 主串: ABABABC 模式串:ABABC 第一次匹配匹配到主串当前位置A的时候,出现不匹配: A B A B A B C A B A B C 移动模式串,公共前后缀一定匹配(红色)所以无需再比,直接从主串当前位置和5号位(与视频保持一致,从1开始索引)的next位置(蓝色的A)开始对比: A B A B A B C A B A B C int match (char* P, char* S){ // KMP 算法 int* next = buildNext(P); // 构造 next 表 int m = (int) strlen (S), i = 0; // 文本串指针 int n = (int) strlen(P), j = 0; //模式串指针 while (j \u003c n \u0026\u0026 i \u003c m) // 自左向右逐个比对字符 if (0 \u003e j || S[i] == P[j]) // 若匹配,或 P 已移除最左侧 {i++; j++;} // 则转到下一字符 else j = next[j]; // 模式串右移(注意:文本串不用回退) delete [] next; // 释放 next 表 return i - j; } int* buildNext(char* P) { // 构造模式串 P 的 next 表 size_t m = strlen(P), j = 0; // “主”串指针 int* N = new int[m]; // next 表 int t = N[0] = -1; // 模式串指针 while (j \u003c m - 1) if ( 0 \u003e t || P[j] == P[t]){ // 匹配 j++; t++; N[j] = t; // 此句可改进为 N[j] = (P[j] != P[t] ? t : N[t]); }else // 失配 t = N[t]; return N; } 双指针技巧 1. 两个指针分别指向数组的开头及末尾 ","date":"2020-11-02","objectID":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/:7:0","tags":["算法","leetcode"],"title":"Leetcode数组和字符串","uri":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/"},{"categories":["算法"],"content":"两数之和 II - 输入有序数组 两数之和很容易想到$O(n^2)$的暴力法,还有利用哈希表的空间和时间复杂度O(n)的方法,但是如果输入有序,可以用双指针实现空间复杂度O(1),时间复杂度O(n)的方法。 class Solution: def twoSum(self, numbers: List[int], target: int) -\u003e List[int]: i, j = 0, len(numbers) - 1 while i\u003cj: if numbers[i] + numbers[j] == target: return [i+1, j+1] elif numbers[i] + numbers[j] \u003c target: i+=1 else: j-=1 return [-1, -1] 双指针,如果和小,左指针移动;如果和大,右指针移动。 class Solution: def twoSum(self, numbers: List[int], target: int) -\u003e List[int]: low, high = 0, len(numbers) - 1 while low \u003c high: total = numbers[low] + numbers[high] if total == target: return [low + 1, high + 1] elif total \u003c target: low += 1 else: high -= 1 return [-1, -1] 2. 快慢指针 ","date":"2020-11-02","objectID":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/:8:0","tags":["算法","leetcode"],"title":"Leetcode数组和字符串","uri":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/"},{"categories":["算法"],"content":"移除元素 给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。元素的顺序可以改变。 一开始很快就用pop函数实现了,但是发现好像复杂度是$O(n^2)$,极端情况是所有元素都pop一遍,但是pop一次的时间复杂度是O(n),感觉还是少用pop解题的好,虽然很好想到,而且其实在leetcode里面这个方法用python比双指针击败的要多。 一开始想到的方法: class Solution: def removeElement(self, nums: List[int], val: int) -\u003e int: i = 0 cur_len = len(nums) while i \u003c cur_len: if nums[i]!=val: i+=1 else: nums.pop(i) cur_len-=1 return cur_len 快慢指针,如果快指针指向的不是移除元素,快慢指针都向前进;如果快指针指向需要移除的元素,快指针先走一步,当快指针走到不需要移除的元素的时候,复制快指针指向的值给慢指针,会覆盖掉需要移除的元素。 class Solution: def removeElement(self, nums: List[int], val: int) -\u003e int: a = 0 b = 0 while a \u003c len(nums): if nums[a] != val: nums[b] = nums[a] b += 1 a += 1 return b 再优化,这里题目有一句不要求顺序一致,实际上上面实现的还是顺序一致的,当出现num=[4,1,2,3,5] val=4时,1,2,3,5会向左复制一遍,实际上不需要,可以换一次,即[5, 1,2,3,4],然后数组长度减一,就不会再遍历到最后一个4了,但是也有可能交换到的还是4(即5的位置是4的情况),这时候再判断一次就可以了。 时间复杂度是O(n),交换位置的次数等于要删除元素的个数,所以如果要删除的元素数量少的话,效率就高。 class Solution: def removeElement(self, nums: List[int], val: int) -\u003e int: cur_len = len(nums) i = 0 while i \u003c cur_len: if nums[i] == val: nums[i], nums[cur_len-1] = nums[cur_len - 1], nums[i] cur_len -= 1 else: i+=1 return cur_len ","date":"2020-11-02","objectID":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/:9:0","tags":["算法","leetcode"],"title":"Leetcode数组和字符串","uri":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/"},{"categories":["算法"],"content":"最大连续1的个数 给定一个二进制数组, 计算其中最大连续1的个数。 示例 1: 输入: [1,1,0,1,1,1] 输出: 3 解释: 开头的两位和最后的三位都是连续1,所以最大连续1的个数是 3. 非常简单的实现了: class Solution: def findMaxConsecutiveOnes(self, nums: List[int]) -\u003e int: res = 0 count = 0 for i in range(len(nums)): if nums[i] == 1: count += 1 res = max(res, count) else: count = 0 return res 万一面试要求双指针的题解,两个指针分别指向1的最前面和最后面,这里用一个小的技巧就是末尾加上一个0: class Solution: def findMaxConsecutiveOnes(self, nums: List[int]) -\u003e int: i = -1 res = 0 nums+=[0] for j in range(len(nums)): if nums[j] == 1: continue else: res = max(res, j - i - 1) i = j # 如果末尾不加零需要额外的判断 # if nums[j] == 1: # res = max(res, j - i) return res ","date":"2020-11-02","objectID":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/:10:0","tags":["算法","leetcode"],"title":"Leetcode数组和字符串","uri":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/"},{"categories":["算法"],"content":"长度最小的子数组 给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。 示例:输入:s = 7, nums = [2,3,1,2,4,3] 输出:2 解释:子数组 [4,3] 是该条件下的长度最小的子数组 一道中等难度的双指针题,画一下图,O(n)复杂度的代码不难想(反而有个O(nlogn)的算法是难想的,这里就不写了): class Solution: def minSubArrayLen(self, s: int, nums: List[int]) -\u003e int: i, j =0, 0 res = len(nums) + 1 tmp_sum = 0 while j \u003c len(nums): tmp_sum += nums[j] while tmp_sum \u003e= s: res = min(j - i + 1, res) tmp_sum -= nums[i] i += 1 j += 1 if res == len(nums) + 1: return 0 else: return res ","date":"2020-11-02","objectID":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/:11:0","tags":["算法","leetcode"],"title":"Leetcode数组和字符串","uri":"https://seacj.github.io/posts/leetcode%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2/"},{"categories":["算法"],"content":"题记 这两天刷了十道链表题,打算做个总结,刚好leetcode也提供了一个系统学习的链接,这里做一个小总结。 双指针技巧 链表题里面双指针、快慢指针还是经常会用的。 ","date":"2020-10-13","objectID":"https://seacj.github.io/posts/leetcode%E9%93%BE%E8%A1%A8/:0:0","tags":["算法","leetcode","链表"],"title":"Leetcode链表题","uri":"https://seacj.github.io/posts/leetcode%E9%93%BE%E8%A1%A8/"},{"categories":["算法"],"content":"141 环形链表 判断是否有环即可。 只需要知道如果有环,快指针和慢指针的距离会原来越远,但是最后快指针快慢指针一圈,从而相遇。否则,快指针会到达None。 class Solution: def hasCycle(self, head: ListNode) -\u003e bool: if not head or not head.next: return False slow = head fast = head.next while slow != fast: if not fast or not fast.next: return False slow = slow.next fast = fast.next.next return True class Solution: def hasCycle(self, head: ListNode) -\u003e bool: if not head or head.next is None: return False fast, slow = head, head while True: if not fast or not fast.next: return False fast = fast.next.next slow = slow.next if slow is fast: return True ","date":"2020-10-13","objectID":"https://seacj.github.io/posts/leetcode%E9%93%BE%E8%A1%A8/:1:0","tags":["算法","leetcode","链表"],"title":"Leetcode链表题","uri":"https://seacj.github.io/posts/leetcode%E9%93%BE%E8%A1%A8/"},{"categories":["算法"],"content":"142 环形链表 II 假设a是环外节点数,b是环内节点数(例如上图a=1,b=3)。 走a+nb步一定会走到环口(如上图从3走一步,就会到环口2处,此后每走一圈都会回到环扣)。 快、慢指针都初始化在head的位置时,第一次slow与fast相遇时,慢指针走了nb步。 证明:a. 快指针的步数f,与慢指针的步数s总有f=2s的关系。 b. 当快慢指针相遇,意味着快指针比慢指针多走了n个环的距离,即f=s+nb。由这两个关系式可得s=nb 慢指针如果再走a步就能到环口,但a未知。 用双指针,让指针p指向head,此时p走a步到环口、慢指针走a步也到环口,也就是慢指针和p指针相遇的未知就是环口。 class Solution: def detectCycle(self, head: ListNode) -\u003e ListNode: fast, slow = head, head while True: if not fast or not fast.next: return None fast = fast.next.next slow = slow.next if fast is slow: # 第一次相遇 break p = head while p is not slow: p = p.next slow = slow.next return p ","date":"2020-10-13","objectID":"https://seacj.github.io/posts/leetcode%E9%93%BE%E8%A1%A8/:2:0","tags":["算法","leetcode","链表"],"title":"Leetcode链表题","uri":"https://seacj.github.io/posts/leetcode%E9%93%BE%E8%A1%A8/"},{"categories":["算法"],"content":"160 相交链表 这题是个简单题,因为用set集合很容易就写出来了。 class Solution: def getIntersectionNode(self, headA: ListNode, headB: ListNode) -\u003e ListNode: s = set() p = headA while p: s.add(p) p = p.next p = headB while p: if p in s: return p else: p = p.next return None 不过有一个空间复杂度O(1)的双指针方法,而且看到有一个评论非常妙——这个算法也浪漫了吧,错的人迟早会走散,而对的人迟早会相逢! class Solution: def getIntersectionNode(self, headA: ListNode, headB: ListNode) -\u003e ListNode: p, q = headA, headB while p!=q: if p is None: p = headB #注意不是回到原本A的起点,而是B的起点 q = q.next elif q is None: q = headA p = p.next else: p = p.next q = q.next return p 找到遇见的地方的方式,是各自都完整走过两个list。 经典问题 ","date":"2020-10-13","objectID":"https://seacj.github.io/posts/leetcode%E9%93%BE%E8%A1%A8/:3:0","tags":["算法","leetcode","链表"],"title":"Leetcode链表题","uri":"https://seacj.github.io/posts/leetcode%E9%93%BE%E8%A1%A8/"},{"categories":["算法"],"content":"206 反转链表 class Solution: def reverseList(self, head: ListNode) -\u003e ListNode: if not head or head.next is None: return head a,b = head, head.next head.next = None while b: c = b.next b.next = a a = b b = c return a ","date":"2020-10-13","objectID":"https://seacj.github.io/posts/leetcode%E9%93%BE%E8%A1%A8/:4:0","tags":["算法","leetcode","链表"],"title":"Leetcode链表题","uri":"https://seacj.github.io/posts/leetcode%E9%93%BE%E8%A1%A8/"},{"categories":["算法"],"content":"203 移除链表元素 删除链表中等于给定值 val 的所有节点。 class Solution: def removeElements(self, head: ListNode, val: int) -\u003e ListNode: p = head while p: if p.val != val: break p = p.next if not p: return None res = p pre = p post = p.next while post: if post.val != val: pre = pre.next else: pre.next = post.next post = post.next return res 用一个哑元节点更优雅一点。 class Solution: def removeElements(self, head: ListNode, val: int) -\u003e ListNode: dummy = ListNode(None) dummy.next = head a, b = dummy, head while b: if b.val == val: a.next = b.next else: a = a.next b = b.next return dummy.next ","date":"2020-10-13","objectID":"https://seacj.github.io/posts/leetcode%E9%93%BE%E8%A1%A8/:5:0","tags":["算法","leetcode","链表"],"title":"Leetcode链表题","uri":"https://seacj.github.io/posts/leetcode%E9%93%BE%E8%A1%A8/"},{"categories":["算法"],"content":"328 奇偶链表 给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。 请尝试使用原地算法完成。 输入: 2-\u003e1-\u003e3-\u003e5-\u003e6-\u003e4-\u003e7-\u003eNULL 输出: 2-\u003e3-\u003e6-\u003e7-\u003e1-\u003e5-\u003e4-\u003eNULL class Solution: def oddEvenList(self, head: ListNode) -\u003e ListNode: if not head or not head.next: return head a, b = head, head.next # a是奇数节点,b是偶数节点 even_start = b # 最后一个奇数节点要指向第一个偶数节点 while b and b.next: a.next = b.next b.next = b.next.next a = a.next # 注意这里的a.next是原本的a.next.next b = b.next a.next = even_start return head ","date":"2020-10-13","objectID":"https://seacj.github.io/posts/leetcode%E9%93%BE%E8%A1%A8/:6:0","tags":["算法","leetcode","链表"],"title":"Leetcode链表题","uri":"https://seacj.github.io/posts/leetcode%E9%93%BE%E8%A1%A8/"},{"categories":["算法"],"content":"234 回文链表 请判断一个链表是否为回文链表。返回True/False。 class Solution: def get_mid(self, head): dummy = ListNode(None) dummy.next = head slow, fast = dummy, dummy while fast and fast.next: slow = slow.next fast = fast.next.next return slow def reverse(self, head): if not head or not head.next: return head a,b = head, head.next head.next = None while b: tmp = b.next b.next = a a = b b = tmp return a def isPalindrome(self, head: ListNode) -\u003e bool: # 1. 找到中心点 (可以用算出链表长度,和快慢指针两个方法) mid = self.get_mid(head) # 2. 反转mid之后的所有节点(不包括mid) mid.next = self.reverse(mid.next) # 3. 判断是否相等 p = head q = mid.next while q: if p.val != q.val: return False else: p = p.next q = q.next return True ","date":"2020-10-13","objectID":"https://seacj.github.io/posts/leetcode%E9%93%BE%E8%A1%A8/:7:0","tags":["算法","leetcode","链表"],"title":"Leetcode链表题","uri":"https://seacj.github.io/posts/leetcode%E9%93%BE%E8%A1%A8/"},{"categories":["算法"],"content":"题记 这一篇是Leetcode动态规划简单题之后的又一个关于动态规划的博客,主要按照leetcode的tag里面写的,可能会不定期更新吧 题目 ","date":"2020-10-06","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E5%87%A0%E9%81%93%E9%A2%98/:0:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划几道题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E5%87%A0%E9%81%93%E9%A2%98/"},{"categories":["算法"],"content":"32 最长有效括号 给定一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长的包含有效括号的子串的长度。 输入: “(()” 输出: 2 解释: 最长有效括号子串为 “()” 注意. “()(())“有效长度是6。 ","date":"2020-10-06","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E5%87%A0%E9%81%93%E9%A2%98/:1:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划几道题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E5%87%A0%E9%81%93%E9%A2%98/"},{"categories":["算法"],"content":"题解 主要是情况有点多,慢慢想都能解出来。 定义状态dp[i]以第i个括号结尾的符号中有效括号串的长度。 第一个大类别是以’(‘均不能组成有效括号串,所以dp为0. 第二个大类有2种情况(粗体字表示s[i-1]):1. ()() 2. a. ()())) b. ()(())) c. ()((())) 第一种情况是dp[i]=dp[i-2]+2,第二种情况(a和c可以合并成一种情况,不过我就按照我第一次写这道题的思路写了): a. 如果dp[i-1]==0, dp[i]=0 b. 如果dp[i-1]!=0, 前面(指s[i- dp[i-1] -1])有可以组成括号的,则dp[i]=dp[i-1]+2 c. 如果dp[i-1]!=0, 前面没有可以组成括号的,则dp[i]=0 class Solution: def longestValidParentheses(self, s: str) -\u003e int: if len(s)\u003c2: return 0 result = 0 dp = [0] * len(s) if s[:2] == '()': dp[1] = 2 result = 2 for i in range(2, len(s)): if s[i] == '(': # 第一个大类别 continue # 第二个大类别 s[i] == ')' if s[i-1] == '(': dp[i] = dp[i-2] + 2 result = max(result, dp[i]) else: if dp[i-1] == 0: dp[i] = 0 else: if i-dp[i-1]-1 \u003e= 0 and s[i-dp[i-1]-1] == '(': dp[i] = dp[i-1] + 2 if i-dp[i-1]-2 \u003e= 0: #有个python答案里面不做这个判决,因为边界dp[-1]为0 dp[i] += dp[i-dp[i-1]-2] result = max(result, dp[i]) else: dp[i] = 0 return result ","date":"2020-10-06","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E5%87%A0%E9%81%93%E9%A2%98/:1:1","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划几道题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E5%87%A0%E9%81%93%E9%A2%98/"},{"categories":["算法"],"content":"72 编辑距离 p.s. 编辑距离算法被数据科学家广泛应用,是用作机器翻译和语音识别评价标准的基本算法。 ","date":"2020-10-06","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E5%87%A0%E9%81%93%E9%A2%98/:2:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划几道题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E5%87%A0%E9%81%93%E9%A2%98/"},{"categories":["算法"],"content":"题解 这个题第一眼看的毫无头绪,直接看别人的题解了。 参考资料:编辑距离面试题详解 class Solution: def minDistance(self, word1: str, word2: str) -\u003e int: m,n = len(word1), len(word2) dp = [[0]*(n+1) for _ in range(m+1)] for i in range(m): dp[i+1][0] = i+1 for j in range(n): dp[0][j+1] = j+1 for i in range(m): for j in range(n): if word1[i] == word2[j]: dp[i+1][j+1] = dp[i][j] else: dp[i+1][j+1] = min(dp[i+1][j], dp[i][j+1], dp[i][j]) + 1 return dp[m][n] ","date":"2020-10-06","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E5%87%A0%E9%81%93%E9%A2%98/:2:1","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划几道题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E5%87%A0%E9%81%93%E9%A2%98/"},{"categories":["算法"],"content":"85 最大矩形 ","date":"2020-10-06","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E5%87%A0%E9%81%93%E9%A2%98/:3:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划几道题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E5%87%A0%E9%81%93%E9%A2%98/"},{"categories":["算法"],"content":"120 三角形最小路径和 ","date":"2020-10-06","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E5%87%A0%E9%81%93%E9%A2%98/:4:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划几道题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E5%87%A0%E9%81%93%E9%A2%98/"},{"categories":["算法"],"content":"题解 我最开始想到的状态转移方程:dp[i][j] = tri[i][j] + min( dp[i-1][j] , dp[i-1][j] ),不过这个是从下往上找到最小路径和,所以结果是tri[m-1][0] ,tri[i][j]实际上是tri[-i-1][j]。 1 dp[2][0] triangle[-3][0] 2 dp[1][0] triangle[-2][0] 3 dp[1][1] triangle[-2][1] 4 dp[0][0] triangle[-1][0] 5 dp[0][1] triangle[-1][1] 6 dp[0][2] triangle[-1][2] class Solution: def minimumTotal(self, triangle: List[List[int]]) -\u003e int: m,n = len(triangle), len(triangle[-1]) # m行n列 dp = [[0]*n for _ in range(m)] for i in range(n): dp[0][i] = triangle[-1][i] for i in range(1,m): for j in range(len(triangle[-i-1])): dp[i][j] = triangle[-i-1][j] + min(dp[i-1][j], dp[i-1][j+1]) return dp[m-1][0] 上面代码的空间复杂度是O(m*n),由于dp只依赖于相邻行,所以很容易优化成O(n),n是底层列数。(实际上行数和列数相等,即m=n,所以也可以说O(m)) class Solution: def minimumTotal(self, triangle: List[List[int]]) -\u003e int: n = len(triangle) dp =[0]*n for i in range(n): dp[i] = triangle[-1][i] for i in range(1,n): for j in range(len(triangle[-i-1])): dp[j] = triangle[-i-1][j] + min(dp[j], dp[j+1]) return dp[0] ","date":"2020-10-06","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E5%87%A0%E9%81%93%E9%A2%98/:4:1","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划几道题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E5%87%A0%E9%81%93%E9%A2%98/"},{"categories":["算法"],"content":"139 单词拆分 给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。 先给我自己的思路,dp[i]表示以第i个字母结尾的子字符串是否能够匹配上。 状态转移方程为:dp[i] = dp[i-匹配的单词长度] \u0026\u0026 第i-匹配的单词长度+1 到 第i个字母 所组成的单词在字典里,这里处理一下边界i-word_size。 例如,假设字典为:is、son、song: i s s o n g i s False True False False True True False True class Solution: def wordBreak(self, s: str, wordDict: List[str]) -\u003e bool: size = len(s) dp = [False] * size for i in range(size): for word in wordDict: word_size = len(word) if s[i-word_size+1: i+1] == word and (dp[i-word_size] is True or i-word_size\u003c0): dp[i] = True return dp[-1] ","date":"2020-10-06","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E5%87%A0%E9%81%93%E9%A2%98/:5:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划几道题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E5%87%A0%E9%81%93%E9%A2%98/"},{"categories":["算法"],"content":"152 乘积最大子数组 给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。 输入: [-2,0,-1] 输出: 0 解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。 dp1为前i个子数组所得到的最大非负数乘积,dp2为前i个子数组所得到的最大非正数乘积。 重点是做一个分类讨论,nums[i]为负数和正数的情况。 class Solution: def maxProduct(self, nums: List[int]) -\u003e int: res = nums[0] dp1 = max(0, nums[0]) dp2 = min(0, nums[0]) for i in range(1, len(nums)): if nums[i] == 0: dp1,dp2 = 0,0 elif nums[i] \u003e 0: dp1 = max(dp1*nums[i], nums[i]) # dp1可能为0,若为0取nums[i] dp2 *= nums[i] # 正数乘正数 else: tmp = dp1 dp1 = dp2 * nums[i] dp2 = min(tmp * nums[i], nums[i]) # print(dp1,dp2) res = max(res, dp1) return res ","date":"2020-10-06","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E5%87%A0%E9%81%93%E9%A2%98/:6:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划几道题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E5%87%A0%E9%81%93%E9%A2%98/"},{"categories":["算法"],"content":"题记 leetcode上刷动态规划有几道股票题,索性就一起刷了。 题目 ","date":"2020-09-30","objectID":"https://seacj.github.io/posts/leetcode%E4%B9%B0%E5%8D%96%E8%82%A1%E7%A5%A8%E9%A2%98/:0:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode买卖股票题","uri":"https://seacj.github.io/posts/leetcode%E4%B9%B0%E5%8D%96%E8%82%A1%E7%A5%A8%E9%A2%98/"},{"categories":["算法"],"content":"121 买卖股票的最佳时机 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。 注意:你不能在买入股票前卖出股票。 只是一道动态规划的简单题,在之前的博客里总结过了。 状态转移方程,可以看到前i天:","date":"2020-09-30","objectID":"https://seacj.github.io/posts/leetcode%E4%B9%B0%E5%8D%96%E8%82%A1%E7%A5%A8%E9%A2%98/:1:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode买卖股票题","uri":"https://seacj.github.io/posts/leetcode%E4%B9%B0%E5%8D%96%E8%82%A1%E7%A5%A8%E9%A2%98/"},{"categories":["算法"],"content":"123 买卖股票的最佳时机 III 与121的区别在于最多可以完成2次交易,就让简单题变成了困难题。 最开始的思路就是遇到降了就卖,最后做一个排序,比如131419,获得收益是2、3、8,排完序之后求最大的两个3、8之和,但是这样解决不了132719,因为这样得到是2、5、8,排序相加结果是13,但是实际上1买7卖收益最大,结果是6+8=14。 没想通,直接看题解了。 定义二维数组 dp[n][4]这的n表示天数,4表示4种不同的状态: dp1[i][0]:第一次买入; dp2[i][1]:第一次卖出; dp3[i][2]:第二次买入; dp4[i][3]:第二次卖出。 第一次买卖: 第一次买入:第一次买入后保持不动, 或者从初始状态转换而来 dp[i][0] = max(dp[i-1][0], -prices[i]) 第一次卖出:第一次卖出后保持不动,或者从第一次买入转换而来 dp[i][1] = max(dp[i-1][1], dp[i-1][0]+prices[i]) 第二次买卖: 第二次买入:第二次买入后保持不动, 或者从第一次卖出转换而来 dp[i][2] = max(dp[i-1][2], dp[i][1] -prices[i]) 第二次卖出:第二次卖出后保持不动,或者从第二次买入转换而来 dp[i][3] = max(dp[i-1][3], dp[i-1][2]+prices[i]) 132719的状态如下表所示,不加粗表示保持不动,加粗表示对当前天进行了买卖操作。 day1 day2 day3 day4 day5 day6 1 3 2 7 1 9 第一次买 -1 -1 -1 -1 -1 -1 第一次卖 0 2 2 6 6 8 第二次买 -1 -1 0 0 5 5 第二次卖 0 2 2 2 2 14 class Solution: def maxProfit(self, prices: List[int]) -\u003e int: if len(prices) \u003c= 1: return 0 days = len(prices) dp = [[0] * 4 for _ in range(days)] dp[0][0] = -prices[0] # 第一天第一次买入 dp[0][2] = -prices[0] # 第一天第二次买入 for i in range(1, days): # 第一次买入卖出 dp[i][0] = max(dp[i-1][0], -prices[i]) dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i]) # 第二次买入卖出 dp[i][2] = max(dp[i-1][2], dp[i-1][1] - prices[i]) dp[i][3] = max(dp[i-1][3], dp[i-1][2] + prices[i]) return max(0, dp[-1][0], dp[-1][3]) 由于第i天只依赖于第i-1天,所以可以优化: class Solution: def maxProfit(self, prices: List[int]) -\u003e int: if len(prices) \u003c= 1: return 0 days = len(prices) dp1 = 0 dp3 = 0 dp0 = -prices[0] # 第一天第一次买入 dp2 = -prices[0] # 第一天第二次买入 for i in range(1, days): # 第一次买入卖出 dp0 = max(dp0, -prices[i]) dp1 = max(dp1, dp0 + prices[i]) # 实际上这一行放在上上行更好理解 # 第二次买入卖出 dp2 = max(dp2, dp1 - prices[i]) dp3 = max(dp3, dp2 + prices[i]) return max(dp1, dp3) ","date":"2020-09-30","objectID":"https://seacj.github.io/posts/leetcode%E4%B9%B0%E5%8D%96%E8%82%A1%E7%A5%A8%E9%A2%98/:2:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode买卖股票题","uri":"https://seacj.github.io/posts/leetcode%E4%B9%B0%E5%8D%96%E8%82%A1%E7%A5%A8%E9%A2%98/"},{"categories":["算法"],"content":"188. 买卖股票的最佳时机 IV 给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。 设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。 输入: [2,4,1], k = 2 输出: 2 解释: 在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。 和123题思路完全一样,但是要加上一个对k值的判别,否则提交之后会报内存错误。 class Solution: def maxProfit(self, k: int, prices: List[int]) -\u003e int: if len(prices) \u003c= 1 or k\u003c=0: return 0 days = len(prices) # 当k非常大时转为无限次交易,否则会造成Memory Error if k \u003e days//2: dp0, dp1 = 0, -prices[0] for i in range(1, days): dp0 = max(dp0, dp1+prices[i]) dp1 = max(dp1, dp0-prices[i]) return max(dp0, dp1) dp = [0] * k*2 for i in range(k*2): if i%2 == 0: dp[i] = - prices[0] for i in range(1, days): dp[1] = max(dp[1], dp[0] + prices[i]) dp[0] = max(dp[0], 0 - prices[i]) # dp[3] = max(dp[3], dp[2] + prices[i]) # dp[2] = max(dp[2], dp[1] - prices[i]) # # dp[4] = max(dp[4], dp[3]+prices[i]) # dp[3] = max(dp[3], dp[2]-prices[i]) for j in range(k-1): dp[2*j+3] = max(dp[2*j+3], dp[2*j+2] + prices[i]) dp[2*j+2] = max(dp[2*j+2], dp[2*j+1] - prices[i]) # print(dp) return max(dp) ","date":"2020-09-30","objectID":"https://seacj.github.io/posts/leetcode%E4%B9%B0%E5%8D%96%E8%82%A1%E7%A5%A8%E9%A2%98/:3:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode买卖股票题","uri":"https://seacj.github.io/posts/leetcode%E4%B9%B0%E5%8D%96%E8%82%A1%E7%A5%A8%E9%A2%98/"},{"categories":["算法"],"content":"题记 卡特兰数(Catalan number)是组合数学中一个常出现在各种计数问题中的数列。 卡塔兰数算是leetcode里面的高频题目了,这里就来解决一下卡塔兰数问题。 卡塔兰数 卡塔兰数的一般式是: 卡塔兰数的等价式为: 卡塔兰数的题目 ","date":"2020-09-23","objectID":"https://seacj.github.io/posts/%E5%8D%A1%E5%A1%94%E5%85%B0%E6%95%B0/:1:0","tags":["算法","leetcode"],"title":"卡塔兰数","uri":"https://seacj.github.io/posts/%E5%8D%A1%E5%A1%94%E5%85%B0%E6%95%B0/"},{"categories":["算法"],"content":"96 不同的二叉搜索树 给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种? 这道题的动态规划解法我已经在这篇博客里面介绍过了。 ","date":"2020-09-23","objectID":"https://seacj.github.io/posts/%E5%8D%A1%E5%A1%94%E5%85%B0%E6%95%B0/:2:0","tags":["算法","leetcode"],"title":"卡塔兰数","uri":"https://seacj.github.io/posts/%E5%8D%A1%E5%A1%94%E5%85%B0%E6%95%B0/"},{"categories":["算法"],"content":"解题思路 可组成的数量就是左子树可组成的数量乘以右子树可组成的数量。 比如[1,2,3,4]: 对1当根节点,则有dp[0]*dp[3],左子树为null,右子树为[2,3,4] 对2当根节点,则有dp[1]*dp[2],左子树为[1],右子树为[3,4] 对3当根节点,则有dp[2]*dp[1],左子树为[1,2],右子树为[4] 对4当根节点,则有dp[3]*dp[0],左子树为[1,2,3],右子树为null 即$dp[n] = \\sum_{i=0}^{n-1}dp[i]*dp[n-i-1]$ 上式满足卡塔兰数的第二个等价式,可以替换成第三个等价式,从而得到空间复杂度O(1),时间复杂度O(n)的代码。 ","date":"2020-09-23","objectID":"https://seacj.github.io/posts/%E5%8D%A1%E5%A1%94%E5%85%B0%E6%95%B0/:2:1","tags":["算法","leetcode"],"title":"卡塔兰数","uri":"https://seacj.github.io/posts/%E5%8D%A1%E5%A1%94%E5%85%B0%E6%95%B0/"},{"categories":["算法"],"content":"代码 class Solution(object): def numTrees(self, n): C = 1 for i in range(0, n): C = C * 2*(2*i+1)/(i+2) return int(C) 同样可以写出卡塔兰数一般式$\\frac{1}{n+1}C_{2 n}^{n}$的代码,不过力扣提交结果的执行耗时上上面的代码更好;而acwing 130由于测试用例的n值很大,只有下面使用math库的代码通过了,其他2个都超时了。 使用math库: import math class Solution: def numTrees(self, n): A = math.factorial(2*n) B = math.factorial(n) return A//B//B//(n+1) 不使用math库(这个代码是三个里面最不推荐的): class Solution: def numTrees(self, n): C = 1 # print(A//B) for i in range(n+1,2*n+1): C*=i for i in range(1,n+1): C=C//i # 这里一定要是整除,不然有精度问题 return C//(n+1) ","date":"2020-09-23","objectID":"https://seacj.github.io/posts/%E5%8D%A1%E5%A1%94%E5%85%B0%E6%95%B0/:2:2","tags":["算法","leetcode"],"title":"卡塔兰数","uri":"https://seacj.github.io/posts/%E5%8D%A1%E5%A1%94%E5%85%B0%E6%95%B0/"},{"categories":["算法"],"content":"进出栈序列 n 个元素进栈序列为:1,2,3,4,...,n,则有多少种出栈序列。 上图所示是一种出栈序列,可以用[+1, -1, +1, +1, -1, -1]来表示 。 所有的-1,+1的排列组合有$C_{2n}^n$个,因为相当于有2n个位置,放n个+1(剩下的位置都是-1)。 但是因为-1之前一定要有足够的+1,如[+1, -1, -1, +1]是不合法的,所以要减去不合法的数量。 观察不合法的序列[+1, -1, -1, +1],不合法的位置是第3个-1,将前3项取反,得到[-1, +1, +1, +1]。取反后是1个-1和3个+1。 不合法序列总是出现在第2m+1(1、3、5….)处有一个-1,且前2m个数值为m个+1 和 m个-1,后2n-(2m+1)个数里面有n-m个+1 和 n-m-1个-1。 将前2m+1个数取反: 前2m个数字中有m个+1 和 m个-1,第2m+1个数是+1,后2n-(2m+1)个数字中有n-m个+1 和 n-m-1个-1, n+1个+1 和 n-1个-1的序列共有$C_{2n}^{n+1}$种。 不合法的序列和n+1个+1、n-1个-1序列是一一对应的关系,所以不合法序列的数量为$C_{2n}^{n+1}$。 n=4的不合法序列有: m=1 [-1, -1, +1, +1] -\u003e [+1, -1, +1, +1] [-1, +1, -1, +1] -\u003e [+1, +1, -1, +1] [-1, +1, +1, -1] -\u003e [+1, +1, +1, -1] m=3 [+1, -1, -1, +1] -\u003e [-1, +1, +1, +1] 最终结果为$C_{2n}^n - C_{2n}^{n+1}$,也就是第一个等价式。 ","date":"2020-09-23","objectID":"https://seacj.github.io/posts/%E5%8D%A1%E5%A1%94%E5%85%B0%E6%95%B0/:3:0","tags":["算法","leetcode"],"title":"卡塔兰数","uri":"https://seacj.github.io/posts/%E5%8D%A1%E5%A1%94%E5%85%B0%E6%95%B0/"},{"categories":["算法"],"content":"非降路径(另一种解释方法) 画一个nxn的格子,从左下角出发,+1表示向右走一格,-1表示向上走一个,走到右上角为有一个序列。 而不合法的序列就是会走到超过对角线虚线的序列。 下图为n=4的可行走法: 可以发现从左下角走到右上角的步数是一定的,即向右走n步、并向上走n步。转化成排列组合,一共2n步,有n个向右走,剩下的都是向上走,一共有$C_{2n}^n$种走法。 而非法路径的特点是,一定会走到y\u003e=x+1的地方。 如下图所示,到绿色的点(n,n)的蓝线是一条非法路径,其与y=x+1的交点用蓝圈标出,将其后半段作y=x+1的对称,此时得到一条从左下角到蓝色的点(n-1,n+1)的路径。 每一条非法路径作对称操作,都能得到这样的一条路径,到点(n-1, n+1)的路径一共有$C_{2n}^{n+1}$个。 p.s. $C_{2n}^{n+1}$是怎么得到的:一共2n(n-1 + n+1)步,有n+1个向上走,剩下的都是向右走。 ","date":"2020-09-23","objectID":"https://seacj.github.io/posts/%E5%8D%A1%E5%A1%94%E5%85%B0%E6%95%B0/:3:1","tags":["算法","leetcode"],"title":"卡塔兰数","uri":"https://seacj.github.io/posts/%E5%8D%A1%E5%A1%94%E5%85%B0%E6%95%B0/"},{"categories":["算法"],"content":"括号序列 n 对括号,则有多少种 “括号匹配” 的括号序列 这题和进出栈序列完全一致,左括号是+1,右括号是-1。 相同的问题还有火车进出栈问题(这是acwing的第130题一列火车n节车厢,依次编号为1,2,3,…,n。每节车厢有两种运动方式,进栈与出栈,问n节车厢出栈的可能排列方式有多少种。acwing的测试样例的n值很大,有个测试用例n是15000,结果手写的阶乘超时了,手写阶乘运算会有精度问题,python用个math库可以解决精度问题,C的话要用质因数分解解决精度问题) ","date":"2020-09-23","objectID":"https://seacj.github.io/posts/%E5%8D%A1%E5%A1%94%E5%85%B0%E6%95%B0/:4:0","tags":["算法","leetcode"],"title":"卡塔兰数","uri":"https://seacj.github.io/posts/%E5%8D%A1%E5%A1%94%E5%85%B0%E6%95%B0/"},{"categories":["算法"],"content":"买票找零 《编程之美》4.3买票找零:2n个人排队买票,其中n个人持50元,n个人持100元。每张票50元,且一人只买一张票。初始时售票处没有零钱找零。请问这2n个人一共有多少种排队顺序,不至于使售票处找不开钱? 50相当于进栈、100相当于出栈。 证明 递推式推通项式:https://zhuanlan.zhihu.com/p/56821103 感觉这个证明还是挺难的。 除递推式以外的证明:链接 这个相对来说就简单多了 参考资料 「算法入门笔记」卡特兰数 https://leetcode-cn.com/circle/article/lWYCzv/ AcWing 130. 火车进出栈问题(卡特兰数、高精度、分解质因数) https://blog.csdn.net/qq_42815188/article/details/104286409?utm_medium=distribute.pc_relevant.none-task-blog-title-2\u0026spm=1001.2101.3001.4242 ","date":"2020-09-23","objectID":"https://seacj.github.io/posts/%E5%8D%A1%E5%A1%94%E5%85%B0%E6%95%B0/:5:0","tags":["算法","leetcode"],"title":"卡塔兰数","uri":"https://seacj.github.io/posts/%E5%8D%A1%E5%A1%94%E5%85%B0%E6%95%B0/"},{"categories":["算法"],"content":"题记 刷一下动态规划的简单题,一共也没几道。顺便把前100道题里面的中等难度的动态规划题刷了。 第53、121、198是简单题。 题目 ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:0:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"53 最大子序和 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:1:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"题解 定义状态转移方程f(i)表示以i结尾的子串连续子数组的最大和,最后的答案应当是一开始的想法是定义f(i)是数组前i个数中连续子数组的的最大和,但是发现写不出来转移方程。 ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:1:1","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"代码 class Solution: def maxSubArray(self, nums: List[int]) -\u003e int: a = nums[0] result = nums[0] for i in range(1, len(nums)): a = max(a+nums[i], nums[i]) result = max(result, a) return result ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:1:2","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"121 买卖股票的最佳时机 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。 注意:你不能在买入股票前卖出股票。 ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:2:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"题解 状态转移方程,可以看到前i天:","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:2:1","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"代码 class Solution: def maxProfit(self, prices: List[int]) -\u003e int: if len(prices) \u003c= 1: return 0 a = 0 min_price = prices[0] for i in range(1, len(prices)): a = max(a, prices[i] - min_price) if prices[i] \u003c min_price: min_price = prices[i] return a ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:2:2","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"198 打家劫舍 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个房屋存放金额的非负整数数组,计算你不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:3:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"题解 dp 方程为:dp[i] = max(dp[i-2]+nums[i], dp[i-1]) 一开始想的很复杂,分享一下心路历程: dp[i] 分3种情况 1. i-1偷了,i只能不偷,那么dp[i]=dp[i-1],并且要维护一个变量说明i没有偷; 2. i-1没偷,那么可以选择偷i,则有dp[i]=dp[i-1]+nums[i],维护一个状态说明偷了i;3. i-1没偷, i仍然选择不偷,则有dp[i] = dp[i-1],维护一个状态表示i没偷。 这是一开始的分析,显然很复杂,而且如果i-1没偷,那么就一定要偷i才能利益最大化,之所以考虑不偷,是为了让i+1能够偷。真正的思路就是2种情况:1. 偷前k-2个房子,并且偷最后一间 2. 偷前k-1个房子,不偷最后一间。 ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:3:1","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"代码 class Solution: def rob(self, nums: List[int]) -\u003e int: if len(nums) == 0: return 0 elif len(nums) == 1: return nums[0] a,b = nums[0], max(nums[0],nums[1]) for i in range(2, len(nums)): tmp = b b = max(a+nums[i], b) a = tmp return b ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:3:2","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"62 不同路径 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 问总共有多少条不同的路径? ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:4:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"题解 dp方程为:dp[i][j] = dp[i-1][j] + dp[i][j-1] , 表示走到第i,j个格子有多少种走法,分别是从上面一格和从左边一格走过来。 ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:4:1","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"代码 class Solution: def uniquePaths(self, m: int, n: int) -\u003e int: dp = [[1] * n] + [[1]+[0]*(n-1) for _ in range(m-1)] for i in range(1,m): for j in range(1,n): dp[i][j] = dp[i][j-1] + dp[i-1][j] return dp[-1][-1] 上面的代码空间复杂度是O(m*n),可以进行优化成空间复杂度为O(n)的下面的代码。 class Solution: def uniquePaths(self, m: int, n: int) -\u003e int: dp = [1]*n for i in range(1, m): for j in range(1, n): dp[j] = dp[j-1] + dp[j] return dp[-1] 另外这道题还有数学解法,即计算$C_{m+n-2}^{m-1}$. ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:4:2","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"63. 不同路径 II 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径? ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:5:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"代码 class Solution: def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -\u003e int: m, n = len(obstacleGrid), len(obstacleGrid[0]) dp = [[0] * n for _ in range(m)] for i in range(m): if obstacleGrid[i][0] == 0: dp[i][0] = 1 else: break for i in range(n): print(dp) if obstacleGrid[0][i] == 0: dp[0][i] = 1 else: break for i in range(1, m): for j in range(1, n): if obstacleGrid[i][j] == 1: continue else: dp[i][j] = dp[i-1][j] + dp[i][j-1] return dp[-1][-1] 优化空间复杂度O(n+1): class Solution: def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -\u003e int: m, n = len(obstacleGrid), len(obstacleGrid[0]) dp = [0] * (n+1) dp[1] = 1 for i in range(m): for j in range(n): print(dp) if obstacleGrid[i][j] == 1: dp[j+1] = 0 else: dp[j+1] = dp[j+1] + dp[j] return dp[-1] 可视化如下表所示,其中表头[0,1,0,0]表示dp初始化的值,括号中的0表示虽然dp没有存第一列但是因为第一列都是0,所以dp[0]一个值就相当于第一列的m个值。 0 1 0 0 (0) 0 0 0 (0) 1 1 0 (0) 0 0 0 ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:5:1","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"64 最小路径和 给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 输出最小的路径和。 ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:6:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"代码 class Solution: def minPathSum(self, grid: List[List[int]]) -\u003e int: m ,n = len(grid), len(grid[0]) dp = [float('inf')]*(n+1) dp[1] = 0 for i in range(m): for j in range(n): dp[j+1] = min(dp[j+1], dp[j]) + grid[i][j] # print(dp) return dp[-1] 空间复杂度是O(n+1),与62 不同路径的优化方法类似,但是会巧妙地使用inf,使得第一列总是选择上面一格,第一行总是选择左边一格。 比如当输入是[[1,3,1],[1,5,1],[4,2,1]],会进行下表所示的初始化,其中可以发现第一个inf可以模拟成一列inf: inf 0 inf inf (inf) 1 3 1 (inf) 1 5 1 (inf) 4 2 1 另一种方法存dp: class Solution: def minPathSum(self, grid: List[List[int]]) -\u003e int: m ,n = len(grid), len(grid[0]) for i in range(m): for j in range(n): if i==0 and j==0: continue if i == 0: grid[i][j] = grid[i][j-1]+grid[i][j] elif j == 0: grid[i][j] = grid[i-1][j]+grid[i][j] else: grid[i][j] = min(grid[i-1][j], grid[i][j-1]) + grid[i][j] return grid[-1][-1] 空间复杂度是O(1),因为是直接修改的原矩阵。 ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:6:1","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"91 解码方法 ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:7:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"题解 难点是对0的处理,共有4种情况,刚开始只想到了第三个状态方程: s[i]是0,后2位\u003c=26,则dp[i]=dp[i-2]。e.g. 110 s[i]是0,后2位\u003e26,或等于0,则return 0。e.g. 100,190 s[i]不是0,后两位大于10,或小于等于26,则dp[i]=dp[i-1]+dp[i-2]。e.g. 123,111 s[i]不是0,后两位小于10(因为末尾不为0,所以没有等于10),或大于26,则dp[i]=dp[i-1]。e.g. 109,199 ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:7:1","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"代码 class Solution: def numDecodings(self, s: str) -\u003e int: if s[0] == '0': return 0 if len(s) == 1: return 1 if s[1] == '0': if int(s[:2]) \u003c= 26: a,b = 1,1 else: return 0 else: if int(s[:2]) \u003c= 26: #由于前面判定过s[0]=='0'了,所以没有大于10的判断 a,b = 1,2 else: a,b = 1,1 for i in range(2, len(s)): two_ss = int(s[i-1:i+1]) if s[i] == '0': if two_ss \u003c= 26 and two_ss != 0: tmp = b b = a a = tmp else: return 0 else: if two_ss \u003e 10 and two_ss \u003c= 26: tmp = b b = a + b a = tmp else: # b = b a = b return b ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:7:2","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"95 不同的二叉搜索树II 二叉搜索树(Binary Search Tree)的定义: 其左子树(left subtree)下的每个后代节点(descendant node)的值都小于节点 n 的值; 其右子树(right subtree)下的每个后代节点的值都大于节点 n 的值。 一句话概括:左小右大 ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:8:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"题解 对于[1,2,3,4]而言: 对1当根节点,则有dp[0], 1, dp[3],左子树为null,右子树为[2,3,4] 对2当根节点,则有dp[1], 2, dp[2],左子树为[1],右子树为[3,4] 对3当根节点,则有dp[2], 3, dp[1],左子树为[1,2],右子树为[4] 对4当根节点,则有dp[3], 4, dp[0],左子树为[1,2,3],右子树为null ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:8:1","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"代码 递归代码,递归的出口是start\u003eend时,返回None。 # 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 generateTrees(self, n: int) -\u003e List[TreeNode]: def generateTrees(start, end): if start \u003e end: return [None, ] allTrees = [] for i in range(start, end+1): # 枚举可行根节点 # 获得所有可行的左子树集合 leftTrees = generateTrees(start, i-1) # 获得所有可行的右子树集合 rightTrees = generateTrees(i+1, end) # 从左子树集合中选出一棵左子树,从右子树集合中选出一棵右子树,拼接到根节点上 for l in leftTrees: for r in rightTrees: currTree = TreeNode(i) currTree.left = l currTree.right = r allTrees.append(currTree) return allTrees return generateTrees(1, n) if n else [] ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:8:2","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"96 不同的二叉搜索树 给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种? ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:9:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"题解 可组成的数量就是左子树可组成的数量乘以右子树可组成的数量。 比如[1,2,3,4]: 对1当根节点,则有dp[0]*dp[3],左子树为null,右子树为[2,3,4] 对2当根节点,则有dp[1]*dp[2],左子树为[1],右子树为[3,4] 对3当根节点,则有dp[2]*dp[1],左子树为[1,2],右子树为[4] 对4当根节点,则有dp[3]*dp[0],左子树为[1,2,3],右子树为null 即$dp[n] = \\sum_{i=0}^{n-1}dp[i]*dp[n-i-1]$ p.s. 实际上这道题有时间复杂度O(n)和空间复杂度O(1)的卡塔兰数的解法,因为卡塔兰数就是dp[0]=1,dp[1]=1且满足上面递归式的数列。但是先专注于动态规划,卡塔兰数留着以后总结。 ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:9:1","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"代码 代码用递归解法,加上一个表格self.n2result来存储计算过的值,从而减少计算量。(不用表,直接递归会超时) class Solution: def __init__(self): self.n2result = {} def numTrees(self, n: int) -\u003e int: if n\u003c=1: return 1 if n in self.n2result: return self.n2result[n] total = 0 for i in range(n): total += self.numTrees(i) * self.numTrees(n-i-1) self.n2result[n] = total return total 递归可以换成迭代,自底向上,dp[3]的计算需要dp[0]、dp[1]、dp[2],计算出dp[3]之后就存起来,然后再通过dp[0]…dp[3]计算出dp[4]: class Solution: def numTrees(self, n: int) -\u003e int: store = [1, 1] # dp[0],dp[1] if n \u003c= 1: return store[n] for m in range(2, n+1): s = m-1 count = 0 for i in range(m): count += store[i]*store[s-i] store.append(count) return store[n] 两种方法时间复杂度都是O($n^2$),空间复杂度是O(n)。 ","date":"2020-09-17","objectID":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/:9:2","tags":["算法","leetcode","动态规划"],"title":"Leetcode动态规划简单题","uri":"https://seacj.github.io/posts/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%AE%80%E5%8D%95%E9%A2%98/"},{"categories":["算法"],"content":"题目 题目一. N个人去旅行,在旅店开了N个房间,钥匙挂在大厅的墙上,钥匙上没有标号,每人随手拿一把钥匙,请用程序实现算出所有人都拿错钥匙的可能性有几种。 题目二. N个人坐在六把椅子上,不能坐自己的,有几种坐法。 以上问题可以统称为错排问题。 思考 刚开始以为答案是(n-1)!,这里的思路是第一个人有n-1个可能拿错的钥匙,第二个人有n-2个,以此类推。 实际上并不是,因为有2种情况: 第一个人拿了第二个人的钥匙,那么第二个人此时有n-1个可能拿错的钥匙。第二个人拿错钥匙的可能性有1、3、4、…、N 第一个人拿的不是第二个人的钥匙,那么第二个人此时有n-2个可能拿错的钥匙。假设第一个人拿的是3,那么第二个人拿错钥匙可能性有1、4、…、N 所以并不是(n-1)!,实际上应当是: 推导 记D(N)为有N个人时的可能性个数。 ","date":"2020-09-16","objectID":"https://seacj.github.io/posts/%E9%94%99%E6%8E%92%E9%97%AE%E9%A2%98/:0:0","tags":["算法","动态规划"],"title":"错排问题","uri":"https://seacj.github.io/posts/%E9%94%99%E6%8E%92%E9%97%AE%E9%A2%98/"},{"categories":["算法"],"content":"N=2 1 2 2 1 D(N) = 1 ","date":"2020-09-16","objectID":"https://seacj.github.io/posts/%E9%94%99%E6%8E%92%E9%97%AE%E9%A2%98/:1:0","tags":["算法","动态规划"],"title":"错排问题","uri":"https://seacj.github.io/posts/%E9%94%99%E6%8E%92%E9%97%AE%E9%A2%98/"},{"categories":["算法"],"content":"N=3 1 2 3 2 3 1 3 1 2 D(N) = 2 ","date":"2020-09-16","objectID":"https://seacj.github.io/posts/%E9%94%99%E6%8E%92%E9%97%AE%E9%A2%98/:2:0","tags":["算法","动态规划"],"title":"错排问题","uri":"https://seacj.github.io/posts/%E9%94%99%E6%8E%92%E9%97%AE%E9%A2%98/"},{"categories":["算法"],"content":"N=4 1 2 3 4 2 1 4 3 2 3 4 1 2 4 1 3 3 4 1 2 3 4 2 1 3 1 4 2 4 3 2 4 4 1 2 3 4 3 1 2 D(N) = 9 刚开始推找不到规律就很难,但是发现规律了就简单了。 观察N=4的情况,对第一个人而言,可以拿N-1个人的钥匙,如果第一个人拿的是第k个人的钥匙,此时将问题分为2类问题: 第一个人的钥匙与第k个人的钥匙交换 第一个人的钥匙没有进行交换,即第一个人拿了第k把钥匙,但是第k个人拿的却不是第一把钥匙。 如上表所示,如果进行了交换的,用红字标出,现在讨论表中前3行。 发生交换:当k=2时,此时问题变成第3个人和第4个人,第3把钥匙和第4把钥匙的分配问题,即2个人、2把钥匙的错排问题,有D(2)种情况,可以推广到D(N-2)。 不做交换:当k=2时,此时问题变成了第2、3、4个人,第1,4,3把钥匙的分配问题。实际上,可以将第1把钥匙看作是第2把钥匙,因为第1把钥匙不能给第2个人(因为这种情况就是交换钥匙,包括在D(2)里面了)。如下表所示,问题变成3个人、3把钥匙的错排问题,有D(N-1)种情况: 2 3 4 3 4 1(2) 4 1(2) 3 由于k可以取值N-1个,所以一共是D(N) = (N-1)*( D(N-1) + D(N-2) ) 代码的话就跟爬楼梯问题基本是一样的,这里给个参考代码: class Solution: def climbStairs(self, n: int) -\u003e int: if n == 1: return 0 elif n == 2: return 1 elif n == 3: return 2 a,b = 1,2 for i in range(4, n+1): tmp = b b = (i-1) * (b + a) a = tmp return b ","date":"2020-09-16","objectID":"https://seacj.github.io/posts/%E9%94%99%E6%8E%92%E9%97%AE%E9%A2%98/:3:0","tags":["算法","动态规划"],"title":"错排问题","uri":"https://seacj.github.io/posts/%E9%94%99%E6%8E%92%E9%97%AE%E9%A2%98/"},{"categories":["编程-ღ-技术"],"content":"C语言里面可以用2个库FFTW和kissfft做傅里叶变换,第一次装的时候因为不懂dll、lib怎么用所以配置了很久。 代码的例子如下,由于我是做语音的所以只给了语音里面的例子,sndfile库是用来读写音频的,例子是时域转频域,再从频域转时域。 #include \"kiss_fftr.h\"#include \"fftw3.h\"#include \u003csndfile.h\u003e#include \"kiss_fft.h\" void fft_d(double * in, fftw_complex* out, int nfft) { fftw_plan p; //p = fftw_plan_dft_1d(nfft, in, out, FFTW_FORWARD, FFTW_ESTIMATE); // 一维实数据的DFT p = fftw_plan_dft_r2c_1d(nfft, in, out, FFTW_ESTIMATE); fftw_execute(p); fftw_destroy_plan(p); } void ifft_d(fftw_complex* in, double* out, int nfft) { fftw_plan p; //p = fftw_plan_dft_1d(nfft, in, out, FFTW_BACKWARD, FFTW_ESTIMATE); p = fftw_plan_dft_c2r_1d(nfft, in, out, FFTW_ESTIMATE); fftw_execute(p); fftw_destroy_plan(p); } void testfft() { int method = 1; //const int N = 11125; SF_INFO sf_info; SNDFILE *snd_file; snd_file = sf_open(\"p232_084_8kHz.wav\", SFM_READ, \u0026sf_info); float buf2[M]; //buf2 = (double *)malloc(sf_info.frames * sizeof(double)); //sf_readf_double(snd_file, buf2, sf_info.frames); printf(\"Sample Rate : %d\\n\", sf_info.samplerate); printf(\"Channels : %d\\n\", sf_info.channels); printf(\"Sections : %d\\n\", sf_info.sections); printf(\"Frames : %d\\n\", (int)sf_info.frames); int n_frames = (int)sf_info.frames / M; SNDFILE *output_file; SF_INFO outsf_info; if (!(output_file = sf_open(\"sine.wav\", SFM_WRITE, \u0026sf_info))) { printf(\"Error : Not able to open output file.\\n\"); return 1; } if (method == 0) {//方法一使用kissfft kiss_fft_cpx cpx_in[M]; kiss_fft_cpx cx_out[M]; kiss_fft_cfg cfg = kiss_fft_alloc(M, 0, NULL, NULL); kiss_fftr_cfg icfg = kiss_fftr_alloc(M, 1, 0, 0); kiss_fft_cpx freq_data[M / 2 + 1]; kiss_fft_scalar time_data[M]; for (int n = 0; n \u003c n_frames; n++) { sf_readf_float(snd_file, buf2, M); for (int i = 0; i \u003c M; i++) { cpx_in[i].r = buf2[i]; cpx_in[i].i = 0; } kiss_fft(cfg, cpx_in, cx_out); /* inverse FFT */ for (int i = 0; i \u003c M / 2 + 1; i++) { freq_data[i].r = cx_out[i].r; freq_data[i].i = cx_out[i].i; } kiss_fftri(icfg, freq_data, time_data); float write_data[M]; for (int i = 0; i \u003c M; i++) { write_data[i] = time_data[i] / M; if (i == 0) printf(\"%f %f %f\\n\", write_data[i], buf2[i], cpx_in[i].r); } sf_write_float(output_file, write_data, M); } free(cfg); } else {//方法二:使用FFTW double in[M]; fftw_complex out[M*2]; fftw_complex freq_data[M * 2]; double time_data[M]; for (int n = 0; n \u003c n_frames; n++) { sf_readf_float(snd_file, buf2, M); for (int i = 0; i \u003c M; i++) { in[i] = buf2[i]; } fft_d(in, out, M); /* inverse FFT */ for (int i = 0; i \u003c M; i++) { freq_data[i][0] = out[i][0]; freq_data[i][1] = out[i][1]; } ifft_d(freq_data, time_data, M); float write_data[M]; for (int i = 0; i \u003c M; i++) { write_data[i] = time_data[i] / M; if (i == 0) printf(\"%f %f %f\\n\", write_data[i], buf2[i], in[i]); } sf_write_float(output_file, write_data, M); } } sf_close(output_file); sf_close(snd_file); } 小总结 kissfft用结构体存复数(.r是实数、.c是虚数),fftw是二维数组(array[0]是实数、[1]是虚数)。 kissfft和fftw做一遍fft变换然后再做一遍逆变换之后,数值上和原始输入不同,需要除以M(M是帧长)。 Visual Studio的lib、dll文件之类的是真的难搞,这样看来python的pip装包真是方便。 windows上安装fftw需要找到visual studio的lib.exe文件,我的这个文件路径在C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Tools\\MSVC\\14.25.28610\\bin\\Hostx64\\x64\u003e,为了方便我直接把这个加入环境变量了。 参考资料 FFTW3学习笔记2:FFTW(快速傅里叶变换)中文参考 Win10+VS2017安装FFTW库 【里面有找不到dll文件的解决方法】 FFTW官网 http://fftw.org/install/windows.html kissfft下载地址 http://sourceforge.net/projects/kissfft/ ","date":"2020-08-24","objectID":"https://seacj.github.io/posts/c%E8%AF%AD%E8%A8%80%E4%B8%AD%E7%9A%84%E5%82%85%E9%87%8C%E5%8F%B6%E5%8F%98%E6%8D%A2/:0:0","tags":["信号处理","C语言"],"title":"C语言傅里叶变换库","uri":"https://seacj.github.io/posts/c%E8%AF%AD%E8%A8%80%E4%B8%AD%E7%9A%84%E5%82%85%E9%87%8C%E5%8F%B6%E5%8F%98%E6%8D%A2/"},{"categories":["随笔-ღ-心得"],"content":"题记 这几天看完了重启咲良田 ,毫无疑问的优秀作品,虽然理清里面的所有事件的逻辑可能还需要思考思考,不过里面最吸引我的地方还是这部番提出的各种思想实验,更难能可贵的是剧情与这些思想实验完美的契合,而不是故弄玄虚或者故作深沉的处理,于是想记录一下这部作品里面我印象深刻思想实验。 对思想实验的看法 思想实验就像是一个陷阱,而且是那种非常具有诱惑力的陷阱,好像毫无意义,但是却让人情不自禁开始思考。其中的很多命题都无比残酷,这就导致了连思考思想实验都是痛苦的。不过好在,我这个人不喜欢这样的思考,对会引起痛苦的思考,能够本能的停止思考,面对本能觉得自己没法解的题目,本能的就跳过这道题目就好了。 话说回来,哲学家真的是很痛苦呢,居然要去思考那些那么残酷的命题,最后的结果要么是终生得不到答案,要么是得到一些毫无意义的答案,不过对他们来说这个陷阱太过有诱惑力,由于对某些東西的追求乐于饮鸩止血,有可能如果是哲学家的话会乐在其中吧。不过对我来说,有些思想实验的思考还是禁止去思考比较好。 咲良田的思想实验 参考资料:神圣的重生:咲良田的思想实验合集 ","date":"2020-07-25","objectID":"https://seacj.github.io/posts/%E5%92%B2%E8%89%AF%E7%94%B0%E7%9A%84%E6%80%9D%E6%83%B3%E5%AE%9E%E9%AA%8C/:1:0","tags":["随笔"],"title":"咲良田的思想实验","uri":"https://seacj.github.io/posts/%E5%92%B2%E8%89%AF%E7%94%B0%E7%9A%84%E6%80%9D%E6%83%B3%E5%AE%9E%E9%AA%8C/"},{"categories":["随笔-ღ-心得"],"content":"1、善与伪善的寓言(EP01) 相麻堇:神明对某个少年施下了一个诅咒,只要他看到悲伤的人,痛苦就会遍布全身。青年为了缓解自己的痛苦,就对所有悲伤的人都伸出了援手。接着,神明复制了青年,做出了一个假货,没有自己的意识,只会做出和真正的青年一样的行动。假青年也同样对所有悲伤的人都伸出了援手。神明分别给他们取了名字,一个是“伪善”,一个是“善”。那么,哪一个是“伪善”,哪一个是“善”呢? ","date":"2020-07-25","objectID":"https://seacj.github.io/posts/%E5%92%B2%E8%89%AF%E7%94%B0%E7%9A%84%E6%80%9D%E6%83%B3%E5%AE%9E%E9%AA%8C/:2:0","tags":["随笔"],"title":"咲良田的思想实验","uri":"https://seacj.github.io/posts/%E5%92%B2%E8%89%AF%E7%94%B0%E7%9A%84%E6%80%9D%E6%83%B3%E5%AE%9E%E9%AA%8C/"},{"categories":["随笔-ღ-心得"],"content":"2、沼泽人(EP06) 沼泽人(swampman)思想实验是1987年美国哲学家唐纳德·戴维森提出的思考实验,常常用于思考“我到底是什么”这一自我认证的命题。沼泽人(swampman)是由沼泽(swamp)和人(man)两个英文单词组成。 某个人出门去散步,在经过一个沼泽边上的时候不幸的被闪电击中而死亡。与此同时在他的旁边正好也有一束闪电击中了沼泽,十分罕见的是这个落雷和沼泽发生了反应,产生了一个与刚才死掉的人无论形体还是质量都完全相同的生物。 我们将这个新产生的生物叫做沼泽人。沼泽人在原子级别上与原来那个人的构造完全相同,外观也完全一样,当然大脑的状态(被落雷击中的人死前的大脑状态)也完全被复制了下来,也就是记忆和知识看起来也完全一样。走出沼泽的沼泽人就像刚死去的男人一样边散步边回到了家中,然后打开了刚死去的男人的家门,和刚死去的男人的家人打电话,接着边读刚死去的男人没读完的书边睡去。第二天早上起床后,到刚死去的男人的公司上班。 ","date":"2020-07-25","objectID":"https://seacj.github.io/posts/%E5%92%B2%E8%89%AF%E7%94%B0%E7%9A%84%E6%80%9D%E6%83%B3%E5%AE%9E%E9%AA%8C/:3:0","tags":["随笔"],"title":"咲良田的思想实验","uri":"https://seacj.github.io/posts/%E5%92%B2%E8%89%AF%E7%94%B0%E7%9A%84%E6%80%9D%E6%83%B3%E5%AE%9E%E9%AA%8C/"},{"categories":["随笔-ღ-心得"],"content":"3、世界5分钟前假设(EP20) 浅井惠:假设世界是在5分钟前形成的,早于5分钟前的记忆都是假的,世界诞生的瞬间,我们就被灌输了世界早就存在的记忆。那么,在这个世界形成前,可能还有着其它世界。或许我们有着完全不同的记忆,做着完全不同的事情。然而记忆被改写,形成了现在的世界。假设我们记忆都是假的,即便如此,我们还能像现在这样吗? 重启咲良田的海报重启咲良田 \" 重启咲良田的海报 ","date":"2020-07-25","objectID":"https://seacj.github.io/posts/%E5%92%B2%E8%89%AF%E7%94%B0%E7%9A%84%E6%80%9D%E6%83%B3%E5%AE%9E%E9%AA%8C/:4:0","tags":["随笔"],"title":"咲良田的思想实验","uri":"https://seacj.github.io/posts/%E5%92%B2%E8%89%AF%E7%94%B0%E7%9A%84%E6%80%9D%E6%83%B3%E5%AE%9E%E9%AA%8C/"},{"categories":["博客"],"content":"凭借本博客喜获github北极开源贡献者徽章 这两天,在 GitHub 上积极贡献代码的许多开发者都收到了「Arctic Code Vault Contributor」的荣誉勋章的通知,并非常兴奋地晒起了朋友圈。因为这标志着自己在 GitHub 上贡献的代码、写的库都被 GitHub 打包运送到了北极永冻土数百米深处的一个暗室里保存了起来,这些代码与库将被保存 1000 多年。 https://www.sohu.com/a/408230132_115128 我的个人网站一直在github上托管,今天发现这个仓库被给了奖章,意味着这个博客会被运到北极保存个一千年 ,真就黑历史永久保存… 其实相比github上真正值得被记录的代码相比,我的東西真的不值得被保存,甚至在这个角度讲,这个计划的宣传目的可能是大于实际意义的。不过还是很开心获得了一个奖章,让我更有动力维持这个博客了。 ","date":"2020-07-23","objectID":"https://seacj.github.io/posts/%E5%BB%BA%E7%AB%99%E5%8E%86%E5%8F%B2008/:0:0","tags":["博客搭建","建站历史"],"title":"建站历史008","uri":"https://seacj.github.io/posts/%E5%BB%BA%E7%AB%99%E5%8E%86%E5%8F%B2008/"},{"categories":["编程-ღ-技术"],"content":"题记 用了快半年的screen,最近想换成tmux试一试,所以用博客记录一下简单的使用体验。这篇博客的内容主要参考了How to Use tmux on Linux (and Why It’s Better Than Screen)。 为什么使用tmux Screen和tmux都是用来在连接远程服务器的时候管理会话的工具,这类工具的专用名词叫终端复用器(terminal multiplexers),最早接触到这类工具的时候,还是搭建我的世界服务器的时候,我还写了一篇搭建的博客。 ","date":"2020-07-18","objectID":"https://seacj.github.io/posts/tmux%E5%AD%A6%E4%B9%A0/:1:0","tags":["linux"],"title":"Tmux学习","uri":"https://seacj.github.io/posts/tmux%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"作用 本地关闭远程连接程序,远程继续执行,并且能恢复会话 分屏操作 ","date":"2020-07-18","objectID":"https://seacj.github.io/posts/tmux%E5%AD%A6%E4%B9%A0/:2:0","tags":["linux"],"title":"Tmux学习","uri":"https://seacj.github.io/posts/tmux%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"为什么从screen转向tmux 事实上2个工具功能几乎完全一样,我也非常喜欢使用screen,第一个原因是可以看见网上很多人都是更推荐tmux的,因为screen稍显粗糙,但是对我来说我替换成tmux的最大的理由是screen的分屏不好看,如下图分屏的大白线是什么鬼啊,而且不支持左右分屏。 Tmux的使用 ","date":"2020-07-18","objectID":"https://seacj.github.io/posts/tmux%E5%AD%A6%E4%B9%A0/:3:0","tags":["linux"],"title":"Tmux学习","uri":"https://seacj.github.io/posts/tmux%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"安装 sudo apt-get install tmux ","date":"2020-07-18","objectID":"https://seacj.github.io/posts/tmux%E5%AD%A6%E4%B9%A0/:4:0","tags":["linux"],"title":"Tmux学习","uri":"https://seacj.github.io/posts/tmux%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"开启一个tmux的session 执行tmux命令,就会进入tmux的session,下方出现绿色的状态栏(status bar)。 其中[0]是session名,默认是按数字(后文将介绍自定义session名);0:bash*中的0表示这个session中的第一个window,而在window中运行的进程是bash;星号*的意思是这是你在操作的window。 p.s. screen中没有这个状态栏,这也是tmux的优势 ","date":"2020-07-18","objectID":"https://seacj.github.io/posts/tmux%E5%AD%A6%E4%B9%A0/:5:0","tags":["linux"],"title":"Tmux学习","uri":"https://seacj.github.io/posts/tmux%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"Tmux的命令简单尝试 tmux的所有命令分为两部分,先按Ctrl+B,让tmux知道要执行命令了,然后第二步按住需要执行的命令的按键。 如关闭命令,先按Ctrl+B,然后按 X,此时出现是否确认kill pane的提示,按住y,此时如果session中只有一个window(和一个pane,pane会在后文讲到),那么session也会被终止,从而退回最初执行tmux命令之前的状态。 p.s. tmux的 Ctrl+B相当于screen的Ctrl+A,不过这个是可以设置的,后文介绍的修改Tmux的配置中修改成了Ctrl+A。 ","date":"2020-07-18","objectID":"https://seacj.github.io/posts/tmux%E5%AD%A6%E4%B9%A0/:6:0","tags":["linux"],"title":"Tmux学习","uri":"https://seacj.github.io/posts/tmux%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"创建一个自命名的session并使用多个window 使用以下命令,即可创建一个名为mysession的session。 tmux new -s mysession 在一个session中可以有多个window, 使用Ctrl+B C命令,就在当前(current)会话创建一个新的window了,在新的window中执行top命令,可以看到绿色状态栏原本bash的位置变成了top。 接着使用Ctrl+B W命令,查看所有的window的列表,从而随意切换。也可以直接使用Ctrl+B然后按数字键0-9进行快速切换。 ","date":"2020-07-18","objectID":"https://seacj.github.io/posts/tmux%E5%AD%A6%E4%B9%A0/:7:0","tags":["linux"],"title":"Tmux学习","uri":"https://seacj.github.io/posts/tmux%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"分离(Detach) 和 接入(Attach) Session 如果detach一个session,界面会退出,但是session中的程序会继续在后台运行,而Attach就是回到指定session的界面。 detach的命令为Ctrl+B D attach的命令为tmux attach-session -t mysession ,(t意为target,mysession为session名),也可以简写成tmux a -t mysession 使用命令tmux ls可以查看当前所有session,从而确定要进入哪个session, 如果你已经在一个session中了使用Ctrl+B S能够快速查看和切换session。 按住右键能够展开查看window并切换,这是真的方便。 p.s. 在screen中,如果要进入一个已经attached的session,就要使用命令screen -D -r \u003csession-name\u003e强行将detach掉那个session(或者说先踢掉使用那个session的用户)然后再进入。而tmux中可以多个终端attach同一个session,比如想教别人远程操作服务器,两个人分别用不同电脑远程连接到同一个session中,其中一个人的所有的操作,在另一个人的电脑都会同时显示出来。 ","date":"2020-07-18","objectID":"https://seacj.github.io/posts/tmux%E5%AD%A6%E4%B9%A0/:8:0","tags":["linux"],"title":"Tmux学习","uri":"https://seacj.github.io/posts/tmux%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"分屏操作 Ctrl+b %:划分左右两个窗格。 Ctrl+b \":划分上下两个窗格。 Ctrl+b z:当前窗格全屏显示,再使用一次会变回原来大小。 Ctrl+b q:显示窗格编号。 分屏在tmux里面叫做窗格(pane),如上图就是3个pane,你会发现退出命令Ctrl+B X退出的时候也是提示你是否退出pane。session和window和pane的关系是:一个session里面可以有很多window,一个window里面可以有很多pane。 具体使用方式根据需求而异, 我自己比较喜欢2个session(一个跑python脚本、一个跑shell脚本)+多个window+单个或多个pane的工作方式, 如果是后台长期挂着的程序,比如我的世界服务端我还是会用screen。 常用命令总结 创建会话 tmux new -s \u003csession-name\u003e 分离session Ctrl+B D 接入session tmux a -t \u003csession-name\u003e 查看所有window Ctrl+B W 查看所有session tmux ls 或在session中使用Ctrl+B S,在列表按方向右键会展开看到window。 重命名session tmux rename-session -t \u003cold-session-name\u003e \u003cnew-name\u003e 修改Tmux的配置 修改tmux的配置基本是必须的,不然和screen根本拉不开差距。 配置文件来源:https://www.jianshu.com/p/fd3bbdba9dc9 修改方法是touch ~/.tmux.conf, 输入以下内容。 # Send prefix set-option -g prefix C-a unbind-key C-a bind-key C-a send-prefix # Use Alt-arrow keys to switch panes bind -n M-Left select-pane -L bind -n M-Right select-pane -R bind -n M-Up select-pane -U bind -n M-Down select-pane -D # Shift arrow to switch windows bind -n S-Left previous-window bind -n S-Right next-window # Mouse mode set -g mouse on # Set easier window split keys bind-key v split-window -h bind-key h split-window -v # Easy config reload bind-key r source-file ~/.tmux.conf \\; display-message \"tmux.conf reloaded\" 修改包括: Send prefix 把prefix的ctrl+b变为了ctrl+a,因为这样按起来方便些。基本上用tmux的都改了这个。 Use Alt-arrow keys to switch panes 不用按prefix,直接用alt+箭头在pane之间switch。实际用过之后才发现真是太方便了! Shift arrow to switch windows 不用按prefix,直接用shift+箭头在window之间switch。太方便了! Mouse mode 开启鼠标模式。用鼠标就能切换window,pane,还能调整pane的大小,方便! Set easier window split keys 这一部分是用来更方便切分pane的。prefix + v 代表竖着切,prefix + h 代表横着切。比起默认的切割方法不仅直观而且方便。 Easy config reload 下一次如果修改了.tmux.conf的设置的话,不用关掉tmux。直接用prefix+r,就能重新加载设置。 相关评价 开发工具 screen vs tmux https://ruby-china.org/topics/4834 How to Use tmux on Linux (and Why It’s Better Than Screen) https://www.howtogeek.com/671422/how-to-use-tmux-on-linux-and-why-its-better-than-screen/ ","date":"2020-07-18","objectID":"https://seacj.github.io/posts/tmux%E5%AD%A6%E4%B9%A0/:9:0","tags":["linux"],"title":"Tmux学习","uri":"https://seacj.github.io/posts/tmux%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"题记 早上起来室友在群里发消息说服务器被人黑了,还被用来挖矿了,多了一个dota3.tar.gz的文件,又让我开始警觉。我赶紧看了一下自己的阿里云服务器,发现每天都有ip在访问ssh,心态有点爆炸,不知道是本来就很普遍,还是我被盯上了。其实挖矿已经算好的了,前不久b站的党妹服务器被植入勒索软件,服务器里面所有文件包括他的视频素材都不能打开。这篇博客记录一下我看的一些数据资料,还有提升一下服务器安全等级的过程。 如何判断服务器被爆破 针对系统漏洞攻击的黑客我也防不住,但是SSH弱密码攻击的倒是可以防一手,腾讯安全服务中心的一篇文章写道说有个组织每天攻击十万个IP,所以说受到攻击的概率还是很大的。 使用lastb命令列出登入系统失败的用户相关信息。 我的服务器从六月一日到六月九日,9天时间有157条登入系统失败的记录! lastbimage-20200609150631079 \" lastb 提升服务器安全 腾讯安全服务中心有以下4条安全建议 1.用密钥登录,不要用密码登录 2.使用安全的密码策略,使用高强度密码,切勿使用弱口令,防止黑客暴力破解 3.开SSH只监听本地内网IP 4.尽量不给服务器外网IP 5.推荐部署腾讯御界高级威胁检测系统。 我这里使用修改ssh端口+密钥对登陆+禁止密码登陆的方式提升安全等级。 ","date":"2020-06-09","objectID":"https://seacj.github.io/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%A2%AB%E9%BB%91/:1:0","tags":["linux"],"title":"记一次服务器被黑","uri":"https://seacj.github.io/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%A2%AB%E9%BB%91/"},{"categories":["编程-ღ-技术"],"content":"修改ssh端口 ","date":"2020-06-09","objectID":"https://seacj.github.io/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%A2%AB%E9%BB%91/:2:0","tags":["linux"],"title":"记一次服务器被黑","uri":"https://seacj.github.io/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%A2%AB%E9%BB%91/"},{"categories":["编程-ღ-技术"],"content":"Step 1 注:一般需要root权限。 vim /etc/ssh/sshd_config打开配置文件, 在#Port 22下面添加一行Port [xxxx],[xxxx]为你要修改的端口号。 # The strategy used for options in the default sshd_config shipped with # OpenSSH is to specify options with their default value where # possible, but leave them commented. Uncommented options override the # default value. #Port 22 Port 4522 #AddressFamily any #ListenAddress 0.0.0.0 #ListenAddress :: 保存退出之后,用systemctl restart sshd(CentOS6系统用/etc/init.d/sshd restart)重启SSH服务,此时你就不能再用默认的22端口访问服务器了。 ","date":"2020-06-09","objectID":"https://seacj.github.io/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%A2%AB%E9%BB%91/:2:1","tags":["linux"],"title":"记一次服务器被黑","uri":"https://seacj.github.io/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%A2%AB%E9%BB%91/"},{"categories":["编程-ღ-技术"],"content":"Step 2 a. 如果是云服务器ECS的话,在服务器面板的安全组添加你设定的端口之后,就能用那个端口SSH访问服务器了 添加安全组image-20200609152649355 \" 添加安全组 b. 如果是自己的服务器的话使用下面的2条命令之后,即可访问了。 iptables -I INPUT -p tcp --dport 4522 -j ACCEPT service iptables save 此时就不再能用默认的22号端口连接,而要指定你配置的端口号。 ","date":"2020-06-09","objectID":"https://seacj.github.io/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%A2%AB%E9%BB%91/:2:2","tags":["linux"],"title":"记一次服务器被黑","uri":"https://seacj.github.io/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%A2%AB%E9%BB%91/"},{"categories":["编程-ღ-技术"],"content":"密钥对登陆 整个过程就是在本地主机生成公钥和私钥(两个合称密钥对),然后把公钥内容给服务器,这样只有带有私钥的主机才能访问服务器。 ","date":"2020-06-09","objectID":"https://seacj.github.io/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%A2%AB%E9%BB%91/:3:0","tags":["linux"],"title":"记一次服务器被黑","uri":"https://seacj.github.io/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%A2%AB%E9%BB%91/"},{"categories":["编程-ღ-技术"],"content":"Step 1. 本地生成密钥对 在命令行使用ssh-keygen命令,如果是windows用户,默认是在存放在C:\\Users\\[用户名]\\.ssh文件夹中的,详情可参考生成/添加SSH公钥。 现在我们有id_rsa(私钥)和id_rsa.pub(公钥)两个文件(这是默认的文件名,如果指定了文件名,就是xxx和xxx.pub文件)。 ","date":"2020-06-09","objectID":"https://seacj.github.io/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%A2%AB%E9%BB%91/:3:1","tags":["linux"],"title":"记一次服务器被黑","uri":"https://seacj.github.io/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%A2%AB%E9%BB%91/"},{"categories":["编程-ღ-技术"],"content":"Step 2. 配置服务器 首先确定之前有没有配置过ssh,在服务器的用户根目录使用命令ls -al,查看有没有名为.ssh的目录,如果没有就创建一个,并且权限设置为700. 在.ssh目录中,创建名为authorized_keys的文件,权限设置为600. 进入authorized_keys文件,将本地的id_rsa.pub的内容直接复制进authorized_keys中,并保存。 p.s. 配置我的另一个服务器的时候遇到了个问题,用chown g-w $HOME命令解决了。其原因是不能让所有者之外的用户对authorized_keys文件有写权限,否则,sshd将不允许使用该文件,因为它可能会被其他用户篡改。ref.ssh配置authorized_keys后仍然需要输入密码的问题 ","date":"2020-06-09","objectID":"https://seacj.github.io/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%A2%AB%E9%BB%91/:3:2","tags":["linux"],"title":"记一次服务器被黑","uri":"https://seacj.github.io/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%A2%AB%E9%BB%91/"},{"categories":["编程-ღ-技术"],"content":"Step 3. 测试无密码连接 用任意一个ssh软件使用密钥进行连接,这里以xshell为例。 连接方式从密码改为public key(有些软件可能叫Credentials),然后导入id_rsa文件。 测试无密码连接image-20200609164346003 \" 测试无密码连接 然后就可以无密码连接了。 ","date":"2020-06-09","objectID":"https://seacj.github.io/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%A2%AB%E9%BB%91/:3:3","tags":["linux"],"title":"记一次服务器被黑","uri":"https://seacj.github.io/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%A2%AB%E9%BB%91/"},{"categories":["编程-ღ-技术"],"content":"禁止密码登陆 注意:这个对所有用户都生效,而上面的密钥登陆只对某个用户生效。不过仍然可以用设置了密钥的用户登陆之后,再用su命令,切换到其他用户上。 和修改端口一样,修改文件vim /etc/ssh/sshd_config. vim中:$跳到最后一行,将PasswordAuthentication那一行改成no,保存后,同样systemctl restart sshd更新ssh。 UseDNS no AddressFamily inet SyslogFacility AUTHPRIV PermitRootLogin yes PasswordAuthentication no 如果觉得不能密码登陆可能会不方便的话,也可以设置禁止root登陆的方式提高安全性,即将PermitRootLogin那一行换成no或者without-password(即仅密钥登陆root). 至此,施工完成~ 参考资料 黑客利用SSH弱密码攻击控制Linux服务器,潜在目标约十万IP天 https://www.freebuf.com/articles/system/205384.html 修改ssh远程默认端口 https://www.cnblogs.com/wzstudy/p/10711104.html 设置禁止密码登陆 https://blog.csdn.net/superljn/article/details/81532073 sshd_config 中 PermitRootLogin 的探讨 https://yq.aliyun.com/articles/62501 ","date":"2020-06-09","objectID":"https://seacj.github.io/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%A2%AB%E9%BB%91/:4:0","tags":["linux"],"title":"记一次服务器被黑","uri":"https://seacj.github.io/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%A2%AB%E9%BB%91/"},{"categories":["编程-ღ-技术"],"content":"题记 偶然刷到一个视频如何使用 IDEA 插件刷 LeetCode 的题目介绍了一个插件,能够在IDE中刷题,感觉非常适合摸鱼,所以来安装体验一下,环境使用的是Pycharm。理论上支持IntelliJ IDEA、PhpStorm、WebStorm、PyCharm、RubyMine、AppCode、CLion GoLand、DataGrip、Rider MPS、Android Studio。 安装教程 ","date":"2020-06-08","objectID":"https://seacj.github.io/posts/ide%E6%8F%92%E4%BB%B6%E5%88%B7leetcode/:1:0","tags":["leetcode"],"title":"IDE插件刷LeetCode","uri":"https://seacj.github.io/posts/ide%E6%8F%92%E4%BB%B6%E5%88%B7leetcode/"},{"categories":["编程-ღ-技术"],"content":"Step 1. 下载插件 进入网站https://plugins.jetbrains.com/plugin/12132-leetcode-editor下载插件的压缩包leetcode-editor-6.4.zip。 ","date":"2020-06-08","objectID":"https://seacj.github.io/posts/ide%E6%8F%92%E4%BB%B6%E5%88%B7leetcode/:2:0","tags":["leetcode"],"title":"IDE插件刷LeetCode","uri":"https://seacj.github.io/posts/ide%E6%8F%92%E4%BB%B6%E5%88%B7leetcode/"},{"categories":["编程-ღ-技术"],"content":"Step 2. 导入插件 找到leetcode-editor-6.4.zip的位置,然后导入进去,重启pycharm。 ","date":"2020-06-08","objectID":"https://seacj.github.io/posts/ide%E6%8F%92%E4%BB%B6%E5%88%B7leetcode/:3:0","tags":["leetcode"],"title":"IDE插件刷LeetCode","uri":"https://seacj.github.io/posts/ide%E6%8F%92%E4%BB%B6%E5%88%B7leetcode/"},{"categories":["编程-ღ-技术"],"content":"Step 3. 配置账号(仅第一次安装需要配置) 需要注意的是和网站刷题一样,URL有leetcode.com和leetcode-cn.com两个可供选择,两者之间不互通。 使用体验 ","date":"2020-06-08","objectID":"https://seacj.github.io/posts/ide%E6%8F%92%E4%BB%B6%E5%88%B7leetcode/:4:0","tags":["leetcode"],"title":"IDE插件刷LeetCode","uri":"https://seacj.github.io/posts/ide%E6%8F%92%E4%BB%B6%E5%88%B7leetcode/"},{"categories":["编程-ღ-技术"],"content":"打开Leetcode窗口 点击右下角leetcode图标。 按照提示登陆之后就能刷题了。 ","date":"2020-06-08","objectID":"https://seacj.github.io/posts/ide%E6%8F%92%E4%BB%B6%E5%88%B7leetcode/:5:0","tags":["leetcode"],"title":"IDE插件刷LeetCode","uri":"https://seacj.github.io/posts/ide%E6%8F%92%E4%BB%B6%E5%88%B7leetcode/"},{"categories":["编程-ღ-技术"],"content":"刷题 界面挺漂亮的。 右键可选择提交、查看历史提交、查看解答。 题目的答案是markdown格式,可以下载markdown格式的插件查看。 ","date":"2020-06-08","objectID":"https://seacj.github.io/posts/ide%E6%8F%92%E4%BB%B6%E5%88%B7leetcode/:6:0","tags":["leetcode"],"title":"IDE插件刷LeetCode","uri":"https://seacj.github.io/posts/ide%E6%8F%92%E4%BB%B6%E5%88%B7leetcode/"},{"categories":["编程-ღ-技术"],"content":"查看解答 ","date":"2020-06-08","objectID":"https://seacj.github.io/posts/ide%E6%8F%92%E4%BB%B6%E5%88%B7leetcode/:6:1","tags":["leetcode"],"title":"IDE插件刷LeetCode","uri":"https://seacj.github.io/posts/ide%E6%8F%92%E4%BB%B6%E5%88%B7leetcode/"},{"categories":["编程-ღ-技术"],"content":"提交代码 参考资料 在编译器中刷LeetCode https://blog.csdn.net/weixin_40849588/article/details/96934259 上班摸鱼神器?如何使用 IDEA 插件刷 LeetCode 的题目 https://www.bilibili.com/video/BV16A411v71i PyCharm 在编辑 Markdown 的时候字体模糊的不行 https://www.v2ex.com/t/474649 【把Markdown Support插件卸载之后,打开一个markdown文件,会提示你有可用的插件,安装就行了】 ","date":"2020-06-08","objectID":"https://seacj.github.io/posts/ide%E6%8F%92%E4%BB%B6%E5%88%B7leetcode/:6:2","tags":["leetcode"],"title":"IDE插件刷LeetCode","uri":"https://seacj.github.io/posts/ide%E6%8F%92%E4%BB%B6%E5%88%B7leetcode/"},{"categories":["编程-ღ-技术"],"content":"题记 做了一个C程序,需要移植到安卓上,移植到安卓上就需要使用JNI(Java Native Interface),这里做一个Android Studio(AS)上使用JNI的记录。版本和环境信息如下,不同版本的AS操作有较大区别,也只能踩坑试试才知道行不行: 步骤 ","date":"2020-06-04","objectID":"https://seacj.github.io/posts/android_jni/:1:0","tags":["java"],"title":"Android Studio上使用JNI","uri":"https://seacj.github.io/posts/android_jni/"},{"categories":["编程-ღ-技术"],"content":"Step 1. 下载NDK NDK(Native Developer Kit)是谷歌给开发人员的工具包,感觉主要功能就是能够更容易使用JNI的工具。 下载之后解压,并将解压后的目录加入环境变量,我的路径是G:\\android-ndk-r21。 在终端输入 ndk-build 验证一下配置是否成功: ","date":"2020-06-04","objectID":"https://seacj.github.io/posts/android_jni/:2:0","tags":["java"],"title":"Android Studio上使用JNI","uri":"https://seacj.github.io/posts/android_jni/"},{"categories":["编程-ღ-技术"],"content":"Step 2. 新建项目 新建一个项目,不需要勾选Include C++ support.,新建之后调整成Project视图。 ","date":"2020-06-04","objectID":"https://seacj.github.io/posts/android_jni/:3:0","tags":["java"],"title":"Android Studio上使用JNI","uri":"https://seacj.github.io/posts/android_jni/"},{"categories":["编程-ღ-技术"],"content":"Step 3. AS环境配置 ","date":"2020-06-04","objectID":"https://seacj.github.io/posts/android_jni/:4:0","tags":["java"],"title":"Android Studio上使用JNI","uri":"https://seacj.github.io/posts/android_jni/"},{"categories":["编程-ღ-技术"],"content":"配置NDK路径 Ctrl+Alt+Shift+S打开配置,设定NDK路径: ","date":"2020-06-04","objectID":"https://seacj.github.io/posts/android_jni/:4:1","tags":["java"],"title":"Android Studio上使用JNI","uri":"https://seacj.github.io/posts/android_jni/"},{"categories":["编程-ღ-技术"],"content":"配置build.gradle文件 打开app文件夹中的build.gradle文件,在defaultConfig中输入以下代码,其中moduleName与最后生成的.so文件的文件名有关,即lib[moduleName].so,且Java源文件中loadLibrary方法的参数就是这个moduleName的名字。abiFilters表示CPU的型号,不过好像可以删掉,不删的话反而在调试的时候可能手机型号不支持。 ndk{ moduleName \"SEnhanceNDK\" // abiFilters \"arm64-v8a\", \"armeabi-v7a\", \"x86\", \"x86_64\" } 同样在这个build.gradle文件中,在buildTypes下输入以下代码。 sourceSets { main() { // jniLibs.srcDirs = ['src/main/libs'] jniLibs.srcDirs = [] jni.srcDirs = [] //屏蔽掉默认的jni编译生成过程 } } ref. Error: Your project contains C++ files but it is not using a supported native build system. ","date":"2020-06-04","objectID":"https://seacj.github.io/posts/android_jni/:4:2","tags":["java"],"title":"Android Studio上使用JNI","uri":"https://seacj.github.io/posts/android_jni/"},{"categories":["编程-ღ-技术"],"content":"新建Android.mk和Application.mk 跳过这步在后续步骤中报的错(图示第一次输入命令ndk-build是没有这两个.mk文件的情况): 在jni目录下新建Android.mk和Application.mk,输入内容如下: Android.mk: LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := myNDK # moduleName LOCAL_SRC_FILES := com_whu_jniproj_JNI.c # 下一步中c文件名字 include $(BUILD_SHARED_LIBRARY) Application.mk: APP_ABI := all ","date":"2020-06-04","objectID":"https://seacj.github.io/posts/android_jni/:4:3","tags":["java"],"title":"Android Studio上使用JNI","uri":"https://seacj.github.io/posts/android_jni/"},{"categories":["编程-ღ-技术"],"content":"Step 4. 编写JNI文件,并生成.so文件 ","date":"2020-06-04","objectID":"https://seacj.github.io/posts/android_jni/:5:0","tags":["java"],"title":"Android Studio上使用JNI","uri":"https://seacj.github.io/posts/android_jni/"},{"categories":["编程-ღ-技术"],"content":"1. 创建JAVA源文件,编写 native 方法 如图示创建一个java文件,写入代码(注意修改代码中package的名称),然后复制这个文件的路径,命令行切换到main/java目录中. 输入命令javah -d ../jni com.xxx.xxx.JNI,其中com.xxx.xxx.JNI需要替换成你复制的路径。 p.s. 新版本的jdk没有javah的命令,有资料说可以用javac -h这种命令,但是我尝试之后并没有解决,所以我的建议是直接换成jdk8,而且安装也很简单 命令执行完之后,就会生成一个.h文件,在同级目录创建一个名字相同的.c文件,输入以下代码用于测试,注意函数名要和.h文件的函数名一致: #include \"com_whu_senhance_JNI.h\" char* gettext(){ return \"Hi\"; } /** jstring: 返回值 Java_全类名_方法名 JNIEnv* env: 里面有很多方法 jobject jobj:谁调用这个方法就是谁;即JNI.this */ jstring Java_com_whu_senhance_JNI_sayHello(JNIEnv* env, jobject jobj){ //jstring (*NewStringUTF)(JNIEnv*, const char*); char* text = \"I am from C\"; text = gettext(); return (*env)-\u003eNewStringUTF(env,text); } ","date":"2020-06-04","objectID":"https://seacj.github.io/posts/android_jni/:5:1","tags":["java"],"title":"Android Studio上使用JNI","uri":"https://seacj.github.io/posts/android_jni/"},{"categories":["编程-ღ-技术"],"content":"2. 生成.so文件 方法一. 在jni目录中,执行ndk-build命令。 执行结束后会生成libs和obj文件夹,里面就是.so文件,需要创建一个名为jniLibs的文件夹,然后把libs中的内容全部复制进这个文件夹里面,才能使用.so文件。 方法二. AS菜单栏Build-\u003eRebuild Project Rebuild这个方法好像不太行,不确定问题在哪里,可能与build.gradle文件jniLibs.srcDirs或者jni.srcDirs的配置有关。 对build.gradle文件进行修改: sourceSets { main() { // jniLibs.srcDirs = ['src/main/libs'] jniLibs.srcDirs = [] jni.srcDirs = ['src/main/jni'] } } externalNativeBuild{ ndkBuild{ path file('src/main/jni/Android.mk') } } 然后rebuild,生成.so文件在app/build/intermediates/ndkBuild/debug/obj/local(实际上还有其他2个目录也生成了.so文件\"D:\\AndroidStudioProjects\\JNIProj\\app\\build\\intermediates\\transforms\\stripDebugSymbol\\debug\\0\\lib\\x86\\libmyNDK.so\"和\"D:\\AndroidStudioProjects\\JNIProj\\app\\build\\intermediates\\transforms\\mergeJniLibs\\debug\\0\\lib\\x86\\libmyNDK.so”)路径中(看参考资料,可能有的在intermediates/ndk路径里面,我这里没有ndk文件夹生成)。 与方法一不同,使用这个方式不需要复制.so的位置,可能默认的jnilib就是这个路径。 ","date":"2020-06-04","objectID":"https://seacj.github.io/posts/android_jni/:5:2","tags":["java"],"title":"Android Studio上使用JNI","uri":"https://seacj.github.io/posts/android_jni/"},{"categories":["编程-ღ-技术"],"content":"Step 5. 修改MainActivity并执行程序 package com.whu.senhance; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); String result = new JNI().sayHello(); System.out.println(\"result = \"+result); } } 结果 参考资料 JNI 开发详细初体验(Mac Android Studio 3.1+) - 掘金 https://juejin.im/post/5b90a7c1e51d450e697311de 尚硅谷Android视频教程《JNI》【注:AS版本很老】 https://www.bilibili.com/video/BV1qW411L7oY?p=35 Android+真机调试 https://www.cnblogs.com/xqz0618/p/12725515.html Android之JNI实现函数中LOG打印 【注:LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog要写在include $(CLEAR_VARS)的后面】https://blog.csdn.net/jjunjoe/article/details/7020236 其他事项 ","date":"2020-06-04","objectID":"https://seacj.github.io/posts/android_jni/:6:0","tags":["java"],"title":"Android Studio上使用JNI","uri":"https://seacj.github.io/posts/android_jni/"},{"categories":["编程-ღ-技术"],"content":"Android.mk添加目录中所有源文件 ref. Android.mk添加目录中所有源文件 使用场景:com_whu_jniproj_JNI.c中include了cljtest.h,并且使用了cljtest中的函数。 报错: 如果Android.mk为 LOCAL_SRC_FILES := com_whu_jniproj_JNI.c时,Build就会报错: Build command failed. Error while executing process G:\\android-ndk-r21\\ndk-build.cmd with arguments {NDK_PROJECT_PATH=null APP_BUILD_SCRIPT=D:\\AndroidStudioProjects\\JNIProj\\app\\src\\main\\jni\\Android.mk NDK_APPLICATION_MK=D:\\AndroidStudioProjects\\JNIProj\\app\\src\\main\\jni\\Application.mk APP_ABI=x86_64 NDK_ALL_ABIS=x86_64 NDK_DEBUG=1 APP_PLATFORM=android-21 NDK_OUT=D:/AndroidStudioProjects/JNIProj/app/build/intermediates/ndkBuild/debug/obj NDK_LIBS_OUT=D:\\AndroidStudioProjects\\JNIProj\\app\\build\\intermediates\\ndkBuild\\debug\\lib D:/AndroidStudioProjects/JNIProj/app/build/intermediates/ndkBuild/debug/obj/local/x86_64/libmyNDK.so} [x86_64] Compile : myNDK \u003c= com_whu_jniproj_JNI.c [x86_64] SharedLibrary : libmyNDK.so D:/AndroidStudioProjects/JNIProj/app/src/main/jni/com_whu_jniproj_JNI.c:17: error: undefined reference to 'gettesttext' clang++: error: linker command failed with exit code 1 (use -v to see invocation) make: *** [G:/android-ndk-r21/build//../build/core/build-binary.mk:725: D:/AndroidStudioProjects/JNIProj/app/build/intermediates/ndkBuild/debug/obj/local/x86_64/libmyNDK.so] Error 1 如果Android.mk为 LOCAL_SRC_FILES := cljtest.c时,Build通过,报错: 如果Android.mk为 LOCAL_SRC_FILES := cljtest.c com_whu_jniproj_JNI.c时,不报错。 调试方法,在android.mk中使用下列代码,然后在终端中使用ndk-build编译: $(warning \"the value of LOCAL_PATH is $(LOCAL_PATH)\") $(warning \"the value of LOCAL_SRC_FILES is $(LOCAL_SRC_FILES)\") 由于ref中的其他方案都报错,只能使用下面这种,注意这个方法不会递归找.c文件,是单目录的,不过也比一个一个加方便多了: FILE_LIST := $(wildcard $(LOCAL_PATH)/*.c) LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%) ","date":"2020-06-04","objectID":"https://seacj.github.io/posts/android_jni/:7:0","tags":["java"],"title":"Android Studio上使用JNI","uri":"https://seacj.github.io/posts/android_jni/"},{"categories":["教程-ღ-点评"],"content":"题记 最近又要用到latex,虽然说我自己不喜欢用(比如我觉得几秒的编译影响体验,能做到像markdown一样几乎是即时展示的就好了),真希望所有期刊和会议都像ICASSP2020一样有那么好用的word模板(好用指各种格式直接有母板,一键格式化,序号全都自动生成。不过word写公式依然是个问题,有一些兼容性的问题)。 之前是用TeXworks或者WinEdt去写latex,总觉得界面丑(主要原因),而且不会自动重新编译(也可能只是我不会用)觉得不方便。搜索latex教程的时候偶然发现可以用VSCode写LaTeX。所以决定用一下,这篇博客记录一下安装过程和简单的使用。 安装 ","date":"2020-05-20","objectID":"https://seacj.github.io/posts/vscode%E4%B8%8A%E5%AE%89%E8%A3%85latex/:1:0","tags":["LaTeX"],"title":"VSCode上安装LaTeX","uri":"https://seacj.github.io/posts/vscode%E4%B8%8A%E5%AE%89%E8%A3%85latex/"},{"categories":["教程-ღ-点评"],"content":"1. 安装 texlive Liam Huang的博客写的很详细:TeX Live 下载及安装说明 由于我是Windows系统,就直接选择下面链接的镜像下载了。 http://mirror.ctan.org/systems/texlive/Images/texlive2020.iso 下载得到的是一个近4G的texlive2020.iso,双击进入之后,用管理员身份运行。 安装 texlive安装 texlive \" 安装 texlive 然后我参照《使用VSCode编写LaTeX》进行安装,把安装目录改了一下。 把不需要的语言和宏包去掉把不需要的语言和宏包去掉 \" 把不需要的语言和宏包去掉 安装过程非常久,我安装了1个小时,这一点非常不友好,能像python那样需要什么包再装就好了。可以先同时开始进行下一步。 安装完成安装完成 \" 安装完成 ","date":"2020-05-20","objectID":"https://seacj.github.io/posts/vscode%E4%B8%8A%E5%AE%89%E8%A3%85latex/:2:0","tags":["LaTeX"],"title":"VSCode上安装LaTeX","uri":"https://seacj.github.io/posts/vscode%E4%B8%8A%E5%AE%89%E8%A3%85latex/"},{"categories":["教程-ღ-点评"],"content":"2. 安装VSCode中的LaTeX插件 安装VSCode中的LaTeX插件安装VSCode中的LaTeX插件 \" 安装VSCode中的LaTeX插件 随便打开一个.tex文件,可以看到有高亮了。 高亮LaTeX高亮LaTeX \" 高亮LaTeX 由于Texlive安装完成之后有加进环境变量,比如我的环境变量的PATH有G:\\texlive\\2020\\bin\\win32,所以这个时候不需要其他的配置,就已经可以直接编译使用了。 点击Build Latex project,此时就生成了一些新的文件: Build Latex projectBuild Latex project \" Build Latex project 再点击View LaTeX PDF,此时有3个选项,默认是VSCode tab,效果如下图所示在右侧有个pdf展示。如果要用外部的pdf浏览器需要设置配置文件。 View LaTeX PDFView LaTeX PDF \" View LaTeX PDF 现在其实已经可以简单使用了,但是想要充分利用,就要对插件进行配置。 ","date":"2020-05-20","objectID":"https://seacj.github.io/posts/vscode%E4%B8%8A%E5%AE%89%E8%A3%85latex/:3:0","tags":["LaTeX"],"title":"VSCode上安装LaTeX","uri":"https://seacj.github.io/posts/vscode%E4%B8%8A%E5%AE%89%E8%A3%85latex/"},{"categories":["教程-ღ-点评"],"content":"3. 配置LaTeX插件 Step 1. Ctrl+Shift+P 打开命令面板 Step 2. 选择Settings JSON,点击进入。 Settings JSONSettings JSON \" Settings JSON settings.json如下图所示,里面有个remote.SSH…那个是我自己安装的另一个插件的配置,不需要管它。 setting.jsonsetting JSON \" setting.json 配置中的一些路径(用方括号[]标出)需要自己修改。 ","date":"2020-05-20","objectID":"https://seacj.github.io/posts/vscode%E4%B8%8A%E5%AE%89%E8%A3%85latex/:4:0","tags":["LaTeX"],"title":"VSCode上安装LaTeX","uri":"https://seacj.github.io/posts/vscode%E4%B8%8A%E5%AE%89%E8%A3%85latex/"},{"categories":["教程-ღ-点评"],"content":"a. pdf设置 让编译生成的pdf,用指定阅读器打开,我这里用的是SumatraPDF。 \"latex-workshop.view.pdf.viewer\": \"external\", \"latex-workshop.view.pdf.external.viewer.command\": \"【G:/Program Files/SumatraPDF/SumatraPDF.exe】\", ","date":"2020-05-20","objectID":"https://seacj.github.io/posts/vscode%E4%B8%8A%E5%AE%89%E8%A3%85latex/:4:1","tags":["LaTeX"],"title":"VSCode上安装LaTeX","uri":"https://seacj.github.io/posts/vscode%E4%B8%8A%E5%AE%89%E8%A3%85latex/"},{"categories":["教程-ღ-点评"],"content":"b. 配置正向和反向 让你能从tex文件定位到pdf文件所在的位置,正向就是从tex定位到pdf,反向是从pdf定位到tex。 \"latex-workshop.view.pdf.external.synctex.command\": \"【G:/Program Files/SumatraPDF/SumatraPDF.exe】\", \"latex-workshop.view.pdf.external.synctex.args\": [ \"-forward-search\", \"%TEX%\", \"%LINE%\", \"-reuse-instance\", \"-inverse-search\", \"code \\\"【C:/Program Files/Microsoft VS Code/resources/app/out/cli.js】\\\" -r -g \\\"%f:%l\\\"\", \"%PDF%\", ], 点击SyncTex from cursor,就会跳到pdf中的对应位置,效果如下。 正向搜索正向搜索 \" 正向搜索 在pdf中直接双击就可以跳到tex中了。 ","date":"2020-05-20","objectID":"https://seacj.github.io/posts/vscode%E4%B8%8A%E5%AE%89%E8%A3%85latex/:4:2","tags":["LaTeX"],"title":"VSCode上安装LaTeX","uri":"https://seacj.github.io/posts/vscode%E4%B8%8A%E5%AE%89%E8%A3%85latex/"},{"categories":["教程-ღ-点评"],"content":"c. 取消保存自动编译 默认是会Ctrl S保存就自动编译,这就意味着每次保存,pdf也会随之改变。我习惯用markdown,所以我自己是不设置这个的。 如果想取消保存自动编译可以加上下面这条。 \"latex-workshop.latex.autoBuild.run\": \"never\", 我的配置文件我的配置文件 \" 我的配置文件 简单的使用 先随便拿一个模板,我用的是Springer LNCS LaTeX投稿模板 简单的使用简单的使用 \" 简单的使用 非常简单的使用了一下,目前感觉有以下三大优势。 ","date":"2020-05-20","objectID":"https://seacj.github.io/posts/vscode%E4%B8%8A%E5%AE%89%E8%A3%85latex/:4:3","tags":["LaTeX"],"title":"VSCode上安装LaTeX","uri":"https://seacj.github.io/posts/vscode%E4%B8%8A%E5%AE%89%E8%A3%85latex/"},{"categories":["教程-ღ-点评"],"content":"优势 好看(VSCode的界面非常整洁,风格我也很喜欢,另外可能是因为是windows的产品做了优化的原因,我总感觉字体很柔顺) 自动代码填充(和一般的代码填充类似,比如1. 输入一个\\be,就会跳出\\begin 之类的让你选择,按回车就键入补全了 2. 输入一个\\begin{figure},就会自动出来一个\\end{figure}) VSCode上还有其他的一些插件可以配合使用,比如Spell Right进行像Word一样的拼写检查,不过检查不出语法错误。 ","date":"2020-05-20","objectID":"https://seacj.github.io/posts/vscode%E4%B8%8A%E5%AE%89%E8%A3%85latex/:5:0","tags":["LaTeX"],"title":"VSCode上安装LaTeX","uri":"https://seacj.github.io/posts/vscode%E4%B8%8A%E5%AE%89%E8%A3%85latex/"},{"categories":["教程-ღ-点评"],"content":"结论 其实好看这一点就赢了,应该是再也不会用WinEdt了。 注:目前插件(不过似乎不是插件的问题,是latex本身设计的问题)不支持使用中文路径下的文件,可参考TexLive+VSCode如何支持中文路径解决这个问题。不过我个人建议在无中文的路径下编辑,编辑完了再移动文件。 参考资料 使用VSCode编写LaTeX 知乎:使用VSCode编写LaTeX LaTeX初体验 草祭の博客: LaTeX初体验 ","date":"2020-05-20","objectID":"https://seacj.github.io/posts/vscode%E4%B8%8A%E5%AE%89%E8%A3%85latex/:6:0","tags":["LaTeX"],"title":"VSCode上安装LaTeX","uri":"https://seacj.github.io/posts/vscode%E4%B8%8A%E5%AE%89%E8%A3%85latex/"},{"categories":["教程-ღ-点评"],"content":"题记 想给我的博客换个头像,顺便查了一下avatar的词源,是印度教里面神仙下凡的意思,觉得也是挺有意思的。那既然是avatar,肯定是想要“独一无二”的。自己做觉得自己水平非常有限,网上的捏脸和像素画又觉得很一般,这时候就自然的想到我的b站收藏夹里面LKs的一个网站推荐视频提到了一个名为Artbreeder的网站。之前简单的体验过,还是有点惊艳的,所以决定自己用它来“合成”一个博客的头像。 什么是Artbreeder 网站地址:https://artbreeder.com/ 功能:通过在网站中点选图片,自动生成同时具备选定图片特性的新的图像 原理:神经网络(AI) 其他事项:1. 需要注册才能使用 2. 只能使用站内的图片进行合成 3. 高清图片只有10次下载,超过10次需要升级账号 记录使用过程 ","date":"2020-05-18","objectID":"https://seacj.github.io/posts/artbreeder/:1:0","tags":["产品体验","网站推荐"],"title":"图片生成器Artbreeder","uri":"https://seacj.github.io/posts/artbreeder/"},{"categories":["教程-ღ-点评"],"content":"Step. 1选择生成图像的分类 我先点选Anime Portraits. ","date":"2020-05-18","objectID":"https://seacj.github.io/posts/artbreeder/:2:0","tags":["产品体验","网站推荐"],"title":"图片生成器Artbreeder","uri":"https://seacj.github.io/posts/artbreeder/"},{"categories":["教程-ღ-点评"],"content":"Step. 2 选择创造方式 有随机和合成两个方式可供选择。 ","date":"2020-05-18","objectID":"https://seacj.github.io/posts/artbreeder/:3:0","tags":["产品体验","网站推荐"],"title":"图片生成器Artbreeder","uri":"https://seacj.github.io/posts/artbreeder/"},{"categories":["教程-ღ-点评"],"content":"随机模式 不断随机给你一些图片(但是我刷了半天都没有男性头像),选择任意一个之后,点击进行进一步调整。 最为有亮点的调整就是crossbreed,它会让你在选择一个图片,从而得到两个图片合成出的新图片。 Edit-Genes让你在细节上可以进行微调,感觉是用到了CVPR best paper simGAN。 ","date":"2020-05-18","objectID":"https://seacj.github.io/posts/artbreeder/:3:1","tags":["产品体验","网站推荐"],"title":"图片生成器Artbreeder","uri":"https://seacj.github.io/posts/artbreeder/"},{"categories":["教程-ღ-点评"],"content":"合成模式 合成模式下就可以直接先选择图片,不过看起来random里面绝大部分都是女性角色。 好在可以直接搜索关键字。 选择好几张图片之后,可以滑动小图片旁边的圆形滚轮,对Content和Style的比例进行调整。 调整好之后,同样是可以进入到和随机模式一样的微调阶段。 相关评价 怎么看artbreeder.com这个网站? 知乎 ","date":"2020-05-18","objectID":"https://seacj.github.io/posts/artbreeder/:3:2","tags":["产品体验","网站推荐"],"title":"图片生成器Artbreeder","uri":"https://seacj.github.io/posts/artbreeder/"},{"categories":["博客"],"content":" 添加即时通讯 修改Hugo主题为LoveIt 修改彩蛋页面 新增站内搜索(第一次配置好了之后,以后更新博客只需要运行hugo-algolia -s就能更新搜索了。简单的使用是没有问题的,但是会有一些小bug,不过问题不大吧,就不修复了) 使用typora-plugins-win-img 新增RSS 相关链接 添加即使通讯 博客添加在线通讯 LoveIt主题配置 主题文档 - 基本概念 用 Hugo 配合 Algolia 实现高效美观的站内搜索 用 Hugo 配合 Algolia 实现高效美观的站内搜索 typora-plugins-win-img typora-plugins-win-img ","date":"2020-05-12","objectID":"https://seacj.github.io/posts/%E5%BB%BA%E7%AB%99%E5%8E%86%E5%8F%B2007/:0:0","tags":["博客搭建","建站历史"],"title":"建站历史007","uri":"https://seacj.github.io/posts/%E5%BB%BA%E7%AB%99%E5%8E%86%E5%8F%B2007/"},{"categories":["博客"],"content":"题记 又想折腾一下博客,用LeaveIt主题有段时间了,打算换一个主题,Hugo官网的top1的主题academic折腾了一下感觉不太适合我,因为外观太官方了正式了,所以就换成LoveIt主题。这篇博客就是简单的展示一下LoveIt主题的显示,也用来为以后主题迁移做调试使用。 代码 ","date":"2020-05-11","objectID":"https://seacj.github.io/posts/%E6%B5%8B%E8%AF%95%E6%96%B0%E4%B8%BB%E9%A2%98loveit/:0:0","tags":["博客搭建"],"title":"测试博客新主题","uri":"https://seacj.github.io/posts/%E6%B5%8B%E8%AF%95%E6%96%B0%E4%B8%BB%E9%A2%98loveit/"},{"categories":["博客"],"content":"C代码 #include \u003cstdio.h\u003e int main() #main 入口函数 { printf(\"Hello,World!\"); #printf 函数打印 return 1; #函数返回值 } ","date":"2020-05-11","objectID":"https://seacj.github.io/posts/%E6%B5%8B%E8%AF%95%E6%96%B0%E4%B8%BB%E9%A2%98loveit/:1:0","tags":["博客搭建"],"title":"测试博客新主题","uri":"https://seacj.github.io/posts/%E6%B5%8B%E8%AF%95%E6%96%B0%E4%B8%BB%E9%A2%98loveit/"},{"categories":["博客"],"content":"C++代码 #include \u003ciostream\u003e //std::cout 要用到的头文件 #include \u003cstdio.h\u003e //标准输入输出头文件 int main() { printf(\"Hello,World!--Way 1\\n\"); //printf 语句打印 puts(\"Hello,World!--Way 2\"); //puts 语句 puts(\"Hello,\" \" \" \"World!--Way 3\"); //字符串拼接 std::cout \u003c\u003c \"Hello,World!--Way 4\" \u003c\u003c std::endl; //C++ 教科书上写法 return 1; //作为注释 } ","date":"2020-05-11","objectID":"https://seacj.github.io/posts/%E6%B5%8B%E8%AF%95%E6%96%B0%E4%B8%BB%E9%A2%98loveit/:2:0","tags":["博客搭建"],"title":"测试博客新主题","uri":"https://seacj.github.io/posts/%E6%B5%8B%E8%AF%95%E6%96%B0%E4%B8%BB%E9%A2%98loveit/"},{"categories":["博客"],"content":"有趣的代码 print(' ─────────▄──────────────▄──── \\n', \\ ' ─ wow ──▌▒█───────────▄▀▒▌─── \\n', \\ ' ────────▌▒▒▀▄───────▄▀▒▒▒▐─── \\n', \\ ' ───────▐▄▀▒▒▀▀▀▀▄▄▄▀▒▒▒▒▒▐─── \\n', \\ ' ─────▄▄▀▒▒▒▒▒▒▒▒▒▒▒█▒▒▄█▒▐─── \\n', \\ ' ───▄▀▒▒▒▒▒▒ such difference ─ \\n', \\ ' ──▐▒▒▒▄▄▄▒▒▒▒▒▒▒▒▒▒▒▒▒▀▄▒▒▌── \\n', \\ ' ──▌▒▒▐▄█▀▒▒▒▒▄▀█▄▒▒▒▒▒▒▒█▒▐── \\n', \\ ' ─▐▒▒▒▒▒▒▒▒▒▒▒▌██▀▒▒▒▒▒▒▒▒▀▄▌─ \\n', \\ ' ─▌▒▀▄██▄▒▒▒▒▒▒▒▒▒▒▒░░░░▒▒▒▒▌─ \\n', \\ ' ─▌▀▐▄█▄█▌▄▒▀▒▒▒▒▒▒░░░░░░▒▒▒▐─ \\n', \\ ' ▐▒▀▐▀▐▀▒▒▄▄▒▄▒▒▒ electrons ▒▌ \\n', \\ ' ▐▒▒▒▀▀▄▄▒▒▒▄▒▒▒▒▒▒░░░░░░▒▒▒▐─ \\n', \\ ' ─▌▒▒▒▒▒▒▀▀▀▒▒▒▒▒▒▒▒░░░░▒▒▒▒▌─ \\n', \\ ' ─▐▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▐── \\n', \\ ' ──▀ amaze ▒▒▒▒▒▒▒▒▒▒▒▄▒▒▒▒▌── \\n', \\ ' ────▀▄▒▒▒▒▒▒▒▒▒▒▄▄▄▀▒▒▒▒▄▀─── \\n', \\ ' ───▐▀▒▀▄▄▄▄▄▄▀▀▀▒▒▒▒▒▄▄▀───── \\n', \\ ' ──▐▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▀▀──────── \\n' ) 公式 $$\\begin{aligned} x \u0026=a+b+c \\\\\u0026=d+e \\\\\u0026=f+g \\end{aligned} $$ $$ \\begin{aligned} x \u0026=a+b+c \\ \u0026=d+e \\ \u0026=f+g \\end{aligned} $$ 表格 左对齐 右对齐 居中对齐 单元格 单元格 单元格 单元格 单元格 单元格 最后 以后再去慢慢魔改 ","date":"2020-05-11","objectID":"https://seacj.github.io/posts/%E6%B5%8B%E8%AF%95%E6%96%B0%E4%B8%BB%E9%A2%98loveit/:3:0","tags":["博客搭建"],"title":"测试博客新主题","uri":"https://seacj.github.io/posts/%E6%B5%8B%E8%AF%95%E6%96%B0%E4%B8%BB%E9%A2%98loveit/"},{"categories":["博客"],"content":" 修改文章分类 修改字体大小(修改LeaveIt\\assets\\css_common_page\\post.scss中的font-size) 新增音频播放器 ","date":"2020-04-08","objectID":"https://seacj.github.io/posts/%E5%BB%BA%E7%AB%99%E5%8E%86%E5%8F%B2006/:0:0","tags":["博客搭建","建站历史"],"title":"建站历史006","uri":"https://seacj.github.io/posts/%E5%BB%BA%E7%AB%99%E5%8E%86%E5%8F%B2006/"},{"categories":["博客"],"content":"相关链接 用CSS和JS实现的简易的音乐播放器 ","date":"2020-04-08","objectID":"https://seacj.github.io/posts/%E5%BB%BA%E7%AB%99%E5%8E%86%E5%8F%B2006/:1:0","tags":["博客搭建","建站历史"],"title":"建站历史006","uri":"https://seacj.github.io/posts/%E5%BB%BA%E7%AB%99%E5%8E%86%E5%8F%B2006/"},{"categories":["随笔-ღ-心得"],"content":"\r\r “人死了,就变成一个星星。” “干嘛变成星星呀?” “给走夜道儿的人照个亮儿……” ——史铁生 我们从古以来,就有埋头苦干的人,又拼命硬干的人,有为民请命的人,有舍身求法的人,……虽是等于为帝王将相作家谱的所谓“正史”,也往往掩不住他们的光耀,这就是中国的脊梁。 ——鲁迅《且介亭杂文》 这一天不是为了让我们无聊致死,而是为了让我们活的明白。 我们只是被困住了两个月,他们却被永远困在了这个冬天。逝者安息,英雄不朽,致敬! “他们不是数字” 以人类命运共同体的名义驱散乌云 文明互鉴 Fight as ONE \r视频链接 视频链接 ","date":"2020-04-04","objectID":"https://seacj.github.io/posts/2020%E5%B9%B44%E6%9C%884%E6%97%A5%E4%B8%BE%E5%9B%BD%E5%93%80%E6%82%BC/:0:0","tags":["抗击新冠肺炎"],"title":"2020年4月4日举国哀悼","uri":"https://seacj.github.io/posts/2020%E5%B9%B44%E6%9C%884%E6%97%A5%E4%B8%BE%E5%9B%BD%E5%93%80%E6%82%BC/"},{"categories":["编程-ღ-技术"],"content":"题记 需要实现一个音频重采样的功能,发现没有我想象的那么简单,这里通过学习python中librosa的代码,来学习一下重采样,这里将以48kHz下采样到8kHz为例进行讲解。为方便阅读,本文将采样频率写成sr(sampling rate)。注意本文并没有最终实现一个性能很好的重采样程序。 原始48kHz音频:p232_072.wav 点击下载 最简单的实现输出音频:my_p232_072_8kHz.wav 点击下载 librosa重采样输出音频:p232_072_8kHz.wav 点击下载 重采样代码2.0输出音频:my2_p232_072_8kHz.wav 点击下载 最简单的实现 每4(4=48/8)个采样点取一个点. # 直接每隔times个采样点取一个值 def my_downsample(data,input_sr,output_sr): assert input_sr%output_sr == 0 times = input_sr//output_sr out = np.empty(len(data)//times+1) for i, d in enumerate(data): if i%times == 2: out[i//times] = data[i] return out 从结果上来看,好像勉强成功了,但是与librosa的实现相比,明显多了很多噪声,为什么会这样呢? 混叠 当采样频率设置不合理时,即采样频率低于2倍的信号频率时,会导致原本的高频信号被采样成低频信号。如下图所示,红色信号是原始的高频信号,但是由于采样频率不满足采样定理的要求,导致实际采样点如图中蓝色实心点所示,将这些蓝色实际采样点连成曲线,可以明显地看出这是一个低频信号。在图示的时间长度内,原始红色信号有18个周期,但采样后的蓝色信号只有2个周期。也就是采样后的信号频率成分为原始信号频率成分的1/9,这就是所谓的混叠:高频混叠成低频了。——参考资料[1] 如上图所示,频率分别为4500Hz和5500Hz的信号,经过相同的sr10000之后,采样结果完全相同。 我們對 5500 Hz 訊號用 10000 的取樣率得到的結果跟 4500 Hz 的結果是分不出來的。同樣 7700 Hz 與 2300 Hz 的結果也是分不出來,9900 Hz 與 100 Hz 也是。 這個效應叫做 aliasing,因為高頻訊號被取樣時,他變得跟低頻的一樣。 在這個例子,10000 取樣率的一半是 5000,就是我們可取率的最高頻率。超過 5000 的頻率,就會被折掉 5000 Hz。這個取樣率的上限就因此叫做折疊頻率。它有時也被叫做 奈奎斯特頻率。請參閱 http://en.wikipedia.org/wiki/Nyquist_frequency 如果 aliasing 頻率被折到低於 0,這折疊頻率要再繼續折。例如,1100 Hz 的三角波的第 5 個諧波是在 12100 Hz,折疊頻率是 5000 Hz,那它應該出現在 -2100 Hz,但它應該對 0 Hz 折一次,就變成出現在 2100 Hz。事實上,在圖2.4 裡你可以看到有個小尖峰在 2100 Hz,再下一個是在 4300 Hz。 ——参考资料[2] 另一个例子: 上图可以观察得: 红色线所表示的信号在20个Δt的时间里,走过了18个周期(数波峰数量就行了),也就是说它的实际信号频率是$\\frac{18}{20Δt}$。 而如果我们每隔一个Δt采样一次,即采样率sr为$\\frac{1}{Δt}$。 而蓝色的线就展示了,采样之后原始信号被解释成了频率为$\\frac{2}{20Δt}$的信号。 最简单的实现方法里之所以多了很多噪声,是因为将高频(高指的是高于采样率一半)的信号转化成了更低频的信号。那么如果在最简单的实现方法之前加入一个低通滤波器,就可以过滤掉可能会造成混叠的高频信号。 重采样代码2.0 from scipy import signal import soundfile as sf data,sr = sf.read('p232_072.wav') Fs_highest = 4000 wn=2*Fs_highest/sr #要滤除Fs_highest hz以上频率成分 b, a = signal.butter(8, wn, 'lowpass') #配置滤波器 8 表示滤波器的阶数 filtedData = signal.filtfilt(b, a, data) #data为要过滤的信号 new = my_downsample(filtedData,48000,8000) sf.write('my2_p232_072_8kHz.wav',new,8000) 效果相比第一种方法有很大提升. 重采样代码3.0 取times个采样点的平均值,而不是每times个点取一次。 # 计算times个采样点的平均值作为一个采样点 def my_downsample_avg(data,input_sr,output_sr): assert input_sr%output_sr == 0 times = input_sr//output_sr out = np.empty(len(data)//times+1) accu = 0 for i, d in enumerate(data): accu+=data[i] if i%times == 3: out[i//times] = accu/4 accu = 0 return out v3 = my_downsample_avg(data,48000,8000) sf.write('my3_p232_072_8kHz.wav',v3,8000) 效果比v1要好,但是波形幅度有一些变化. 在前面加一个低通滤波器之后,效果上和v2差距不大,波形的幅值有些变化。 后记 在信号处理上,反转180度称作卷积,直接滑动计算称作自相关,在大部分深度学习框架(包括tensorflow和pytorch)上都没有反转180度的操作。 关于滤波器之类的我也没有深入去了解,所以就略过了。这篇博客只是想说明一下重采样不是那么简单的,以及混叠现象的存在。 参考资料 [1] 什么是混叠? https://zhuanlan.zhihu.com/p/23923059 [2] [超譯]ThinkDSP 第二章 http://timag-shield.blogspot.com/2017/04/thinkdsp_27.html [3] 几种常见窗函数的特性 https://blog.csdn.net/juhou/article/details/81194566 [4] 在定义卷积时为什么要对其中一个函数进行翻转? https://www.zhihu.com/question/20500497 [5] 卷积要旋转180度 https://www.jianshu.com/p/8dfe02b61686 [7] Resampling a sound sample, what filter do I use? https://stackoverflow.com/questions/4393545/resampling-a-sound-sample-what-filter-do-i-use [8] filtfilt源代码 https://github.com/scipy/scipy/blob/v1.4.1/scipy/signal/signaltools.py#L3687-L3885 [9] lfilter源代码https://github.com/scipy/scipy/blob/adc4f4f7bab120ccfab9383aba272954a0a12fb0/scipy/signal/signaltools.py#L1695 [10] Applying filter in scipy.signal: Use lfilter or filtfilt? https://dsp.stackexchange.com/questions/19084/applying-filter-in-scipy-signal-use-lfilter-or-filtfilt [11] 模拟低通滤波器 陈后金 https://www.bilibili.com/video/av26706434/?p=51\u0026t=91 [12] scipy.signal.resample https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.resample.html ","date":"2020-03-20","objectID":"https://seacj.github.io/posts/%E9%9F%B3%E9%A2%91%E9%87%8D%E9%87%87%E6%A0%B7/:1:0","tags":["信号处理","python"],"title":"音频重采样简述","uri":"https://seacj.github.io/posts/%E9%9F%B3%E9%A2%91%E9%87%8D%E9%87%87%E6%A0%B7/"},{"categories":["随笔-ღ-心得"],"content":"题记 做了一个梦,这个梦非常神奇所以赶紧趁没忘记记下来。 今天的梦 我坐地铁和朋友A(感觉好像是wsh,但是他家不住那附近呀)回家,从哪里回去的就不知道了,结果出地铁的时候居然一出去就是东方半岛门口的那座天桥,然后朋友A就出地铁走了,然后我这时候要转地铁,结果坐扶手电梯的时候做错了方向,不过很快又坐回来了,不过在梦里折腾了一下。然后我这时候想看地铁线路图,看怎么转车,这个时候,一个朋友(醒来之后,意识到这个朋友是我在以前的一个梦里交的朋友)过来,说好巧啊,居然碰见了,他家住赤湾(我明明对赤湾这地方一点印象都没有,只知道赤湾应该是个地铁站的名字,不知道为啥会会有这种梦的设定),然后我这时候说我眼镜坏了(又好像是没戴眼镜)帮我看一下地铁线路图,然后我就一直说帮我看地铁线路图,也一边看我现在在哪的位置,但是找半天都没找到,他就一直在捣乱,一下说我们在这边,一下说我们在那边。 折腾半天,我发现好像有点不对劲,突然发现这不是地铁线路图,是世界地图,我们俩狂笑,躺在地上爆笑,居然对着个世界地图找了半天现在的故事,这时候我差不多就要笑醒了。 然后点了一下世界地图(感觉好像这是个墙上的LED触控之类的屏幕),切换到了地铁线路图,就醒了。 以前的梦 ","date":"2020-03-15","objectID":"https://seacj.github.io/posts/%E5%A5%87%E5%A5%87%E6%80%AA%E6%80%AA%E7%9A%84%E6%A2%A6/:1:0","tags":null,"title":"奇奇怪怪的梦","uri":"https://seacj.github.io/posts/%E5%A5%87%E5%A5%87%E6%80%AA%E6%80%AA%E7%9A%84%E6%A2%A6/"},{"categories":["随笔-ღ-心得"],"content":"题记 因为是以前的梦,所以很多都忘记了,因为居然在今天的梦里碰见了以前的梦里的人,赶紧记下来。 ","date":"2020-03-15","objectID":"https://seacj.github.io/posts/%E5%A5%87%E5%A5%87%E6%80%AA%E6%80%AA%E7%9A%84%E6%A2%A6/:2:0","tags":null,"title":"奇奇怪怪的梦","uri":"https://seacj.github.io/posts/%E5%A5%87%E5%A5%87%E6%80%AA%E6%80%AA%E7%9A%84%E6%A2%A6/"},{"categories":["随笔-ღ-心得"],"content":"梦中事 在某个地方(不知道是哪里)打篮球,认识了一个朋友,在梦里因为是第一次认识的人,我自己本身很难记住别人的名字,就赶紧把他的名字用手机(或者是书吧)记下来了(然而是梦里的手机/书)。 打完篮球之后回家,我跟他说我到木棉湾转车,他跟我一起到木棉湾,然后就分开了,不过地铁上(又好像是巴士,因为中途好像经过了草埔的那个关口)我们聊天聊了挺久(但是忘了内容),其中聊到说他家离我家挺近的(指的是站数,而不是步行距离)。 ","date":"2020-03-15","objectID":"https://seacj.github.io/posts/%E5%A5%87%E5%A5%87%E6%80%AA%E6%80%AA%E7%9A%84%E6%A2%A6/:3:0","tags":null,"title":"奇奇怪怪的梦","uri":"https://seacj.github.io/posts/%E5%A5%87%E5%A5%87%E6%80%AA%E6%80%AA%E7%9A%84%E6%A2%A6/"},{"categories":["编程-ღ-技术"],"content":"题记 最近在b站刷了好几个游戏区up主更新了Minecraft(我的世界)的视频,心血来潮我也想开始玩一玩,依稀记得好像Minecraft可以装在远程,然后多人共同使用,查了一下同时联机人数为5或以下,内存需要512MB ,所以心血来潮在我的乞丐配置的服务器上建一个Minecraft服务器。我主要参照如何搭建一个“我的世界”服务器 - 快速指南进行搭建,这篇博客就来记录一下我搭建的过程。 我的服务器配置 Ubuntu系统 2G内存 1核CPU 带宽1M(我在使用1.15.2的版本进行多人游戏时,需要升级带宽到2M,否则当玩家大于2人时就会出现频繁断线,甚至连接不上的情况) 服务器搭建 mkdir minecraft 新建一个名为minecraft的文件夹 cd minecraft 进入minecraft文件夹 打开25565号端口,如果是阿里云服务器直接通过控制台的安全组就能配置。 wget https://s3.amazonaws.com/Minecraft.Download/versions/1.12.2/minecraft_server.1.12.2.jar 下载minecraft服务器,这里版本为1.12.2. sudo apt install default-jdk 安装JDK,如果你的服务器执行javac -version命令,能够打印出javac版本号,则跳过此步。 sudo apt-get install screen 安装screen,用于让minecraft在服务器后台运行。 free -m查看内存(我的空闲内存只有986M(ㄒoㄒ)) total used free shared buff/cache available Mem: 1993 215 986 3 790 1601 Swap: 947 160 786 java -Xms1024M -Xmx2048M -jar minecraft_server.1.12.2.jar nogui -Xms设置服务器最小内存 -Xmx是最大内存。 此时会报一个错,是正常的, 此时会生成一些文件,其中一个名为eula.txt。 [03:53:30] [Server thread/INFO]: Starting minecraft server version 1.12.2 [03:53:30] [Server thread/INFO]: Loading properties [03:53:30] [Server thread/WARN]: server.properties does not exist [03:53:30] [Server thread/INFO]: Generating new properties file [03:53:30] [Server thread/WARN]: Failed to load eula.txt [03:53:30] [Server thread/INFO]: You need to agree to the EULA in order to run the server. Go to eula.txt for more info. [03:53:30] [Server thread/INFO]: Stopping server [03:53:30] [Server Shutdown Thread/INFO]: Stopping server sed -i.orig 's/eula=false/eula=true/g' eula.txt 执行这个命令,来修改里面的内容,相当于安装软件时勾选同意协议的意思。 java -Xms1024M -Xmx2048M -jar minecraft_server.1.12.2.jar nogui 运行minecraft服务器。 一切正常的话会显示 [04:05:03] [Server thread/INFO]: Done (4.439s)! For help, type \"help\" or \"?\"。此时服务器就在运行了。 本地客户端连接 在本地(我是windows10系统,使用的HMCL启动器),打开minecraft(注意版本要和服务器一样),点击Direct Connect(Add Server可以存下ip地址,就不需要每次输入了),输入你服务器的ip地址。 如果此时你没有连上,且你的服务器显示如下的信息: xxxxxxxxx@xxxxxxx[id=\u003cnull\u003e,name=xxxxx,properties={},legacy=false] (/xxxxxxxx:50786) lost connection: Disconnected 这是因为非正版玩家被设定成禁止进入了。 此时在服务器上修改minecraft目录中的server.properties文件,将online-mode=true修改成online-mode=false. 再次启动服务器之后,在本地就可以进入了! 最后一步 正式运行minecraft服务器,在之前的步骤中,启动mincraft服务器之后,窗口不能关闭,否则服务器就会断开,现在让服务器一直在服务器后台开启。 screen -S mc 创建名为mc的session。 java -Xms1024M -Xmx2048M -jar minecraft_server.1.12.2.jar nogui启动服务器。 Screen的指令: screen -ls 查看所有session,会打印出如后台的名称。 screen -r mc 会切换到名为mc的session中。 先按住ctrl+a,然后按d. 能够跳出当前的session,但是后台程序依然会继续运行。 exit严格来说这不是Screen的命令,而是Linux中退出shell的命令,但是screen中的会在你退出最后一个程序(通常就是shell)的时候自动关闭,所以可以用来彻底关闭session用。 追加mod Note:我自己重新搭建了1.15.2版本的服务器,注意与上文1.12.2区分开,请选择你需要的版本. ","date":"2020-03-13","objectID":"https://seacj.github.io/posts/ubuntu%E6%90%AD%E5%BB%BAminecraft%E6%9C%8D%E5%8A%A1%E5%99%A8/:1:0","tags":["minecraft","linux"],"title":"Ubuntu搭建minecraft服务器","uri":"https://seacj.github.io/posts/ubuntu%E6%90%AD%E5%BB%BAminecraft%E6%9C%8D%E5%8A%A1%E5%99%A8/"},{"categories":["编程-ღ-技术"],"content":"下载Forge 官网下载地址: http://files.minecraftforge.net 下载相应版本的Forge,我自己下载的是forge-1.15.2-31.1.0-installer.jar ","date":"2020-03-13","objectID":"https://seacj.github.io/posts/ubuntu%E6%90%AD%E5%BB%BAminecraft%E6%9C%8D%E5%8A%A1%E5%99%A8/:2:0","tags":["minecraft","linux"],"title":"Ubuntu搭建minecraft服务器","uri":"https://seacj.github.io/posts/ubuntu%E6%90%AD%E5%BB%BAminecraft%E6%9C%8D%E5%8A%A1%E5%99%A8/"},{"categories":["编程-ღ-技术"],"content":"运行Forge java -jar ./forge-1.15.2-31.1.0-installer.jar nogui --installServer 将下载好的jar包放在minecraft_server.1.15.2.jar所在目录,然后在服务器上输入上面的命令,然后就会经过漫长的下载过程,途中可能因为下载的资源在外网,所以会由于网络问题断开,多重复输入命令就能全部下载下来。 执行完毕之后会生成几个新的文件夹,和forge-1.15.2-31.1.0.jar文件。 ","date":"2020-03-13","objectID":"https://seacj.github.io/posts/ubuntu%E6%90%AD%E5%BB%BAminecraft%E6%9C%8D%E5%8A%A1%E5%99%A8/:3:0","tags":["minecraft","linux"],"title":"Ubuntu搭建minecraft服务器","uri":"https://seacj.github.io/posts/ubuntu%E6%90%AD%E5%BB%BAminecraft%E6%9C%8D%E5%8A%A1%E5%99%A8/"},{"categories":["编程-ღ-技术"],"content":"运行服务端 java -jar forge-1.15.2-31.1.0.jar 使用这个命令就会让服务器运行了。 ","date":"2020-03-13","objectID":"https://seacj.github.io/posts/ubuntu%E6%90%AD%E5%BB%BAminecraft%E6%9C%8D%E5%8A%A1%E5%99%A8/:4:0","tags":["minecraft","linux"],"title":"Ubuntu搭建minecraft服务器","uri":"https://seacj.github.io/posts/ubuntu%E6%90%AD%E5%BB%BAminecraft%E6%9C%8D%E5%8A%A1%E5%99%A8/"},{"categories":["编程-ღ-技术"],"content":"在客户端追加mod 我自己是HMCL的客户端,直接在线安装就很方便地安装上了。 ","date":"2020-03-13","objectID":"https://seacj.github.io/posts/ubuntu%E6%90%AD%E5%BB%BAminecraft%E6%9C%8D%E5%8A%A1%E5%99%A8/:5:0","tags":["minecraft","linux"],"title":"Ubuntu搭建minecraft服务器","uri":"https://seacj.github.io/posts/ubuntu%E6%90%AD%E5%BB%BAminecraft%E6%9C%8D%E5%8A%A1%E5%99%A8/"},{"categories":["编程-ღ-技术"],"content":"装载mod 有些mod需要同时装在服务器和客户端(比如矿工的优势)上,有的只需要装在客户端上(比如Xaero地图)。 客户端将jar包放在.minecraft\\mods目录下,服务器将jar包放在mods目录下即可。需要重启程序才能装载mod。 参考资料 阿里云ECS搭建我的世界服务器 https://yq.aliyun.com/articles/675070 Ubuntu下搭建我的世界服务器 https://blog.csdn.net/CSDNzyq/article/details/52735097 Linux下搭建带mod的Minecraft服务器 https://www.jianshu.com/p/89f14756d16c Minecraft Forge 安装 https://blog.csdn.net/oschina_41734362/article/details/79319399 linux下建立可加载mod的minecraft服务器 https://blog.csdn.net/github_39068149/article/details/72910409#243-%E5%8A%A0%E8%BD%BDliteloader 我的世界中文论坛MCBBS https://www.mcbbs.net/portal.php ","date":"2020-03-13","objectID":"https://seacj.github.io/posts/ubuntu%E6%90%AD%E5%BB%BAminecraft%E6%9C%8D%E5%8A%A1%E5%99%A8/:6:0","tags":["minecraft","linux"],"title":"Ubuntu搭建minecraft服务器","uri":"https://seacj.github.io/posts/ubuntu%E6%90%AD%E5%BB%BAminecraft%E6%9C%8D%E5%8A%A1%E5%99%A8/"},{"categories":["编程-ღ-技术"],"content":"题记 awk是unix下很实用的文本编辑语言。可以快速处理很多操作。参考资料是b站up主正月点灯笼的awk入门教程。这篇博客用来记录一下awk里面非常基础的操作。 待处理的文本 gold 1 1986 USA American Eagle gold 1 1908 Austria-Hungary Franz josef 100 Korona silver 10 1981 USA ingot gold 1 1984 Switzerland ingot gold 1 1979 RSA Krugerrand gold 0.5 1981 RSA Krugerrand gold 0.1 1986 PRC Panda silver 1 1986 USA Liberty dollar gold 0.25 1986 USA Liberty 5-dollar piece silver 1 1986 USA Liberty 50-cent piece silver 1 1987 USA Constitution dollar gold 0.25 1987 USA Constitution 5-dollar piece gold 1 1988 Canada Maple leaf 命名为coins.txt, 每一列从左到右分别表示材料、重量、生产时间、生产地、产品名称。 打印内容 awk '{print NR \"\\t\" $0 \"\\t\" $5}' coins.txt' 知识点: 行是record,列是field。打印行号用NR $1表示第一列 $0表示打印全部内容 注意原本设想的产品名称 列被拆成了多列,比如American Eagle被拆成了2列。 1 gold 1 1986 USA American Eagle American 2 gold 1 1908 Austria-Hungary Franz josef 100 Korona Franz 3 silver 10 1981 USA ingot ingot 4 gold 1 1984 Switzerland ingot ingot 5 gold 1 1979 RSA Krugerrand Krugerrand 6 gold 0.5 1981 RSA Krugerrand Krugerrand 7 gold 0.1 1986 PRC Panda Panda 8 silver 1 1986 USA Liberty dollar Liberty 9 gold 0.25 1986 USA Liberty 5-dollar piece Liberty 10 silver 1 1986 USA Liberty 50-cent piece Liberty 11 silver 1 1987 USA Constitution dollar Constitution 12 gold 0.25 1987 USA Constitution 5-dollar piece Constitution 13 gold 1 1988 Canada Maple leaf Maple awk '{print NR NR, NR}' coins.txt 知识点:用,逗号来分开,只用空格会被直接忽略掉。 NR NR的打印结果中间没有空格,而NR, NR打印结果是有空格的(注意命令中逗号后面有一个空格)。 11 1 22 2 33 3 44 4 55 5 66 6 77 7 88 8 99 9 1010 10 1111 11 1212 12 1313 13 搜索 awk '$3==1986{print $0}' coins.txt 打印满足第三列是1986的所有行。 gold 1 1986 USA American Eagle gold 0.1 1986 PRC Panda silver 1 1986 USA Liberty dollar gold 0.25 1986 USA Liberty 5-dollar piece silver 1 1986 USA Liberty 50-cent piece awk '$3==1986\u0026\u0026$1==\"gold\"{print $0}' coins.txt 知识点:对字符串的匹配要加上双引号,否则会被当做变量名 gold 1 1986 USA American Eagle gold 0.1 1986 PRC Panda gold 0.25 1986 USA Liberty 5-dollar piece 分割符 为了方便演示,使用Hello,world作为输入。 知识点: awk分割符分为输入分割符和输出分割符 BEGIN 模式:是指 awk 将在读取任何输入行之前立即执行BEGIN 中指定的动作 和C语言一样,分号;用来分开多个语句 echo Hello,world | awk '{print $1, NF}' 输出:Hello,world 1 echo Hello,world | awk 'BEGIN{FS=\",\"} {print $1, $2, NF}' 输出:Hello world 2 echo Hello,world | awk 'BEGIN{FS=\",\"; OFS=\",\"} {print $1, $2, NF}' 输出:Hello,world,2 变量 awk '{$3=\"xxx\"; print FILENAME $0}' coins.txt 知识点: FILENAME可以输出文件名,比如coins.txt. $3=\"xxx\"把第三列替换了 coins.txtgold 1 xxx USA American Eagle coins.txtgold 1 xxx Austria-Hungary Franz josef 100 Korona coins.txtsilver 10 xxx USA ingot coins.txtgold 1 xxx Switzerland ingot coins.txtgold 1 xxx RSA Krugerrand coins.txtgold 0.5 xxx RSA Krugerrand coins.txtgold 0.1 xxx PRC Panda coins.txtsilver 1 xxx USA Liberty dollar coins.txtgold 0.25 xxx USA Liberty 5-dollar piece coins.txtsilver 1 xxx USA Liberty 50-cent piece coins.txtsilver 1 xxx USA Constitution dollar coins.txtgold 0.25 xxx USA Constitution 5-dollar piece coins.txtgold 1 xxx Canada Maple leaf awk '{print $NF \"\\t\" $(NF-1)}' coins.txt 知识点:$NF可以用来打印倒数第某列 Eagle American Korona 100 ingot USA ingot Switzerland Krugerrand RSA Krugerrand RSA Panda PRC dollar Liberty piece 5-dollar piece 50-cent dollar Constitution piece 5-dollar leaf Maple 正则表达式 awk '/^g/{print $0}' coins.txt 匹配开头是g的行。 gold 1 1986 USA American Eagle gold 1 1908 Austria-Hungary Franz josef 100 Korona gold 1 1984 Switzerland ingot gold 1 1979 RSA Krugerrand gold 0.5 1981 RSA Krugerrand gold 0.1 1986 PRC Panda gold 0.25 1986 USA Liberty 5-dollar piece gold 0.25 1987 USA Constitution 5-dollar piece gold 1 1988 Canada Maple leaf 正则表达式语法: .匹配任何单字符,要匹配 . ,需使用 \\. /a.c/ 能够匹配abc, axc等 ^和$分别表示开始和结尾 []表示匹配方括号中的一个字符 /a[xy]b/能够匹配axb, ayb. /a[a-zA-Z]b/能够匹配axb, aXb等. ^在方括号中表示否定 /a[^a-z]c/能够匹配aAc,但不能匹配aac. *表示可以匹配0次或多次 +表示匹配1次或多次 ?表示前面的字符可以有也可以没有 /a?b/可以匹配ab,b。 {}用于匹配多个重复字符 /a{3}c/可匹配aaac。 /a{1,3}c/可匹配ac,aac,aaac。 /a{2,}c/可匹配aac,aaaaac等。 ()标记一个子表达式的开始和结束位置 /(ab)+c/可匹配abc, ababababc. ","date":"2020-03-12","objectID":"https://seacj.github.io/posts/awk%E5%AD%A6%E4%B9%A0/:1:0","tags":["linux"],"title":"Awk学习","uri":"https://seacj.github.io/posts/awk%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"题记 Git是一个非常优秀的开源的分布式版本控制系统,非常值得好好学习一下。 这里分享一个可能是学习git的最好的网站:https://learngitbranching.js.org/ ,这篇博客用来记录我在这里学习到的知识点。并附上示意图的链接,没有附上图片的原因是希望图片能作为cheat sheet。 分支 ","date":"2020-03-03","objectID":"https://seacj.github.io/posts/git%E5%AD%A6%E4%B9%A0/:1:0","tags":["博客搭建","Git"],"title":"Git学习","uri":"https://seacj.github.io/posts/git%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"分支的新建和切换 新建分支,但不切换:git branch 分支名 切换分支:git checkout 分支名 新建分支,并切换:git checkout -b 分支名 新建分支并切换分支 ","date":"2020-03-03","objectID":"https://seacj.github.io/posts/git%E5%AD%A6%E4%B9%A0/:2:0","tags":["博客搭建","Git"],"title":"Git学习","uri":"https://seacj.github.io/posts/git%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"分支的合并 ","date":"2020-03-03","objectID":"https://seacj.github.io/posts/git%E5%AD%A6%E4%B9%A0/:3:0","tags":["博客搭建","Git"],"title":"Git学习","uri":"https://seacj.github.io/posts/git%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"方法一. merge 合并分支前 git merge bugFix bugFix分支合并进master中。 合并分支后1 git checkout bugFix git merge master master分支合并进bugfix。 合并分支后2 ","date":"2020-03-03","objectID":"https://seacj.github.io/posts/git%E5%AD%A6%E4%B9%A0/:3:1","tags":["博客搭建","Git"],"title":"Git学习","uri":"https://seacj.github.io/posts/git%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"方法二. rebase 我们想要把 bugFix 分支里的工作直接移到 master 分支上。 移动以后会使得两个分支的功能看起来像是按顺序开发,但实际上它们是并行开发的。 合并分支前 git rebase master 合并分支后 注意,提交记录 C3 依然存在(树上那个半透明的节点),而 C3’ 是我们 Rebase 到 master 分支上的 C3 的副本。现在唯一的问题就是 master 还没有更新,下面咱们就来更新它吧。 git rebase bugFix 由于 bugFix 继承自 master,所以 Git 只是简单的把 master 分支的引用向前移动了一下而已。 更新master 提交树上移动 git log 查看提交记录的哈希值 git checkout C1 让HEAD指向提交记录C1. ^向上移动1个提交记录。比如 master^ 相当于“master 的父节点”;master^^ 是 master 的第二个父节点 ~\u003cnum\u003e向上移动多个提交记录,如~3 git branch -f master HEAD~3 将 master 分支强制指向 HEAD 的第 3 级父提交。 撤销变更 ","date":"2020-03-03","objectID":"https://seacj.github.io/posts/git%E5%AD%A6%E4%B9%A0/:3:2","tags":["博客搭建","Git"],"title":"Git学习","uri":"https://seacj.github.io/posts/git%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"git reset git reset HEAD~1 向上移动分支,原来指向的提交记录就跟从来没有提交过一样。在reset后, C2 所做的变更还在,但是处于未加入暂存区状态。 git reset ","date":"2020-03-03","objectID":"https://seacj.github.io/posts/git%E5%AD%A6%E4%B9%A0/:4:0","tags":["博客搭建","Git"],"title":"Git学习","uri":"https://seacj.github.io/posts/git%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"git revert 虽然在你的本地分支中使用 git reset 很方便,但是这种“改写历史”的方法对大家一起使用的远程分支是无效的哦! git revert HEAD git revert 奇怪!在我们要撤销的提交记录后面居然多了一个新提交!这是因为新提交记录 C2' 引入了更改 —— 这些更改刚好是用来撤销 C2 这个提交的。也就是说 C2' 的状态与 C1 是相同的。 Git Cherry-pick git cherry-pick C2 C4 将C2和C4复制过来 这里有一个仓库, 我们想将 side 分支上的工作复制到 master 分支,你立刻想到了之前学过的 rebase 了吧?但是咱们还是看看 cherry-pick 有什么本领吧。 cherry-pick 本地栈式提交 来看一个在开发中经常会遇到的情况:我正在解决某个特别棘手的 Bug,为了便于调试而在代码中添加了一些调试命令并向控制台打印了一些信息。 这些调试和打印语句都在它们各自的提交记录里。最后我终于找到了造成这个 Bug 的根本原因,解决掉以后觉得沾沾自喜! 最后就差把 bugFix 分支里的工作合并回 master 分支了。你可以选择通过 fast-forward 快速合并到 master 分支上,但这样的话 master 分支就会包含我这些调试语句了。你肯定不想这样,应该还有更好的方式…… 待提交分支 git checkout master git cherry-pick bugFix 相关资料 Git Learning 学习笔记 https://blog.csdn.net/Edisonleeee/article/details/94554710 ","date":"2020-03-03","objectID":"https://seacj.github.io/posts/git%E5%AD%A6%E4%B9%A0/:5:0","tags":["博客搭建","Git"],"title":"Git学习","uri":"https://seacj.github.io/posts/git%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"题记 以前配实验室的服务器的时候经常出现各种问题,但是因为又不是自己的服务器,所以不敢乱整,很多问题只要影响不大就将就着用了。不过现在买了个服务器,当然要折腾一下。所以先从远程搭建图形化界面开始吧。这不是教程,只是记录一下我的实验而已,不过应该还是有借鉴价值的,至少先踩一些坑。 目标是直到下面这个错能消失,后文中连接不上都是指会出现下面这个错误,所有命令都是通过命令行工具(如xshell)进行操作。 实验一. 使用Ubuntu desktop apt-get install ubuntu-desktop reboot 参考:https://blog.csdn.net/zwq912318834/article/details/80528374 上面两个命令操作完了之后,在阿里云服务器管理平台,查看获取实例屏幕截图就能看见桌面登陆界面了。 到这一步,直接连连接不上,我直接放弃,因为这玩意挺大的,有2个G。apt-get install ubuntu-desktop删除。 实验二. xrdp 一共就三行命令: sudo apt-get install xrdp apt-get install vnc4server apt-get install xubuntu-desktop 下面进行详细介绍: sudo apt-get install xrdp 安装xrdp工具后,用windows自带的远程连接居然直接就连过去了。以前自己实验室装的时候,装完xrdp也不能连上。 不过用root账户在输入完账号密码之后,就卡在蓝屏界面了。 要用自己新建的用户就可以进去,进去之后出现下图的输入密码界面,输入密码之后,点Authenticate,马上这弹窗又弹出来,叫你输入密码,无限循环。 感觉可能是因为没有安装图形化界面的原因,所以我先装个图形化界面。 apt-get install vnc4server 然后安装xubuntu-desktop,这个大小是ubuntu-desktop的1/3。 apt-get install xubuntu-desktop 查阅了一下相关资料,就直接选gdm3就好了。 重启服务器之后,再连接,外观变了,但是依然还是无限循环登陆。 然后我搜到了解决方案: 在路径/etc/polkit-1/localauthority.conf.d/处新增一个文件,文件名为 02-allow-colord.conf 文件内容如下 polkit.addRule(function(action, subject) { if ((action.id == “org.freedesktop.color-manager.create-device” || action.id == “org.freedesktop.color-manager.create-profile” || action.id == “org.freedesktop.color-manager.delete-device” || action.id == “org.freedesktop.color-manager.delete-profile” || action.id == “org.freedesktop.color-manager.modify-device” || action.id == “org.freedesktop.color-manager.modify-profile”) \u0026\u0026 subject.isInGroup(\"{users}\")) { return polkit.Result.YES; } }); 保存重启ubuntu ———————————————— 版权声明:本文为CSDN博主「samtaoys」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/samtaoys/article/details/91042262 设置好就可以登陆进去了。 用起来很卡顿,虽然我知道服务器配置很低但也不至于这样,cpu和内存占用都不高,应该是远程配置的问题。找到解决方法了: https://www.ivpser.com/windows-smooth/ 调整分辨率和色彩之类的参数之后,就变柔顺了。 参考资料 xrdp和vnc之间区别+xrdp完美实现Windows远程访问Ubuntu 16.04 https://www.linuxidc.com/Linux/2017-09/147112.htm ","date":"2020-03-01","objectID":"https://seacj.github.io/posts/%E9%85%8D%E7%BD%AE%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%9B%BE%E5%BD%A2%E5%8C%96%E7%95%8C%E9%9D%A2/:1:0","tags":["博客搭建","linux"],"title":"记一次Ubuntu图形化界面配置","uri":"https://seacj.github.io/posts/%E9%85%8D%E7%BD%AE%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%9B%BE%E5%BD%A2%E5%8C%96%E7%95%8C%E9%9D%A2/"},{"categories":["博客"],"content":"题记 终于还是花了100RMB钱买了一年的阿里云服务器,现在把github pages的网站部署到服务器上。由于踩的坑有点多,所以全篇下来可能有点啰嗦。 准备工作 Hugo博客(参见 Hugo搭建博客视频教程) 云服务器 (我的系统是Ubuntu) 域名 部署博客到阿里云服务器 ","date":"2020-02-29","objectID":"https://seacj.github.io/posts/%E9%83%A8%E7%BD%B2%E5%8D%9A%E5%AE%A2%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/:1:0","tags":["博客搭建","linux"],"title":"部署博客到阿里云服务器","uri":"https://seacj.github.io/posts/%E9%83%A8%E7%BD%B2%E5%8D%9A%E5%AE%A2%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/"},{"categories":["博客"],"content":"Step 1. 配置nginx 初次阿里云服务器需要在控制台重置密码,之后重启。 登陆的时候使用root用户名。 安装nginxsudo apt-get install nginx我安装的版本是nginx 1.14.0 (Ubuntu)。 开启nginxservice nginx start p.s. 停止命令:service nginx stop ,重启命令nginx -s reload 在阿里云安全组配置中开启HTTP 80端口 ref. 阿里云服务器怎么开启或关闭8080端口 在开启HTTP服务之后,在浏览器中输入你的公网ip就能看见Welcome to nginx的网页页面了。 创建一个网站目录,如/home/user/www/blog/,并在里面新建一个index.html文件 内容随意,只是用来测试而已,我就直接在里面写了个HI。 注意:index.html文件需要放在具有可执行权限的文件夹中,不要放在权限如drwx------的文件夹里面,我自己是放在权限为drwxr-xr-x的目录里的。 查看nginx的配置文件路径 nginx -t 编辑nginx的配置文件 location / { root 你的index.html所在的文件路径; } 这一步我查阅的方法绝大部分是在nginx的配置文件找到location字段,将root后面的项改成你的index.html所在的文件路径即可。但是我发现我自己版本的nginx的/etc/nginx/nginx.conf中没有这个字段,所以我是将 /etc/nginx/sites-enabled/default文件,将预设的root /var/www/html;修改成 root 你的index.html所在的文件路径;。 很多发行版为了方便管理,在nginx.conf底部加了一条include sites-enabled/*.conf, 但不会 include sites-available。 重启nginx服务 nginx -s reload 重启之后在浏览器访问你的公网IP,就能显示出index.html所展示的内容了。如果此时网页页面显示403 Forbidden的错误,说明index.html所在的文件夹缺少权限,此时应该换一个文件夹或添加权限。 ","date":"2020-02-29","objectID":"https://seacj.github.io/posts/%E9%83%A8%E7%BD%B2%E5%8D%9A%E5%AE%A2%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/:2:0","tags":["博客搭建","linux"],"title":"部署博客到阿里云服务器","uri":"https://seacj.github.io/posts/%E9%83%A8%E7%BD%B2%E5%8D%9A%E5%AE%A2%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/"},{"categories":["博客"],"content":"Step 2. 配置服务器git仓库 可参考服务器上的 Git - 配置服务器 ","date":"2020-02-29","objectID":"https://seacj.github.io/posts/%E9%83%A8%E7%BD%B2%E5%8D%9A%E5%AE%A2%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/:3:0","tags":["博客搭建","linux"],"title":"部署博客到阿里云服务器","uri":"https://seacj.github.io/posts/%E9%83%A8%E7%BD%B2%E5%8D%9A%E5%AE%A2%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/"},{"categories":["博客"],"content":"命令行切换到服务器网页根目录 ","date":"2020-02-29","objectID":"https://seacj.github.io/posts/%E9%83%A8%E7%BD%B2%E5%8D%9A%E5%AE%A2%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/:3:1","tags":["博客搭建","linux"],"title":"部署博客到阿里云服务器","uri":"https://seacj.github.io/posts/%E9%83%A8%E7%BD%B2%E5%8D%9A%E5%AE%A2%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/"},{"categories":["博客"],"content":"创建用户:adduser git root@xxx:/xxx/xxx# adduser git Adding user `git' ... Adding new group `git' (1000) ... Adding new user `git' (1000) with group `git' ... Creating home directory `/home/git' ... Copying files from `/etc/skel' ... Enter new UNIX password: Retype new UNIX password: 好像有的时候创建用户不会跳出配置密码,此时可以用passwd git配置密码。 ","date":"2020-02-29","objectID":"https://seacj.github.io/posts/%E9%83%A8%E7%BD%B2%E5%8D%9A%E5%AE%A2%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/:3:2","tags":["博客搭建","linux"],"title":"部署博客到阿里云服务器","uri":"https://seacj.github.io/posts/%E9%83%A8%E7%BD%B2%E5%8D%9A%E5%AE%A2%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/"},{"categories":["博客"],"content":"配置公钥 切换到git用户:su git ,切换用户之后会自动切换到该用户的根目录中。 创建.ssh目录:mkdir .ssh \u0026\u0026 chmod 700 .ssh 然后在云服务创建authorized_keys公钥保存文件:touch .ssh/authorized_keys \u0026\u0026 chmod 600 .ssh/authorized_keys 将公钥添加到authorized_keys文件中。 如果不知道公钥是什么的话可以参见https://gitee.com/help/articles/4181#article-header0 来配置公钥。Windows系统生成的公钥会存放在C:\\Users\\xxx\\.ssh中。 公钥看起来是这样的: $ cat /xxx/id_rsa.john.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCB007n/ww+ouN4gSLKssMxXnBOvf9LGt4L ","date":"2020-02-29","objectID":"https://seacj.github.io/posts/%E9%83%A8%E7%BD%B2%E5%8D%9A%E5%AE%A2%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/:3:3","tags":["博客搭建","linux"],"title":"部署博客到阿里云服务器","uri":"https://seacj.github.io/posts/%E9%83%A8%E7%BD%B2%E5%8D%9A%E5%AE%A2%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/"},{"categories":["博客"],"content":"创建git仓库目录 mkdir 裸仓库路径 你的仓库路径如/home/git/repo cd 裸仓库路径 git init --bare blog.git 裸仓库与 git init 初使化的仓库不太一样,裸仓库其实相当于通过克隆来的仓库里的.git文件夹,整个裸仓库中只有git索引(index),不包含工作目录。 ","date":"2020-02-29","objectID":"https://seacj.github.io/posts/%E9%83%A8%E7%BD%B2%E5%8D%9A%E5%AE%A2%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/:3:4","tags":["博客搭建","linux"],"title":"部署博客到阿里云服务器","uri":"https://seacj.github.io/posts/%E9%83%A8%E7%BD%B2%E5%8D%9A%E5%AE%A2%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/"},{"categories":["博客"],"content":"配置git hooks 关于git hooks我也写了一篇博客,不过只是非常简单的描述,我对git的学习并不深入。 cd 你的仓库路径/blog.git/hooks` vim post-receive 参考了一些资料,我最后写的内容是以下 : #!/bin/sh set -e DIR_ONE=/home/user/www/blog/ #此目录为服务器页面展示目录 cd $DIR_ONE git --work-tree=./ --git-dir=裸仓库路径/blog.git checkout -f chmod 771 post-receive 用于赋予文件可执行权限 ","date":"2020-02-29","objectID":"https://seacj.github.io/posts/%E9%83%A8%E7%BD%B2%E5%8D%9A%E5%AE%A2%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/:3:5","tags":["博客搭建","linux"],"title":"部署博客到阿里云服务器","uri":"https://seacj.github.io/posts/%E9%83%A8%E7%BD%B2%E5%8D%9A%E5%AE%A2%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/"},{"categories":["博客"],"content":"Step 3. 修改用户权限 如跳过本步骤,直接进行Step 4,可能会报remote: error: could not lock config file /home/user/www/blog/.git/config: Permission denied的错误。 此时我们只需要执行命令: chown git 服务器页面展示目录 chgrp git 服务器页面展示目录 ","date":"2020-02-29","objectID":"https://seacj.github.io/posts/%E9%83%A8%E7%BD%B2%E5%8D%9A%E5%AE%A2%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/:4:0","tags":["博客搭建","linux"],"title":"部署博客到阿里云服务器","uri":"https://seacj.github.io/posts/%E9%83%A8%E7%BD%B2%E5%8D%9A%E5%AE%A2%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/"},{"categories":["博客"],"content":"Step 4. 配置本地git仓库 切换到本地仓库,如果是Hugo搭建的博客,就是public文件夹。 git remote set-url --add origin xxx.git 其中xxx.git为usrname@服务器ip地址:/裸仓库路径/blog.git 此时输入git remote -v即可查看是否正确添加。 输入错了的时候的删除命令: git remote set-url --delete origin xxx.git 接着git push 你的服务器在服务器页面展示目录就会出现你push的代码,此时再用浏览器访问,你的网站就出来了!! 参考资料 linux目录权限中,r(浏览目录)和x(进入目录)的区别 知乎回答 HEXO 部署到云服务器详细指南 HEXO 部署到云服务器详细指南 如何使用阿里云+Hexo搭建个人静态博客 知乎孔晨皓的回答 用 Git 钩子进行简单自动部署 图文讲解 Git Hooks实现代码自动部署 姑苏流白的博客 Hexo博客同时部署到GitHub Page和个人服务器 Leeyuxun’s blog 不重要的知识 Git Hooks中有个post-update的hook,是post-receive的阉割版本 The ‘post-update’ hook can tell what are the heads that were pushed, but it does not know what their original and updated values are, so it is a poor place to do log old..new. The \u003cpost-receive\u003e hook does get both original and updated values of the refs. You might consider it instead if you need them. —— https://stackoverflow.com/questions/9653165/whats-the-difference-between-post-receive-and-post-update git init多次是安全的 Running git init in an existing repository is safe. It will not overwrite things that are already there. The primary reason for rerunning git init is to pick up newly added templates. ——http://www.voidcn.com/article/p-cvrvucje-bsk.html ","date":"2020-02-29","objectID":"https://seacj.github.io/posts/%E9%83%A8%E7%BD%B2%E5%8D%9A%E5%AE%A2%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/:5:0","tags":["博客搭建","linux"],"title":"部署博客到阿里云服务器","uri":"https://seacj.github.io/posts/%E9%83%A8%E7%BD%B2%E5%8D%9A%E5%AE%A2%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/"},{"categories":["编程-ღ-技术"],"content":"题记 由于打算把博客从Git Pages同步到自己的服务器上,需要借助Git hooks技术,所以来简单学习一下。 Git hooks是什么 Git Hooks就是那些在Git执行特定事件(如commit、push、receive等)后触发运行的脚本(这个脚本可以是shell、python、Ruby等等)。可以类比成一个触发器,当监听到某种事件时,就会自动触发,并执行脚本。 Git hooks能做什么 自定义commit消息:看一下《5分钟实现自定义commit信息~ 秒懂git hooks~》这个视频就懂了。 自动部署代码 Git hooks基本操作 查看Git目录里面的文件结构: xxx@xxx:~/xxx/xxx.git$ ls -a . .. branches config description HEAD hooks info objects refs hooks子目录就是存放hooks的地方。 xxx@xxx:~/xxx/xxx.git/hooks$ ls -a applypatch-msg.sample fsmonitor-watchman.sample pre-applypatch.sample prepare-commit-msg.sample pre-rebase.sample update.sample commit-msg.sample post-update.sample pre-commit.sample pre-push.sample pre-receive.sample 看这些样例的文件名我们就能知道这些hooks是在什么时候触发的,以prepare-commit-msg.sample为例,这个hooks就是当执行git commit命令时被触发。实际使用的时候,只需要在hooks目录新建``prepare-commit-msg文件,然后在里面编写你的脚本,记得写上!# 你的解释器的位置` 以下列举几个不同hooks所能实现的功能: pre-commit: 检查每次的commit message是否有拼写错误,或是否符合某种规范。 pre-receive: 统一上传到远程库的代码的编码。 post-receive: 每当有新的提交的时候就通知项目成员(可以使用Email或SMS等方式)。 post-receive: 把代码推送到生产环境。 举一个hooks脚本的例子,例子来源:《git: 提交前强制检查各个项目用户名邮箱设置》 该脚本写在pre-commit文件中, 用途是检查用户名邮箱设置: #!/bin/sh # # A git hook to make sure user.email and user.mail in repository exists before committing set -e global_email=$(git config --global user.email || true) global_name=$(git config --global user.name || true) repository_email=$(git config user.email || true) repository_name=$(git config user.name || true) if [ -z \"$repository_email\" ] || [ -z \"$repository_name\" ] || [ -n \"$global_email\" ] || [ -n \"$global_name\" ]; then # user.email is empty echo \"ERROR: [pre-commit hook] Aborting commit because user.email or user.name is missing. Configure them for this repository. Make sure not to configure globally.\" exit 1 fi python_file=./git-hooks.py if [ -f \"$python_file\" ]; then python $python_file pre-commit fi 参考资料 Git Hooks 简书文章 Git Hooks实现代码自动部署 姑苏流白的文章 HEXO 部署到云服务器详细指南 简书文章 自定义 Git - Git 钩子 Git community ","date":"2020-02-29","objectID":"https://seacj.github.io/posts/git_hooks%E5%88%9D%E6%8E%A2/:1:0","tags":["博客搭建","Git"],"title":"Git hooks初探","uri":"https://seacj.github.io/posts/git_hooks%E5%88%9D%E6%8E%A2/"},{"categories":["博客"],"content":" 修改阅读进度条外观 在主页增加邮箱联系方式 删除Tool页面 修改引用内容的背景色 同时部署 Hugo 静态博客到 Netlify 和 Github Pages seacj.netlify.com 在阿里云新增4个国际DNS服务器(可能会加快域名解析速度) 据说这样配有可能会导致网站被墙掉… ","date":"2020-02-26","objectID":"https://seacj.github.io/posts/%E5%BB%BA%E7%AB%99%E5%8E%86%E5%8F%B2005/:0:0","tags":["博客搭建","建站历史"],"title":"建站历史005","uri":"https://seacj.github.io/posts/%E5%BB%BA%E7%AB%99%E5%8E%86%E5%8F%B2005/"},{"categories":["博客"],"content":"相关链接 LeaveIt主题配置 LeaveIt Hugo Theme 作者github 网站迁移至码云 码云托管的文章 添加阅读进度条 为 LeaveIt 主题添加阅读进度条 Coding静态网站官方文档 Coding如何搭建静态网站 同时部署 Hugo 静态博客到 Netlify 和 Github Pages 同时部署 Hugo 静态博客到 Netlify 和 Github Pages ","date":"2020-02-26","objectID":"https://seacj.github.io/posts/%E5%BB%BA%E7%AB%99%E5%8E%86%E5%8F%B2005/:1:0","tags":["博客搭建","建站历史"],"title":"建站历史005","uri":"https://seacj.github.io/posts/%E5%BB%BA%E7%AB%99%E5%8E%86%E5%8F%B2005/"},{"categories":["博客"],"content":"题记 之前我的个人博客都是直接挂在github上,但是考虑到国内访问github的速度不是很稳定,VPN有时候都不好使,如下: 来自 185.199.xxx.xxx 的回复: 字节=32 时间=175ms TTL=52 我自己没事也喜欢翻一翻博客,这种速度实在受不了,所以决定还是把仓库转移到国内的gitee。这篇博客就来简单记录一下在gitee上使用hugo托管博客的使用体验,并补充官方说明中不太详细的部分。 注意:由于用自己的域名绑定是付费的,所以我体验了一小段时间之后重回github pages了。如果对域名没有要求的话,码云仍然是不错的选择。 关于码云Pages 对应github提供的git page服务,码云也有码云Pages,下面是码云Pages的部分说明: 码云 Pages是一个免费的静态网页托管服务,您可以使用 码云Pages托管博客、项目官网等静态网页。如果您使用过 Github Pages 那么您会很快上手使用码云的Pages服务。目前码云 Pages 支持 Jekyll、Hugo、Hexo编译静态资源。 Pages 服务仅供博客 / 门户 / 开源项目网站 / 开源项目静态效果演示用途,请勿用于违规内容,包括但不仅限于: 发布诱导分享/诱导关注/诱导下载/诱导跳转内容 发布欺诈/谣言/骚扰信息/广告信息/垃圾信息/特殊识别码、口令类信息 说是免费,但是!!!我在搭建过程中出现仓库创建者未绑定手机号码,不允许部署 pages ,而且自定义域名和HTTPS是收费服务!!!付费之后域名也需要是已经备案的域名。 搭建码云Pages托管 ","date":"2020-02-25","objectID":"https://seacj.github.io/posts/%E7%BD%91%E7%AB%99%E8%BF%81%E7%A7%BB%E8%87%B3gitee/:1:0","tags":["博客搭建"],"title":"博客迁移至码云","uri":"https://seacj.github.io/posts/%E7%BD%91%E7%AB%99%E8%BF%81%E7%A7%BB%E8%87%B3gitee/"},{"categories":["博客"],"content":"Step 1. 配置码云仓库 首先在码云中创建一个仓库名和用户名相同的仓库,这样才会给你your_name.gitee.io的域名。 如果名字不同,会导致域名中带有仓库名,如https://your_name.gitee.io/repo_name。 配置过程可参见同时使用 Gitlab、Github、Gitee(码云) 。 重点是别忘了配置公钥,否则push命令会出现以下错误: [email protected]: Permission denied (publickey).\rfatal: Could not read from remote repository. 码云配置公钥直接在https://gitee.com/profile/sshkeys 里配置就行了。 如果不知道公钥是什么的话可以参见https://gitee.com/help/articles/4181#article-header0 来配置公钥。Windows系统生成的公钥会存放在C:\\Users\\xxx\\.ssh中。 测试一下: mkdir pages cd pages git init touch README.md git add README.md git commit -m \"first commit\" git remote add origin https://gitee.com/yourname/yourname.git git push -u origin master 仓库出现README.md文件,说明OK了。 ","date":"2020-02-25","objectID":"https://seacj.github.io/posts/%E7%BD%91%E7%AB%99%E8%BF%81%E7%A7%BB%E8%87%B3gitee/:2:0","tags":["博客搭建"],"title":"博客迁移至码云","uri":"https://seacj.github.io/posts/%E7%BD%91%E7%AB%99%E8%BF%81%E7%A7%BB%E8%87%B3gitee/"},{"categories":["博客"],"content":"Step 2. 启动码云Pages服务 直接在码云仓库页面点击服务,选择pages服务就可以了。 p.s. : 启动服务的前提是仓库里面要有文件,我自己是先把hugo生成的public中的文件push到仓库再启动Pages服务的。也可以和官方说明一样,新建一个空的index.html文件,然后里面随便写点東西,再开启Pages服务。 ","date":"2020-02-25","objectID":"https://seacj.github.io/posts/%E7%BD%91%E7%AB%99%E8%BF%81%E7%A7%BB%E8%87%B3gitee/:3:0","tags":["博客搭建"],"title":"博客迁移至码云","uri":"https://seacj.github.io/posts/%E7%BD%91%E7%AB%99%E8%BF%81%E7%A7%BB%E8%87%B3gitee/"},{"categories":["博客"],"content":"Step 3. 将博客推上去(以Hugo为例) 使用以下命令,命令执行之后public文件夹会生成新的文件 hugo -D --baseUrl=“www.你的域名.com\" 命令行cd到public文件夹 将public文件夹push上去,会用到的命令参见Step1中测试时用的代码 至此就可以正常访问博客了,以我为例,此时就可以直接访问http://username.gitee.io/了。 总结 速度确实快,这点没得说:来自 212.64.62.174 的回复: 字节=32 时间=36ms TTL=51 域名要付费这一点是大坑(我还是试了一下用配github域名的方法,也就是用CNAME,也是不行的,必须要付费),不知道以后会不会开放成免费。 参考资料 码云(Gitee.com)帮助文档 V1.2 http://git.mydoc.io/?t=154714 配置同时使用 Gitlab、Github、Gitee(码云) 共存的开发环境 https://www.jianshu.com/p/68578d52470c 码云Pages如何自定义域名(收费,但可试用1个月) https://jingyan.baidu.com/article/6181c3e0cb0fae152ef153f1.html 将代码同时push到github和码云 https://blog.csdn.net/u014532775/article/details/89684206 ","date":"2020-02-25","objectID":"https://seacj.github.io/posts/%E7%BD%91%E7%AB%99%E8%BF%81%E7%A7%BB%E8%87%B3gitee/:4:0","tags":["博客搭建"],"title":"博客迁移至码云","uri":"https://seacj.github.io/posts/%E7%BD%91%E7%AB%99%E8%BF%81%E7%A7%BB%E8%87%B3gitee/"},{"categories":["随笔-ღ-心得"],"content":"看到朋友圈有个什么一生必须完成的100件事情的小程序,有位同学发了个文字,大致意思就是自己的人生更加精彩,虽然所谓的一生必做的100件事情没做多少,不过他勾选的比我多多了喂。 还有同学在朋友圈晒了个类似刷题的图片截图,哇,简直了,又应该把业精于勤,荒于嬉这句话拿出来了。 还有有位认识很久的同学在朋友圈发了非常沉重的信息,让我又不得不开始正视生活了,你永远不知道明天和意外哪个先来这句话在某些时刻是非常重要的。 所以说是时候开始重新回归到生活了,不过其实生活精不精彩对我来说其实根本就无所谓,别人生活过得怎么样,如果这个\"别人\"不是比较亲近的人其实也根本无所谓,不过我对于有趣还是有很强的追求的,前几天刷完的碧蓝之海是真的有趣,话说有趣这两个字本身就非常有趣(禁止套娃)。所以说是时候立一些flag,让生活有趣一点。 用pygame完成一款游戏 sv的比赛好好打 蛋糕做好吃一点 每天刷点题 大致地看完MIT 6.046J ","date":"2020-02-23","objectID":"https://seacj.github.io/posts/%E7%AB%8B%E4%B8%AAflag/:0:0","tags":["随笔"],"title":"立个flag","uri":"https://seacj.github.io/posts/%E7%AB%8B%E4%B8%AAflag/"},{"categories":["教程-ღ-点评"],"content":"题记 看到有人用双拼打字感觉挺好的,稍微搜了一下关于双拼的评价,感觉还不错,其中节奏感这三个字是最吸引我的地方,每个字都需要按两个键的特点实际使用的时候应该挺爽的。我有时总感觉打中文的时候不顺畅,希望双拼能拯救我吧。 学习 Step 1. 首先当然是搞一张键位图 注意:双拼键位并不唯一,这个可以自己设定,上面的图是大多数人使用的小鹤双拼。 Step 2. 设置输入法 秉承奥卡姆剃刀原理的准则,我没下载一直直接用windows自带的输入法。 由于windows自带的双拼键位图有点不一样,所以还需要自己设定一下,具体见参考资料。 顺带一提,在ipad上设置双拼,可以直接选择微软双拼、小鹤双拼、搜狗双拼。 参考资料 请问应该如何学习双拼输入法 https://www.v2ex.com/t/592527 怎样在win10 中将输入法设置为双拼模式 https://jingyan.baidu.com/article/fd8044fad5e1ca5030137a6b.html 如何在windows10的微软拼音中设置小鹤双拼 https://www.jianshu.com/p/cc4cc916f5fc ","date":"2020-02-21","objectID":"https://seacj.github.io/posts/%E5%8F%8C%E6%8B%BC%E6%89%93%E5%AD%97/:1:0","tags":null,"title":"双拼打字","uri":"https://seacj.github.io/posts/%E5%8F%8C%E6%8B%BC%E6%89%93%E5%AD%97/"},{"categories":["随笔-ღ-心得"],"content":" 今天天气终于好了,阳光明媚,舒服 https://www.nbfox.com/wp-content/uploads/2020/03/11/20200311190119-5e69357f0ef97.jpg ","date":"2020-02-17","objectID":"https://seacj.github.io/posts/%E5%A5%BD%E5%A4%A9%E6%B0%94/:0:0","tags":["随笔"],"title":"好天气","uri":"https://seacj.github.io/posts/%E5%A5%BD%E5%A4%A9%E6%B0%94/"},{"categories":["随笔-ღ-心得"],"content":"\r\r## 背景\r武汉肺炎 时序记录 现在是2020年1月29日,现按时序记录. 地点:武汉 2020年1月1日 无事发生 2020年1月2日19:45 昨晚梦见有个食堂(也可能是餐厅吧)有自助寿司,然后我夹了好多,结果还没吃意识到不对,就醒了。今天看了看数模作业和流利python,进度缓慢。中午梅园的玉米蛋花汤挺好喝的。 2020年1月3日23:59 b站年度报告364天在线,看了2w+的视频。 明日方舟最难本通关。 2020年1月4日至1月8日 实验室和宿舍日常 2020年1月9日-1月10日 交了数模作业,本学期所有课程结束 2020年1月11日23:59 明日方舟活动本通关 2020年1月12日周日 王者荣耀3胜1负 2020年1月13日-1月15日 实验室日常 2020年1月16日 ipad2019的快递到了 2020年1月17日晚 当天才买了一包5个的口罩,我当时居然觉得50块钱贵,之后根本买不到。 火车回家,火车站戴口罩的人非常之少,过安检的时候保安还让我摘掉口罩。 我的所见所闻 2019年12月30日前后开始有消息,但是很快就被压下来了。 2020年1月23日凌晨2点,武汉市宣布自10时起交通封城 感想 微博从某种意义上来说是真的挺厉害的,消息还是很快的,不过这也改变不了它是个垃圾的事实,或者说它本身就是个垃圾桶吧。年轻人还是少刷点微博,不然很影响情绪,不过有时候也能够从中找到其他地方搞不到的消息。 医疗人员在逆行; 海外有人幸灾乐祸,总是有人不能意识到狭隘的民族利己主义是多么的让人作呕; 也有人散播谣言就喜欢看社会不安宁的样子; 还有这种时候还在谋求利益,干些伤天害理的事情的人。 我应该用怎样的心情写下文字呢,我的回答是平静。那些卑鄙小人就视他们为蝼蚁就行了,根本不值得引起情绪的波动,反正任何人只要有机会就都会想整死他们的。 以前想古代人是怎么活着的呀,没有电没有自来水。现在看来,应该说不知道怎么就活下来了,就是人类的特点。 人类的史诗真的十分的精彩。 以上结束。我也许会成为幸存者,也亦或是受害者。毕竟人活着其实都算是一种奇迹。 “中国人总是被他们中最勇敢的人保护的很好”这句话非常漂亮,我挺喜欢的,但是我还是想抬个杠,没有牺牲准备的人是没有资格使用这句话的,这句话只有曾经冲上前线的人们可以这样说,可以自豪地这样说。就像奇葩说节目里李姓作家说的这世界总是有人既不苦其心志,也不劳其筋骨天天就想着牺牲别人,怎么为了大的去牺牲小的。 希望医疗人员以及那些勇敢并且内心温暖纯良的人平安幸福。 好像有个故事,大概意思就是是说躺在病床上的人,如果想看来年窗外的花开,就会获得属性加成的早日康复的buff。嗯,是个非常煽情又非常温暖的故事。 ","date":"2020-01-29","objectID":"https://seacj.github.io/posts/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E9%9B%B6%E7%BA%AA%E5%AE%9E%E5%BD%95/:0:0","tags":["抗击新冠肺炎"],"title":"二零二零年纪实录","uri":"https://seacj.github.io/posts/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E9%9B%B6%E7%BA%AA%E5%AE%9E%E5%BD%95/"},{"categories":["随笔-ღ-心得"],"content":"2020年4月4日修正: 虽然我的博客没什么人看,但是还是要更正错误的。 本博文第一次发布于1月29日,发布后发现其中部分内容来源严重失实,已删除。参见以下视频: 纽约时报如何扭曲事实将疫情矛头指向中国 新冠病毒来源于美国?此说法是阴谋论还是合理怀疑? 其他的暂且不提,真正捋清时间线之后再看,从新闻发布,到事件处理,都可以问心无愧地说已经做到了最好了。现在看来,虽然我以为我当时已经足够客观,但是还是被部分媒体误导。 ","date":"2020-01-29","objectID":"https://seacj.github.io/posts/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E9%9B%B6%E7%BA%AA%E5%AE%9E%E5%BD%95/:1:0","tags":["抗击新冠肺炎"],"title":"二零二零年纪实录","uri":"https://seacj.github.io/posts/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E9%9B%B6%E7%BA%AA%E5%AE%9E%E5%BD%95/"},{"categories":["编程-ღ-技术"],"content":"题记 由于要跑kaldi(一个语音识别工具),里面有很多shell脚本要运行,所以快速学习一遍shell,主要是参考https://www.runoob.com/linux/linux-shell-variable.html 学习。 变量 ","date":"2020-01-10","objectID":"https://seacj.github.io/posts/shell%E5%AD%A6%E4%B9%A0/:1:0","tags":["linux"],"title":"Shell学习","uri":"https://seacj.github.io/posts/shell%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"显式定义 代码 your_name=\"cj\" echo $your_name echo \"Hi ${your_name}!!!\" 输出 cj Hi cj!!! 注意:1. 等号=两边不能有空格. 2. 调用的时候要写$ 3.调用变量可以加上花括号{},来帮助确定变量名的边界. ","date":"2020-01-10","objectID":"https://seacj.github.io/posts/shell%E5%AD%A6%E4%B9%A0/:2:0","tags":["linux"],"title":"Shell学习","uri":"https://seacj.github.io/posts/shell%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"隐式定义 代码 echo `ls ./|grep sh` echo $(ls ./|grep sh) echo $(pwd) 输出 learn.sh learn.sh /home ","date":"2020-01-10","objectID":"https://seacj.github.io/posts/shell%E5%AD%A6%E4%B9%A0/:3:0","tags":["linux"],"title":"Shell学习","uri":"https://seacj.github.io/posts/shell%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"只读变量和删除变量 myUrl=\"http://www.google.com\" readonly myUrl variable=“cj” unset variable 要点:1. 若修改只读变量会报错 2. unset 命令不能删除只读变量 ","date":"2020-01-10","objectID":"https://seacj.github.io/posts/shell%E5%AD%A6%E4%B9%A0/:4:0","tags":["linux"],"title":"Shell学习","uri":"https://seacj.github.io/posts/shell%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"字符串 ","date":"2020-01-10","objectID":"https://seacj.github.io/posts/shell%E5%AD%A6%E4%B9%A0/:5:0","tags":["linux"],"title":"Shell学习","uri":"https://seacj.github.io/posts/shell%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"单引号 代码 v='variable' s='${v}\\n Hi' echo -e $s join='hi '$v echo $join 输出 ${v} Hi hi variable 要点:不能使用变量 ","date":"2020-01-10","objectID":"https://seacj.github.io/posts/shell%E5%AD%A6%E4%B9%A0/:5:1","tags":["linux"],"title":"Shell学习","uri":"https://seacj.github.io/posts/shell%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"双引号 要点:可以使用转义符和变量 代码 v='variable' s=\"${v}\\n Hi\" echo -e $s join=\"hi $v\" echo $join 输出 variable Hi hi variable ","date":"2020-01-10","objectID":"https://seacj.github.io/posts/shell%E5%AD%A6%E4%B9%A0/:5:2","tags":["linux"],"title":"Shell学习","uri":"https://seacj.github.io/posts/shell%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"不用引号 中间不能有空格,转义的\\n变成输出n了。 v=variable s=${v}\\nHi echo -e $s join='hi '$v echo $join 输出 variablenHi hi variable ","date":"2020-01-10","objectID":"https://seacj.github.io/posts/shell%E5%AD%A6%E4%B9%A0/:5:3","tags":["linux"],"title":"Shell学习","uri":"https://seacj.github.io/posts/shell%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"字符串操作 获取字符串长度 string=\"abcd\" echo ${#string} #输出 4 提取子字符串 string=\"runoob is a great site\" echo ${string:1:4} # 输出 unoo 查找子字符串 string=\"runoob is a great site\" echo `expr index \"$string\" io` # 输出 4 数组 ","date":"2020-01-10","objectID":"https://seacj.github.io/posts/shell%E5%AD%A6%E4%B9%A0/:5:4","tags":["linux"],"title":"Shell学习","uri":"https://seacj.github.io/posts/shell%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"定义数组 array_name=(value0 value1 value2 value3) ","date":"2020-01-10","objectID":"https://seacj.github.io/posts/shell%E5%AD%A6%E4%B9%A0/:6:0","tags":["linux"],"title":"Shell学习","uri":"https://seacj.github.io/posts/shell%E5%AD%A6%E4%B9%A0/"},{"categories":["编程-ღ-技术"],"content":"读取数组 ${数组名[下标]} @获取数组所有元素 echo ${array_name[@]} ","date":"2020-01-10","objectID":"https://seacj.github.io/posts/shell%E5%AD%A6%E4%B9%A0/:7:0","tags":["linux"],"title":"Shell学习","uri":"https://seacj.github.io/posts/shell%E5%AD%A6%E4%B9%A0/"},{"categories":["教程-ღ-点评"],"content":"入手2周罗技的K480键盘,写一下个人的体验。 ","date":"2020-01-08","objectID":"https://seacj.github.io/posts/%E7%BD%97%E6%8A%80k480%E8%93%9D%E7%89%99%E9%94%AE%E7%9B%98%E4%BD%93%E9%AA%8C/:0:0","tags":["产品体验"],"title":"罗技K480蓝牙键盘の体验","uri":"https://seacj.github.io/posts/%E7%BD%97%E6%8A%80k480%E8%93%9D%E7%89%99%E9%94%AE%E7%9B%98%E4%BD%93%E9%AA%8C/"},{"categories":["教程-ღ-点评"],"content":"我的使用环境 一个台式机和一个笔记本(因为笔记本弄了个支架,所以不会使用笔记本自带的键盘),都是windows系统,平时用来敲代码写文档。 K480就很适合我的这种情况,就是需要在两个设备上(K480最多支持同时在3台设备上切换)切换打字,毕竟桌子上摆2个键盘感觉很占地方,这是我买K480的TOP1的原因。 ","date":"2020-01-08","objectID":"https://seacj.github.io/posts/%E7%BD%97%E6%8A%80k480%E8%93%9D%E7%89%99%E9%94%AE%E7%9B%98%E4%BD%93%E9%AA%8C/:1:0","tags":["产品体验"],"title":"罗技K480蓝牙键盘の体验","uri":"https://seacj.github.io/posts/%E7%BD%97%E6%8A%80k480%E8%93%9D%E7%89%99%E9%94%AE%E7%9B%98%E4%BD%93%E9%AA%8C/"},{"categories":["教程-ღ-点评"],"content":"关于手感 我其实对手感要求不高,体验来看没有什么特别的地方,两个字——”够用“,设计上有一定的人体工学的设计,也就是字母键是凹下去的,空格键的一排的键是凸出来的。 有评论说比不上机械键盘什么的,我觉得确实如此,但是也是要看跟什么价位的键盘比吧,K480这种128元价位真的买得到多好的机械键盘吗。 ","date":"2020-01-08","objectID":"https://seacj.github.io/posts/%E7%BD%97%E6%8A%80k480%E8%93%9D%E7%89%99%E9%94%AE%E7%9B%98%E4%BD%93%E9%AA%8C/:2:0","tags":["产品体验"],"title":"罗技K480蓝牙键盘の体验","uri":"https://seacj.github.io/posts/%E7%BD%97%E6%8A%80k480%E8%93%9D%E7%89%99%E9%94%AE%E7%9B%98%E4%BD%93%E9%AA%8C/"},{"categories":["教程-ღ-点评"],"content":"功能性 首先附上K480的说明书:https://www.logitech.com.cn/zh-cn/manuals/k480-immersion-guide. 设备之间的切换几乎没有延迟(个人觉得这个速度应该跟键盘无关,主要是连接设备的蓝牙好不好使). 罗技的键盘配合配套软件使用可以自定义快捷键,这个功能我没有深度使用,但是应该还是挺不错的. 一开始买过来发现居然没有home(不是那个返回主页的home,是移动光标的)和end键,后来在产品说明里找到home键是fn+方向键←,end键是fn+方向键→ . 另外,我也看到《怎么给罗技K480 增加HOME、END键》用自定义快捷键设置这两个键的. 因为最初是给手机pad这些设备使用,所以会有一些一般电脑用的键盘没有的功能,比如在chrome浏览器按f1还可以跳到首页,f4是返回,这个在产品说明书里有详细介绍. 总结 我买K480的原因和K480的优势(有优先级顺序): 可以在多台设备上切换. 我的需求是无线键盘,所以很多优秀的有线键盘我都没看. 产品介绍说K480的电池一次可以用1到2年,对我来说这点就跟不用电池一样,而且快递寄过来的时候里面是有电池的. K480的不足: 重(但是我自己都是在一个地方用,所以对我来说根本不是问题). 不好看(按键跟很多键盘不一样,是那种圆角正方形,个人感觉长得像个玩具。不过弄个键盘膜应该会好一些). 卡槽(这个产品当初设计就是为iPad或者手机使用的,但我是给PC用,所以卡槽对我来说很多余。我当时也可以买没有卡槽的K380,但是我又嫌K380键程短,不适合我这种手比较大的). 相关测评 《值不值得买》第106期:你以为它是个键盘 其实它是个吹风机——罗技K480蓝牙键盘 写小红书的必备蓝牙键盘 罗技K480私人订制绝配 ","date":"2020-01-08","objectID":"https://seacj.github.io/posts/%E7%BD%97%E6%8A%80k480%E8%93%9D%E7%89%99%E9%94%AE%E7%9B%98%E4%BD%93%E9%AA%8C/:3:0","tags":["产品体验"],"title":"罗技K480蓝牙键盘の体验","uri":"https://seacj.github.io/posts/%E7%BD%97%E6%8A%80k480%E8%93%9D%E7%89%99%E9%94%AE%E7%9B%98%E4%BD%93%E9%AA%8C/"},{"categories":["编程-ღ-技术"],"content":"背景 声纹识别上x-vector被作为很多赛事的baseline使用,包括aishell2018、ASVspoof2019。介绍x-vector的文章主要有[1] [2]两篇,[1]介绍x-vector的整体和细节部分,[2]对实验进行了补充分析。 Prerequisites: TDNN,embedding。 核心思路 将系统分成两个部分: Embedding:将不定长的语音通过加噪和加混响进行数据扩充,然后经由深度神经网络映射成定长的向量,将映射之后的向量称为x-vector。 Compare pairs of embeddings:采用PLDA。 x-vector系统 输入:24维filterbanks(在[1]中是20维MFCC),帧长25ms,经过 a. 3秒滑动窗口的均值归一化 b. speech activity detection(SAD)去除没有说话人语音的帧 处理。 系统框架: 系统参数: ","date":"2020-01-02","objectID":"https://seacj.github.io/posts/x-vector/:1:0","tags":["声纹识别","机器学习"],"title":"声纹识别X-Vector","uri":"https://seacj.github.io/posts/x-vector/"},{"categories":["编程-ღ-技术"],"content":"Step 1. Time Delay Neural Networks (TDNN) 系统框架图中的Statistics Pooling之前的部分就是TDNN,下图是TDNN的示意图,来源为 [3]。 根据系统参数有如下图的TDNN: 根据系统参数所示,frame5的output是1500,因此每一次TDNN的输入都是15帧,输出都是1500维的向量。 对语音每15帧提取一次1500维的向量,如果输入的语音有T帧长,那么最终就能得到T个1500维的向量。 p.s. 如果说输入的语音特征总共有T帧,在边缘不补零的情况下,输出是T-2*7帧,而不是刚好T帧。这里说得到T个向量是为了与论文一致,后文也将沿用论文的说法。(p.s.具体实现的时候可以用dilation,可参见代码) ","date":"2020-01-02","objectID":"https://seacj.github.io/posts/x-vector/:1:1","tags":["声纹识别","机器学习"],"title":"声纹识别X-Vector","uri":"https://seacj.github.io/posts/x-vector/"},{"categories":["编程-ღ-技术"],"content":"Step 2. Statistics pooling 对这T个向量计算均值和方差(因为每个1500维的向量都是从一个15帧的数据提取的,这样能够集合不同时间上的信息),将均值和方差合并起来,则得到一个2x1500=3000维的向量。 与系统框架所示,pooling之后,使用两层全连接层,最后softmax之后的输出长度是说话人的数量K(p.s.在[2]中说话人数量的符号是N,为了与损失函数公式符号保持一致,这里统一用K表示说话人数量)。 模型的损失函数为下式的多分类交叉熵: 其中,有K个说话人,每人说了N个片段(segment)。当对说话人片段n打上的标签是k时,$d_{n k} $是1,否则为0。 第n段语音的loss计算可以用以下的表格表示: * 说话人A 说话人B 说话人K Label 0 1 0 Pred 0.3 0.6 0.1 $$ \\text {loss}=-(0 \\times \\log (0.3)+1 \\times \\log (0.6)+0 \\times \\log (0.1) $$ ","date":"2020-01-02","objectID":"https://seacj.github.io/posts/x-vector/:1:2","tags":["声纹识别","机器学习"],"title":"声纹识别X-Vector","uri":"https://seacj.github.io/posts/x-vector/"},{"categories":["编程-ღ-技术"],"content":"Step 3. extract embedding 神经网络并不仅仅是一个分类器,而是一个特征提取器和分类器的结合,每一层都有极强的特征提取能力。因此可以将模型的一部分作为特征提取器,也就是embeddings。 在训练完前述模型之后,截取模型的前一部分。截取哪部分要考量两点: 选取能够利用整段语音信息的部分(所以选取pooling之后的部分) 。 输出不宜过长(pooling之后的输出结果的长度为3000,所以选择经过全连接层的,输出结果长度为512)。 如系统参数所示,文献[1]选取segment6的输出作为embeddings。 如系统框架所示,文献[2]选取了两种embedding方法进行对比,并增加了联合两种embedding的方法( Instead of concatenating embeddingstogether,wecomputeseparatePLDAbackendsfor each embedding, and average the scores),效果如下图: 结论:1. 测试语音比较长时,i-vector的优势比较明显;2. 测试语音在5-20s时,dnn效果较好;3. embedding的综合要比只用单个embedding好;4. dnn对out of domain 的效果优于i-vector ","date":"2020-01-02","objectID":"https://seacj.github.io/posts/x-vector/:1:3","tags":["声纹识别","机器学习"],"title":"声纹识别X-Vector","uri":"https://seacj.github.io/posts/x-vector/"},{"categories":["编程-ღ-技术"],"content":"Step 4. PLDA backend PLDA(Probabilistic Linear Discriminant Analysis)是一种信道补偿算法,号称概率形式的LDA算法,PLDA算法的信道补偿能力比LDA更好,已经成为目前最好的信道补偿算法。 利用前述的embeddings训练PLDA模型, 使用PLDA的方法和i-vector是一样的。具体做法如下: 建模 定义第i个说话人的第j条语音为xij,然后定义xij的生成模型为$x_{i j}=\\mu+F h_{i}+G w_{i j}+\\epsilon_{i j}$。 模型分为两部分: 信号部分:$\\mu+F h_{i}$,只与说话人身份有关,该项描述了**个体之间**的差异。 噪声部分:$G w_{i j}+\\epsilon_{i j}$,同一个人每次说话也会有差异,描述了**个体内部**的差异。 $\\mu$是全体训练数据的平均值; F可以看做是身份空间,包含了可以用来表示各种身份的基底; $h_i$就可以看做是一个人的身份(或者是人物在身份空间中的位置); G可以看做是误差空间,包含了可以用来表示同一身份不同变化的基底; $w_{ij}$表示的是在误差空间中的位置; $\\epsilon_{i j}$ 用来表示随机误差,该项为零均高斯分布,方差为 Σ。 由于我们只关心区分说话人,所以并不需要计算误差空间,所以可以把噪声部分的两项合并,则得到$X_{i j}=\\mu+F h_{i}+\\epsilon_{i j}$,其中$\\epsilon$~N(0, Σ),$h_i$~N(0,1)。 训练 训练PLDA模型,就是通过训练数据估计出参数$\\phi$和Σ。估计这两个参数的方法是经典EM算法迭代求解,即猜(E-step)-反思(M-step),重复。 测试 模型测试阶段的思想是使用对数似然比来计算得分: n1和n2分别是两个语音的x-vector(或i-vector)矢量,这两条语音来自同一空间的假设为Hs,来自不同的空间的假设为Hd。 其中p(n1, n2 | hs)为两条语音来自同一空间的似然函数; p(n1 | hd),p(n2 | hd)分别为n1和n2来子不同空间的似然函数。通过计算对数似然比,就能衡量两条语音的相似程度。 得分越高,两条语音属于同一说话人的可能性越大。 具体的PLDA得分计算也有不同的实现,[4]中介绍了在i-vector上用的Gaussian PLDA(GPLDA)。 ","date":"2020-01-02","objectID":"https://seacj.github.io/posts/x-vector/:1:4","tags":["声纹识别","机器学习"],"title":"声纹识别X-Vector","uri":"https://seacj.github.io/posts/x-vector/"},{"categories":["编程-ღ-技术"],"content":"论文结论 文献[2]主要是对数据扩充对不同模型的影响进行了对比分析,性能结果如下图: 文献[2]的结论: x-vector取得了比另外两种ivector更优异的效果,尤其是在out-domain(Cantonese)上保持了优势; 本文所提出的data augmentation 可以大大减少EER,同时data augmentation在xvector上效果最好; PLDA aug 或者extractor aug 在三种模型上都有所改进,但是二者结合取得的效果最好; 参考资料 [1] Snyder D, Garcia-Romero D, Povey D, et al. Deep Neural Network Embeddings for Text-Independent Speaker Verification[C]//Interspeech. 2017: 999-1003. [2] Snyder D, Garcia-Romero D, Sell G, et al. X-vectors: Robust dnn embeddings for speaker recognition[C]//2018 IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP). IEEE, 2018: 5329-5333. [3] Peddinti V, Povey D, Khudanpur S. A time delay neural network architecture for efficient modeling of long temporal contexts[C]//Sixteenth Annual Conference of the International Speech Communication Association. 2015. [4] Garcia-Romero D, Espy-Wilson C Y. Analysis of i-vector length normalization in speaker recognition systems[C]//Twelfth Annual Conference of the International Speech Communication Association. 2011. Embedding的优势 https://towardsdatascience.com/deep-learning-4-embedding-layers-f9a02d55ac12 Embedding的讲解(以NLP为例) https://zhuanlan.zhihu.com/p/34975871 TDNN相关资料 https://www.jianshu.com/p/0207536ebc6c 交叉熵相关 https://blog.csdn.net/tsyccnh/article/details/79163834 PLDA算法解释 1. https://blog.csdn.net/xmu_jupiter/article/details/47281211 2. https://blog.csdn.net/shuzfan/article/details/51672050 3. https://blog.csdn.net/weixin_38206214/article/details/81115275 ","date":"2020-01-02","objectID":"https://seacj.github.io/posts/x-vector/:2:0","tags":["声纹识别","机器学习"],"title":"声纹识别X-Vector","uri":"https://seacj.github.io/posts/x-vector/"},{"categories":["编程-ღ-技术"],"content":"题记 由于有些東西是命题逻辑没办法表示的,所以引出谓词逻辑,需要说明的是不管是命题逻辑也好,谓词逻辑也好,只是逻辑学中被广泛使用的其中的分支而已,根据实际应用需要可以使用其中的方法,自己创造一种新的逻辑(虽然不容易)也是完全没问题的。 对谓词的介绍 谓词逻辑也被称为一阶逻辑(first-order logic), 谓词(predicate)其实就是一种关系,只是为了方便起见我们规定一些符号,并给符号一些定义。 S(x)就是一个一元关系,它可以表示x是一个学生。 Y(x,y)是一个二元关系,它可以是x比y年龄更老。 为什么计算机专业要学谓词逻辑:其实一个计算机语言(C、java等)的实现离不开包括谓词逻辑在内的各种逻辑,比如一个等号=可以看作一个二元的谓词,更直观的说,a=b可以看作=(a,b)。其实谓词逻辑还有各种用途,比如Type Theory类型理论,这里不做过多介绍。 和命题逻辑一样,我们给出谓词逻辑的formal的形式,首先formal的谓词逻辑语言包括三个部分: A set of predicate symbols 谓词符号 P (谓词符号是定义域D,值域{0,1}的映射.这一点区别于函数) A set of function symbols 函数符号 F A set of constant symbols 常量符号 C (其实常量就是没有自变量的函数,不过为了方便,还是称作常量) 为方便起见,我们先定义项的概念,如下图所示: 谓词逻辑的公式定义,如下图所示,其中t_1,t_2等是项: 一个谓词逻辑公式的例子 自由变量和约束变量 定义见下图: 如下图,是公式$(\\forall x(P(x) \\wedge Q(x))) \\rightarrow(\\neg P(x) \\vee Q(y))$的语法树,并标注自由变量和约束变量。 也就是说,虽然在公式中都写作x,但是其实有可能是不同的(如上图右边自由的x和左边约束的x是不同的)。 代换 代换(substitution)定义:给定一个变量$x$, 一个项$t$,和公式$\\phi $。我们就可以定义公式$\\phi[t / x]$,它是通过将$\\phi $中所有是自由变量的$x$替换成$t$得来的。 Example: 将上图[自由变量和约束变量的例子],使用替换,表示成公式$\\phi[f(x, y) / x]$,代换之后的结果如下图所示: 谓词逻辑的自然推演 和命题逻辑一样,谓词逻辑也有自然推演的规则,命题推演规则在谓词逻辑里同样适用,这里就不列出全部的规则,只给出2道证明题作为例子。 这里需要注意的一个要点是,假设的条件只能在框内有效,否则会引发\"只要有一个x满足性质P,则所有x都满足P\"的经典逻辑语义危机,见下图例子。 相关资料 英文版教材[《logic in computer science》] (http://dcn.icc.spbstu.ru/~karpov/%D0%9A%D1%83%D1%80%D1%81%20%D0%9B%D0%9E%D0%93%D0%98%D0%9A%D0%90/%D0%94%D0%9E%D0%9F%D0%9E%D0%9B%D0%9D%D0%98%D0%A2%D0%95%D0%9B%D0%AC%D0%9D%D0%9E%D0%95%20%D0%A7%D1%82%D0%B5%D0%BD%D0%B8%D0%B5/LogicInCS.pdf) (p.s. 中文版的书名叫《面向计算机科学的数理逻辑 系统建模与推理》) ","date":"2019-12-15","objectID":"https://seacj.github.io/posts/%E8%B0%93%E8%AF%8D%E9%80%BB%E8%BE%91/:1:0","tags":["计算机科学中的逻辑学","数学"],"title":"谓词逻辑","uri":"https://seacj.github.io/posts/%E8%B0%93%E8%AF%8D%E9%80%BB%E8%BE%91/"},{"categories":["编程-ღ-技术"],"content":"题记 本来这个系列的博客是打算上一两节课就更新一次,结果一转眼《计算机科学中的逻辑学》这门课已经结课了,打算花两天时间整理一遍,作为记录,也作为复习吧。 另外由于精力有限,很多地方没办法给出非常细致的证明,所以这个系列博客更多的是作为介绍性的博客,而非详尽的教程式的東西,如果想详细学习数理逻辑或者计算机逻辑学可以参考文末给出的教材。 Propositional logic as a formal language 一个对命题逻辑公式直观的感受是,一个命题逻辑公式是一个由字母表(alphabet) {p,q,r,…}∪{p1,p2,p3,…}∪{$\\neg, \\wedge, \\vee, \\rightarrow,(,)$} 组成的字符串(string)。但是这样是不够的,比如\"$(\\neg)() \\vee p q \\rightarrow$\"这样的字符串对我么来说没有什么意义,所以需要严格定义一些公式,这样严格定义的公式是well-formed的(一个well-formed formula其实就是能够被称做formula的字符串,p.s. well-formed被翻译成\"合式的”)。命题逻辑的well-formed的定义如下图所示: 判断一个公式是否well-formed的最简单的方法,就是画出公式的parse tree, 如下图是公式$((\\neg(p \\vee(q \\rightarrow(\\neg p)))) \\wedge r)$的parse tree. 下图是一个非合式公式的parse tree. Semantics of propositional logic 前提(premise) $\\phi_{1}…$和结论(conclusion) $\\psi$ 直接的关系有两大类,我们写成以下两种形式。 $\\phi_1, \\phi_2, \\cdots, \\phi_n \\vdash \\psi$ 关系① $\\phi_1, \\phi_2, \\cdots, \\phi_n \\vDash \\psi$ 关系② 在关系①上:若要从前提证明结论,是语法层面的证明(即在上一篇博客介绍的自然推演规则下进行证明),没有真假的判定。 在关系②上:若要从前提证明结论,是语义层面的证明(即真值表上,需要证若$\\phi_1, \\phi_2, \\cdots, \\phi_n$为T,则$\\psi$也为T),需要在实际空间(被称为语义空间)上证明。 命题逻辑具有可靠性(soundness)(即关系①成立那么关系②也成立)和完备性(completeness)(即若关系②成立,则关系①就成立)。 实际上我强烈建议关于这部分包括可靠性和完备性的证明看一下教材(在文末相关资料已经列出),由于解释需要较长的篇幅,所以这里略去。 Normal form 决定$\\vDash$有一些不同的方法,其中最原始的就是使用真值表进行判定,这种方法很费时,所以提出范式的概念。 范式(Normal form)本质上是用于加速算法的,相当于一种数据结构。这里我不详细介绍范式,给出一道例题体会一下即可。 基于下面的真值表构建CNF(合取范式)形式的公式: 相关资料 可靠性与完备性(Soundness and Completeness) https://zhuanlan.zhihu.com/p/49956452 英文版教材[《logic in computer science》] (http://dcn.icc.spbstu.ru/~karpov/%D0%9A%D1%83%D1%80%D1%81%20%D0%9B%D0%9E%D0%93%D0%98%D0%9A%D0%90/%D0%94%D0%9E%D0%9F%D0%9E%D0%9B%D0%9D%D0%98%D0%A2%D0%95%D0%9B%D0%AC%D0%9D%D0%9E%D0%95%20%D0%A7%D1%82%D0%B5%D0%BD%D0%B8%D0%B5/LogicInCS.pdf) (p.s. 中文版的书名叫《面向计算机科学的数理逻辑 系统建模与推理》) ","date":"2019-12-13","objectID":"https://seacj.github.io/posts/%E5%91%BD%E9%A2%98%E9%80%BB%E8%BE%91%E7%9A%84%E8%AF%AD%E4%B9%89/:1:0","tags":["计算机科学中的逻辑学","数学"],"title":"命题逻辑的语义","uri":"https://seacj.github.io/posts/%E5%91%BD%E9%A2%98%E9%80%BB%E8%BE%91%E7%9A%84%E8%AF%AD%E4%B9%89/"},{"categories":["编程-ღ-技术"],"content":"题记 为了严谨地描述一些问题,我们需要一些语言去表达问题中的逻辑结构,这篇博客将讲述其中一种语言——命题逻辑(propositional logic), 声明式语言(Declarative sentences) 命题逻辑是基于声明式(declarative)语句的,即能够判别真假的语句. (It is based on propositions, or declarative sentences which one can, in principle, argue as being true or false. ) 声明式语句的例子: The sum of the numbers 3 and 5 equals 8. 非声明式(non-declarative)语句的例子: Could you please pass me the salt? Ready, steady, go! 相比非声明式, 我们专注于声明式语句,是因为它符合计算机系统/程序的行为,此外,使用声明式语句能够用于判断程序是否满足需求/规格说明(specification). ","date":"2019-09-22","objectID":"https://seacj.github.io/posts/%E5%91%BD%E9%A2%98%E9%80%BB%E8%BE%91%E5%92%8C%E8%87%AA%E7%84%B6%E6%8E%A8%E6%BC%94/:1:0","tags":["计算机科学中的逻辑学","数学"],"title":"命题逻辑和自然推演","uri":"https://seacj.github.io/posts/%E5%91%BD%E9%A2%98%E9%80%BB%E8%BE%91%E5%92%8C%E8%87%AA%E7%84%B6%E6%8E%A8%E6%BC%94/"},{"categories":["编程-ღ-技术"],"content":"格式化声明式语言(Formalize declarative sentences) 所谓Formalize, 主要是为了3点: 能够有效地进行论证 能够严密地定义(defined rigorously) 能够被执行 非常自然的formalize的方法就是使用符号(symbol). ","date":"2019-09-22","objectID":"https://seacj.github.io/posts/%E5%91%BD%E9%A2%98%E9%80%BB%E8%BE%91%E5%92%8C%E8%87%AA%E7%84%B6%E6%8E%A8%E6%BC%94/:2:0","tags":["计算机科学中的逻辑学","数学"],"title":"命题逻辑和自然推演","uri":"https://seacj.github.io/posts/%E5%91%BD%E9%A2%98%E9%80%BB%E8%BE%91%E5%92%8C%E8%87%AA%E7%84%B6%E6%8E%A8%E6%BC%94/"},{"categories":["编程-ღ-技术"],"content":"Example 如果公交车晚点, 而且没有出租车, 那么John就会迟到. John没有迟到 火车晚点了 因此, 有出租车 下雨了, Jane没带伞, 那么Jane就会淋湿 Jane没有淋湿 下雨了 因此, ,Jane带伞了 ","date":"2019-09-22","objectID":"https://seacj.github.io/posts/%E5%91%BD%E9%A2%98%E9%80%BB%E8%BE%91%E5%92%8C%E8%87%AA%E7%84%B6%E6%8E%A8%E6%BC%94/:2:1","tags":["计算机科学中的逻辑学","数学"],"title":"命题逻辑和自然推演","uri":"https://seacj.github.io/posts/%E5%91%BD%E9%A2%98%E9%80%BB%E8%BE%91%E5%92%8C%E8%87%AA%E7%84%B6%E6%8E%A8%E6%BC%94/"},{"categories":["编程-ღ-技术"],"content":"Structures Example1 Example2 公交车晚点 下雨了 有出租车 Jane带伞了 John迟到了 Jane淋湿了 ","date":"2019-09-22","objectID":"https://seacj.github.io/posts/%E5%91%BD%E9%A2%98%E9%80%BB%E8%BE%91%E5%92%8C%E8%87%AA%E7%84%B6%E6%8E%A8%E6%BC%94/:2:2","tags":["计算机科学中的逻辑学","数学"],"title":"命题逻辑和自然推演","uri":"https://seacj.github.io/posts/%E5%91%BD%E9%A2%98%E9%80%BB%E8%BE%91%E5%92%8C%E8%87%AA%E7%84%B6%E6%8E%A8%E6%BC%94/"},{"categories":["编程-ღ-技术"],"content":"Formal argument 其实上面的两个问题属于同一个问题,可以用下面的符号来表示. If p and not q, then r. -\u003e Not r.p. Therefore, q. 自然推演(Natural deduction) 我们希望建立一套规则(a set of rules)去通过给定的前提(premise)来推出一些结论(conclusion). 其中一个非常重要且普遍的规则称为自然推演. 先了解一些名词和符号: ¬ :negation 逻辑否定 ∧ :conjunction 逻辑合取 ∨ :disjunction逻辑析取 →:implication蕴涵 sequent : 一种通过前提证明出结果的意图(intension), 相当于一个证明题的提问部分(区别于证明完成之后的结论). 在谈及自然推演规则之前想聊一些别的话题。其实自然推演我们早就在中学或者本科的离散数学里学习过,中学课程会告诉我们这是很正常的思想(比如否定的否定就是不变,¬¬p可以变成p),本科会通过真值表定义这些规则. 但是逻辑学想说的是,这些符号规则本身没有意义, 也就是说我们完全可以定义一套规则说¬p可以变成p, 而我们之所以默认使用自然推演这套规则,仅仅是因为他能解决实际生活中的很多问题(比如之前John迟到的例子). 很多数学家一生所做的事情就是自己根据自己制定的规则,然后不断推导,得到一些性质,这些规则本身其实毫无意义,只不过如果幸运的话这套规则可能能够运用到一些场景中,想到一句话——数学不是科学,因为数学不能被证伪. 现在给出自然推演的一些规则,需要说明的下面使用的表达方法可能以前少见,但是它是在顶刊中大家公认的表达方式,即 $$ \\frac{premise \\ premise}{conclusion} rule $$ 其中上面的\"rule\"只是指规则的名字,比如某个规则名为\"$\\wedge i$”. 推演规则分类为introduction(用于定义符号)和elimination(用于消去符号,以达到推演的目的). ","date":"2019-09-22","objectID":"https://seacj.github.io/posts/%E5%91%BD%E9%A2%98%E9%80%BB%E8%BE%91%E5%92%8C%E8%87%AA%E7%84%B6%E6%8E%A8%E6%BC%94/:2:3","tags":["计算机科学中的逻辑学","数学"],"title":"命题逻辑和自然推演","uri":"https://seacj.github.io/posts/%E5%91%BD%E9%A2%98%E9%80%BB%E8%BE%91%E5%92%8C%E8%87%AA%E7%84%B6%E6%8E%A8%E6%BC%94/"},{"categories":["编程-ღ-技术"],"content":"Rules for conjunction And-introduction rule $$\\frac{\\phi \\qquad \\psi}{\\phi \\wedge \\psi} \\wedge i$$ And-elimination rules $$ \\frac{\\phi \\wedge \\psi}{\\phi} \\wedge e_{1} $$ $$ \\frac{\\phi \\wedge \\psi}{\\psi} \\wedge e_{2} $$ ","date":"2019-09-22","objectID":"https://seacj.github.io/posts/%E5%91%BD%E9%A2%98%E9%80%BB%E8%BE%91%E5%92%8C%E8%87%AA%E7%84%B6%E6%8E%A8%E6%BC%94/:2:4","tags":["计算机科学中的逻辑学","数学"],"title":"命题逻辑和自然推演","uri":"https://seacj.github.io/posts/%E5%91%BD%E9%A2%98%E9%80%BB%E8%BE%91%E5%92%8C%E8%87%AA%E7%84%B6%E6%8E%A8%E6%BC%94/"},{"categories":["编程-ღ-技术"],"content":"Rules of double negation Introduction rule $$ \\frac{\\phi}{\\neg \\neg \\phi} \\neg \\neg i $$ Elimination rules $$ \\frac{\\neg \\neg \\phi}{\\phi} \\neg \\neg e $$ 现在我们用上面的规则就可以解下面这道题目了(第三行\"$\\neg \\neg i \\ 1$\"的意思是根据规则\"$\\neg \\neg i$\"和第一行的p,后面同理). ","date":"2019-09-22","objectID":"https://seacj.github.io/posts/%E5%91%BD%E9%A2%98%E9%80%BB%E8%BE%91%E5%92%8C%E8%87%AA%E7%84%B6%E6%8E%A8%E6%BC%94/:2:5","tags":["计算机科学中的逻辑学","数学"],"title":"命题逻辑和自然推演","uri":"https://seacj.github.io/posts/%E5%91%BD%E9%A2%98%E9%80%BB%E8%BE%91%E5%92%8C%E8%87%AA%E7%84%B6%E6%8E%A8%E6%BC%94/"},{"categories":["编程-ღ-技术"],"content":"Rules of implication Introduction 这里使用到假设的方法,用方框框住的区域为假设区域,假设的内容只在方框内有效, 方框内可以使用方框外的信息. Implies-elimination rule $$ \\frac{\\phi \\quad \\phi \\rightarrow \\psi}{\\psi} \\rightarrow e $$ Modus Tollens (MT for short) rule 即 逆否命题,可以通过其他规则(primitive rule)推导得出的规则(称为derived rule) $$ \\frac{\\phi \\rightarrow \\psi\\quad \\neg \\psi}{\\neg \\phi} \\mathrm{MT} $$ ","date":"2019-09-22","objectID":"https://seacj.github.io/posts/%E5%91%BD%E9%A2%98%E9%80%BB%E8%BE%91%E5%92%8C%E8%87%AA%E7%84%B6%E6%8E%A8%E6%BC%94/:2:6","tags":["计算机科学中的逻辑学","数学"],"title":"命题逻辑和自然推演","uri":"https://seacj.github.io/posts/%E5%91%BD%E9%A2%98%E9%80%BB%E8%BE%91%E5%92%8C%E8%87%AA%E7%84%B6%E6%8E%A8%E6%BC%94/"},{"categories":["编程-ღ-技术"],"content":"Rules of disjunction ","date":"2019-09-22","objectID":"https://seacj.github.io/posts/%E5%91%BD%E9%A2%98%E9%80%BB%E8%BE%91%E5%92%8C%E8%87%AA%E7%84%B6%E6%8E%A8%E6%BC%94/:2:7","tags":["计算机科学中的逻辑学","数学"],"title":"命题逻辑和自然推演","uri":"https://seacj.github.io/posts/%E5%91%BD%E9%A2%98%E9%80%BB%E8%BE%91%E5%92%8C%E8%87%AA%E7%84%B6%E6%8E%A8%E6%BC%94/"},{"categories":["编程-ღ-技术"],"content":"Rules of negation 为了定义否命题(注意前面只是定义了double negation),需要引入contradiction(矛盾)来完备自然推演这套规则. 定义contradiction: Contradictions are expressions of the form $\\neg \\phi \\wedge \\phi$ or $\\phi \\wedge \\neg \\phi$, where $\\phi$ is an arbitrary formula. (Note that $p \\wedge \\neg p \\vdash q$ is valid. 可以理解成如果你连矛盾的事情都能推出来, 还有什么推不出来呢,。但是注意这样只是便于理解,要记住符号本身没有任何意义.) Bottom-elimination 即 $$ \\frac{\\perp}{\\phi} \\perp \\mathrm{e} $$ Not-elimination $$ \\frac{\\phi \\quad \\neg \\phi}{\\perp} \\neg e $$ 例题 总结 ","date":"2019-09-22","objectID":"https://seacj.github.io/posts/%E5%91%BD%E9%A2%98%E9%80%BB%E8%BE%91%E5%92%8C%E8%87%AA%E7%84%B6%E6%8E%A8%E6%BC%94/:2:8","tags":["计算机科学中的逻辑学","数学"],"title":"命题逻辑和自然推演","uri":"https://seacj.github.io/posts/%E5%91%BD%E9%A2%98%E9%80%BB%E8%BE%91%E5%92%8C%E8%87%AA%E7%84%B6%E6%8E%A8%E6%BC%94/"},{"categories":["博客"],"content":"题记 写博客的时候有时候会需要写公式,我自己的习惯是用Typora进行博客编写,本地上Typora自带latex支持,但是推送到网页的话只能显示latex语法而不会转化成公式,之前本来想要么把公式截图,要么用Chrome插件,但是这样感觉不太友好,所以找了个用来渲染LaTeX的方法. 使用KaTeX对博客LaTeX进行渲染 ","date":"2019-09-21","objectID":"https://seacj.github.io/posts/hugo%E5%8D%9A%E5%AE%A2latex%E6%B8%B2%E6%9F%93/:1:0","tags":["博客搭建","LaTeX"],"title":"Hugo博客LaTeX渲染","uri":"https://seacj.github.io/posts/hugo%E5%8D%9A%E5%AE%A2latex%E6%B8%B2%E6%9F%93/"},{"categories":["博客"],"content":"第一步. 引入KaTeX的代码 \u003c!-- KaTeX --\u003e \u003clink rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.10.0-rc.1/katex.min.css\"\u003e \u003cscript src=\"https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.10.0-rc.1/katex.min.js\"\u003e\u003c/script\u003e \u003cscript src=\"https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.10.0-rc.1/contrib/auto-render.min.js\"\u003e\u003c/script\u003e \u003cscript\u003e document.addEventListener(\"DOMContentLoaded\", function() { renderMathInElement(document.body, { delimiters: [ {left: \"$$\", right: \"$$\", display: true}, {left: \"$\", right: \"$\", display: false} ] }); }); \u003c/script\u003e 将上面的代码写入html中,我是放在single.html里面的. 需要说明的是上面的代码考虑到了行内公式. ","date":"2019-09-21","objectID":"https://seacj.github.io/posts/hugo%E5%8D%9A%E5%AE%A2latex%E6%B8%B2%E6%9F%93/:2:0","tags":["博客搭建","LaTeX"],"title":"Hugo博客LaTeX渲染","uri":"https://seacj.github.io/posts/hugo%E5%8D%9A%E5%AE%A2latex%E6%B8%B2%E6%9F%93/"},{"categories":["博客"],"content":"第二步. 测试 ","date":"2019-09-21","objectID":"https://seacj.github.io/posts/hugo%E5%8D%9A%E5%AE%A2latex%E6%B8%B2%E6%9F%93/:3:0","tags":["博客搭建","LaTeX"],"title":"Hugo博客LaTeX渲染","uri":"https://seacj.github.io/posts/hugo%E5%8D%9A%E5%AE%A2latex%E6%B8%B2%E6%9F%93/"},{"categories":["博客"],"content":"样例1 $$2^2$$ $$ 2^2 $$ ","date":"2019-09-21","objectID":"https://seacj.github.io/posts/hugo%E5%8D%9A%E5%AE%A2latex%E6%B8%B2%E6%9F%93/:3:1","tags":["博客搭建","LaTeX"],"title":"Hugo博客LaTeX渲染","uri":"https://seacj.github.io/posts/hugo%E5%8D%9A%E5%AE%A2latex%E6%B8%B2%E6%9F%93/"},{"categories":["博客"],"content":"样例2 $$f(x)=\\int_{-\\infty}^\\infty\\widehatf\\xi\\,e^{2\\pii\\xix}\\,d\\xi$$ $$ f(x)=\\int_{-\\infty}^\\infty\\widehat f\\xi,e^{2\\pi i\\xi x},d\\xi $$ ","date":"2019-09-21","objectID":"https://seacj.github.io/posts/hugo%E5%8D%9A%E5%AE%A2latex%E6%B8%B2%E6%9F%93/:3:2","tags":["博客搭建","LaTeX"],"title":"Hugo博客LaTeX渲染","uri":"https://seacj.github.io/posts/hugo%E5%8D%9A%E5%AE%A2latex%E6%B8%B2%E6%9F%93/"},{"categories":["博客"],"content":"样例3 行内$2^2$行内 行内$2^2$行内 ","date":"2019-09-21","objectID":"https://seacj.github.io/posts/hugo%E5%8D%9A%E5%AE%A2latex%E6%B8%B2%E6%9F%93/:3:3","tags":["博客搭建","LaTeX"],"title":"Hugo博客LaTeX渲染","uri":"https://seacj.github.io/posts/hugo%E5%8D%9A%E5%AE%A2latex%E6%B8%B2%E6%9F%93/"},{"categories":["博客"],"content":"样例4 $$ \\frac{\\phi \\wedge \\psi}{\\phi} \\wedge e_{1} $$ ","date":"2019-09-21","objectID":"https://seacj.github.io/posts/hugo%E5%8D%9A%E5%AE%A2latex%E6%B8%B2%E6%9F%93/:3:4","tags":["博客搭建","LaTeX"],"title":"Hugo博客LaTeX渲染","uri":"https://seacj.github.io/posts/hugo%E5%8D%9A%E5%AE%A2latex%E6%B8%B2%E6%9F%93/"},{"categories":["博客"],"content":"样例5 $$\\begin{aligned} x \u0026=a+b+c \\\\\u0026=d+e \\\\\u0026=f+g \\end{aligned} $$ $$ \\begin{aligned} x \u0026=a+b+c \\ \u0026=d+e \\ \u0026=f+g \\end{aligned} $$ 小结 总体来说很方便,但是对换行(样例5)和中文暂时没有找到解决的方法, 不过已经满足绝大多数情况的需要了. 参考资料 Blog养成记(6) Hugo中的LaTeX渲染 https://orianna-zzo.github.io/sci-tech/2018-03/blog%E5%85%BB%E6%88%90%E8%AE%B06-hugo%E4%B8%AD%E7%9A%84latex%E6%B8%B2%E6%9F%93/ 使用KaTeX代替MathJax渲染公式 https://www.jianshu.com/p/f2b28954d902 KaTeX官网 https://katex.org/ LaTeX符号表 http://math.ecnu.edu.cn/~latex/docs/others/mathsymb.pdf ","date":"2019-09-21","objectID":"https://seacj.github.io/posts/hugo%E5%8D%9A%E5%AE%A2latex%E6%B8%B2%E6%9F%93/:3:5","tags":["博客搭建","LaTeX"],"title":"Hugo博客LaTeX渲染","uri":"https://seacj.github.io/posts/hugo%E5%8D%9A%E5%AE%A2latex%E6%B8%B2%E6%9F%93/"},{"categories":["编程-ღ-技术"],"content":"题记 上了三周袁梦霆老师的计算机科学中的逻辑学课程,不得不说是一节开拓视野的课程,从逻辑学的角度看待计算机非常有意思。第一节课的时候老师开玩笑的提到了计算机学界里面的鄙视链和编程的三个境界,给我感触很深。 鄙视链具体说什么我已经忘了,大概意思就是做理论的(主要指四个方向logic, formal language即如何用数学方法描述问题, computability即是否可计算, complexity)处于鄙视链的顶层,所以能发会议名字带T(Theory, 如STOC)、带F(Foundation, 如FOCS)的人各种意义上来说都很强,因为他们甚至可以不用编程实践来看做的对不对,比性能好不好,直接数学理论上就可以证明算法好不好. 而那些做实验的通常只能通过性能对比(一堆图表各种指标那种)来发文章. 打个比方,做实验的就是在证明1个苹果+1个苹果=2个苹果,然后换一群人在那里看1个橘子+1个橘子=2个橘子吗, 直到有一个搞理论证明出1+1=2,做实验的就不能再玩这个了. 当然, 鄙视链其实更多是自嘲吧,毕竟又有多少人有资格真正玩理论的東西呢, 另一方面靠实验做出的成果也未必全是1个苹果+1个苹果=2个苹果跟风式的東西,举个数理统计课上老师讲了两次的案例,一个博士生靠实验做出了一个技术,甚至在模拟数据上验证这种技术很好,但是到了要写文章的时候写不出来,因为他自己没办法数学推导,就请了我们数理统计的老师合作,一步步推导出来,确实从数学上是可行的. 所以说实验和理论还是不一样,应该说是相辅相成,或者说其实就是工科和理科的区别吧,但是培养数学素养总是没错的. 编程的三个境界就是1. It works 你的代码没毛病 2. It works well 你的代码不仅没毛病跑得还很快 3. It is correct 你的代码是最优的. 说起来很直观,但是很多时候不容易,第三点有一个专门的议题叫programming verification程序准确性验证,老师举了个例子,华为的一个芯片,设计完了之后要检验是没有问题的,还要专门找国外的一家公司花大价钱用专门的工具来测,测完通过了才敢大范围使用. 那么为什么学计算机科学中的逻辑学呢,其一是Try to think at a higher abstraction level(逻辑学对思考能力有帮助),其二是Learn to argue formally(写文章和高手交流的时候要专业一点,不然都不在一个频道上),其三就是Open another perspective to programming(和第一点意思差不多,说不定到时候有个想法灵光一现刚好数学上有一种方法能解决),两相结合就实现了. 逻辑学重要的是思想,那些套路什么的只是启发式的介绍,说不定以后自己都能创建一套逻辑规则. 相关内容 命题逻辑的语义 谓词逻辑 ","date":"2019-09-21","objectID":"https://seacj.github.io/posts/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6%E4%B8%AD%E7%9A%84%E9%80%BB%E8%BE%91%E5%AD%A6/:1:0","tags":["计算机科学中的逻辑学","数学"],"title":"计算机科学中的逻辑学题记","uri":"https://seacj.github.io/posts/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6%E4%B8%AD%E7%9A%84%E9%80%BB%E8%BE%91%E5%AD%A6/"},{"categories":["博客"],"content":" 添加即使通讯 修改主页 添加Tool页面 相关链接 添加即使通讯 博客添加在线通讯 LeaveIt主题配置 LeaveIt Hugo Theme 作者github ","date":"2019-09-20","objectID":"https://seacj.github.io/posts/%E5%BB%BA%E7%AB%99%E5%8E%86%E5%8F%B2004/:0:0","tags":["博客搭建","建站历史"],"title":"建站历史004","uri":"https://seacj.github.io/posts/%E5%BB%BA%E7%AB%99%E5%8E%86%E5%8F%B2004/"},{"categories":["编程-ღ-技术"],"content":"题记 使用threading进行多线程处理的时候,运算速度并没有提高。原因在于python中的全局锁(GIL, global interpreter lock). 样例 计算圆周率的程序 ","date":"2019-09-20","objectID":"https://seacj.github.io/posts/python%E5%85%A8%E5%B1%80%E9%94%81/:1:0","tags":["python"],"title":"Python全局锁(GIL)","uri":"https://seacj.github.io/posts/python%E5%85%A8%E5%B1%80%E9%94%81/"},{"categories":["编程-ღ-技术"],"content":"代码 \r点击展开代码\r import threading import random import math import time def print_run_time(func): def wrapper(*args, **kw): begin_time = time.time() ret = func(*args, **kw) end_time = time.time() run_time = end_time - begin_time print('Run time',run_time) return ret return wrapper @print_run_time def run(): sample_count = 10000000 #采样次数 inner = 0 #落在圆内的点 i = 0 while i \u003csample_count: x_i = random.uniform(-1,1) y_i = random.uniform(-1,1) if (math.pow(x_i,2) + math.pow(y_i,2)) \u003c 1: inner+=1 i+=1 pi = 4*(inner * 1.0)/sample_count print(pi) @print_run_time def single_run(): print('single','='*20) for _ in range(5): run() @print_run_time def multi_run(): print('multi','='*20) ts = [] for i in range(5): ts.append(threading.Thread(target=run)) ts[i].start() for i in range(5): ts[i].join() if __name__ == '__main__': multi_run() single_run() \r","date":"2019-09-20","objectID":"https://seacj.github.io/posts/python%E5%85%A8%E5%B1%80%E9%94%81/:2:0","tags":["python"],"title":"Python全局锁(GIL)","uri":"https://seacj.github.io/posts/python%E5%85%A8%E5%B1%80%E9%94%81/"},{"categories":["编程-ღ-技术"],"content":"结果 ~/dataset/xiaoaiKWS/G/code$ python3 测试多线程速度.py multi 多线程 3.140688 Run time 83.02815461158752 3.1416188 Run time 85.89040064811707 3.1412312 Run time 86.34525346755981 3.14129 Run time 88.51046800613403 3.1419212 Run time 88.64371156692505 Run time 88.65449285507202 single 3.1421216 Run time 10.199804782867432 3.1420676 Run time 10.172762870788574 3.1419712 Run time 10.3925302028656 3.1417784 Run time 10.204853534698486 3.1416324 Run time 10.210267305374146 Run time 51.180365800857544 ","date":"2019-09-20","objectID":"https://seacj.github.io/posts/python%E5%85%A8%E5%B1%80%E9%94%81/:3:0","tags":["python"],"title":"Python全局锁(GIL)","uri":"https://seacj.github.io/posts/python%E5%85%A8%E5%B1%80%E9%94%81/"},{"categories":["编程-ღ-技术"],"content":"启示 多线程速度要慢于串行,多线程不适合cpu密集型程序(但是可以用在IO密集的程序里). GIL 参考: http://cenalulu.github.io/python/gil-in-python/ https://zhuanlan.zhihu.com/p/20953544 ","date":"2019-09-20","objectID":"https://seacj.github.io/posts/python%E5%85%A8%E5%B1%80%E9%94%81/:4:0","tags":["python"],"title":"Python全局锁(GIL)","uri":"https://seacj.github.io/posts/python%E5%85%A8%E5%B1%80%E9%94%81/"},{"categories":["编程-ღ-技术"],"content":"为什么有GIL GIL是线程之间的排他锁(官方文档使用mutex这个词),用于保持线程间数据的一致性和状态的同步(这本身是一个非常难的议题, MySQL用了五年时间优化,而python作为高度社区化团队开发为解决这个问题更为艰难),而GIL是一种非常简单粗暴但是优雅的方法. ","date":"2019-09-20","objectID":"https://seacj.github.io/posts/python%E5%85%A8%E5%B1%80%E9%94%81/:5:0","tags":["python"],"title":"Python全局锁(GIL)","uri":"https://seacj.github.io/posts/python%E5%85%A8%E5%B1%80%E9%94%81/"},{"categories":["编程-ღ-技术"],"content":"图解多线程 GIL释放条件 在python2.x里,GIL的释放逻辑是当前线程遇见IO操作或者ticks计数达到100(ticks可以看作是python自身的一个计数器,专门做用于GIL,每次释放后归零,这个计数可以通过 sys.setcheckinterval 来调整),进行释放. 在python3.x中,GIL不使用ticks计数,改为使用计时器(执行时间达到阈值后,当前线程释放GIL),这样对CPU密集型程序更加友好,但依然没有解决GIL导致的同一时间只能执行一个线程的问题,所以效率依然不尽如人意. 两个线程在双核CPU上的执行情况. 绿色部分表示该线程在运行 红色部分为线程被调度唤醒,但是无法获取GIL导致无法进行有效运算等待的时间 白色部分表示IO线程处于等待 ","date":"2019-09-20","objectID":"https://seacj.github.io/posts/python%E5%85%A8%E5%B1%80%E9%94%81/:6:0","tags":["python"],"title":"Python全局锁(GIL)","uri":"https://seacj.github.io/posts/python%E5%85%A8%E5%B1%80%E9%94%81/"},{"categories":["编程-ღ-技术"],"content":"两个线程均为CPU密集型运算线程 ","date":"2019-09-20","objectID":"https://seacj.github.io/posts/python%E5%85%A8%E5%B1%80%E9%94%81/:6:1","tags":["python"],"title":"Python全局锁(GIL)","uri":"https://seacj.github.io/posts/python%E5%85%A8%E5%B1%80%E9%94%81/"},{"categories":["编程-ღ-技术"],"content":"一个IO密集型和一个CPU密集型线程 IO密集型代码(文件处理、网络爬虫等),多线程能够有效提升效率(单线程下有IO操作会进行IO等待,造成不必要的时间浪费,而开启多线程能在线程A等待时,自动切换到线程B,可以不浪费CPU的资源,从而能提升程序执行效率)。所以python的多线程对IO密集型代码比较友好。 结论 python尽量使用多进程 (使用python multiprocessing模块),而不是多线程,因为每个进程有各自独立的GIL,互不干扰,这样就可以真正意义上的并行执行. 扩展资料 IO同步和异步,阻塞和非阻塞 https://juejin.im/post/5b94e2995188255c5c45d0ec 廖雪峰python3多进程多线程 (适合代码学习) https://www.liaoxuefeng.com/wiki/1016959663602400/1017628290184064 ","date":"2019-09-20","objectID":"https://seacj.github.io/posts/python%E5%85%A8%E5%B1%80%E9%94%81/:6:2","tags":["python"],"title":"Python全局锁(GIL)","uri":"https://seacj.github.io/posts/python%E5%85%A8%E5%B1%80%E9%94%81/"},{"categories":["编程-ღ-技术"],"content":"题记 个人觉得计算机体系结构的内容很散,这里就随便记录一些上课内容. ","date":"2019-09-18","objectID":"https://seacj.github.io/posts/%E8%AE%A1%E7%AE%97%E6%9C%BA%E4%BD%93%E7%B3%BB%E7%BB%93%E6%9E%841/:1:0","tags":["计算机体系结构"],"title":"内存层次设计","uri":"https://seacj.github.io/posts/%E8%AE%A1%E7%AE%97%E6%9C%BA%E4%BD%93%E7%B3%BB%E7%BB%93%E6%9E%841/"},{"categories":["编程-ღ-技术"],"content":"Point 现在cpu处理器的速度已经超过内存。多核处理器也加剧了内存的压力. 因为disk读取速度慢,所以页的大小要比块的大小大很多. 寄存器由编译器来管理。如c=a+b,编译器来分配地址,add $1,$2,$3. Hit rate(在内存中找数据,能够在上层存储中找到的比率;即cache/(cache+memory))5和Hit time(RAM access time+Time to determine hit/miss) Miss penalty: time to replace a block from lower level, including time to replace in CPU 在单CPU串行的情况下,Average memory access time=Hit time + Miss rate * Miss penalty. 在多线程等情况下由多种优化策略,降低Miss penalty对速度的影响,而不简单是这个公式. 虚拟地址空间被分块,每一块称之为页(page),页表(page table)可以通过虚拟地址索引,页表用于从virtual page numbes映射到physical frames. 参考资料 主存到Cache直接映射、全相联映射和组相联映射 https://blog.csdn.net/dongyanxia1000/article/details/53392315 ","date":"2019-09-18","objectID":"https://seacj.github.io/posts/%E8%AE%A1%E7%AE%97%E6%9C%BA%E4%BD%93%E7%B3%BB%E7%BB%93%E6%9E%841/:2:0","tags":["计算机体系结构"],"title":"内存层次设计","uri":"https://seacj.github.io/posts/%E8%AE%A1%E7%AE%97%E6%9C%BA%E4%BD%93%E7%B3%BB%E7%BB%93%E6%9E%841/"},{"categories":["博客"],"content":"题记 偶然找到Wyane的博客,发现里面有个即使通讯功能感觉挺不错的,然后又找到一篇Hexo 搭建个人博客系列:进阶设置篇里面有介绍很多搭建博客的可能用到的東西,所以就来给我的博客加上这个功能。 使用Tidio给Hugo添加即使通讯功能 ","date":"2019-09-15","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E6%B7%BB%E5%8A%A0%E5%9C%A8%E7%BA%BF%E9%80%9A%E8%AE%AF/:1:0","tags":["博客搭建"],"title":"博客添加在线通讯","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E6%B7%BB%E5%8A%A0%E5%9C%A8%E7%BA%BF%E9%80%9A%E8%AE%AF/"},{"categories":["博客"],"content":"Step 1. 注册一个账号 注册Tidio账号的地址: https://www.tidiochat.com/panel/login。 注册账号的时候需要输入邮箱、密码、网站地址就可以了,可以说非常方便了。 然后简单设置一些基础的配置,像是第一句话是什么: ","date":"2019-09-15","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E6%B7%BB%E5%8A%A0%E5%9C%A8%E7%BA%BF%E9%80%9A%E8%AE%AF/:2:0","tags":["博客搭建"],"title":"博客添加在线通讯","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E6%B7%BB%E5%8A%A0%E5%9C%A8%E7%BA%BF%E9%80%9A%E8%AE%AF/"},{"categories":["博客"],"content":"Step 2. 复制代码 \u003cscript async src=\"//code.tidio.co/{{ theme.tidio.key }}.js\"\u003e\u003c/script\u003e 设置基础的配置的最后,会提供给你一个代码,把它复制下来就可以了,然后放在合适的位置(我是放在footer.html里面)。 如果没有复制到也没关系,注册完毕后进入Tidio提供的控制台。 找到SETTINGS -\u003e Developer -\u003e Project data,然后用自己的public key替换掉上面的{{ theme.tidio.key }}。 ","date":"2019-09-15","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E6%B7%BB%E5%8A%A0%E5%9C%A8%E7%BA%BF%E9%80%9A%E8%AE%AF/:3:0","tags":["博客搭建"],"title":"博客添加在线通讯","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E6%B7%BB%E5%8A%A0%E5%9C%A8%E7%BA%BF%E9%80%9A%E8%AE%AF/"},{"categories":["博客"],"content":"Step 3. 修改外观 找到Channel -\u003e Live chat -\u003eAppearance。 外观这个根据实际情况可以调整在左边或右边,颜色是什么样子。 参考资料 Hexo 搭建个人博客系列:进阶设置篇 http://yearito.cn/posts/hexo-advanced-settings.html ","date":"2019-09-15","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E6%B7%BB%E5%8A%A0%E5%9C%A8%E7%BA%BF%E9%80%9A%E8%AE%AF/:4:0","tags":["博客搭建"],"title":"博客添加在线通讯","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E6%B7%BB%E5%8A%A0%E5%9C%A8%E7%BA%BF%E9%80%9A%E8%AE%AF/"},{"categories":["教程-ღ-点评"],"content":"题记 想随便练练英语听力吧,刚好找到一个挺有趣的教英语的老师,他的视频挺不错的。打算做个系列文章稍微简单地记录一下,文章结构就用第一次听写 + 原文 的形式 视频名称: Daily English Dictation 1-370(Coach Shane) 视频地址:https://www.bilibili.com/video/av24173069/?p=24 备用地址:https://www.youtube.com/watch?v=q-9NCR5MW3U Daily English Dictation 24 An * SAT cheating scandals has been uncovered at a * long island high school. *** been taken the ** exam for at least 6 students. An alleged SAT cheating scandal has been uncovered at a prestigious Long Island high school. This college student is accused of taking the college entrance exam for at least six students. alleged = supposed 声称的 uncover = discover揭穿 prestigious 有声望的,用于形容大学或奖项 Long Island 一个地名 需要注意的发音或连读: been ,taking the, accused of Daily English Dictation 25 Snake luis take the world seris open on a cool night. It was 49 degrees at the start of the game. St Louis takes the World Series opener on a cool night. It was 49 degrees at the start of the game. St Louis圣路易斯,美国中部的一个城市, 这里是指一个棒球队 opener 开幕赛 takes =wins 49华氏度 =9.4摄氏度 需要注意的发音或连读: opener on a ,start of the Daily English Dictation 26 They search for the missing * for the night under generated power of flood lights. Some are tears. They searched for the missing throughout the night under generator-powered flood lights as family members waited by the mounds of debris- some in tears. throughout the night = all night flood lights 泛光灯,运动场上的那种大灯 generator 发电机 需要注意的发音或连读: mounds of debris ","date":"2019-09-14","objectID":"https://seacj.github.io/posts/englishdictation01/:1:0","tags":["公开课"],"title":"English Dictation 01","uri":"https://seacj.github.io/posts/englishdictation01/"},{"categories":["算法"],"content":"题记 今天上了数理统计和数学模型与优化,感觉终于上到了我认为的好课。第一眼看到数学模型与优化的董文永老师感觉自带气场,带着帽子上课,不过讲课还是挺幽默的。非常想吐槽一下教室的设备,两个教室的麦克风完全没声音,幸好坐在前排,要是抢不到位置估计很难听到吧(或者是我带耳机久了,听力下降了吗)。 上数学模型与优化的时候,有道题还是没弄清楚,所以这里来解决一下。这道题的解法不止一种,课上老师介绍的方法是并查集。 NOIP 2015 信息传递 \r点击展开题目详情\r\r以下的所有说明围绕样例进行说明。 当从别人那里得知自己的生日,实质上就是形成了一个环。所以这道题实际上是: 找到所构建的图中的最小环的长度。 找最小环的过程,这里我们用并查集的方法。即把每个同学都看成点,A同学将信息传给B同学,就相当于在A和B之间建立了一条有向条边,将其加入并查集中,当遇到两个点的祖先节点相同时,则说明他们已经在同一个集合,那么就能构成环,此时判断一下环的长度即可。 ","date":"2019-09-05","objectID":"https://seacj.github.io/posts/%E5%B9%B6%E6%9F%A5%E9%9B%86/:1:0","tags":["算法"],"title":"并查集","uri":"https://seacj.github.io/posts/%E5%B9%B6%E6%9F%A5%E9%9B%86/"},{"categories":["算法"],"content":"Step 0. 初始化 初始化的状态可以表示成下图所示,此时我们把每个节点看成一棵只有一个节点的树,那么每棵树的根自然就是唯一的那个节点。 代码来表示上图,创建两个数组a和b,其中a中每个元素表示每棵树的根节点,b来存放传递信息的方向。 为了方便起见,这里的index 0设置为空。 index 1 2 3 4 5 a 1 2 3 4 5 b 2 4 2 3 1 从左到右开始同时扫描一遍两个数组(即一个for循环)。 ","date":"2019-09-05","objectID":"https://seacj.github.io/posts/%E5%B9%B6%E6%9F%A5%E9%9B%86/:1:1","tags":["算法"],"title":"并查集","uri":"https://seacj.github.io/posts/%E5%B9%B6%E6%9F%A5%E9%9B%86/"},{"categories":["算法"],"content":"Step 1. 扫描1 当扫描到1时,发现1传递信息给2(即b[1]值为2),可以用以下的有向图表示。 此时1和2连在了一起,从两棵树合并成了一棵树,树的根我们定义成所指向的那个节点(需要注意, 并查集是孩子节点指向父节点,这点与一般的树相反)。 我们首先判断是否构成了环: 查看1所指向的节点, 即b[1] = 2 查看2的父节点, 即a[2] = 2, 因为此时2的父节点就是它自己,所以停止搜索。此刻认定1所指向的节点的根是2 判断1是不是1所指向的节点的根,即1是否等于2 此时不相等,所以没有构成环。没有构成环则更新1的根,即更新a[1]的值为2。 index 1 2 3 4 5 a 2 2 3 4 5 b 2 4 2 3 1 ","date":"2019-09-05","objectID":"https://seacj.github.io/posts/%E5%B9%B6%E6%9F%A5%E9%9B%86/:1:2","tags":["算法"],"title":"并查集","uri":"https://seacj.github.io/posts/%E5%B9%B6%E6%9F%A5%E9%9B%86/"},{"categories":["算法"],"content":"Step 2. 扫描2 当扫描到2时,发现2传递信息给4(即b[2]值为4),可以用以下的有向图表示。 index 1 2 3 4 5 a 2 4 3 4 5 b 2 4 2 3 1 ","date":"2019-09-05","objectID":"https://seacj.github.io/posts/%E5%B9%B6%E6%9F%A5%E9%9B%86/:1:3","tags":["算法"],"title":"并查集","uri":"https://seacj.github.io/posts/%E5%B9%B6%E6%9F%A5%E9%9B%86/"},{"categories":["算法"],"content":"Step 3. 扫描3 index 1 2 3 4 5 a 2 4 2 4 5 b 2 4 2 3 1 ","date":"2019-09-05","objectID":"https://seacj.github.io/posts/%E5%B9%B6%E6%9F%A5%E9%9B%86/:1:4","tags":["算法"],"title":"并查集","uri":"https://seacj.github.io/posts/%E5%B9%B6%E6%9F%A5%E9%9B%86/"},{"categories":["算法"],"content":"Step 4. 扫描4 index 1 2 3 4 5 a 2 4 2 4 5 b 2 4 2 3 1 从图上我们可以发现出现了一个环,代码上是如何发现的呢: 查看4所指向的节点, 即b[4] = 3 查看3的父节点, 即a[3] = 2 查看2的父节点, 即a[2] = 4 查看4的父节点, 即a[4] = 4, 因为此时4的父节点就是它自己,所以停止搜索。此刻4所指向的节点的根是4 判断4是不是 4所指向的节点 的根,即4是否等于4 如果相同则认为构成了环。此时构成了环,为了避免循环,则不更新a[4] ","date":"2019-09-05","objectID":"https://seacj.github.io/posts/%E5%B9%B6%E6%9F%A5%E9%9B%86/:1:5","tags":["算法"],"title":"并查集","uri":"https://seacj.github.io/posts/%E5%B9%B6%E6%9F%A5%E9%9B%86/"},{"categories":["算法"],"content":"Step 5. 扫描5 index 1 2 3 4 5 a 2 4 2 4 1 b 2 4 2 3 1 题解 P2661 【信息传递】代码: #include \u003ciostream\u003e#include \u003ccstdio\u003eusing namespace std; const int N = 200010; int n, fa[N], ans = 0x3f3f3f3f; int get (int x, int \u0026cnt) { //cnt记录环的长度 cnt ++; if (fa[x] == x) return x; else return get(fa[x], cnt); } int main () { scanf(\"%d\", \u0026n); for (int i = 1; i \u003c= n; i ++) fa[i] = i; for (int i = 1; i \u003c= n; i ++) { int cnt = 0, f; scanf(\"%d\", \u0026f); if (get(f, cnt) == i) { ans = min(ans, cnt); //维护最小的环 }else fa[i] = f; } printf(\"%d\", ans); return 0; } 参考资料 Graph Editor https://csacademy.com/app/graph_editor/ (画图论相关的图片非常好用) P2661 信息传递 题解 https://www.luogu.org/problemnew/solution/P2661 (提供这道题的C代码) 题解 P2661 【信息传递】https://duoluoluo.blog.luogu.org/solution-p2661 (提供这道题的C代码) ","date":"2019-09-05","objectID":"https://seacj.github.io/posts/%E5%B9%B6%E6%9F%A5%E9%9B%86/:1:6","tags":["算法"],"title":"并查集","uri":"https://seacj.github.io/posts/%E5%B9%B6%E6%9F%A5%E9%9B%86/"},{"categories":["算法"],"content":"概念 类似穷举的方法,不断搜索可能解的路径,当发现不满足的条件时,就回溯返回,返回后再继续尝试别的路径。 (如下图示, 为了找到迷宫的出口,不断尝试不同的路径,当发现路径不通时,回溯到岔路口,并继续走其他路径) 基本思想 将上面的走迷宫进一步抽象,可以将这个问题转化成树结构,称之为解空间树。 解空间树和迷宫有如下对应关系: 根节点→初始状态 中间节点→分叉路口 不满足问题解的叶子节点→迷宫死路 满足问题解的叶子节点→迷宫出口 回溯法解题步骤 (1)针对所给问题,确定问题的解空间: 首先应明确定义问题的解空间,问题的解空间应至少包含问题的一个(最优)解。 (2)确定结点的扩展搜索规则 (3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数(在搜索算法中优化中,剪枝,就是通过某种判断,避免一些不必要的遍历过程)避免无效搜索。 实现时的代码细节 ","date":"2019-08-26","objectID":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95/:0:0","tags":["算法"],"title":"回溯算法初探","uri":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95/"},{"categories":["算法"],"content":"递归实现 void backtrack(state s) { if(s is end){ // 当前结点为可行解 sols.append(path); // 保存该解 } else if(s has no ways){ // 当前结点为不可达叶子结点 return; } else{ // 当前结点为中间结点 for(i in possible ways){ next_s = do i in s; // 选择一条边 backtrack(next_s); // 在选择的边上往下走 s = redo i in s; // 回溯到父结点 } } } 例题 39. Combination Sum class Solution: def combinationSum(self, candidates: List[int], target: int) -\u003e List[List[int]]: size = len(candidates) if size == 0: return [] # 剪枝的前提是数组元素排序 # 深度深的边不能比深度浅的边还小 # 要排序的理由:1、前面用过的数后面不能再用;2、下一层边上的数不能小于上一层边上的数。 candidates.sort() # 在遍历的过程中记录路径,一般而言它是一个栈 path = [] res = [] # 注意要传入 size ,在 range 中, size 取不到 self.__dfs(candidates, 0, size, path, res, target) return res def __dfs(self, candidates, begin, size, path, res, target): # 先写递归终止的情况 if target == 0: # Python 中可变对象是引用传递,因此需要将当前 path 里的值拷贝出来 # 或者使用 path.copy() res.append(path[:]) for index in range(begin, size): residue = target - candidates[index] // “剪枝”操作,不必递归到下一层,并且后面的分支也不必执行 if residue \u003c 0: break path.append(candidates[index]) # 因为下一层不能比上一层还小,起始索引还从 index 开始 self.__dfs(candidates, index, size, path, res, residue) path.pop() #作者:liweiwei1419 #链接:https://leetcode-cn.com/problems/combination-sum/solution/hui-su-suan-fa-jian-zhi-python-dai-ma-java-dai-m-2/ 842. 将数组拆分成斐波那契序列 class Solution: def splitIntoFibonacci(self, S: str) -\u003e List[int]: res = list() def backtrack(index: int): # 递归终止情况 if index == len(S) and len(res) \u003e= 3: return True curr = 0 for i in range(index, len(S)): # 两位以上的数字不能以0开头 if i \u003e index and S[index] == \"0\": break # 截取字符串转化为数字 curr = curr * 10 + ord(S[i]) - ord(\"0\") # 如果截取的数字大于int的最大值,则终止截取 if curr \u003e 2**31 - 1: break # 如果截取的数字大于res中前两个数字的和,说明这次截取的太大,直接终止,因为后面越截取越大 if len(res) \u003e 2 and curr \u003e res[-2] + res[-1]: break if len(res) \u003c= 1 or curr == res[-2] + res[-1]: res.append(curr) if backtrack(i + 1): return True res.pop() return False backtrack(0) return res # https://leetcode-cn.com/problems/split-array-into-fibonacci-sequence/solution/javahui-su-suan-fa-tu-wen-xiang-jie-ji-b-vg5z/ java回溯算法图文详解,击败了100%的用户 17. 电话号码的字母组合 回溯法是暴力解法的一个主要实现手段, 暴力解法可以通过多重循环实现,但是当n不定时,循环的层数也不固定,可能不能使用多重循环来暴力解,此时就只能使用回溯法. 参考资料 Backtracking https://github.com/selfboot/LeetCode/tree/master/Backtracking (个人强烈推荐) 五大常用算法之四:回溯法 https://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741376.html ","date":"2019-08-26","objectID":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95/:1:0","tags":["算法"],"title":"回溯算法初探","uri":"https://seacj.github.io/posts/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95/"},{"categories":["算法"],"content":"39. Combination Sum tag. array Given a set of candidate numbers (candidates) (without duplicates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target. The same repeated number may be chosen from candidates unlimited number of times. Example 1: Input: candidates = [2,3,6,7], target = 7, A solution set is: [ [7], [2,2,3] ] Solution来源:https://leetcode-cn.com/problems/combination-sum/solution/xue-yi-tao-zou-tian-xia-hui-su-suan-fa-by-powcai/ class Solution: def combinationSum(self, candidates: List[int], target: int) -\u003e List[List[int]]: # 剪枝的前提是数组元素排序 # 深度深的边不能比深度浅的边还小 # 要排序的理由:1、前面用过的数后面不能再用;2、下一层边上的数不能小于上一层边上的数。 candidates.sort() n = len(candidates) res = [] def backtrack(i, tmp_sum, tmp): if tmp_sum \u003e target or i == n: return if tmp_sum == target: res.append(tmp) return for j in range(i, n): if tmp_sum + candidates[j] \u003e target: break backtrack(j,tmp_sum + candidates[j],tmp+[candidates[j]]) backtrack(0, 0, []) return res 作者:powcai 链接:https://leetcode-cn.com/problems/combination-sum/solution/xue-yi-tao-zou-tian-xia-hui-su-suan-fa-by-powcai/ 来源:力扣(LeetCode) 这道题的图解: https://leetcode-cn.com/problems/combination-sum/solution/hui-su-suan-fa-jian-zhi-python-dai-ma-java-dai-m-2/ 输入: candidates = [2, 3, 6, 7],target = 7,所求解集为: [[7], [2, 2, 3]] 补充:事实上,不排序也是可以的,只要保证按顺序读取,也可以通过测试用例。但排序更好一些,这样“剪枝”工作可以更彻底一些。 class Solution: def combinationSum(self, candidates: List[int], target: int) -\u003e List[List[int]]: size = len(candidates) if size == 0: return [] # 剪枝的前提是数组元素排序 # 深度深的边不能比深度浅的边还小 # 要排序的理由:1、前面用过的数后面不能再用;2、下一层边上的数不能小于上一层边上的数。 candidates.sort() # 在遍历的过程中记录路径,一般而言它是一个栈 path = [] res = [] # 注意要传入 size ,在 range 中, size 取不到 self.__dfs(candidates, 0, size, path, res, target) return res def __dfs(self, candidates, begin, size, path, res, target): # 先写递归终止的情况 if target == 0: # Python 中可变对象是引用传递,因此需要将当前 path 里的值拷贝出来 # 或者使用 path.copy() res.append(path[:]) for index in range(begin, size): residue = target - candidates[index] // “剪枝”操作,不必递归到下一层,并且后面的分支也不必执行 if residue \u003c 0: break path.append(candidates[index]) # 因为下一层不能比上一层还小,起始索引还从 index 开始 self.__dfs(candidates, index, size, path, res, residue) path.pop() 作者:liweiwei1419 链接:https://leetcode-cn.com/problems/combination-sum/solution/hui-su-suan-fa-jian-zhi-python-dai-ma-java-dai-m-2/ 在力扣讨论区提到这是一道典型的回溯算法题,点击了解回溯算法。 40. Combination Sum II tag. List \r点击展开题目详情\rGiven a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target. Each number in candidates may only be used once in the combination. Note: All numbers (including target) will be positive integers. The solution set must not contain duplicate combinations. Example 1: Input: candidates = [10,1,2,7,6,1,5], target = 8, A solution set is: [ [1, 7], [1, 2, 5], [2, 6], [1, 1, 6] ] \r这道题和上面的第39道题非常相似,不同的是candidates里面的数字只能取一次。 class Solution: def combinationSum2(self, candidates: List[int], target: int) -\u003e List[List[int]]: size = len(candidates) if size == 0: return [] candidates.sort() res = [] self.__dfs(candidates, size, 0, [], target, res) return res def __dfs(self, candidates, size, start, path, residue, res): if residue == 0: res.append(path[:]) return for index in range(start, size): if candidates[index] \u003e residue: break # 剪枝的前提是数组升序排序 if index \u003e start and candidates[index - 1] == candidates[index]: # [1, 1, 2, 5, 6, 7, 10] # 0 号索引的 1 ,从后面 6 个元素中搜索 # 1 号索引也是 1 ,从后面 5 个元素(是上面 6 个元素的真子集)中搜索, # 这种情况显然上面已经包含。 continue path.append(candidates[index]) # 这里要传入 index + 1,因为当前元素不能被重复使用 # 如果 index + 1 越界,传递到下一个方法中,什么也不执行 self.__dfs(candidates, size, index + 1, path, residue - candidates[index], res) path.pop() 作者:liweiwei1419 链接:https://leetcode-cn.com/problems/combination-sum-ii/solution/hui-su-suan-fa-jian-zhi-python-dai-ma-java-dai-m-3/ 202. Happy Number tag. Math \r点击展开详情\rWrite an algorithm to determine if a number is “happy”. A happy number is a number defined by the following process: Starting wi","date":"2019-08-24","objectID":"https://seacj.github.io/posts/leetcodedaily3/:0:0","tags":["算法","leetcode"],"title":"每日Leetcode03","uri":"https://seacj.github.io/posts/leetcodedaily3/"},{"categories":["博客"],"content":" 添加侧边导航栏 修改代码高亮 添加阅读进度条 相关链接 侧边导航栏 Hugo博客侧边导航栏 代码高亮 代码高亮 添加阅读进度条 为 LeaveIt 主题添加阅读进度条 ","date":"2019-08-23","objectID":"https://seacj.github.io/posts/%E5%BB%BA%E7%AB%99%E5%8E%86%E5%8F%B2003/:0:0","tags":["博客搭建","建站历史"],"title":"建站历史003","uri":"https://seacj.github.io/posts/%E5%BB%BA%E7%AB%99%E5%8E%86%E5%8F%B2003/"},{"categories":["博客"],"content":"感觉博客TOC(侧边导航栏,Table of Content)还是挺有必要的,在LeaveIt主题issue有人谈到TOC,但是我实践了一遍好像不太行,所以我自己又另找了方法, 需要说明的是我并没有系统学过前端开发,所以可能会有疏漏的地方,不过从结果上来看是能用的。 这篇文章是根据LeaveIt主题来做的,所以部分文件名可能会有差别。此外,这个方法的toc主要在Chrome浏览器上进行测试。 ","date":"2019-08-22","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/:0:0","tags":["博客搭建"],"title":"Hugo博客侧边导航栏","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/"},{"categories":["博客"],"content":"Step 1. 新建并编写toc.html文件 在文件夹\\layouts\\partials中新建文件toc.html,写入以下代码。 {{ if (.Params.toc | default true ) }} \u003cdiv class=\"post-toc\" id=\"post-toc\"\u003e \u003caside\u003e \u003cheader\u003e \u003ch3\u003e{{.Title}}\u003c/h3\u003e \u003c/header\u003e {{ $emtLiPtrn := \"(?s)\u003cul\u003e\\\\s\u003cli\u003e\\\\s\u003cul\u003e(.*)\u003c/li\u003e\\\\s\u003c/ul\u003e\" }} {{ $rplcEmtLi := \"\u003cul\u003e$1\" }} {{ .TableOfContents | replaceRE $emtLiPtrn $rplcEmtLi | safeHTML }} \u003c!-- https://github.com/gohugoio/hugo/issues/1778#issuecomment-483880269 --\u003e \u003c!-- {{.TableOfContents}} --\u003e \u003c/aside\u003e \u003ca href=\"#\" id=\"toc-toggle\"\u003e\u003c/a\u003e \u003c/div\u003e {{ end }} 说明: 实际上{{.TableOfContents}} 就够用了,但是在Hugo TOC 的issue里面存在一个开头没有H1的bug,所以以上代码是一种改进方案。 ","date":"2019-08-22","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/:1:0","tags":["博客搭建"],"title":"Hugo博客侧边导航栏","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/"},{"categories":["博客"],"content":"Step 2. 修改single.html文件 在layouts\\_default文件夹中找到single.html这个文件,single.html是文章的布局文件,在合适的位置(我直接写在文件的最前面)插入以下代码。 \u003cdiv style=\"position: fixed; right:5px;max-width:300px; overflow:auto; top: 100px; width: 10vw; bottom:50px\"\u003e {{ partial \"toc.html\" . }} \u003c/div\u003e 说明: max-width:300px;当TOC的内容过长时会换行。 overflow:auto; top: 100px; bottom:50px作用为TOC过长时,出现滚轮。 p.s.优化滚轮外观: 因为默认的滚轮宽度很宽很违和,添加以下代码可以使滚轮变窄(在Chrome浏览器上有效,但是Edge浏览器上样式不会变)。可以直接将下面的代码写入single.html文件开头,即\u003chead\u003e\u003cstyle type=\"text/css\"\u003e这里是下面的代码\u003c/style\u003e\u003c/head\u003e ::-webkit-scrollbar { width: 8px; height: 8px; } ::-webkit-scrollbar-thumb { height: 40px; background-color: #eee; border-radius: 16px; \u0026:hover { background-color: #ddd; } } ","date":"2019-08-22","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/:2:0","tags":["博客搭建"],"title":"Hugo博客侧边导航栏","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/"},{"categories":["博客"],"content":"Step 3. 处理手机兼容性的问题 设置成:如果是移动设备就不要显示TOC了。 在\\assets\\css\\_custom.scss文件中加入以下代码。 @media only screen and (max-width: 1224px) { .post-toc { display: none; } } 这样就已经完工了~ ","date":"2019-08-22","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/:3:0","tags":["博客搭建"],"title":"Hugo博客侧边导航栏","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/"},{"categories":["博客"],"content":"Step 4. (可选)优化代码 p.s. 如果想将样式分离(这样可能可以避免后期出现滚轮样式冲突的问题),就继续在\\assets\\css\\_custom.scss文件中写入下面的代码。 .post-toc{ position: fixed; left:20px;max-width:300px; overflow:auto; top: 100px; bottom:50px; } .toc-wrapper { ::-webkit-scrollbar { width: 6px; height: 8px; } ::-webkit-scrollbar-thumb { height: 40px; background-color: #eee; border-radius: 16px; \u0026:hover { background-color: #ddd; } } } 并将在Step2介绍的\\layouts\\partials\\toc.html的代码修改成下面的代码(注意class一定要改成toc-wrapper),并删除Step2里面外观优化的\u003chead\u003e\u003cstyle type=\"text/css\"\u003e这里是下面的代码\u003c/style\u003e\u003c/head\u003e 这段代码。 \u003cdiv class = \"toc-wrapper\"\u003e {{ partial \"toc.html\" . }} \u003c/div\u003e \r","date":"2019-08-22","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/:4:0","tags":["博客搭建"],"title":"Hugo博客侧边导航栏","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/"},{"categories":["博客"],"content":"测试一下 H1 ","date":"2019-08-22","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/:5:0","tags":["博客搭建"],"title":"Hugo博客侧边导航栏","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/"},{"categories":["博客"],"content":"H2 ","date":"2019-08-22","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/:6:0","tags":["博客搭建"],"title":"Hugo博客侧边导航栏","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/"},{"categories":["博客"],"content":"H3 H4 H5 这是一个很长的用来测试的H1标题,这是一个很长的用来测试的标题 ","date":"2019-08-22","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/:6:1","tags":["博客搭建"],"title":"Hugo博客侧边导航栏","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/"},{"categories":["博客"],"content":"这是一个很长的用来测试的H2标题,这是一个很长的用来测试的标题很多个H1 ","date":"2019-08-22","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/:7:0","tags":["博客搭建"],"title":"Hugo博客侧边导航栏","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/"},{"categories":["博客"],"content":"很多个H2 ","date":"2019-08-22","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/:8:0","tags":["博客搭建"],"title":"Hugo博客侧边导航栏","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/"},{"categories":["博客"],"content":"很多个H2 ","date":"2019-08-22","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/:9:0","tags":["博客搭建"],"title":"Hugo博客侧边导航栏","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/"},{"categories":["博客"],"content":"很多个H2 ","date":"2019-08-22","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/:10:0","tags":["博客搭建"],"title":"Hugo博客侧边导航栏","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/"},{"categories":["博客"],"content":"很多个H2 ","date":"2019-08-22","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/:11:0","tags":["博客搭建"],"title":"Hugo博客侧边导航栏","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/"},{"categories":["博客"],"content":"很多个H2 ","date":"2019-08-22","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/:12:0","tags":["博客搭建"],"title":"Hugo博客侧边导航栏","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/"},{"categories":["博客"],"content":"很多个H2 ","date":"2019-08-22","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/:13:0","tags":["博客搭建"],"title":"Hugo博客侧边导航栏","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/"},{"categories":["博客"],"content":"很多个H2 ","date":"2019-08-22","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/:14:0","tags":["博客搭建"],"title":"Hugo博客侧边导航栏","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/"},{"categories":["博客"],"content":"很多个H2 ","date":"2019-08-22","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/:15:0","tags":["博客搭建"],"title":"Hugo博客侧边导航栏","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/"},{"categories":["博客"],"content":"很多个H2 ","date":"2019-08-22","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/:16:0","tags":["博客搭建"],"title":"Hugo博客侧边导航栏","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/"},{"categories":["博客"],"content":"很多个H2结束 \r","date":"2019-08-22","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/:17:0","tags":["博客搭建"],"title":"Hugo博客侧边导航栏","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/"},{"categories":["博客"],"content":"文章字数统计 可以在侧边栏里面加上字数统计,如果字数多的时候才显示侧边栏。主要利用的是Hugo的一个Variable——WordCount,修改toc.html文件,最终修改之后的代码如下。 \u003cdiv class=\"post-toc\"\u003e {{ if and (gt .WordCount 80 ) (.Params.toc | default true ) }} \u003c!-- 关键是这一行 --\u003e \u003caside\u003e \u003cheader\u003e \u003ch4\u003e{{.Title}}の字数: {{ .WordCount }}\u003c/h4\u003e \u003c/header\u003e {{- $toc := .TableOfContents -}} {{- $toc := replaceRE `\u003cul\u003e\\n\u003cli\u003e\\n\u003cul\u003e` `\u003cul\u003e` $toc -}} {{- safeHTML $toc -}} \u003c/aside\u003e \u003ca href=\"#\" id=\"toc-toggle\"\u003e\u003c/a\u003e {{ end }} \u003c!-- 不要忘记写end --\u003e \u003c/div\u003e 参考资料 添加 gitalk 评论和 disqus 懒加载;添加文章目录导航 https://github.com/liuzc/LeaveIt/pull/11/commits/d5aa80b08057bbdf5ac7f7c319ce68e5646226b9 Hugo官方文档TOC https://gohugo.io/content-management/toc/ Hugo TOC的issue https://github.com/gohugoio/hugo/issues/1778 ","date":"2019-08-22","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/:18:0","tags":["博客搭建"],"title":"Hugo博客侧边导航栏","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BE%A7%E8%BE%B9%E5%AF%BC%E8%88%AA%E6%A0%8F/"},{"categories":["算法"],"content":"35. Search Insert Position tags. Array 向有序列表里找到需要插入值的index,且You may assume no duplicates in the array. Example 1: Input: [1,3,5,6], 5 Output: 2 我的Solution: class Solution: def searchInsert(self, nums: List[int], target: int) -\u003e int: for i in range(len(nums)): if nums[i]\u003e=target: return i return len(nums) Runtime: 64 ms, faster than 22.39% of Python3 online submissions for Search Insert Position. Memory Usage: 14.3 MB, less than 5.97% of Python3 online submissions for Search Insert Position. 这道题很简单,我给出的复杂度是O(N),实际上还可以提升。 Solution来源: https://leetcode.com/problems/search-insert-position/discuss/15306/6-line-Python-solution-similar-to-34.Search-for-a-Range-44ms 使用binary search的方法使复杂度降至O(log(N)) def searchInsert(self, nums, target): left, right = 0, len(nums) - 1 while left \u003c= right: mid = (left + right) // 2 if target \u003e nums[mid]: left = mid + 1 else: right = mid - 1 return left Runtime: 56 ms, faster than 86.79% of Python3 online submissions for Search Insert Position. Memory Usage: 14.2 MB, less than 5.97% of Python3 online submissions for Search Insert Position. 268. Missing Number tag. math 列表长度为n,找到列表中从0到n里面少的那个数字。要求时间复杂度是O(N), 空间复杂度是O(1). Example 1: Input: [3,0,1] Output: 2 我的解法: class Solution: def missingNumber(self, nums: List[int]) -\u003e int: total_sum = sum(range(len(nums)+1)) return total_sum - sum(nums) Solution来源: https://leetcode.com/problems/missing-number/solution/ class Solution: def missingNumber(self, nums): expected_sum = len(nums)*(len(nums)+1)//2 #这里用高斯公式首项加末项乘以相数除以二的方法,相比sum函数,复杂度降低到O(1) actual_sum = sum(nums) # 但是这里的复杂度还是O(N) return expected_sum - actual_sum 方法二. Bit Manipulation 利用XOR抑或运算^ Index 0 1 2 3 Value 0 1 3 4 missing=4∧(0∧0)∧(1∧1)∧(2∧3)∧(3∧4) =(4∧4)∧(0∧0)∧(1∧1)∧(3∧3)∧2 =0∧0∧0∧0∧2=2 class Solution: def missingNumber(self, nums): missing = len(nums) for i, num in enumerate(nums): missing ^= i ^ num return missing python中抑或的运算 原文链接:https://blog.csdn.net/xtj332/article/details/6639009 按位异或 ^ 方法: 对位相加,特别要注意的是不进位. 举例: 2^5 解法:10^101=111,二进制111得到十进制的结果是7. 1^1 解法:1+1=0.(本来二进制1+1=10,但不能进位,所以结果是0) -3^4 解法: -3的补码是11111101,4的补码是100 (也即00000100),11111101^00000100=11111101,补码11111101,转为原码是1000111,即十进制的-7. XOR小结 :两个相同的整数XOR的结果恒为0,0与任意整数N抑或的结果恒为N。 344. Reverse String tag. String 反转字符数组,要求要inplace。 我的解法: class Solution: def reverseString(self, s: List[str]) -\u003e None: \"\"\" Do not return anything, modify s in-place instead. \"\"\" for i in range(len(s)//2): s[i],s[-i-1] = s[-i-1],s[i] p.s. 一个仅适用于python的解法:return s[::-1] 19. Remove Nth Node From End of List tag. linkedlist Given a linked list, remove the n-th node from the end of list and return its head. 即去掉倒数第N个节点(Given n will always be valid.) Example: Given linked list: 1-\u003e2-\u003e3-\u003e4-\u003e5, and n = 2. After removing the second node from the end, the linked list becomes 1-\u003e2-\u003e3-\u003e5. 我的Solution: class Solution: def removeNthFromEnd(self, head: ListNode, n: int) -\u003e ListNode: #if head.next is None: # return None a,b = head,head for _ in range(n): b = b.next if b is None: #去掉第一个节点 head = head.next return head while b.next is not None: a = a.next b = b.next a.next = a.next.next return head 101. Symmetric Tree tag. tree 判断一个树是否对称。 1 / \\ 2 2 / \\ / \\ 3 4 4 3 Solution来源: https://leetcode.com/problems/symmetric-tree/discuss/33050/Recursively-and-iteratively-solution-in-Python Iterative解法 class Solution: def isSymmetric(self, root): if not root: return True stack = [(root.left, root.right)] while stack: cur = stack.pop() l, r = cur[0], cur[1] if not l and not r: continue if not l and r or not r and l or l.val != r.val: return False stack.append((l.right, r.left)) stack.append((l.left, r.right)) return True ","date":"2019-08-21","objectID":"https://seacj.github.io/posts/leetcodedaily2/:0:0","tags":["算法","leetcode"],"title":"每日Leetcode02","uri":"https://seacj.github.io/posts/leetcodedaily2/"},{"categories":["算法"],"content":"从今天起开始做每日LeetCode这个系列,从array, string, tree, linkedlist, math标签里选题做。 189. Rotate Array tag. array Solution来源 :https://leetcode.com/problems/rotate-array/discuss/54426/Summary-of-solutions-in-Python 3-step array rotation: O(n) in time, O(1) in space class Solution(object): def rotate(self, nums, k): if k is None or k \u003c= 0: return k, end = k % len(nums), len(nums) - 1 self.reverse(nums, 0, end - k) self.reverse(nums, end - k + 1, end) self.reverse(nums, 0, end) def reverse(self, nums, start, end): while start \u003c end: nums[start], nums[end] = nums[end], nums[start] start, end = start + 1, end - 1 Algorithm This approach is based on the fact that when we rotate the array k times, k%nk elements from the back end of the array come to the front and the rest of the elements from the front shift backwards. In this approach, we firstly reverse all the elements of the array. Then, reversing the first k elements followed by reversing the rest n-kn−k elements gives us the required result. Let n=7n=7 and k=3k=3. Original List : 1 2 3 4 5 6 7 After reversing all numbers : 7 6 5 4 3 2 1 After reversing first k numbers : 5 6 7 4 3 2 1 After revering last n-k numbers : 5 6 7 1 2 3 4 –\u003e Result 67. Add Binary tag. math, string 实现二进制的加法 Solution来源 :https://leetcode.com/problems/add-binary/discuss/359628/python3-solutioneasy-to-understand class Solution: def addBinary(self, a: str, b: str) -\u003e str: \"\"\"二进制加法运算\"\"\" # 承接最后要返回的答案 ans = \"\" a_index = len(a) - 1 b_index = len(b) - 1 # 进位标志 carry = 0 while a_index \u003e= 0 or b_index \u003e= 0: # p、q是从右到左依次取得的各位数,如果不存在,则用0代替 p = int(a[a_index]) if a_index \u003e= 0 else 0 q = int(b[b_index]) if b_index \u003e= 0 else 0 a_index -= 1 b_index -= 1 # 计算对应位的和 sum = p + q + carry # 保存加和后得到的字符串 ans = str(sum % 2) + ans # 保存进位值 carry = sum // 2 # 如果到最后,进位值为1,则总位数加一位,首位为1 return \"1\" + ans if carry == 1 else ans 58. Length of Last Word tag. string 算最后一个word的长度,这道题好评率略低。 我的Solution class Solution: def lengthOfLastWord(self, s: str) -\u003e int: last_space = True len_without_space = len(s) for i in range(len(s),0,-1): # 需要考虑s最后有空格的情况,即\"ab \"长度是2. if last_space: if s[i-1] == ' ': len_without_space -= 1 continue else: last_space = False if s[i-1] == ' ': return len_without_space - i return len_without_space Runtime: 32 ms, faster than 90.14% of Python3 online submissions for Length of Last Word. Memory Usage: 14 MB, less than 5.26% of Python3 online submissions for Length of Last Word. Discussion里面python的solution大部分使用split函数, 但有说明这种属于cheat的方法,真正interview的时候不要用这种方法。 Solution来源:https://leetcode.com/problems/length-of-last-word/discuss/21901/One-line-Python-solution def lengthOfLastWord(self, s): return len(s.rstrip(' ').split(' ')[-1]) 21. Merge Two Sorted Lists tag. linkedlist 合并两个有序链表,需要注意的是有一个额外的要求就是希望改变输入的两个链表(The new list should be made by splicing together the nodes of the first two lists) Solution来源: https://leetcode.com/problems/merge-two-sorted-lists/discuss/9735/Python-solutions-(iteratively-recursively-iteratively-in-place). # Definition for singly-linked list. # class ListNode: # def __init__(self, x): # self.val = x # self.next = None # iteratively def mergeTwoLists1(self, l1, l2): # cur总是指向我们最终想要建立的list的tail dummy = cur = ListNode(0) while l1 and l2: if l1.val \u003c l2.val: cur.next = l1 l1 = l1.next else: cur.next = l2 l2 = l2.next cur = cur.next cur.next = l1 or l2 return dummy.next ","date":"2019-08-19","objectID":"https://seacj.github.io/posts/leetcodedaily1/:0:0","tags":["算法","leetcode"],"title":"每日Leetcode01","uri":"https://seacj.github.io/posts/leetcodedaily1/"},{"categories":["算法"],"content":"Linkedlist问题小结 在Linkedlist的问题上,经常会利用到dumyhead,因为这样做能有效地处理empty list。(如果不使用dumyhead,那cur需要指向None,那又需要小心地处理cur-\u003enext) 100. Same Tree tag. tree 判断两个树是否相同 Solution来源: https://leetcode.com/problems/same-tree/discuss/32729/Shortest%2Bsimplest-Python def isSameTree(self, p, q): if p and q: return p.val == q.val and self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right) return p is q 递归来实现是非常直接简单的思路,值得学习的是上面有一个trick就是and和is的用法。 p is q等价于True if p==None and q==None else False, 即只有两个同时为None返回True。 另一个递归解法: Time complexity : O(N), where N is a number of nodes in the tree, since one visits each node exactly once. Space complexity :O(log(N)) in the best case of completely balanced tree and O(N) in the worst case of completely unbalanced tree, to keep a recursion stack. class Solution: def isSameTree(self, p, q): \"\"\" :type p: TreeNode :type q: TreeNode :rtype: bool \"\"\" # p and q are both None if not p and not q: return True # one of p and q is None if not q or not p: return False if p.val != q.val: return False return self.isSameTree(p.right, q.right) and \\ self.isSameTree(p.left, q.left) Solution来源: https://leetcode.com/problems/same-tree/solution/ Iteration方法 from collections import deque class Solution: def isSameTree(self, p, q): \"\"\" :type p: TreeNode :type q: TreeNode :rtype: bool \"\"\" def check(p, q): # if both are None if not p and not q: return True # one of p and q is None if not q or not p: return False if p.val != q.val: return False return True deq = deque([(p, q),]) while deq: p, q = deq.popleft() if not check(p, q): return False if p: deq.append((p.left, q.left)) deq.append((p.right, q.right)) return True deque是double-end queue双向队列。 相比于list实现的队列,deque实现拥有更低的时间和空间复杂度。list实现在出队(pop)和插入(insert)时的空间复杂度大约为O(n),deque在出队(pop)和入队(append)时的时间复杂度是O(1)。 https://zhuanlan.zhihu.com/p/37093602 ","date":"2019-08-19","objectID":"https://seacj.github.io/posts/leetcodedaily1/:1:0","tags":["算法","leetcode"],"title":"每日Leetcode01","uri":"https://seacj.github.io/posts/leetcodedaily1/"},{"categories":["随笔-ღ-心得"],"content":"上个月写了随便写了一些小学的東西(小学),这次放假刚好去看了一下。 所以这篇就按照时间顺序记录一下我昨天去逛了一圈拍的照片。 下地铁到木棉湾地铁站,旁边就是布吉镇政府的楼。 布吉镇政府大楼布吉镇政府大楼 \" 布吉镇政府大楼 去东方半岛的路上会经过一个十字路口和一个很短不到5米的桥下通道。 十字路口的那栋楼以前是豪宴酒楼十字路口的那栋楼以前是豪宴酒楼 \" 十字路口的那栋楼以前是豪宴酒楼 桥下通道桥下通道 \" 桥下通道 到了东方半岛,健身区好像因为翻修下水道,所以所有的東西都拆掉了。 健身区健身区 \" 健身区 到佳佳文具店买了一瓶冰红茶,小学经常在这里买辣条什么的零食。旁边就是华美超市。 以前家楼下的佳佳文具店和华美超市以前家楼下的佳佳文具店和华美超市 \" 以前家楼下的佳佳文具店和华美超市 走到小区的角落的地方。 停车的地方的后面停车的地方的后面 \" 停车的地方的后面 这里可以看到学校后门了,后面很高的楼就是帝景峰这里可以看到学校后门了,后面很高的楼就是帝景峰 \" 这里可以看到学校后门了,后面很高的楼就是帝景峰 走到东方半岛小学了,感觉好新啊,而且都冲洗装修了,不过还是以前的那种红白配色。 东方半岛小学东方半岛小学 \" 东方半岛小学 以前这个墙没有涂这么花的以前这个墙没有涂这么花的 \" 以前这个墙没有涂这么花的 操场都翻新了,堆在外面一堆旧的桌椅,应该是都要换新的操场都翻新了,堆在外面一堆旧的桌椅,应该是都要换新的 \" 操场都翻新了,堆在外面一堆旧的桌椅,应该是都要换新的 学校外面旁边有一个停自行车的场地,从小道穿过去有一个草坡。 以前抓蟋蟀的草坡以前抓蟋蟀的草坡 \" 以前抓蟋蟀的草坡 大滑滑梯大滑滑梯 \" 大滑滑梯 小滑滑梯小滑滑梯 \" 小滑滑梯 会所会所 \" 会所 去帝景峰的铁门去帝景峰的铁门 \" 去帝景峰的铁门 帝景峰的篮球场,以前没有网,地板也是水泥的帝景峰的篮球场,以前没有网,地板也是水泥的 \" 帝景峰的篮球场,以前没有网,地板也是水泥的 从左到右同时拍到帝景峰、东方半岛和茂业城的楼从左到右同时拍到帝景峰、东方半岛和茂业城的楼 \" 从左到右同时拍到帝景峰、东方半岛和茂业城的楼 ","date":"2019-08-12","objectID":"https://seacj.github.io/posts/%E6%9D%B1%E6%96%B9%E5%8D%8A%E5%B2%9B/:0:0","tags":["过去的事","照片"],"title":"東方半岛","uri":"https://seacj.github.io/posts/%E6%9D%B1%E6%96%B9%E5%8D%8A%E5%B2%9B/"},{"categories":["随笔-ღ-心得"],"content":"Most Important Most Important1 \" Most Important ","date":"2019-08-12","objectID":"https://seacj.github.io/posts/%E6%9D%B1%E6%96%B9%E5%8D%8A%E5%B2%9B/:1:0","tags":["过去的事","照片"],"title":"東方半岛","uri":"https://seacj.github.io/posts/%E6%9D%B1%E6%96%B9%E5%8D%8A%E5%B2%9B/"},{"categories":["博客"],"content":"在写LaTeX的时候,感觉个人博客的代码高亮很有问题,在不该高亮的地方高亮,所以想解决一下这个问题。 ","date":"2019-08-01","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BB%A3%E7%A0%81%E9%AB%98%E4%BA%AE/:0:0","tags":["博客搭建"],"title":"Hugo博客代码高亮","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BB%A3%E7%A0%81%E9%AB%98%E4%BA%AE/"},{"categories":["博客"],"content":"方法一. 使用Hugo扩展包 其实关于代码高亮,如果是使用LeaveIt主题的话,在issue里面有人提出过,解决方案主要是使用Hugo的extented包中对Sass/SCSS的支持。 ","date":"2019-08-01","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BB%A3%E7%A0%81%E9%AB%98%E4%BA%AE/:1:0","tags":["博客搭建"],"title":"Hugo博客代码高亮","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BB%A3%E7%A0%81%E9%AB%98%E4%BA%AE/"},{"categories":["博客"],"content":"Step 1. 下载hugo extended 在网址https://github.com/gohugoio/hugo/releases/ 中下载对应操作系统的extended版本的压缩包,比如我下载的是hugo_extended_0.56.3_Windows-64bit.zip。 ","date":"2019-08-01","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BB%A3%E7%A0%81%E9%AB%98%E4%BA%AE/:1:1","tags":["博客搭建"],"title":"Hugo博客代码高亮","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BB%A3%E7%A0%81%E9%AB%98%E4%BA%AE/"},{"categories":["博客"],"content":"Step 2. 修改行内样式 使用LeaveIt主题的话,对代码高亮编辑的是scss文件,打开themes\\LeaveIt\\assets\\css\\_common\\_page\\post.scss。 如果想把行内代码的样式,修改为更为常见的浅红底红字,可以按照https://blog.hgtweb.com/2019/code-csdn/ 修改。 ","date":"2019-08-01","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BB%A3%E7%A0%81%E9%AB%98%E4%BA%AE/:1:2","tags":["博客搭建"],"title":"Hugo博客代码高亮","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BB%A3%E7%A0%81%E9%AB%98%E4%BA%AE/"},{"categories":["博客"],"content":"Step 3. 修改代码块样式 参照https://liuzhichao.com/2018/hugo-theme-leaveit/ 代码高亮说明,通过编辑themes\\LeaveIt\\assets\\css\\_common\\_prettyprint\\default.scss文件即可,我用这个文件改了一下代码块的背景。 关于配色可以参见我的另一篇文章——网站配色 ","date":"2019-08-01","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BB%A3%E7%A0%81%E9%AB%98%E4%BA%AE/:1:3","tags":["博客搭建"],"title":"Hugo博客代码高亮","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BB%A3%E7%A0%81%E9%AB%98%E4%BA%AE/"},{"categories":["博客"],"content":"方法二. 使用Highlight插件 直接使用下面的代码,复制粘贴到需要使用代码的地方即可。但是好像我使用这个主题的时候,是先使用这个渲染代码高亮,后使用LeaveIt主题自带的代码高亮,所以会覆盖掉,所以没有用这个方法。 \u003chead\u003e \u003clink href=\"https://cdn.bootcss.com/highlight.js/9.12.0/styles/monokai.min.css\" rel=\"stylesheet\"\u003e \u003cscript src=\"https://cdn.bootcss.com/highlight.js/9.12.0/highlight.min.js\"\u003e\u003c/script\u003e \u003cscript src=\"https://cdn.bootcss.com/highlight.js/9.12.0/languages/tex.min.js\"\u003e\u003c/script\u003e \u003cscript\u003ehljs.initHighlightingOnLoad();\u003c/script\u003e \u003c/head\u003e ","date":"2019-08-01","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BB%A3%E7%A0%81%E9%AB%98%E4%BA%AE/:2:0","tags":["博客搭建"],"title":"Hugo博客代码高亮","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BB%A3%E7%A0%81%E9%AB%98%E4%BA%AE/"},{"categories":["博客"],"content":"测试代码 #include int main(void){ printf(\"Hello world!\"), return 0; } \\documentclass{article} % 这里是导言区 \\begin{document} Hello, world! \\end{document} import os print('Process (%s) start...' % os.getpid()) # Only works on Unix/Linux/Mac: pid = os.fork() if pid == 0: print('I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid())) else: print('I (%s) just created a child process (%s).' % (os.getpid(), pid)) 如果代码块没有写明语言,样式会重复。 no class \\documentclass{article} % 这里是导言区 \\begin{document} Hello, world! \\end{document} ","date":"2019-08-01","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BB%A3%E7%A0%81%E9%AB%98%E4%BA%AE/:3:0","tags":["博客搭建"],"title":"Hugo博客代码高亮","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BB%A3%E7%A0%81%E9%AB%98%E4%BA%AE/"},{"categories":["博客"],"content":"参考资料 制作了一款 Hugo 主题, LeaveIt https://liuzhichao.com/2018/hugo-theme-leaveit/ Hugo中添加代码高亮支持 https://note.qidong.name/2017/06/24/hugo-highlight/ 修改LeaveIt中的行内code样式 https://blog.hgtweb.com/2019/code-csdn/ ","date":"2019-08-01","objectID":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BB%A3%E7%A0%81%E9%AB%98%E4%BA%AE/:4:0","tags":["博客搭建"],"title":"Hugo博客代码高亮","uri":"https://seacj.github.io/posts/%E5%8D%9A%E5%AE%A2%E4%BB%A3%E7%A0%81%E9%AB%98%E4%BA%AE/"},{"categories":["教程-ღ-点评"],"content":"感觉做科研的话很多人都在用LaTeX,这里记录一下初次使用LaTeX的心得。 使用TeXworks编辑器,排版工具使用TeXworks上的XeLaTeX。 感谢Liam Huang分享的教程, 这篇文章只是简单演示一下我按照教程第一次使用LaTeX的情况,如果需要学习LaTeX,建议直接看Liam Huang的教程: TeX Live 下载及安装说明:https://liam.page/texlive/ 一份其实很短的 LaTeX 入门文档: https://liam.page/2014/09/08/latex-introduction/ Hello World 在编辑框中,输入如下内容: \\documentclass{article} % 这里是导言区 \\begin{document} Hello, world! \\end{document} 选中 XeLaTeX 后,按下绿色的编译按钮,等待片刻屏幕右边就会出现新的窗口: 从\\documentclass{article}到\\begin{document}之前的内容是导言区,通常是对布局(如页面大小、页眉页脚样式、章节标题样式等等)的设置。 我们将以反斜杠·\\ 开头,以第一个空格或非字母 的字符结束的一串文字, 称为控制序列(或称命令/标记)。 中英文混排 在 TeXworks 编辑框中输入以下内容,以 UTF-8 编码保存,使用 XeLaTeX 编译: \\documentclass[UTF8]{ctexart} \\begin{document} 你好,world! \\end{document} 相较于之前的例子,这份代码只有细微的差异: 文档类从 article 变为 ctexart; 增加了文档类选项 UTF8。 组织你的文章 ","date":"2019-07-31","objectID":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/:0:0","tags":["LaTeX"],"title":"LaTeX初体验","uri":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/"},{"categories":["教程-ღ-点评"],"content":"作者、标题、日期 \\documentclass[UTF8]{ctexart} \\title{你好,world!} \\author{clj} \\date{\\today} \\begin{document} \\maketitle 你好,world! \\end{document} 也就是在导言区增加一些内容,并加上一个控制序列maketitle。如果不加的话,导言区的作者信息这些内容编译之后就不会显示出来。 ","date":"2019-07-31","objectID":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/:1:0","tags":["LaTeX"],"title":"LaTeX初体验","uri":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/"},{"categories":["教程-ღ-点评"],"content":"章节和段落 \\documentclass[UTF8]{ctexart} \\title{你好,world!} \\author{clj} \\date{\\today} \\begin{document} \\maketitle \\section{你好广东section} 广东在南方. \\subsection{Hello Guangdong subsection} 广东是南方的城市. \\subsubsection{Hello Shenzhen subsubsection} \\paragraph{Shenzhen paragraph} is a city \\subparagraph{Welcome subparagraph} to Shenzhen \\subsection{Hello World subsection} \\paragraph{World} means 世界。 \\end{document} 在文档类 article/ctexart 中,定义了五个控制序列来调整行文组织结构。他们分别是 \\section{·} \\subsection{·} \\subsubsection{·} \\paragraph{·} \\subparagraph{·} 在report/ctexrep中,还有\\chapter{·};在文档类book/ctexbook中,还定义了\\part{·}。 ","date":"2019-07-31","objectID":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/:2:0","tags":["LaTeX"],"title":"LaTeX初体验","uri":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/"},{"categories":["教程-ღ-点评"],"content":"插入目录 加上\\maketitle就会自动生成目录。 \\documentclass[UTF8]{ctexart} \\title{你好,world!} \\author{Liam} \\date{\\today} \\begin{document} \\maketitle \\tableofcontents \\section{你好广东section} 广东在南方. 第一行 这也是第一行 这才是第二行 \\subsection{Hello Guangdong subsection} 广东是南方的城市. \\subsubsection{Hello Shenzhen subsubsection} \\paragraph{Shenzhen paragraph} is a city \\subparagraph{Welcome subparagraph} to Shenzhen \\end{document} 在LaTeX中,如果需要换行,需要空出一行来。 ","date":"2019-07-31","objectID":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/:3:0","tags":["LaTeX"],"title":"LaTeX初体验","uri":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/"},{"categories":["教程-ღ-点评"],"content":"插入数学公式 为了使用 AMS-LaTeX 提供的数学功能,我们需要在导言区加载 amsmath 宏包:\\usepackage{amsmath} ","date":"2019-07-31","objectID":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/:4:0","tags":["LaTeX"],"title":"LaTeX初体验","uri":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/"},{"categories":["教程-ღ-点评"],"content":"行内和行外 LaTeX 的数学模式有两种:行内模式 (inline) 和行间模式 (display)。 前者在正文的行文中,插入数学公式;后者独立排列单独成行,并自动居中。 行内: $ ... $ 行外: \\[ ... \\] 带编号的行内公式(使用 equation 环境): \\begin{equation} ... \\end{equation} ","date":"2019-07-31","objectID":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/:4:1","tags":["LaTeX"],"title":"LaTeX初体验","uri":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/"},{"categories":["教程-ღ-点评"],"content":"上下标 默认只作用于之后的一个字符: 上标: ^ 下标: _ 如果需要连续的几个字符需要用花括号{}括起来。如: 上标:e^{2\\pi i} 下标:a_{123} \\documentclass{article} \\usepackage{amsmath} \\begin{document} \\[z =e^{2\\pii}. \\] \\[a_{123}\\] Einstein 's $E=mc^2$. \\[E=mc^2. \\] \\begin{equation} E=mc^2. \\end{equation} \\end{document} ","date":"2019-07-31","objectID":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/:4:2","tags":["LaTeX"],"title":"LaTeX初体验","uri":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/"},{"categories":["教程-ღ-点评"],"content":"根式和分式 根式: \\sqrt{·} 分式: \\frac{·}{·}(第一个参数为分子,第二个为分母)。 在行间公式和行内公式中,分式的输出大小是有差异的(分式行内大小稍小)。如果要强制行内模式的分式显示为行间模式的大小,可以使用 \\dfrac, 反之可以使用 \\tfrac。 \\documentclass[UTF8]{ctexart} \\usepackage{amsmath} \\begin{document} $\\sqrt{x}$, $\\frac{1}{2}$. \\[\\sqrt{x}, \\] \\[\\frac{1}{2}. \\] $\\dfrac{1}{2}$.行内加d变大 \\[\\tfrac{1}{2}. \\] \\end{document} ","date":"2019-07-31","objectID":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/:4:3","tags":["LaTeX"],"title":"LaTeX初体验","uri":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/"},{"categories":["教程-ღ-点评"],"content":"运算符 \\[ \\pm\\; \\times \\; \\div\\; \\cdot\\; \\cap\\; \\cup\\;\r\\geq\\; \\leq\\; \\neq\\; \\approx \\; \\equiv \\] 连加、连乘、极限、积分等大型运算符分别用 \\sum, \\prod, \\lim, \\int 生成。他们的上下标在行内公式中被压缩,以适应行高。我们可以用 \\limits 和\\nolimits来强制显式地指定是否压缩这些上下标。 \\documentclass[UTF8]{ctexart} \\usepackage{amsmath} \\begin{document} \\[\\pm\\;\\times\\;\\div\\;\\cdot\\;\\cap\\;\\cup\\;\\geq\\;\\leq\\;\\neq\\;\\approx\\;\\equiv\\] $\\sum_{i=1}^n i\\quad\\prod_{i=1}^n $ $\\sum\\limits_{i=1}^n i\\quad\\prod\\limits_{i=1}^n $ \\[\\lim_{x\\to0}x^2\\quad\\int_a^b x^2dx \\] \\[\\lim\\nolimits_{x\\to0}x^2\\quad\\int\\nolimits_a^b x^2dx \\] \\end{document} ","date":"2019-07-31","objectID":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/:4:4","tags":["LaTeX"],"title":"LaTeX初体验","uri":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/"},{"categories":["教程-ღ-点评"],"content":"定界符(括号等) 各种括号用 (), [], \\{\\}, \\langle\\rangle 等命令表示;注意花括号通常用来输入命令和环境的参数,所以在数学公式中它们前面要加 \\。 |推荐使用\\lvert\\rvert。 amsmath 宏包推荐使用 \\big, \\Big, \\bigg, \\Bigg 等一系列命令放在上述括号前面调整大小。如: \\[ \\Biggl(\\biggl(\\Bigl(\\bigl((x)\\bigr)\\Bigr)\\biggr)\\Biggr) \\] ","date":"2019-07-31","objectID":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/:4:5","tags":["LaTeX"],"title":"LaTeX初体验","uri":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/"},{"categories":["教程-ღ-点评"],"content":"省略号 省略号用 \\dots, \\cdots, \\vdots, \\ddots 等命令表示。\\dots 和 \\cdots 的纵向位置不同,前者一般用于有下标的序列。 \\documentclass[UTF8]{ctexart} \\usepackage{amsmath} \\begin{document} \\[x_1,x_2,\\dots,x_n\\quad1,2,\\cdots,n\\quad\\vdots\\quad\\ddots\\] \\end{document} ","date":"2019-07-31","objectID":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/:4:6","tags":["LaTeX"],"title":"LaTeX初体验","uri":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/"},{"categories":["教程-ღ-点评"],"content":"其他公式 感觉一些公式使用起来频率不高,这里就先略去,想看关于矩阵,多行公式,公式组,分段函数的可以到Liam Huang的博客查阅。 ","date":"2019-07-31","objectID":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/:4:7","tags":["LaTeX"],"title":"LaTeX初体验","uri":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/"},{"categories":["教程-ღ-点评"],"content":"图片 在 LaTeX 中插入图片,有很多种方式。最好用的应当属利用 graphicx 宏包提供的 \\includegraphics 命令。 比如你在你的 TeX 源文件同目录下,有名为 a.jpg 的图片,你可以用这样的方式将它插入到输出文档中: \\documentclass{article} \\usepackage{graphicx} \\begin{document} \\includegraphics{a.jpg} \\end{document} 控制图片大小:\\includegraphics[width = .8\\textwidth]{a.jpg}这样会长宽等比缩放,图片宽度会到页面宽度的80%。 ","date":"2019-07-31","objectID":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/:5:0","tags":["LaTeX"],"title":"LaTeX初体验","uri":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/"},{"categories":["教程-ღ-点评"],"content":"表格 \\hline 命令表示横线,在列格式中用 | 表示竖线;用\u0026来分列,用\\\\来换行;每列可以采用居左、居中、居右等横向对齐方式,分别用l、c、r来表示。(感叹一下Markdown真好用) \\documentclass[UTF8]{ctexart} \\usepackage{amsmath} \\begin{document} \\begin{tabular}{|l|c|r|} \\hline 操作系统\u0026 发行版\u0026 编辑器\\\\ \\hline Windows \u0026 MikTeX \u0026 TexMakerX \\\\ \\hline Unix/Linux \u0026 teTeX \u0026 Kile \\\\ \\hline Mac OS \u0026 MacTeX \u0026 TeXShop \\\\ \\hline 通用\u0026 TeX Live \u0026 TeXworks \\\\ \\hline \\end{tabular} \\end{document} ","date":"2019-07-31","objectID":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/:6:0","tags":["LaTeX"],"title":"LaTeX初体验","uri":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/"},{"categories":["教程-ღ-点评"],"content":"总结 林林总总记了一些,总的来说LaTeX还是非常强大的,在很多细节上(像字体大小)做的很细致,但是个人觉得如果有一个简单的统一的模板岂不是更加方便,不过LaTeX里面肯定还有很多值得探索的功能,不然也不会得到很多人的追捧。 再次感谢Liam Huang分享教程,并同意我转载其中的内容。 ","date":"2019-07-31","objectID":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/:7:0","tags":["LaTeX"],"title":"LaTeX初体验","uri":"https://seacj.github.io/posts/latex%E5%88%9D%E4%BD%93%E9%AA%8C/"},{"categories":["教程-ღ-点评"],"content":"学了那么多年计算机,感觉自己打字速度依然不太行,盲打的速度太慢,所以最近开始练习打字。这里推荐我筛选出来的两个打字练习的网站,并给出我的一些体验心得。 1.https://www.keybr.com/ 强烈推荐,界面简介舒适,有多种统计数据可以看到自己的进步。 足够智能:根据你的打字速度逐步提高难度,会对出错较多的键位提高出现的次数。 2.https://www.typingclub.com/ 知乎上推荐这个网站比较多,但是个人体验不算太好。 如果追求极快的打字速度并且有足够的耐心系统地练习,这个网站可以满足需求,优势是有大量的动画教程,但是这种循序渐进的流程略微繁琐,网站加载的速度可能会偏慢。 心得 打字首先要把指位弄清楚(有次搜索到的某个网站居然教学错误的指位,害我练了一段时间,真的是误人),然后学会盲打,剩下的多加练习就好了。 ","date":"2019-07-23","objectID":"https://seacj.github.io/posts/%E6%89%93%E5%AD%97/:0:0","tags":null,"title":"打字练习","uri":"https://seacj.github.io/posts/%E6%89%93%E5%AD%97/"},{"categories":["随笔-ღ-心得"],"content":"\r这几天总是想到东方半岛小学 像是外教课的时候会坐在校门口的铁椅上围成一圈, 直线的跑道,跑道旁边的雕刻 小学的楼梯,每一级有一道防滑的突起的横线 只去过一次的医务室,还是因为额头眉骨那里撞破,\r那次也挺有意思的,出教室被人撞了一下,很痛但是以为没什么问题,用手捂着伤口,完全没有注意到已经在流血了,\r还是老师发现把我带到医务室的,然后又被带去小区的诊所缝了几针 铁通装的饮用水 小学每层楼是一个圆形的走廊,中间是空心的,从走廊往下看,可以看到一楼的大大的中国地图的贴纸 因为小学就在小区里面,所以中午都会回家,中午上学都要排队,等到时间才能进去,排队的时候都会看贴满一整个墙的小小的各国国旗贴纸 每周升国旗的时候,站在操场,如果有直升飞机飞过,都会有小小的“骚动”,有一次还看见了滑翔翼 小学有两个门,连着两个小区 放学了,会玩抓人,我们也叫“123”,因为被抓的人,跑的时候可以说“123”,这样被抓的人就不能动,但是也不能被抓,直到有人来救 小区里有3个地方可以玩,我们称它为大滑滑梯,小滑滑梯和健身区 偶尔还会去会所,会所就在小学的对面,会所里面还有游泳馆,不知道为什么会所里面总是很凉快,会所有两层但是很多房间都没有使用 小区的楼外面贴的主要是红色偏粉色的瓷砖,还有白色的瓷砖 我们会在大滑滑梯旁边的草坪玩“猫抓老鼠”,就是在猫不能走进草坪,老鼠可以随便跑,中间还有一个小灌木丛被我们穿行无数次,\r当然践踏草坪是不好的 小区的小灌木丛很有特点,绿叶很嫩 去朋友家的路上有紫荆花,很漂亮 有一个地方有一条小的水沟,旁边有停车位,大概是因为汽油,所以经常可以看到水面有彩虹一样的花纹 那时候的天空很蓝 晚上会早早的睡觉, 十点钟是最晚的睡觉时间, 七点还是八点有动漫世界节目,那个时候觉得憨八龟很好看,现在看觉得怎么颜色那么恐怖 回家的第一件事情是写作业,有的时候边看作业边写作业 LF2和口袋妖怪很好玩 流行的悠悠球、四驱车我都买过 早餐的包子和放学后的辣条是5毛钱的,早餐的肠粉是两块五的 菜市场里面有玩台球的,但是我没有碰过 早上能听见清洁员沙沙的扫地声 大滑滑梯旁边有课树,很高大,到了某个季节会掉很多小拇指甲大小的黄色叶子 健身区有一棵树,好像是槐树,垂着长长的但是非常结实的枝条 小区门口往里走有一个很大很粗壮的树,有的时候掉下来的叶子上会有白色的毛一样的東西,不知道是什么 小学旁边的单车停车间,从窄窄的通道穿过去,有一个草地的斜坡,跟朋友们抓蟋蟀玩,但是现在变得很怕虫子了 小区清洁蟑螂的时候,会往井盖充白色的气,就会有很多蟑螂跑出来,然后过不久就会死掉 会跑到屋顶玩,有一栋楼的屋顶跟小的花园一样,种了很多植物 我家的屋顶可以看到石芽岭, 石芽岭那边以前是没有路的,硬生生把山劈开了,经常有很多人去那里接山泉水,我也去接过 六年级毕业的时候摩尔庄园火了,现在密码忘了账号申诉无数次都不给我通过 我从四楼搬家搬到十一楼的过程中电梯停电了,被困电梯 手指被那个电梯夹过,超疼 叠赛车,叠东南西北 画画,那时候我真的很喜欢画画 小学还有围棋课和葫芦丝课,但是我现在谱不会认,只会我有一只小羊羔 喜欢玩积木,乐高积木很好玩,一个人可以玩一天 天空真的很蓝 晚上大人小孩围在一起打牌 很喜欢看郑渊洁的皮皮鲁、雨魔的兽王 临近的小区,茂业城、帝景峰、半岛苑、爱琴居 最重要的是有非常要好的朋友 这次放暑假,回去要多拍几张照片 上了大学以后就觉得越来越多的東西不见了 像是很难找到一颗很甜的糖果 在东方半岛的时候真的很快乐呀 ","date":"2019-07-17","objectID":"https://seacj.github.io/posts/%E5%B0%8F%E5%AD%A6/:0:0","tags":["过去的事"],"title":"我的小学","uri":"https://seacj.github.io/posts/%E5%B0%8F%E5%AD%A6/"},{"categories":["博客"],"content":"写博客一般都会使用markdown进行编辑,除了排版等基本的markdown语法,在markdown中插入图片音视频也是必不可少的技能。 因为演示用的链接可能会失效,以防万一,贴出链接以便检测链接是否失效: 动态图片链接 静态图片链接 一. 图片 ","date":"2019-07-13","objectID":"https://seacj.github.io/posts/markdown%E6%8F%92%E5%85%A5%E5%9B%BE%E9%9F%B3%E8%A7%86%E9%A2%91/:0:0","tags":["博客搭建"],"title":"Markdown插入图片音视频","uri":"https://seacj.github.io/posts/markdown%E6%8F%92%E5%85%A5%E5%9B%BE%E9%9F%B3%E8%A7%86%E9%A2%91/"},{"categories":["博客"],"content":"动图 \u003ciframe height=500 width=500 src=\"gif地址\"\u003e\u003c/iframe\u003e \r","date":"2019-07-13","objectID":"https://seacj.github.io/posts/markdown%E6%8F%92%E5%85%A5%E5%9B%BE%E9%9F%B3%E8%A7%86%E9%A2%91/:1:0","tags":["博客搭建"],"title":"Markdown插入图片音视频","uri":"https://seacj.github.io/posts/markdown%E6%8F%92%E5%85%A5%E5%9B%BE%E9%9F%B3%E8%A7%86%E9%A2%91/"},{"categories":["博客"],"content":"静态图 ![text](图片链接 \"optional title\") 需要说明的是一般情况是不会显示text的,我的博客上会显示text(即\"静态图”)的原因是LeaveIt的博客主题的设置。 title静态图 \" title title静态图 \" title \u003cimg src=\"图片链接\" height=\"50\" width=\"50\" align=right alt=\"图片名称\"\u003e 使用这种语法,对图片有更多可选的操作,其中alt是图片链接失效的时候会显示的文字;align是对齐方式,默认是center。 在src=\"“的引号内可以插入svg,这样就不用担心图片链接失效的问题了。具体方式是将图片转化成svg,将data:image/png;base64,iVBOR…这一串代码写入图片链接的位置。 p.s.在线jpg,png图片转SVG工具 二. 视频 \u003ciframe height=500 width=510 src=\"视频地址\" allowfullscreen\u003e\u003c/iframe\u003e\" 以b站为例 Step 1. 通过b站分享功能获取视频链接(部分视频由于版权原因是不能获取视频链接的) Step 2. 将视频地址插入上面的代码的src中 Step 3. 感觉外观不好看,而且不能最大化。进行外观优化: `` # 三. 音频\r","date":"2019-07-13","objectID":"https://seacj.github.io/posts/markdown%E6%8F%92%E5%85%A5%E5%9B%BE%E9%9F%B3%E8%A7%86%E9%A2%91/:2:0","tags":["博客搭建"],"title":"Markdown插入图片音视频","uri":"https://seacj.github.io/posts/markdown%E6%8F%92%E5%85%A5%E5%9B%BE%E9%9F%B3%E8%A7%86%E9%A2%91/"},{"categories":["博客"],"content":"有外链的音乐: \u003ciframe frameborder=\"no\" width=\"100%\" height=\"86\" src=\"音频链接\"\u003e\u003c/iframe\u003e 需要说明的是**音频链接**怎么获取,以网易云音乐为例(不得不说网易云提供的接口还是很好用的): ### 最直接的方式:\r进入下面的链接,直接复制粘贴给的代码,其他歌同理。 music.163.com/#/outchain/2/1374051000/m/use ","date":"2019-07-13","objectID":"https://seacj.github.io/posts/markdown%E6%8F%92%E5%85%A5%E5%9B%BE%E9%9F%B3%E8%A7%86%E9%A2%91/:3:0","tags":["博客搭建"],"title":"Markdown插入图片音视频","uri":"https://seacj.github.io/posts/markdown%E6%8F%92%E5%85%A5%E5%9B%BE%E9%9F%B3%E8%A7%86%E9%A2%91/"},{"categories":["博客"],"content":"另一个方法: Step 1. 安装好PC端网易云音乐(手机版也可以),找到分享按钮,点击复制链接。 此时会得到像这样的链接: http://music.163.com/m/song?id=1374051000\u0026userid=xxxxxxxxx 如果是手机版,分享的链接是像这样: http://music.163.com/song/1374051000/userid=xxxxxxxxx.) Step 2. 将id取出来(在上面的例子里,id是1374051000,注意不是userid),替换下面链接的id, 这样就得到了音频链接: //music.163.com/outchain/player?type=2\u0026id=1374051000\u0026auto=1\u0026height=66 关于音乐自动播放:在音频链接中,把auto=1改成auto=0,跳转到带有音频的页面,音频就不会自动播放了。 ","date":"2019-07-13","objectID":"https://seacj.github.io/posts/markdown%E6%8F%92%E5%85%A5%E5%9B%BE%E9%9F%B3%E8%A7%86%E9%A2%91/:3:1","tags":["博客搭建"],"title":"Markdown插入图片音视频","uri":"https://seacj.github.io/posts/markdown%E6%8F%92%E5%85%A5%E5%9B%BE%E9%9F%B3%E8%A7%86%E9%A2%91/"},{"categories":["博客"],"content":"自己的音频 如果你想用自己的音频,可以试试下面链接中介绍的方法: 姑苏流白·用CSS和JS实现的简易的音乐播放器 总结 以上实现主要都是使用HTML的iframe标签。 部分功能实现是依靠外链的,像网易云、b站、qq音乐等平台一般都会在分享按钮提供外链地址,可能没有涵盖到没有外链的应用场景,欢迎评论区补充。 ","date":"2019-07-13","objectID":"https://seacj.github.io/posts/markdown%E6%8F%92%E5%85%A5%E5%9B%BE%E9%9F%B3%E8%A7%86%E9%A2%91/:4:0","tags":["博客搭建"],"title":"Markdown插入图片音视频","uri":"https://seacj.github.io/posts/markdown%E6%8F%92%E5%85%A5%E5%9B%BE%E9%9F%B3%E8%A7%86%E9%A2%91/"},{"categories":["算法"],"content":"LeetCode 300 longest-increasing-subsequence 最长上升子序列 LeetCode试题链接 问题描述: 给定一个无序的整数数组,找到其中最长上升子序列(LIS)的长度。 示例: 输入: [10,9,2,5,3,7,101,18] 输出: 4 解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。 ","date":"2019-07-02","objectID":"https://seacj.github.io/posts/leetcode300longest-increasing-subsequence/:0:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode300 最长上升子序列","uri":"https://seacj.github.io/posts/leetcode300longest-increasing-subsequence/"},{"categories":["算法"],"content":"题解 从一个实例入手,给定输入 [1,3,9,2,4,5] 采用动态规划的方法解题: 表格解释 计算这个序列LIS的过程如下表所示(从左到右,从上到下阅读): 扫描到的数字 LIS长度 对应每个长度所代表的子序列 子序列最后一个元素的数字 1 1 1 1 1,3 2 1 1,3 1 3 1,3,9 3 1 1,3 1,3,9 1 3 9 1,3,9,2 3 1 1,2 1,3,9 1 2 9 1,3,9,2,4 3 1 1,2 1,2,4 1 2 4 1,3,9,2,4,5 4 1 1,2 1,2,4 1,2,4,5 1 2 4 5 文字解释 我们把上述表格的行号记为i,扫描到的数字用数组d[i]表示,即d[1]=[1],d[2]=[1,3],d[3]=[1,3,9]。 ","date":"2019-07-02","objectID":"https://seacj.github.io/posts/leetcode300longest-increasing-subsequence/:1:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode300 最长上升子序列","uri":"https://seacj.github.io/posts/leetcode300longest-increasing-subsequence/"},{"categories":["算法"],"content":"动态规划总结篇 dynamic programming, DP ","date":"2019-07-02","objectID":"https://seacj.github.io/posts/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/:0:0","tags":["算法","动态规划"],"title":"动态规划","uri":"https://seacj.github.io/posts/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"},{"categories":["算法"],"content":"什么是动态规划? 一句话解释:大事化小,小事化了 简单的解释:想办法将一个复杂的大问题拆成几个子问题,分别求解这些子问题,从而求出大问题的解。 ","date":"2019-07-02","objectID":"https://seacj.github.io/posts/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/:1:0","tags":["算法","动态规划"],"title":"动态规划","uri":"https://seacj.github.io/posts/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"},{"categories":["算法"],"content":"什么样的题适合用DP求解? 具有以下两点特征的问题可以用DP求解: 重叠子问题(Overlapping Subproblems):有些子问题会被重复计算多次。 最优子结构(Optimal Substructure):问题的最优解可以从某个子问题的最优解中获得。 ","date":"2019-07-02","objectID":"https://seacj.github.io/posts/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/:2:0","tags":["算法","动态规划"],"title":"动态规划","uri":"https://seacj.github.io/posts/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"},{"categories":["算法"],"content":"如何设计DP: DP的核心是状态和状态转移方程: 状态:描述该问题的子问题的解,即根据子问题来定义状态。 状态转移方程:状态和状态之间的关系式。大部分情况下,某个状态只与它前面出现的状态有关,而独立于后面的状态(比如 斐波那契数列 f(n)=f(n-1)+f(n-2))。 ","date":"2019-07-02","objectID":"https://seacj.github.io/posts/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/:3:0","tags":["算法","动态规划"],"title":"动态规划","uri":"https://seacj.github.io/posts/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"},{"categories":["算法"],"content":"动态规划的解题思路: 将原问题分解为子问题; 确定状态:状态不是随便定义的,一般定义完就要找到状态转移方程; 确定一些初始状态(边界状态)的值; 确定状态转移方程。 如果问题看起来是个动态规划问题,但是无法定义出状态,那么可以试着将问题规约到一个已知的 DP 问题。 ","date":"2019-07-02","objectID":"https://seacj.github.io/posts/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/:4:0","tags":["算法","动态规划"],"title":"动态规划","uri":"https://seacj.github.io/posts/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"},{"categories":["算法"],"content":"两种设计思路: 默记法(从上到下)/ Memoization (Top Down):设置一个数组,当需要子问题的解时,先去这个数组中查找。如果此问题之前已经求过解,那么就直接返回该值,如果此问题之前并未求过解,那么就计算该值并把结果放入数组中,以备后用。 表格法(从下到上)/ Tabulation (Bottom Up):用迭代法建立一个表格,从该表格中返回所需的值。 p.s.有时候会将默记法作为单独的一种方法。比如在 leetcode300的题解 中,会分为带记忆的递归(Recursion with Memoization)和动态规划(Dynamic Programming)方法,此时的DP方法特指表格法。 ","date":"2019-07-02","objectID":"https://seacj.github.io/posts/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/:5:0","tags":["算法","动态规划"],"title":"动态规划","uri":"https://seacj.github.io/posts/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"},{"categories":["算法"],"content":"例题 从例题中学习才是最直接、有效的. ","date":"2019-07-02","objectID":"https://seacj.github.io/posts/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/:6:0","tags":["算法","动态规划"],"title":"动态规划","uri":"https://seacj.github.io/posts/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"},{"categories":["算法"],"content":"入门题 leetcode70爬楼梯:本题适合入门,题解分别阐释了两种设计思路(自上而下、自下而上)的实现。 ","date":"2019-07-02","objectID":"https://seacj.github.io/posts/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/:6:1","tags":["算法","动态规划"],"title":"动态规划","uri":"https://seacj.github.io/posts/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"},{"categories":["算法"],"content":"普通题 leetcode300最长上升子序列 ","date":"2019-07-02","objectID":"https://seacj.github.io/posts/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/:6:2","tags":["算法","动态规划"],"title":"动态规划","uri":"https://seacj.github.io/posts/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"},{"categories":["算法"],"content":"进阶题 leetcode44通配符匹配:本题属于二维动态规划,从这道题中可以直观的体会为什么自下而上的方法又叫表格法。 leetcode712两个字符串的最小ASCII删除和:本题属于二维动态规划,从这道题中可以直观的体会为什么自下而上的方法又叫表格法。 ","date":"2019-07-02","objectID":"https://seacj.github.io/posts/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/:6:3","tags":["算法","动态规划"],"title":"动态规划","uri":"https://seacj.github.io/posts/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"},{"categories":["算法"],"content":"LeetCode第44题是leetcode难度为hard的一个,解题方法是使用动态规划。题目内容可点击下面的黑色三角展开题目详情。 \r点击展开题目详情\r 题目来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/wildcard-matching 给定一个字符串 (s) 和一个字符模式 (p) ,实现一个支持 ‘?’ 和 ‘*’ 的通配符匹配。 ’?’ 可以匹配任何单个字符。 ’*’ 可以匹配任意字符串(包括空字符串)。 两个字符串完全匹配才算匹配成功。 说明: s 可能为空,且只包含从 a-z 的小写字母。 p 可能为空,且只包含从 a-z 的小写字母,以及字符 ? 和 *。 示例 1: 输入: s = “aa” p = “a” 输出: false 解释: “a” 无法匹配 “aa” 整个字符串。 示例 2: 输入: s = “aa” p = “*” 输出: true 解释: ‘*’ 可以匹配任意字符串。 示例 3: 输入: s = “cb” p = “?a” 输出: false 解释: ‘?’ 可以匹配 ‘c’, 但第二个 ‘a’ 无法匹配 ‘b’。 示例 4: 输入: s = “adceb” p = “*a*b” 输出: true 解释: 第一个 ‘*’ 可以匹配空字符串, 第二个 ‘*’ 可以匹配字符串 “dce”. 这道题的难点在于对星号(*)的处理。 \r点击展开字符模式中不包含\\*的情况\r**个人认为如果没有\\*就是另外一种思路,建议直接看包含\\*的情况,但是也可以把没有\\*的情况当练练手。**\r# 字符模式中不包含\\*的情况\r","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode44wildcard-matching/:0:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode44 通配符匹配","uri":"https://seacj.github.io/posts/leetcode44wildcard-matching/"},{"categories":["算法"],"content":"Case 1 ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode44wildcard-matching/:1:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode44 通配符匹配","uri":"https://seacj.github.io/posts/leetcode44wildcard-matching/"},{"categories":["算法"],"content":"输入: s: 为空 p: 为空 ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode44wildcard-matching/:1:1","tags":["算法","leetcode","动态规划"],"title":"Leetcode44 通配符匹配","uri":"https://seacj.github.io/posts/leetcode44wildcard-matching/"},{"categories":["算法"],"content":"输出: true ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode44wildcard-matching/:1:2","tags":["算法","leetcode","动态规划"],"title":"Leetcode44 通配符匹配","uri":"https://seacj.github.io/posts/leetcode44wildcard-matching/"},{"categories":["算法"],"content":"演绎 null null 1 ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode44wildcard-matching/:1:3","tags":["算法","leetcode","动态规划"],"title":"Leetcode44 通配符匹配","uri":"https://seacj.github.io/posts/leetcode44wildcard-matching/"},{"categories":["算法"],"content":"Case 2 ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode44wildcard-matching/:2:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode44 通配符匹配","uri":"https://seacj.github.io/posts/leetcode44wildcard-matching/"},{"categories":["算法"],"content":"输入: s: azc p: ab? ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode44wildcard-matching/:2:1","tags":["算法","leetcode","动态规划"],"title":"Leetcode44 通配符匹配","uri":"https://seacj.github.io/posts/leetcode44wildcard-matching/"},{"categories":["算法"],"content":"输出: false ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode44wildcard-matching/:2:2","tags":["算法","leetcode","动态规划"],"title":"Leetcode44 通配符匹配","uri":"https://seacj.github.io/posts/leetcode44wildcard-matching/"},{"categories":["算法"],"content":"演绎 null a z c null 1 a 1 b 0 ? ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode44wildcard-matching/:2:3","tags":["算法","leetcode","动态规划"],"title":"Leetcode44 通配符匹配","uri":"https://seacj.github.io/posts/leetcode44wildcard-matching/"},{"categories":["算法"],"content":"Case 3 ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode44wildcard-matching/:3:0","tags":["算法","leetcode","动态规划"],"title":"Leetcode44 通配符匹配","uri":"https://seacj.github.io/posts/leetcode44wildcard-matching/"},{"categories":["算法"],"content":"输入: s: abc p: ?b?? ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode44wildcard-matching/:3:1","tags":["算法","leetcode","动态规划"],"title":"Leetcode44 通配符匹配","uri":"https://seacj.github.io/posts/leetcode44wildcard-matching/"},{"categories":["算法"],"content":"输出: false ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode44wildcard-matching/:3:2","tags":["算法","leetcode","动态规划"],"title":"Leetcode44 通配符匹配","uri":"https://seacj.github.io/posts/leetcode44wildcard-matching/"},{"categories":["算法"],"content":"演绎 null ? b ? ? null 1 a 1 b 1 c 1 ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode44wildcard-matching/:3:3","tags":["算法","leetcode","动态规划"],"title":"Leetcode44 通配符匹配","uri":"https://seacj.github.io/posts/leetcode44wildcard-matching/"},{"categories":["算法"],"content":"说明 如Case 1所示,base case当s和p均为空的情况下,匹配成功,记为1. 如Case 2,3所示,在没有*的情况下,s和q上的每一格应该是一一对应的关系,所以只需要沿着对角线一一匹配就可以了。 满足公式:s[i]==p[j] 或 s[i] == '?'时,表示前i个p和s成功匹配, 如Case 2所示,当前i个中出现不能匹配的情况,则后面就不需要匹配了,即当前i个字符出现不匹配的情况,前i+1个字符也必然不匹配,如若az不能与ab匹配,则azc也必然不能与ab?匹配。 所以当扫描到第i行第i列出现不匹配时,可以直接输出结果false。 如Case 3所示,当匹配到对角线的最后一格(Case 3图中加粗字体处的[c,?]),若仍有需要匹配的(即右方或下方还有空格),表示匹配失败。若没有需要匹配的,表示匹配成功,返回true。 字符模式中包含*的情况 ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode44wildcard-matching/:3:4","tags":["算法","leetcode","动态规划"],"title":"Leetcode44 通配符匹配","uri":"https://seacj.github.io/posts/leetcode44wildcard-matching/"},{"categories":["算法"],"content":"输入: s: abab p: *b* ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode44wildcard-matching/:3:5","tags":["算法","leetcode","动态规划"],"title":"Leetcode44 通配符匹配","uri":"https://seacj.github.io/posts/leetcode44wildcard-matching/"},{"categories":["算法"],"content":"输出: true ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode44wildcard-matching/:3:6","tags":["算法","leetcode","动态规划"],"title":"Leetcode44 通配符匹配","uri":"https://seacj.github.io/posts/leetcode44wildcard-matching/"},{"categories":["算法"],"content":"演绎 null * b * null 1 ①1 a ①1 ②0 b ①1 ③1 ⑥1 a ①1 ④0 ⑥1 b ①1 ⑤1 ⑥1 ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode44wildcard-matching/:3:7","tags":["算法","leetcode","动态规划"],"title":"Leetcode44 通配符匹配","uri":"https://seacj.github.io/posts/leetcode44wildcard-matching/"},{"categories":["算法"],"content":"过程 Step 1. 初始化[null,null]为1. Step 2. 扫描到p[0]处的*, 找到左格为1的格子,从这一格开始(包括这一格)往下一列格子置1(即将表格中的①全部置1). Step 3. 扫描到p[1]处的b,找到左上格(即[null,*])为1的格子, 从这个格子开始从上到下依次进行字符匹配运算,字符匹配运算公式为左上格==1 and (s[i]==p[j] or s[i] == '?'). Step 4. 扫描到p[2]处的*, 找到左格(即④)为1的格子, 从这个格子开始(包括这一格)往下的所有列置为1(即将表格中的⑥全部置1). ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode44wildcard-matching/:3:8","tags":["算法","leetcode","动态规划"],"title":"Leetcode44 通配符匹配","uri":"https://seacj.github.io/posts/leetcode44wildcard-matching/"},{"categories":["算法"],"content":"解释 思考问题1. 为什么扫描到*需要找左格为1的格子?之后又需要往下一列置1? 因为*能匹配空串,所以要找左格为1. 如下表,“a*“可以和\"a\"匹配: null a * null 1 a 1 1 因为*能够匹配字串后面所有的字符,所以下面可以一次性全部置1. 如下表,“a*“可以和\"ab\"匹配,也可以和\"abb匹配”: null a * null 1 a 1 1 b 1 1 b 1 1 思考问题2. 为什么扫描到非*找到左上格为1的格子?之后又需要从上到下依次进行匹配计算? 先看为什么要依次比较一遍,这是*通配符带来的,看下面的例子。\"*b\"与\"a\"不匹配不意味着”*b\"与\"ab\"不匹配。 null * b ? null 1 1 a 1 0 b 1 1 b 1 1 1 在扫描非*的时候,我们希望找到左上方为1的格子(通过对比上下两个表格感受一下)。 在下面的表格(把上面的表格中的*换成?)中: 我们注意到\"ab\"与”?“不匹配,“abb\"与”?b\"也必然不匹配(即使后面的匹配上了p[1]==s[2]==b,前面的子串没匹配上也白搭) null ? b ? null 1 a 1 0 b 0 1 b 0 0 1 \r点击展开代码\r我写的python3实现代码,感觉简洁度上差了一点,主要是为了便于理解,多了一些中间变量什么的。 而且实际上table不需要显式写在代码里的,因为可以发现table只需要记录扫描到的部分的左边一列就够了。但个人认为作为学习来讲,掌握算法思路的前提下,保障代码可懂性也是必要的。 ```python3\rclass Solution: def isMatch(self, s: str, p: str) -\u003e bool:\rtable = []\rn_row = len(s) + 1\rn_column = len(p) + 1\rtable = [[None] * n_column for _ in range(n_row)]\rtable[0][0] = True\rfor j in range(len(p)):\rtable_j = j+1\rcur_p = p[j] #current p当前扫描到的通配符\rleft_column = [e[table_j-1] for e in table]\rif cur_p == '*':\r# 找到左格为1的格子, 注意一下p[0]所在位置对应的是table[:][1]\rhas_one = False\rfor table_i,left in enumerate(left_column):\rif left == True:\r# 从这一格开始往下全部置为1\rfor sub_i in range(table_i,n_row):\rhas_one = True\rtable[sub_i][table_j] = True\rbreak\rif has_one == False: #左格没有为1的格子\rreturn False\relse:\r# 找到左上格为1的格子.如果没有则返回false\rhas_one = False\rfor start_i,left in enumerate(left_column):\rif left == True and start_i\u003cn_row-1: #如果只有最后一行是1也不行\rhas_one = True\rbreak\rif has_one == False:\rreturn False\rfor table_i in range(start_i+1,n_row):\rif table[table_i-1][table_j-1] == 1 and (cur_p == '?' or cur_p == s[table_i-1]):\rtable[table_i][table_j] = True\relse:\rtable[table_i][table_j] = False\rif table[-1][-1] == None: #p扫描完了,但是s还有多余。比如\"ab\"和\"?\"\rreturn False\rreturn table[-1][-1]\r 另一个别人的代码,runtime要比我快,思路跟我的有些不一样: ```python3 作者:powcai 链接:https://leetcode-cn.com/problems/two-sum/solution/shuang-zhi-zhen-he-dong-tai-gui-hua-by-powcai/ 注释补充参考: 作者:*晴儿* 链接:https://blog.csdn.net/qq_34919415/article/details/86686508 class Solution: def isMatch(self, s, p): \"\"\" :type s: str :type p: str :rtype: bool \"\"\" i = 0 j = 0 start = -1 match = 0 while i \u003c len(s): # 非\"*\"的情况,一对一匹配,匹配成功一起往右下角移 if j \u003c len(p) and (s[i] == p[j] or p[j] == \"?\"): i += 1 j += 1 # 是\"*\",记录i,j的位置。因为*能匹配空串,所以i不需要加1. elif j \u003c len(p) and p[j] == \"*\": start = j match = i j += 1 # p[j] 不是*,且前面有*,且不能匹配 # 那么有可能是*未匹配完, # 所以回到最开始i的位置(这个回溯是这个思路的精髓所在),j要指向p的后一个字母开始尝试匹配 elif start != -1: j = start + 1 match += 1 i = match else: return False # s匹配完了,将多余的 * 直接匹配空串 return all(x == \"*\" for x in p[j:]) \r下图为展开代码中的另一种方法的示意图: null * b c * ? null 1 a ①1 ②0 b ③1 ④1 b ⑥1 ⑦1 ⑤0 c ⑧1 d ⑨1 推荐阅读 动态规划总结 ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode44wildcard-matching/:3:9","tags":["算法","leetcode","动态规划"],"title":"Leetcode44 通配符匹配","uri":"https://seacj.github.io/posts/leetcode44wildcard-matching/"},{"categories":["算法"],"content":"Leetcode 712 Minimum ASCII Delete Sum for Two Strings 两个字符串的最小ASCII删除和(动态规划) LeetCode试题来源链接 问题描述: 给定两个字符串s1, s2,找到使两个字符串相等所需删除字符的ASCII值的最小和。 示例1: 输入: s1 = “sea”, s2 = “eat” 输出: 231 解释: 在 “sea” 中删除 “s” 并将 “s” 的值(115)加入总和。 在 “eat” 中删除 “t” 并将 116 加入总和。 结束时,两个字符串相等,115 + 116 = 231 就是符合条件的最小和。 示例2: 输入: s1 = “delete”, s2 = “leet” 输出: 403 解释: 在 “delete” 中删除 “dee” 字符串变成 “let”, 将 100[d]+101[e]+101[e] 加入总和。在 “leet” 中删除 “e” 将 101[e] 加入总和。 结束时,两个字符串都等于 “let”,结果即为 100+101+101+101 = 403 。 如果改为将两个字符串转换为 “lee” 或 “eet”,我们会得到 433 或 417 的结果,比答案更大。 ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode712minimum-ascii-delete-sum-for-two-strings/:0:0","tags":["算法","leetcode"],"title":"Leetcode712 两个字符串的最小ASCII删除和","uri":"https://seacj.github.io/posts/leetcode712minimum-ascii-delete-sum-for-two-strings/"},{"categories":["算法"],"content":"LeetCode 70 Climbing Stairs爬楼梯 LeetCode试题链接(英文) 问题描述: 有一座高度是N(N是大于0的整数)级台阶的楼梯,从下往上走,每跨一步只能向上1级或者2级台阶。求出一共有多少种走法。 示例: 若N=3,共有3种走法 走法1:每次走1级台阶,一共走3步。(1步+1步+1步) 走法2:先走1级,再走2级。(1步+2步) 走法3:先走2级,再走1级。(2步+1步) python3实现代码(表格内容为在leetcode上提交的反馈信息) ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode70climbing-stairs/:0:0","tags":["算法","leetcode"],"title":"Leetcode70 爬楼梯","uri":"https://seacj.github.io/posts/leetcode70climbing-stairs/"},{"categories":["算法"],"content":"方法1.递归求解(自顶向下) 思路: 首先思考只差一步到达第n级台阶时的状态:因为我们一步可选择向上1级或者2级,所以要达到最终的状态前有2个子状态(子状态分别是a.站在第n-1级台阶和b.站在第n-2级台阶上)。由此得到我们算法的核心公式: f(N) = f(N-1) + f(N-2),而每一个子状态又可以当作一种最终状态,比如子状态N-1看作最终状态,则有f(N-1) = f(N-1-1) + f(N-1-2)。所以公式可以直接写成一般式f(n) = f(n-1) + f(n-2). class Solution: def climbStairs(self, n: int) -\u003e int: if n\u003c=0: return 0 if n==1: return 1 if n==2: return 2 return self.climbStairs(n-1)+self.climbStairs(n-2) Status Runtime Memory Language Last executed input Time Limit Exceeded N/A N/A python3 35 树的高度为N,节点个数近似于2的n次方,所以时间复杂度近似于O(2n) ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode70climbing-stairs/:1:0","tags":["算法","leetcode"],"title":"Leetcode70 爬楼梯","uri":"https://seacj.github.io/posts/leetcode70climbing-stairs/"},{"categories":["算法"],"content":"方法2.备忘录方法(自顶向下) 如上图所示,我们发现方法1中很多地方被重复计算了(相同颜色代表着重复的部分),如果我们每算出一个颜色的方块的数值就记录下来,这样就减少了很多重复的计算。 class Solution: n2step={} def climbStairs(self, n: int) -\u003e int: if n\u003c=0: return 0 if n==1: return 1 if n==2: return 2 if n in Solution.n2step: return Solution.n2step[n] else: Solution.n2step[n] = self.climbStairs(n-1)+self.climbStairs(n-2) return Solution.n2step[n] Status Runtime Memory Language Accepted 32 ms 13.3 MB python3 此时空间复杂度为O(n),时间复杂度也为O(n)。 ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode70climbing-stairs/:2:0","tags":["算法","leetcode"],"title":"Leetcode70 爬楼梯","uri":"https://seacj.github.io/posts/leetcode70climbing-stairs/"},{"categories":["算法"],"content":"方法3.动态规划求解(自底向上) 我们之前两个方法的核心的递归式f(n) = f(n-1) + f(n-2)是一个自顶向下求解的式子。 现在尝试一下自底向上的逻辑: 首先是下图示意的初始状态: 接下来迭代出3层楼梯: 然后是4层楼梯: 由此逐步累加到我们的目标第N层楼梯。 此时的思路是: 要达到状态n前有2个子状态(子状态分别是a.站在第n-1级台阶和b.站在第n-2级台阶上), 当在第n-1级台阶时,走一步1级到达最终状态;当在第n-2级台阶时,有两种方法到第n级台阶(走两步1级和走一步2级)。 完成迭代之后,往前移一层,如图a到图b变化的过程。 class Solution: def climbStairs(self, n): if n == 1: return 1 a, b = 1, 2 #a表示f(n-1),b表示f(n-2) for i in range(2, n): tmp = b b = a+b a = tmp return b Status Runtime Memory Language Accepted 32 ms 13 MB python3 当前时间复杂度仍为O(n),但空间复杂度降为O(1)。 ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode70climbing-stairs/:3:0","tags":["算法","leetcode"],"title":"Leetcode70 爬楼梯","uri":"https://seacj.github.io/posts/leetcode70climbing-stairs/"},{"categories":["算法"],"content":"一个思考 个人认为自顶向下和自底向上有一个容易混淆的地方,但是我还没有看到有人讨论过这个问题(可能是只有我会混淆吧) 两种方法中都存在两个子状态n-1和n-2。递归求解(自顶向下)的解法是f(n) = f(n-1) + f(n-2);动态规划(自底向上)的解法有一点像f(n) = f(n-1) + 2*f(n-2)。 乘2是因为站在n-2时,有向上1级和向上2级可以选择,这在动态规划(自底向上)里是很自然的(所以有人说正常的思路应该是一步一步往上走的自底向上的求解方法)。 但是其实在递归求解里,当计算f(n-1)时,会把站在n-2级台阶再向上1级的情况包含进去,所以f(n-2)就不需要乘2了。 递归式是f(n) = f(n-1) + f(n-2) 参考链接 动态规划入门(以爬楼梯为例) ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/leetcode70climbing-stairs/:4:0","tags":["算法","leetcode"],"title":"Leetcode70 爬楼梯","uri":"https://seacj.github.io/posts/leetcode70climbing-stairs/"},{"categories":["博客"],"content":" 添加鼠标跟随的鲸鱼特效 添加访客人次计数功能(相同访客不重复累加) 绑定价值199RMB的域名yyqx.online/ 添加网页图标 相关链接 鲸鱼特效 HTML5鲸鱼动画 访客计数 不蒜子 - 极简网页计数器 在哪里买域名 阿里巴巴万网 网页图标生成和使用 Favicon Generator 域名绑定 GitHub Pages 绑定来自阿里云的域名 为什么要写技术博客 b站up主CodeSheep视频 - 为什么程序员必须写技术博客?以及如何写? ","date":"2019-07-01","objectID":"https://seacj.github.io/posts/%E5%BB%BA%E7%AB%99%E5%8E%86%E5%8F%B2002/:0:0","tags":["博客搭建","建站历史"],"title":"建站历史002","uri":"https://seacj.github.io/posts/%E5%BB%BA%E7%AB%99%E5%8E%86%E5%8F%B2002/"},{"categories":["博客"],"content":"建站有一段时间了,中途其实折腾了挺多东西的,特别是在 hugo-theme-learn 这个主题上折腾了太久,现在换成 LeaveIt 感觉好看多了。 “博客搭建\"这个tag以后就用来记录这个博客搭建的过程,标题带\"建站历史\"就简单记录一下这个破站更新的过程吧。 使用LeaveIt主题 使用Valine的评论和访客计数功能 相关链接 使用Hugo快速建站 Hugo搭建博客视频教程。 Hugo手册 Hugo手册 LeaveIt主题 LeaveIt Valine官网 Valine 添加评论功能 在Hugo中使用Valine评论功能 ","date":"2019-06-30","objectID":"https://seacj.github.io/posts/%E5%BB%BA%E7%AB%99%E5%8E%86%E5%8F%B2001/:0:0","tags":["博客搭建","建站历史"],"title":"建站历史001","uri":"https://seacj.github.io/posts/%E5%BB%BA%E7%AB%99%E5%8E%86%E5%8F%B2001/"},{"categories":["博客"],"content":"搭建这个网站的时候一直找不到好的配色。最后终于确定下来了配色,现在分享一下使用配色网站的心路历程。 首先推荐两个对配色非常有帮助的网站: 1.颜色搭配 http://tool.c7sky.com/webcolor/ 2.好看的单色 http://nipponcolors.com/ (如果打不开左边的链接,可以尝试http://zhongguose.com ) 一. 首先是第一个网站,给出了不同的配色方案。 优点是能够直接使用配色进行初步的网页颜色配置,可以直接避免类似大红配大绿这种糟糕的搭配: 二. 第二个网站我个人非常推荐,与第一个网站注重搭配不同,这个网站只是推荐好看的颜色,如果苦恼于找不到好看的颜色,这个网站帮助就非常大,里面的颜色都非常好看,甚至就算胡乱搭配都可以足够好看。 ","date":"2019-06-24","objectID":"https://seacj.github.io/posts/%E7%BD%91%E7%AB%99%E9%85%8D%E8%89%B2%E6%96%B9%E6%A1%88/:0:0","tags":["博客搭建"],"title":"网站配色","uri":"https://seacj.github.io/posts/%E7%BD%91%E7%AB%99%E9%85%8D%E8%89%B2%E6%96%B9%E6%A1%88/"},{"categories":["博客"],"content":"总结 第一个网站可以解决颜色搭配的问题,初步选择好了你想要的色系之后,可以使用第二个网站对单个颜色进行调整。 ","date":"2019-06-24","objectID":"https://seacj.github.io/posts/%E7%BD%91%E7%AB%99%E9%85%8D%E8%89%B2%E6%96%B9%E6%A1%88/:0:1","tags":["博客搭建"],"title":"网站配色","uri":"https://seacj.github.io/posts/%E7%BD%91%E7%AB%99%E9%85%8D%E8%89%B2%E6%96%B9%E6%A1%88/"},{"categories":null,"content":"我是草祭, 很高兴在这里遇见你。 ","date":"2019-06-17","objectID":"https://seacj.github.io/about/:0:0","tags":null,"title":"About","uri":"https://seacj.github.io/about/"},{"categories":null,"content":"关于本人 我是深外分校2012届毕业生、深外高中部2015届毕业生 性别:男 爱好:B站、飞盘、密室逃脱 坐标: 深圳 邮箱:swampman#foxmail.com (请替换#为@) ","date":"2019-06-17","objectID":"https://seacj.github.io/about/:1:0","tags":null,"title":"About","uri":"https://seacj.github.io/about/"},{"categories":null,"content":"关于本站 本站是使用Hugo搭建的,搭建最初借鉴了b站up主CodeSheep分享的 视频教程。 在这里会不定期地分享技术、闲言碎语、心得体会。欢迎评论,并推荐使用RSS订阅。 如有内容上的问题,欢迎指正。 与我联系建议使用站内实时通讯系统或邮箱联系,文章评论区通常不能及时回复。 ","date":"2019-06-17","objectID":"https://seacj.github.io/about/:2:0","tags":null,"title":"About","uri":"https://seacj.github.io/about/"},{"categories":null,"content":"关于版权 本站所有文章皆采用署名-非商业性使用 4.0 国际 (CC BY-NC 4.0)进行许可。 本站文章欢迎转载,转载请注明本站首页网址 yyqx.online,或所转内容在本站的完整网址。 ","date":"2019-06-17","objectID":"https://seacj.github.io/about/:3:0","tags":null,"title":"About","uri":"https://seacj.github.io/about/"},{"categories":null,"content":"友情链接 名字 博客地址 简介 姑苏流白 点击进入 自我总结 分享经验 助人为乐 摩尔の镇| モル·町 点击进入 一个奇妙的小世界 友链申请发送至我的邮箱即可,申请内容应包含名字和博客地址如: 名字: 草祭 博客地址: https://yyqx.online/ Blog 简介(可选):草祭 最后送上高中时候的起床歌单~ 本站访客数人次 你发现了一个小小的彩蛋,试试把URL中的about/修改成playhouse.html吧~ ","date":"2019-06-17","objectID":"https://seacj.github.io/about/:4:0","tags":null,"title":"About","uri":"https://seacj.github.io/about/"},{"categories":["随笔-ღ-心得"],"content":"在分校上初中的时候,我们的语文老师杜双全每周都会要求做一篇随笔,就是类似于作文的東西。 每周随笔题目不限,想些啥都行,就这样写了三年,写满了一个小本子,很有纪念意义。 在随笔里看过去的自己的所思所想是件很有意思的事情。 所以现在就在博客里想写了就随便写一些東西,反正也没什么人会看吧。 ","date":"2019-06-14","objectID":"https://seacj.github.io/posts/%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E5%86%99%E9%9A%8F%E7%AC%94/:0:0","tags":["随笔"],"title":"随笔","uri":"https://seacj.github.io/posts/%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E5%86%99%E9%9A%8F%E7%AC%94/"},{"categories":["随笔-ღ-心得"],"content":"第一篇随笔诞生了 有人能够访问这个博客应该都能算奇迹吧 ","date":"2019-06-14","objectID":"https://seacj.github.io/posts/%E7%AC%AC%E4%B8%80%E7%AF%87%E9%9A%8F%E7%AC%94/:0:0","tags":["随笔"],"title":"第一篇随笔","uri":"https://seacj.github.io/posts/%E7%AC%AC%E4%B8%80%E7%AF%87%E9%9A%8F%E7%AC%94/"}]