diff --git a/README.md b/README.md
index c492f68..bf974bd 100644
--- a/README.md
+++ b/README.md
@@ -77,13 +77,13 @@ This command specifies the `english` branch and limit the depth of clone, get ri
* [Regular Expression](dynamic_programming/RegularExpression.md)
* [The Strategies of Subsequence Problem](dynamic_programming/StrategiesForSubsequenceProblem.md)
* [Greedy: Interval Scheduling](dynamic_programming/IntervalScheduling.md)
+ * [4 Keys Keyboard](dynamic_programming/FourKeysKeyboard.md)
* [动态规划详解](dynamic_programming/动态规划详解进阶.md)
* [动态规划答疑篇](dynamic_programming/最优子结构.md)
* [动态规划设计:最长递增子序列](dynamic_programming/动态规划设计:最长递增子序列.md)
* [动态规划之KMP字符匹配算法](dynamic_programming/动态规划之KMP字符匹配算法.md)
* [团灭 LeetCode 股票买卖问题](dynamic_programming/团灭股票问题.md)
* [团灭 LeetCode 打家劫舍问题](dynamic_programming/抢房子.md)
- * [动态规划之四键键盘](dynamic_programming/动态规划之四键键盘.md)
* V. Common Knowledge
* [Difference Between Process and Thread in Linux](common_knowledge/linuxProcess.md)
diff --git a/SUMMARY.md b/SUMMARY.md
index e835443..a4d6d43 100644
--- a/SUMMARY.md
+++ b/SUMMARY.md
@@ -64,13 +64,13 @@
* [Regular Expression](dynamic_programming/RegularExpression.md)
* [The Strategies of Subsequence Problem](dynamic_programming/StrategiesForSubsequenceProblem.md)
* [Greedy: Interval Scheduling](dynamic_programming/IntervalScheduling.md)
+ * [4 Keys Keyboard](dynamic_programming/FourKeysKeyboard.md)
* [动态规划详解](dynamic_programming/动态规划详解进阶.md)
* [动态规划答疑篇](dynamic_programming/最优子结构.md)
* [动态规划设计:最长递增子序列](dynamic_programming/动态规划设计:最长递增子序列.md)
* [动态规划之KMP字符匹配算法](dynamic_programming/动态规划之KMP字符匹配算法.md)
* [团灭 LeetCode 股票买卖问题](dynamic_programming/团灭股票问题.md)
* [团灭 LeetCode 打家劫舍问题](dynamic_programming/抢房子.md)
- * [动态规划之四键键盘](dynamic_programming/动态规划之四键键盘.md)
* V. Common Knowledge
* [Difference Between Process and Thread in Linux](common_knowledge/linuxProcess.md)
diff --git a/dynamic_programming/FourKeysKeyboard.md b/dynamic_programming/FourKeysKeyboard.md
new file mode 100644
index 0000000..9690c58
--- /dev/null
+++ b/dynamic_programming/FourKeysKeyboard.md
@@ -0,0 +1,184 @@
+# 4 Keys Keyboard
+
+**Translator: [upbin](https://github.com/upbin)**
+
+**Author: [labuladong](https://github.com/labuladong)**
+
+The problem of *4 keys keyboard* is very interesting and can broaden one's horizon. This problem can make you obviously feel that different definitions of dp arrays need completely different logic to think about, and this logic can produce completely different solutions.
+
+We can't wait to solve this problem:
+
+
+
+After reading the question, think about how to get the maximum number of characters 'A' after typing `N` times on the keyboard? We are more familiar with trying questions using enumeration. Whenever we want to press the keyboard (and can press it), there are `4 buttons` for us to choose from, we can enumerate every possible operation It is obvious that this is a dynamic programming problem.
+
+### Discuss the first method
+
+This kind of problem-solving idea is easy to understand, but the efficiency is not high. We follow the routine directly: **for dynamic programming problems, we must first understand which are [ states ] and which are [ choices ].**
+
+Specific to this problem, what **choices** are obvious for each keystroke: four types are the **4** keys mentioned in the title, which are `A`, `Ctrl-A`, `Ctrl-C`, and `Ctrl-V`.
+
+Next, let's think about what are the **states** of this problem? **In other words, what information do we need to know to break down the original problem into smaller subproblems?**
+
+Now you think about it, Is it correct for me to define the status of this problem as follows?
+
+- The first state is the remaining number of times the key can be pressed, we use `n` to represent it.
+- The second state is the number of characters 'A' on the current screen, we use `a_num`.
+- The third state is the number of characters 'A' still in the clipboard, represented by `copy`.
+
+By defining **state** in this way, we can know the *base case*: when the number of remaining `n` is `0`, `a_num` is the answer we want.
+
+Combining the four **choices** just mentioned, we can express these kinds of choices through state transitions:
+
+```python
+dp(n - 1, a_num + 1, copy) # [ A ]
+# comment: Press the 'A' key to add a character to the screen.
+# Subtract 1 at the same time the number of times you are allowed to press the keyboard.
+
+dp(n - 1, a_num + copy, copy) # [Ctrl-V]
+# comment: Press C-V to paste, the characters in the clipboard are added to the screen.
+# Subtract 1 at the same time the number of times you are allowed to press the keyboard.
+
+dp(n - 2, a_num, a_num) # [Ctrl-A] & [Ctrl-C]
+# comment: Ctrl + A and Ctrl + C can obviously be used together.
+# The number of 'A' in the clipboard becomes the number of 'A' on the screen.
+# Subtract 2 at the same time the number of times you are allowed to press the keyboard.
+```
+
+By describing this, we can see that the scale of the problem `n` is constantly decreasing, and finally we can reach the *base case* of `n == 0`. So this idea is correct: (Do you think so?)
+
+```python
+def maxA(N: int) -> int:
+
+ # It can be verified that for the initial (n, a_num, copy) state,
+ # there can be at most dp (n, a_num, copy) 'A' on the screen.
+ def dp(n, a_num, copy):
+ # base case
+ if n <= 0: return a_num;
+ # Let ’s try all three options and choose the largest one.
+ return max(
+ dp(n - 1, a_num + 1, copy), # [ A ]
+ dp(n - 1, a_num + copy, copy), # [Ctrl-V]
+ dp(n - 2, a_num, a_num) # [Ctrl-A] & [Ctrl-C]
+ )
+
+ # You can press the key n times, then there is no 'A' in the screen
+ # and the clipboard.
+ return dp(N, 0, 0)
+```
+
+This solution should be well understood because it is semantically explicit.
+
+Below we continue to follow the routine and use memorized search to eliminate those overlapping sub-problems:
+
+```python
+def maxA(N: int) -> int:
+ # memorandum
+ memo = dict()
+ def dp(n, a_num, copy):
+ if n <= 0: return a_num;
+ # Avoid overlapping subproblems being recalculated
+ if (n, a_num, copy) in memo:
+ return memo[(n, a_num, copy)]
+
+ memo[(n, a_num, copy)] = max(
+ # These options are still the same
+ )
+ return memo[(n, a_num, copy)]
+
+ return dp(N, 0, 0)
+```
+
+After we optimized our code in this way, although the sub-problem was repeatedly solved, the number of searches was still very large (*if we submit to LeetCode it will definitely time out*).
+
+Now let's try to analyze the time complexity of the algorithm just now. The challenge is that this analysis is not easy. No matter what it is, now we write this *dp function* as a *dp array*:
+
+```c++
+dp[n][a_num][copy]
+// The total number of states (spatial complexity) of this problem
+// is the volume of this three-dimensional array.
+```
+
+We know that the maximum value of the variable `n` is `N`, but it is difficult to calculate the maximum number of `a_num` and `copy`. The lowest complexity is *O(N^3)*. So the algorithm just now is not good, the complexity is too high, and it can no longer be optimized.
+
+The more embarrassing thing is that this also shows that I used to define **state** as it is not very good. Let's change the idea of defining this dp problem.
+
+### Exploration of the second approach
+
+Next, our thinking is a little more complicated, but it is very efficient.
+
+Continue to follow our routine, **choice** has been defined before, or the `4`. But this time we only need to define **a state**, which is the remaining number of available keyboard presses `n`.
+
+This algorithm is based on the following fact. There must be only two cases of the key sequence corresponding to the optimal answer:
+
+- Either keep pressing A: `A`, ` A`, ... , `A` (more when `N` is smaller).
+- Either this is the form: `A`, `A`, ..., `Ctrl-A`, `Ctrl-C`, `Ctrl-V`, `Ctrl-V`, ..., `Ctrl-V` (mostly when `N` is larger). *(Here you can find some mathematical rules, you can study if you are interested)*
+
+Because when the number of characters to be printed is relatively small (`N` is small), "`Ctrl-A`, `Ctrl-C`, `Ctrl-V`" consumes a relatively high number of operations, so we might as well keep pressing `A`. When `N` is relatively large, the gain of `Ctrl-V` in the later period is definitely relatively large. In this case, the entire operation sequence is roughly like this: at the beginning, press several 'A's, then `Ctrl-A`, `Ctrl-C`, and finally several `Ctrl-V`, and then continue `Ctrl-A -> Ctrl-C -> Ctrl-V` Such a loop operation.
+
+In other words, the last keystroke was either `A` or `Ctrl-V`. As long as we are clear on this, we can design the algorithm through these **two situations**:
+
+```java
+int[] dp = new int[N + 1];
+// Definition: dp[i] indicates the maximum number of 'A' that can be displayed after the // first operation.
+for (int i = 0; i <= N; i++)
+ dp[i] = max(
+ // Press [ A ] this time,
+ // This time press [Ctrl-V].
+ )
+```
+
+Think about it. For the case of [pressing the `A` key], it is actually a new 'A' printed on the screen of **state i-1**, so it is easy to get the result:
+
+```java
+// If we press the [ A ] key, it's just one more 'A' than the last time.
+dp[i] = dp[i - 1] + 1;
+```
+
+However, if we want to press `Ctrl-V`, we also need to consider where we did `Ctrl-A` and `Ctrl-C`.
+
+Earlier we said that the optimal sequence of operations must be `Ctrl-A`, `Ctrl-C` followed by several `Ctrl-V`, so we use a variable `j` as the starting point for these `Ctrl-V` operations. Then the two operations before `j` should be `Ctrl-A` and `Ctrl-C`:
+
+```java
+public int maxA(int N) {
+ int[] dp = new int[N + 1];
+ dp[0] = 0;
+ for (int i = 1; i <= N; i++) {
+ // press [ A ]
+ dp[i] = dp[i - 1] + 1;
+ for (int j = 2; j < i; j++) {
+ // [Ctrl-A] & [Ctrl-C] -> dp[j-2], Paste i-j times
+ // There are { dp[j-2] * (i-j+1) }number of 'A' on the screen
+ dp[i] = Math.max(dp[i], dp[j - 2] * (i - j + 1));
+ }
+ }
+ // What is the maximum number of 'A' after N keystrokes?
+ return dp[N];
+}
+```
+
+The `j` variable `minus 2` is used to save the number of operations available to `Ctrl-A`, `Ctrl-C`. See the description picture to understand:
+
+
+
+We have just completed this algorithm. The time complexity of the algorithm is *O(N^2)* and the space complexity is *O(N)*, so this solution seems to be very efficient.
+
+### Review our algorithmic ideas
+
+Dynamic programming is difficult to find the state transition. The different definitions we set will produce different state transition logic. Although we can all get the correct results in the end, the efficiency of the program may have amazing differences.
+
+Let's review the method we tried for the first time. Although the overlapping sub-problem has been eliminated, the efficiency of the program is still low, but where is the low? Let's abstract the recursive framework to find out:
+
+```python
+def dp(n, a_num, copy):
+ dp(n - 1, a_num + 1, copy), # [ A ]
+ dp(n - 1, a_num + copy, copy), # [Ctrl-V]
+ dp(n - 2, a_num, a_num) # [Ctrl-A] & [Ctrl-C]
+```
+
+Let's analyze the logic of this exhaustive scheme. Obviously, it is possible to have such a sequence of operations `Ctrl-A`, `Ctrl+C`, `Ctrl-A`, `Ctrl-C`, ... , or `Ctrl-V`, `Ctrl-V`, ... . However, the result of the operation sequence produced by this method is not optimal, even if we have not figured out a way to circumvent these situations, thereby adding a lot of calculations of unnecessary sub-problems.
+
+After we review the second solution, we only need to think a little bit before we can think that the operation sequence of the optimal answer should be this form: `A`, `A`, ..., `Ctrl-A`, `Ctrl-C`, `Ctrl-V`, `Ctrl-V`, ..., `Ctrl-V`.
+
+Based on the findings we found, we redefined state and re-searched for state transition, which logically reduced the number of invalid sub-problems, and ultimately optimized the program's operating efficiency.
+
diff --git a/dynamic_programming/动态规划之四键键盘.md b/dynamic_programming/动态规划之四键键盘.md
deleted file mode 100644
index 5d289a1..0000000
--- a/dynamic_programming/动态规划之四键键盘.md
+++ /dev/null
@@ -1,173 +0,0 @@
-# 动态规划之四键键盘
-
-四键键盘问题很有意思,而且可以明显感受到:对 dp 数组的不同定义需要完全不同的逻辑,从而产生完全不同的解法。
-
-首先看一下题目:
-
-
-
-如何在 N 次敲击按钮后得到最多的 A?我们穷举呗,每次有对于每次按键,我们可以穷举四种可能,很明显就是一个动态规划问题。
-
-### 第一种思路
-
-这种思路会很容易理解,但是效率并不高,我们直接走流程:**对于动态规划问题,首先要明白有哪些「状态」,有哪些「选择」**。
-
-具体到这个问题,对于每次敲击按键,有哪些「选择」是很明显的:4 种,就是题目中提到的四个按键,分别是 `A`、`C-A`、`C-C`、`C-V`(`Ctrl` 简写为 `C`)。
-
-接下来,思考一下对于这个问题有哪些「状态」?**或者换句话说,我们需要知道什么信息,才能将原问题分解为规模更小的子问题**?
-
-你看我这样定义三个状态行不行:第一个状态是剩余的按键次数,用 `n` 表示;第二个状态是当前屏幕上字符 A 的数量,用 `a_num` 表示;第三个状态是剪切板中字符 A 的数量,用 `copy` 表示。
-
-如此定义「状态」,就可以知道 base case:当剩余次数 `n` 为 0 时,`a_num` 就是我们想要的答案。
-
-结合刚才说的 4 种「选择」,我们可以把这几种选择通过状态转移表示出来:
-
-```python
-dp(n - 1, a_num + 1, copy), # A
-解释:按下 A 键,屏幕上加一个字符
-同时消耗 1 个操作数
-
-dp(n - 1, a_num + copy, copy), # C-V
-解释:按下 C-V 粘贴,剪切板中的字符加入屏幕
-同时消耗 1 个操作数
-
-dp(n - 2, a_num, a_num) # C-A C-C
-解释:全选和复制必然是联合使用的,
-剪切板中 A 的数量变为屏幕上 A 的数量
-同时消耗 2 个操作数
-```
-
-这样可以看到问题的规模 `n` 在不断减小,肯定可以到达 `n = 0` 的 base case,所以这个思路是正确的:
-
-```python
-def maxA(N: int) -> int:
-
- # 对于 (n, a_num, copy) 这个状态,
- # 屏幕上能最终最多能有 dp(n, a_num, copy) 个 A
- def dp(n, a_num, copy):
- # base case
- if n <= 0: return a_num;
- # 几种选择全试一遍,选择最大的结果
- return max(
- dp(n - 1, a_num + 1, copy), # A
- dp(n - 1, a_num + copy, copy), # C-V
- dp(n - 2, a_num, a_num) # C-A C-C
- )
-
- # 可以按 N 次按键,屏幕和剪切板里都还没有 A
- return dp(N, 0, 0)
-```
-
-这个解法应该很好理解,因为语义明确。下面就继续走流程,用备忘录消除一下重叠子问题:
-
-```python
-def maxA(N: int) -> int:
- # 备忘录
- memo = dict()
- def dp(n, a_num, copy):
- if n <= 0: return a_num;
- # 避免计算重叠子问题
- if (n, a_num, copy) in memo:
- return memo[(n, a_num, copy)]
-
- memo[(n, a_num, copy)] = max(
- # 几种选择还是一样的
- )
- return memo[(n, a_num, copy)]
-
- return dp(N, 0, 0)
-```
-
-这样优化代码之后,子问题虽然没有重复了,但数目仍然很多,在 LeetCode 提交会超时的。
-
-我们尝试分析一下这个算法的时间复杂度,就会发现不容易分析。我们可以把这个 dp 函数写成 dp 数组:
-
-```python
-dp[n][a_num][copy]
-# 状态的总数(时空复杂度)就是这个三维数组的体积
-```
-
-我们知道变量 `n` 最多为 `N`,但是 `a_num` 和 `copy` 最多为多少我们很难计算,复杂度起码也有 O(N^3) 把。所以这个算法并不好,复杂度太高,且已经无法优化了。
-
-这也就说明,我们这样定义「状态」是不太优秀的,下面我们换一种定义 dp 的思路。
-
-### 第二种思路
-
-这种思路稍微有点复杂,但是效率高。继续走流程,「选择」还是那 4 个,但是这次我们只定义一个「状态」,也就是剩余的敲击次数 `n`。
-
-这个算法基于这样一个事实,**最优按键序列一定只有两种情况**:
-
-要么一直按 `A`:A,A,...A(当 N 比较小时)。
-
-要么是这么一个形式:A,A,...C-A,C-C,C-V,C-V,...C-V(当 N 比较大时)。
-
-因为字符数量少(N 比较小)时,`C-A C-C C-V` 这一套操作的代价相对比较高,可能不如一个个按 `A`;而当 N 比较大时,后期 `C-V` 的收获肯定很大。这种情况下整个操作序列大致是:**开头连按几个 `A`,然后 `C-A C-C` 组合再接若干 `C-V`,然后再 `C-A C-C` 接着若干 `C-V`,循环下去**。
-
-换句话说,最后一次按键要么是 `A` 要么是 `C-V`。明确了这一点,可以通过这两种情况来设计算法:
-
-```java
-int[] dp = new int[N + 1];
-// 定义:dp[i] 表示 i 次操作后最多能显示多少个 A
-for (int i = 0; i <= N; i++)
- dp[i] = max(
- 这次按 A 键,
- 这次按 C-V
- )
-```
-
-对于「按 `A` 键」这种情况,就是状态 `i - 1` 的屏幕上新增了一个 A 而已,很容易得到结果:
-
-```java
-// 按 A 键,就比上次多一个 A 而已
-dp[i] = dp[i - 1] + 1;
-```
-但是,如果要按 `C-V`,还要考虑之前是在哪里 `C-A C-C` 的。
-
-**刚才说了,最优的操作序列一定是 `C-A C-C` 接着若干 `C-V`,所以我们用一个变量 `j` 作为若干 `C-V` 的起点**。那么 `j` 之前的 2 个操作就应该是 `C-A C-C` 了:
-
-```java
-public int maxA(int N) {
- int[] dp = new int[N + 1];
- dp[0] = 0;
- for (int i = 1; i <= N; i++) {
- // 按 A 键
- dp[i] = dp[i - 1] + 1;
- for (int j = 2; j < i; j++) {
- // 全选 & 复制 dp[j-2],连续粘贴 i - j 次
- // 屏幕上共 dp[j - 2] * (i - j + 1) 个 A
- dp[i] = Math.max(dp[i], dp[j - 2] * (i - j + 1));
- }
- }
- // N 次按键之后最多有几个 A?
- return dp[N];
-}
-```
-
-其中 `j` 变量减 2 是给 `C-A C-C` 留下操作数,看个图就明白了:
-
-
-
-这样,此算法就完成了,时间复杂度 O(N^2),空间复杂度 O(N),这种解法应该是比较高效的了。
-
-### 最后总结
-
-动态规划难就难在寻找状态转移,不同的定义可以产生不同的状态转移逻辑,虽然最后都能得到正确的结果,但是效率可能有巨大的差异。
-
-回顾第一种解法,重叠子问题已经消除了,但是效率还是低,到底低在哪里呢?抽象出递归框架:
-
-```python
-def dp(n, a_num, copy):
- dp(n - 1, a_num + 1, copy), # A
- dp(n - 1, a_num + copy, copy), # C-V
- dp(n - 2, a_num, a_num) # C-A C-C
-```
-
-看这个穷举逻辑,是有可能出现这样的操作序列 `C-A C-C,C-A C-C...` 或者 `C-V,C-V,...`。然这种操作序列的结果不是最优的,但是我们并没有想办法规避这些情况的发生,从而增加了很多没必要的子问题计算。
-
-回顾第二种解法,我们稍加思考就能想到,最优的序列应该是这种形式:`A,A..C-A,C-C,C-V,C-V..C-A,C-C,C-V..`。
-
-根据这个事实,我们重新定义了状态,重新寻找了状态转移,从逻辑上减少了无效的子问题个数,从而提高了算法的效率。
-
-坚持原创高质量文章,致力于把算法问题讲清楚,欢迎关注我的公众号 labuladong 获取最新文章:
-
-
diff --git a/pictures/4keyboard/title.png b/pictures/4keyboard/title.png
index b5b71ad..35a7c5c 100644
Binary files a/pictures/4keyboard/title.png and b/pictures/4keyboard/title.png differ