Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 47b77a7

Browse files
update content
1 parent d2f6f0b commit 47b77a7

File tree

73 files changed

+586
-586
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+586
-586
lines changed

‎动态规划系列/LCS.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<a href="https://space.bilibili.com/14089380"><img src="https://img.shields.io/badge/B站-@labuladong-000000.svg?style=flat-square&logo=Bilibili"></a>
88
</p>
99

10-
![](https://labuladong.github.io/algo/images/souyisou1.png)
10+
![](https://labuladong.gitee.io/pictures/souyisou1.png)
1111

1212
**通知:[数据结构精品课](https://aep.h5.xeknow.com/s/1XJHEO) 已更新到 V2.1,[手把手刷二叉树系列课程](https://aep.xet.tech/s/3YGcq3) 上线。过年前最后一期打卡挑战即将开始,[点这里报名](https://aep.xet.tech/s/1a9ByX)。另外,建议你在我的 [网站](https://labuladong.github.io/algo/) 学习文章,体验更好。**
1313

@@ -81,4 +81,4 @@ int longestCommonSubsequence(String s1, String s2);
8181

8282
应合作方要求,本文不便在此发布,请扫码关注回复关键词「LCS」或 [点这里](https://appktavsiei5995.pc.xiaoe-tech.com/detail/i_6298793ae4b09dda12708be8/1) 查看:
8383

84-
![](https://labuladong.github.io/algo/images/qrcode.jpg)
84+
![](https://labuladong.gitee.io/pictures/qrcode.jpg)

‎动态规划系列/动态规划之KMP字符匹配算法.md‎

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<a href="https://space.bilibili.com/14089380"><img src="https://img.shields.io/badge/B站-@labuladong-000000.svg?style=flat-square&logo=Bilibili"></a>
88
</p>
99

10-
![](https://labuladong.github.io/algo/images/souyisou1.png)
10+
![](https://labuladong.gitee.io/pictures/souyisou1.png)
1111

1212
**通知:[数据结构精品课](https://aep.h5.xeknow.com/s/1XJHEO) 已更新到 V2.1,[手把手刷二叉树系列课程](https://aep.xet.tech/s/3YGcq3) 上线。过年前最后一期打卡挑战即将开始,[点这里报名](https://aep.xet.tech/s/1a9ByX)。另外,建议你在我的 [网站](https://labuladong.github.io/algo/) 学习文章,体验更好。**
1313

@@ -64,17 +64,17 @@ int search(String pat, String txt) {
6464

6565
比如 `txt = "aaacaaab", pat = "aaab"`:
6666

67-
![](https://labuladong.github.io/algo/images/kmp/1.gif)
67+
![](https://labuladong.gitee.io/pictures/kmp/1.gif)
6868

6969
很明显,`pat` 中根本没有字符 c,根本没必要回退指针 `i`,暴力解法明显多做了很多不必要的操作。
7070

7171
KMP 算法的不同之处在于,它会花费空间来记录一些信息,在上述情况中就会显得很聪明:
7272

73-
![](https://labuladong.github.io/algo/images/kmp/2.gif)
73+
![](https://labuladong.gitee.io/pictures/kmp/2.gif)
7474

7575
再比如类似的 `txt = "aaaaaaab", pat = "aaab"`,暴力解法还会和上面那个例子一样蠢蠢地回退指针 `i`,而 KMP 算法又会耍聪明:
7676

77-
![](https://labuladong.github.io/algo/images/kmp/3.gif)
77+
![](https://labuladong.gitee.io/pictures/kmp/3.gif)
7878

7979
因为 KMP 算法知道字符 b 之前的字符 a 都是匹配的,所以每次只需要比较字符 b 是否被匹配就行了。
8080

@@ -97,21 +97,21 @@ pat = "aaab"
9797

9898
只不过对于 `txt1` 的下面这个即将出现的未匹配情况:
9999

100-
![](https://labuladong.github.io/algo/images/kmp/txt1.jpg)
100+
![](https://labuladong.gitee.io/pictures/kmp/txt1.jpg)
101101

102102
`dp` 数组指示 `pat` 这样移动:
103103

104-
![](https://labuladong.github.io/algo/images/kmp/txt2.jpg)
104+
![](https://labuladong.gitee.io/pictures/kmp/txt2.jpg)
105105

106106
> PS:这个`j` 不要理解为索引,它的含义更准确地说应该是**状态**(state),所以它会出现这个奇怪的位置,后文会详述。
107107
108108
而对于 `txt2` 的下面这个即将出现的未匹配情况:
109109

110-
![](https://labuladong.github.io/algo/images/kmp/txt3.jpg)
110+
![](https://labuladong.gitee.io/pictures/kmp/txt3.jpg)
111111

112112
`dp` 数组指示 `pat` 这样移动:
113113

114-
![](https://labuladong.github.io/algo/images/kmp/txt4.jpg)
114+
![](https://labuladong.gitee.io/pictures/kmp/txt4.jpg)
115115

116116
明白了 `dp` 数组只和 `pat` 有关,那么我们这样设计 KMP 算法就会比较漂亮:
117117

@@ -145,45 +145,45 @@ int pos2 = kmp.search("aaaaaaab"); //4
145145

146146
为什么说 KMP 算法和状态机有关呢?是这样的,我们可以认为 `pat` 的匹配就是状态的转移。比如当 pat = "ABABC":
147147

148-
![](https://labuladong.github.io/algo/images/kmp/state.jpg)
148+
![](https://labuladong.gitee.io/pictures/kmp/state.jpg)
149149

150150
如上图,圆圈内的数字就是状态,状态 0 是起始状态,状态 5(`pat.length`)是终止状态。开始匹配时 `pat` 处于起始状态,一旦转移到终止状态,就说明在 `txt` 中找到了 `pat`。比如说当前处于状态 2,就说明字符 "AB" 被匹配:
151151

152-
![](https://labuladong.github.io/algo/images/kmp/state2.jpg)
152+
![](https://labuladong.gitee.io/pictures/kmp/state2.jpg)
153153

154154
另外,处于不同状态时,`pat` 状态转移的行为也不同。比如说假设现在匹配到了状态 4,如果遇到字符 A 就应该转移到状态 3,遇到字符 C 就应该转移到状态 5,如果遇到字符 B 就应该转移到状态 0:
155155

156-
![](https://labuladong.github.io/algo/images/kmp/state4.jpg)
156+
![](https://labuladong.gitee.io/pictures/kmp/state4.jpg)
157157

158158
具体什么意思呢,我们来一个个举例看看。用变量 `j` 表示指向当前状态的指针,当前 `pat` 匹配到了状态 4:
159159

160-
![](https://labuladong.github.io/algo/images/kmp/exp1.jpg)
160+
![](https://labuladong.gitee.io/pictures/kmp/exp1.jpg)
161161

162162
如果遇到了字符 "A",根据箭头指示,转移到状态 3 是最聪明的:
163163

164-
![](https://labuladong.github.io/algo/images/kmp/exp3.jpg)
164+
![](https://labuladong.gitee.io/pictures/kmp/exp3.jpg)
165165

166166
如果遇到了字符 "B",根据箭头指示,只能转移到状态 0(一夜回到解放前):
167167

168-
![](https://labuladong.github.io/algo/images/kmp/exp5.jpg)
168+
![](https://labuladong.gitee.io/pictures/kmp/exp5.jpg)
169169

170170
如果遇到了字符 "C",根据箭头指示,应该转移到终止状态 5,这也就意味着匹配完成:
171171

172-
![](https://labuladong.github.io/algo/images/kmp/exp7.jpg)
172+
![](https://labuladong.gitee.io/pictures/kmp/exp7.jpg)
173173

174174
当然了,还可能遇到其他字符,比如 Z,但是显然应该转移到起始状态 0,因为 `pat` 中根本都没有字符 Z:
175175

176-
![](https://labuladong.github.io/algo/images/kmp/z.jpg)
176+
![](https://labuladong.gitee.io/pictures/kmp/z.jpg)
177177

178178
这里为了清晰起见,我们画状态图时就把其他字符转移到状态 0 的箭头省略,只画 `pat` 中出现的字符的状态转移:
179179

180-
![](https://labuladong.github.io/algo/images/kmp/allstate.jpg)
180+
![](https://labuladong.gitee.io/pictures/kmp/allstate.jpg)
181181

182182
KMP 算法最关键的步骤就是构造这个状态转移图。**要确定状态转移的行为,得明确两个变量,一个是当前的匹配状态,另一个是遇到的字符**;确定了这两个变量后,就可以知道这个情况下应该转移到哪个状态。
183183

184184
下面看一下 KMP 算法根据这幅状态转移图匹配字符串 `txt` 的过程:
185185

186-
![](https://labuladong.github.io/algo/images/kmp/kmp.gif)
186+
![](https://labuladong.gitee.io/pictures/kmp/kmp.gif)
187187

188188
**请记住这个 GIF 的匹配过程,这就是 KMP 算法的核心逻辑**!
189189

@@ -238,29 +238,29 @@ for 0 <= j < M: # 状态
238238

239239
这个 next 状态应该怎么求呢?显然,**如果遇到的字符 `c``pat[j]` 匹配的话**,状态就应该向前推进一个,也就是说 `next = j + 1`,我们不妨称这种情况为**状态推进**:
240240

241-
![](https://labuladong.github.io/algo/images/kmp/forward.jpg)
241+
![](https://labuladong.gitee.io/pictures/kmp/forward.jpg)
242242

243243
**如果字符 `c``pat[j]` 不匹配的话**,状态就要回退(或者原地不动),我们不妨称这种情况为**状态重启**:
244244

245-
![](https://labuladong.github.io/algo/images/kmp/back.jpg)
245+
![](https://labuladong.gitee.io/pictures/kmp/back.jpg)
246246

247247
那么,如何得知在哪个状态重启呢?解答这个问题之前,我们再定义一个名字:**影子状态**(我编的名字),用变量 `X` 表示。**所谓影子状态,就是和当前状态具有相同的前缀**。比如下面这种情况:
248248

249-
![](https://labuladong.github.io/algo/images/kmp/shadow.jpg)
249+
![](https://labuladong.gitee.io/pictures/kmp/shadow.jpg)
250250

251251
当前状态 `j = 4`,其影子状态为 `X = 2`,它们都有相同的前缀 "AB"。因为状态 `X` 和状态 `j` 存在相同的前缀,所以当状态 `j` 准备进行状态重启的时候(遇到的字符 `c``pat[j]` 不匹配),可以通过 `X` 的状态转移图来获得**最近的重启位置**
252252

253253
比如说刚才的情况,如果状态 `j` 遇到一个字符 "A",应该转移到哪里呢?首先只有遇到 "C" 才能推进状态,遇到 "A" 显然只能进行状态重启。**状态 `j` 会把这个字符委托给状态 `X` 处理,也就是 `dp[j]['A'] = dp[X]['A']`**:
254254

255-
![](https://labuladong.github.io/algo/images/kmp/shadow1.jpg)
255+
![](https://labuladong.gitee.io/pictures/kmp/shadow1.jpg)
256256

257257
为什么这样可以呢?因为:既然 `j` 这边已经确定字符 "A" 无法推进状态,**只能回退**,而且 KMP 就是要**尽可能少的回退**,以免多余的计算。那么 `j` 就可以去问问和自己具有相同前缀的 `X`,如果 `X` 遇见 "A" 可以进行「状态推进」,那就转移过去,因为这样回退最少。
258258

259-
![](https://labuladong.github.io/algo/images/kmp/A.gif)
259+
![](https://labuladong.gitee.io/pictures/kmp/A.gif)
260260

261261
当然,如果遇到的字符是 "B",状态 `X` 也不能进行「状态推进」,只能回退,`j` 只要跟着 `X` 指引的方向回退就行了:
262262

263-
![](https://labuladong.github.io/algo/images/kmp/shadow2.jpg)
263+
![](https://labuladong.gitee.io/pictures/kmp/shadow2.jpg)
264264

265265
你也许会问,这个 `X` 怎么知道遇到字符 "B" 要回退到状态 0 呢?因为 `X` 永远跟在 `j` 的身后,状态 `X` 如何转移,在之前就已经算出来了。动态规划算法不就是利用过去的结果解决现在的问题吗?
266266

@@ -354,7 +354,7 @@ for (int i = 0; i < N; i++) {
354354

355355
下面来看一下状态转移图的完整构造过程,你就能理解状态 `X` 作用之精妙了:
356356

357-
![](https://labuladong.github.io/algo/images/kmp/dfa.gif)
357+
![](https://labuladong.gitee.io/pictures/kmp/dfa.gif)
358358

359359
至此,KMP 算法的核心终于写完啦啦啦啦!看下 KMP 算法的完整代码吧:
360360

@@ -440,7 +440,7 @@ KMP 算法也就是动态规划那点事,我们的公众号文章目录有一
440440

441441
**《labuladong 的算法小抄》已经出版,关注公众号查看详情;后台回复关键词「**进群**」可加入算法群;回复「**全家桶**」可下载配套 PDF 和刷题全家桶**:
442442

443-
![](https://labuladong.github.io/algo/images/souyisou2.png)
443+
![](https://labuladong.gitee.io/pictures/souyisou2.png)
444444

445445

446446
======其他语言代码======

‎动态规划系列/动态规划之博弈问题.md‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<a href="https://space.bilibili.com/14089380"><img src="https://img.shields.io/badge/B站-@labuladong-000000.svg?style=flat-square&logo=Bilibili"></a>
88
</p>
99

10-
![](https://labuladong.github.io/algo/images/souyisou1.png)
10+
![](https://labuladong.gitee.io/pictures/souyisou1.png)
1111

1212
**通知:[数据结构精品课](https://aep.h5.xeknow.com/s/1XJHEO) 已更新到 V2.1,[手把手刷二叉树系列课程](https://aep.xet.tech/s/3YGcq3) 上线。过年前最后一期打卡挑战即将开始,[点这里报名](https://aep.xet.tech/s/1a9ByX)。另外,建议你在我的 [网站](https://labuladong.github.io/algo/) 学习文章,体验更好。**
1313

@@ -36,7 +36,7 @@
3636

3737
这样推广之后就变成了一道难度比较高的动态规划问题了,力扣第 486 题「预测赢家」就是一道类似的问题:
3838

39-
![](https://labuladong.github.io/algo/images/博弈问题/title.jpg)
39+
![](https://labuladong.gitee.io/pictures/博弈问题/title.jpg)
4040

4141
函数签名如下:
4242

@@ -63,7 +63,7 @@ public boolean PredictTheWinner(int[] nums) {
6363

6464
介绍 `dp` 数组的含义之前,我们先看一下 `dp` 数组最终的样子:
6565

66-
![](https://labuladong.github.io/algo/images/博弈问题/1.png)
66+
![](https://labuladong.gitee.io/pictures/博弈问题/1.png)
6767

6868
下文讲解时,认为元组是包含 `first``second` 属性的一个类,而且为了节省篇幅,将这两个属性简写为 `fir``sec`。比如按上图的数据,我们说 `dp[1][3].fir = 11`,`dp[0][1].sec = 2`
6969

@@ -141,11 +141,11 @@ dp[i][j].sec = 0
141141
# 后手没有石头拿了,得分为 0
142142
```
143143

144-
![](https://labuladong.github.io/algo/images/博弈问题/2.png)
144+
![](https://labuladong.gitee.io/pictures/博弈问题/2.png)
145145

146146
这里需要注意一点,我们发现 base case 是斜着的,而且我们推算 `dp[i][j]` 时需要用到 `dp[i+1][j]``dp[i][j-1]`:
147147

148-
![](https://labuladong.github.io/algo/images/博弈问题/3.png)
148+
![](https://labuladong.gitee.io/pictures/博弈问题/3.png)
149149

150150
根据前文 [动态规划答疑篇](https://labuladong.github.io/article/fname.html?fname=最优子结构) 判断 `dp` 数组遍历方向的原则,算法应该倒着遍历 `dp` 数组:
151151

@@ -157,7 +157,7 @@ for (int i = n - 2; i >= 0; i--) {
157157
}
158158
```
159159

160-
![](https://labuladong.github.io/algo/images/博弈问题/4.png)
160+
![](https://labuladong.gitee.io/pictures/博弈问题/4.png)
161161

162162
### 三、代码实现
163163

@@ -242,7 +242,7 @@ int stoneGame(int[] piles) {
242242

243243
**《labuladong 的算法小抄》已经出版,关注公众号查看详情;后台回复关键词「**进群**」可加入算法群;回复「**全家桶**」可下载配套 PDF 和刷题全家桶**:
244244

245-
![](https://labuladong.github.io/algo/images/souyisou2.png)
245+
![](https://labuladong.gitee.io/pictures/souyisou2.png)
246246

247247

248248
======其他语言代码======

‎动态规划系列/动态规划之四键键盘.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<a href="https://space.bilibili.com/14089380"><img src="https://img.shields.io/badge/B站-@labuladong-000000.svg?style=flat-square&logo=Bilibili"></a>
88
</p>
99

10-
![](https://labuladong.github.io/algo/images/souyisou1.png)
10+
![](https://labuladong.gitee.io/pictures/souyisou1.png)
1111

1212
**通知:[数据结构精品课](https://aep.h5.xeknow.com/s/1XJHEO) 已更新到 V2.1,[手把手刷二叉树系列课程](https://aep.xet.tech/s/3YGcq3) 上线。过年前最后一期打卡挑战即将开始,[点这里报名](https://aep.xet.tech/s/1a9ByX)。另外,建议你在我的 [网站](https://labuladong.github.io/algo/) 学习文章,体验更好。**
1313

@@ -190,7 +190,7 @@ public int maxA(int N) {
190190

191191
其中 `j` 变量减 2 是给 `C-A C-C` 留下操作数,看个图就明白了:
192192

193-
![](https://labuladong.github.io/algo/images/4keyboard/1.jpg)
193+
![](https://labuladong.gitee.io/pictures/4keyboard/1.jpg)
194194

195195
这样,此算法就完成了,时间复杂度 O(N^2),空间复杂度 O(N),这种解法应该是比较高效的了。
196196

@@ -232,7 +232,7 @@ def dp(n, a_num, copy):
232232

233233
**《labuladong 的算法小抄》已经出版,关注公众号查看详情;后台回复关键词「**进群**」可加入算法群;回复「**全家桶**」可下载配套 PDF 和刷题全家桶**:
234234

235-
![](https://labuladong.github.io/algo/images/souyisou2.png)
235+
![](https://labuladong.gitee.io/pictures/souyisou2.png)
236236

237237

238238
======其他语言代码======

‎动态规划系列/动态规划之正则表达.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<a href="https://space.bilibili.com/14089380"><img src="https://img.shields.io/badge/B站-@labuladong-000000.svg?style=flat-square&logo=Bilibili"></a>
88
</p>
99

10-
![](https://labuladong.github.io/algo/images/souyisou1.png)
10+
![](https://labuladong.gitee.io/pictures/souyisou1.png)
1111

1212
**通知:[数据结构精品课](https://aep.h5.xeknow.com/s/1XJHEO) 已更新到 V2.1,[手把手刷二叉树系列课程](https://aep.xet.tech/s/3YGcq3) 上线。过年前最后一期打卡挑战即将开始,[点这里报名](https://aep.xet.tech/s/1a9ByX)。另外,建议你在我的 [网站](https://labuladong.github.io/algo/) 学习文章,体验更好。**
1313

@@ -145,7 +145,7 @@ bool dp(string& s, int i, string& p, int j);
145145
146146
应合作方要求,本文不便在此发布,请扫码关注回复关键词「正则」或 [点这里](https://appktavsiei5995.pc.xiaoe-tech.com/detail/i_6298796ae4b01a4852072fb9/1) 查看:
147147
148-
![](https://labuladong.github.io/algo/images/qrcode.jpg)
148+
![](https://labuladong.gitee.io/pictures/qrcode.jpg)
149149
150150
======其他语言代码======
151151

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /