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 505dc41

Browse files
Merge pull request MisterBooo#2 from MisterBooo/master
更新项目
2 parents b67eec3 + 109bddb commit 505dc41

File tree

16 files changed

+654
-0
lines changed

16 files changed

+654
-0
lines changed
10.6 MB
Loading[フレーム]
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# LeetCode 第 5 号问题:最长回文串
2+
3+
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
4+
>
5+
> 同步博客:https://www.algomooc.com
6+
>
7+
8+
题目来源于 LeetCode 上第 5 号问题:最长回文串。题目难度为 Medium,目前通过率为 29% 。
9+
10+
## 题目描述
11+
12+
给定一个字符串,要求这个字符串当中最长的回文串。
13+
14+
## 示例
15+
16+
```
17+
Input: "babad"
18+
Output: "bab"
19+
Note: "aba" is also a valid answer.
20+
```
21+
22+
```
23+
Input: "cbbd"
24+
Output: "bb"
25+
```
26+
27+
## 题目分析
28+
29+
这道题目是典型的看着简单,但是实际上并不简单的问题。
30+
31+
我们先从简单的算法开始,最简单的方法当然是暴力。由于我们需要求出最长的回文串,一种方法是求出s串所有的子串,然后一一对比它们是否构成回文。这样当然是可行的,但是我们简单分析一下复杂度就会发现,这并不能接受。对于一个长度为n的字符串来说,我们任意选择其中两个位置,就可以找到它的一个子串,那么我们选择两个位置的数量就是$C_n^2 = \frac{n(n-1)}{2}$。对于每一个子串,我们需要遍历一遍才能判断是否回文,所以整体的复杂度是$O(n^3)$。
32+
33+
但是如果你对回文串非常熟悉的话,会发现其实这是可以优化的。因为我们要求的是最长的回文串,如果我们确定了对称中心的位置,它能够构成的最长回文串就是确定的。所以我们只需要遍历所有的回文串中心,和每个中心能找到的最长回文串。这样我们的复杂度就降低了一维,变成了$O(n^2)$。
34+
35+
回文串有两种形式,一种是奇回文,也就是回文中心是一个字符,比如aba。还有一种是偶回文,回文中心是两个字符之间,比如abba。这两种情况我们需要分开讨论。
36+
37+
我们写出代码:
38+
39+
```python
40+
class Solution:
41+
def longestPalindrome(self, s: str) -> str:
42+
n = len(s)
43+
44+
ret = ''
45+
for i in range(n):
46+
# 奇回文的情况
47+
l, r = i, i
48+
while s[l] == s[r]:
49+
l -= 1
50+
r += 1
51+
if l < 0 or r >= n:
52+
break
53+
if r - l - 1 > len(ret):
54+
ret = s[l+1: r]
55+
56+
# 偶回文的情况
57+
l, r = i-1, i
58+
while l >= 0 and s[l] == s[r]:
59+
l -= 1
60+
r += 1
61+
if l < 0 or r >= n:
62+
break
63+
if r - l - 1 > len(ret):
64+
ret = s[l+1: r]
65+
66+
return ret
67+
```
68+
69+
到这里还没有结束,接下来我们介绍一个经典的回文串求解算法——Manacher,也叫做马拉车算法。
70+
71+
首先,我们需要统一奇回文和偶回文这两种情况,这也很方便,我们把原串进行处理,在两个相邻字符当中插入一个分隔字符#,比如abcd转化成#a#b#c#d#。一般我们还会在首尾加入防止超界的字符,比如$&等。之后我们维护两个值,分别是id和mr。mr表示当前能够构成的回文串向右延伸最远的位置,id表示这个位置对应的对称中心。根据这个位置id以及mr我们可以快速地求解出当前位置i能够构成的合法回文串的长度。
72+
73+
我们假设每一个位置构成的合法回文串半径是p[i], 那么对于i这个位置,我们可以得到p[i] >= min(mr - i, p[id * 2 - i])。其中id * 2 - i是i这个位置关于id的对称位置,并且以i为中心对称的回文串小于mr位置的部分也关于id对称。所以如果p[id * 2 - i] < mr - i的话,说明i关于id的对称位置没能突破id对称的限制,既然i的对称点没有能突破限制,那么i显然也不行。同理,如果p[id * 2 - i] > mr - i的话,说明i的对称位置没有被id限制住,但是这恰恰说明i被限制住了。因为如果i也能突破mr这个限制的话,那么说明id的对称范围还能扩大,这和我们的前提假设矛盾了。所以只有p[id * 2 - i] == mr - i的情况,i才有可能继续延伸。
74+
75+
如果能理解上面的关系,整个算法已经很清楚了,如果没看懂也没有关系,可以看下下面的动图,会展示得更加清楚。
76+
77+
理解了上述的算法过程之后剩下的工作就简单了,我们只需要在求解p[i]的同时维护id和mr即可。
78+
79+
最后我们来看下算法的复杂度,为什么这是一个O(n)的算法呢?原因很简单,我们只需要关注mr这个变量即可。mr这个变量是递增的,mr每次递增的大小,其实就是p[i] - (mr - i)的长度。所以虽然看似我们用了两重循环,但是由于mr最多只能递增n次,所以它依然是O(n)的算法。
80+
81+
#### 动画描述
82+
83+
![](../Animation/LeetCode5.gif)
84+
85+
#### 代码实现
86+
87+
```python
88+
class Solution:
89+
90+
def longestPalindrome(self, s: str) -> str:
91+
# 在所有字符中间插入#
92+
def transform(s):
93+
return '$#' + '#'.join(list(s)) + '#&'
94+
95+
if s == '':
96+
return s
97+
# 初始化
98+
s = transform(s)
99+
p = [0 for _ in range(len(s)+1)]
100+
mr, id_ = 0, 0
101+
# 首尾是特殊字符,所以下标从1到len(s)-2
102+
for i in range(1, len(s)-1):
103+
# 计算p[i]
104+
p[i] = 1 if mr <= i else min(p[2*id_-i], mr - i)
105+
106+
# 只有当前i已经摆脱id限制,或者是第三种情况时,才有可能继续延伸
107+
# 这个只是优化,不加这个判断一样可以运行
108+
if mr <= i or p[2*id_-i] == mr - i:
109+
while s[i - p[i]] == s[i + p[i]]:
110+
p[i] += 1
111+
112+
if i + p[i] > mr:
113+
mr, id_ = i + p[i], i
114+
# 找到长度最长的下标
115+
id_ = p.index(max(p))
116+
# 获得整个回文的字符串
117+
palindromic = s[id_ - p[id_]+1: id_ + p[id_]]
118+
# 过滤掉#,还原为原字符
119+
return ''.join(filter(lambda x: x != '#', list(palindromic)))
120+
121+
```
122+
123+
![](../../Pictures/qrcode.jpg)

‎0011-maxArea/Animation/maxArea.gif‎

5.08 MB
Loading[フレーム]

‎0011-maxArea/Animation/maxArea.mp4‎

1.75 MB
Binary file not shown.

‎0011-maxArea/Article/0011-maxArea.md‎

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
2+
>
3+
> 同步个人博客:https://www.zhangxiaoshuai.fun
4+
5+
**本题选自leetcode的第11题,medium级别,目前通过率:61.3%**
6+
7+
**题目描述:**
8+
9+
```txt
10+
给你n个非负整数a1,a2,...,an,每个数代表坐标中的一个点(i,ai)。在坐标内画n条垂直线,
11+
垂直线i的两个端点分别为(i,ai)和(i,0)。找出其中的两条线,使得它们与x轴共同构成的容器可以容纳最多的水。
12+
说明:你不能倾斜容器,且n的值至少为2。
13+
示例:
14+
输入:[1,8,6,2,5,4,8,3,7]
15+
输出:49
16+
```
17+
18+
我们都应该听说过**木桶原理**,一个木桶可以装入多少水取决于最短的那块板;而这道题也可以与木桶装水的问题对应上。
19+
很容易的可以得到---->**容器可以容纳水的容量=两条垂直线中最短的那条*两条线之间的距离**
20+
现在的情况是,有很多条线,让你计算两两之间能装的最多的水,其实暴力法之间就能解决这个问题,但是它的时间复杂度也达到了**O(n^2)**
21+
22+
ok,那我们先试试用**暴力法**来解 决问题:
23+
24+
### 1.暴力法
25+
26+
直接上代码:
27+
28+
```java
29+
public int maxArea(int[] height) {
30+
int res = 0;
31+
for(int i = 0;i < height.length;i++){
32+
for(int j = i+1;j < height.length;j++){
33+
int temp = Math.min(height[i],height[j]) * (j-i);
34+
res = Math.max(res,temp);
35+
}
36+
}
37+
return res;
38+
}
39+
```
40+
41+
暴力法是可以通过测试的,但是可以看到**程序执行用时**并不理想
42+
43+
```
44+
执行用时 :440 ms, 在所有 Java 提交中击败了17.44% 的用户
45+
内存消耗 :39.9 MB, 在所有 Java 提交中击败了37.86%的用户
46+
```
47+
48+
### 2.双指针
49+
50+
思路:使用两个指针(**resource****last**)分别指向数组的第一个元素和最后一个元素,然后我们计算这两条"线"之间能容纳的水的容量,并更新最大容量(初始值为0);接着我们需要将指向元素值小的那个指针前移一步,然后重复上面的步骤,直到**resource = last**循环截止。
51+
52+
**GIF动画演示:**
53+
54+
![](../Animation/maxArea.gif)
55+
56+
**来看看代码:**
57+
58+
```java
59+
public int maxArea(int[] height) {
60+
int resource = 0;
61+
int last = height.length - 1;
62+
int res = 0;
63+
while (resource < last) {
64+
if (height[resource] >= height[last]) {
65+
res = Math.max(res, (last - resource) * height[last]);
66+
last--;
67+
} else {
68+
res = Math.max(res, (last - resource) * height[resource]);
69+
resource++;
70+
}
71+
}
72+
return res;
73+
}
74+
```
75+
76+
**可以很明显的看到,虽然内存消耗两者是差不多的,但是双指针的速度比暴力解法的速度可是高出好多倍。**
77+
78+
时间复杂度:**O(n)** 空间复杂度:**O(1)**
79+
80+
```
81+
执行用时 :3 ms, 在所有 Java 提交中击败了92.69% 的用户
82+
内存消耗 :40.3 MB, 在所有 Java 提交中击败了7.86%的用户
83+
```
84+
85+
[视频演示](../Animation/maxArea.mp4)
7.6 MB
Binary file not shown.
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# 1394. 找出数组中的幸运数
2+
3+
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
4+
>
5+
> 同步博客:https://www.algomooc.com
6+
7+
题目来源于 LeetCode 上 1394题: 找出数组中的幸运数。,主要涉及哈希表。
8+
9+
## 题目
10+
11+
在整数数组中,如果一个整数的出现频次和它的数值大小相等,我们就称这个整数为「幸运数」。
12+
13+
给你一个整数数组 arr,请你从中找出并返回一个幸运数。
14+
15+
如果数组中存在多个幸运数,只需返回 最大 的那个。
16+
如果数组中不含幸运数,则返回 -1 。
17+
18+
19+
示例 1:
20+
21+
```
22+
输入:arr = [2,2,3,4]
23+
输出:2
24+
解释:数组中唯一的幸运数是 2 ,因为数值 2 的出现频次也是 2 。
25+
```
26+
27+
示例 2:
28+
29+
```
30+
输入:arr = [1,2,2,3,3,3]
31+
输出:3
32+
解释:1、2 以及 3 都是幸运数,只需要返回其中最大的 3 。
33+
```
34+
35+
示例 3:
36+
37+
```
38+
输入:arr = [2,2,2,3,3]
39+
输出:-1
40+
解释:数组中不存在幸运数。
41+
```
42+
43+
示例 4:
44+
45+
```
46+
输入:arr = [5]
47+
输出:-1
48+
```
49+
50+
示例 5:
51+
52+
```
53+
输入:arr = [7,7,7,7,7,7,7]
54+
输出:7
55+
```
56+
57+
提示:
58+
59+
1 <= arr.length <= 500
60+
1 <= arr[i] <= 500
61+
62+
## 题目解析
63+
64+
1. 遍历arr,用哈希表记录每个数组元素出现的次数
65+
2. 遍历哈希表,每次找到一个幸运数就和当前的幸运数对比,最后找到最大的幸运数,如果没有找到的话输出-1
66+
67+
## 动画理解
68+
69+
70+
<video id="video" controls="" preload="none" >
71+
<source id="mp4" src="../Animation/01394.mp4" type="video/mp4">
72+
</video>
73+
74+
## 参考代码
75+
76+
77+
```javaScript
78+
/**
79+
* @param {number[]} arr
80+
* @return {number}
81+
*/
82+
var findLucky = function(arr) {
83+
let map = new Map()
84+
let maxLucky = -1
85+
arr.map(i => {
86+
map.set(i, map.get(i)+1 || 1)
87+
})
88+
map.forEach((key, value)=>{
89+
if(key == value){
90+
maxLucky = Math.max(maxLucky, key)
91+
}
92+
})
93+
return maxLucky
94+
};
95+
```
96+
97+
## 复杂度分析
98+
99+
假设元素的个数是n个,那么哈希表中最多有 n 个键值对。
100+
101+
时间复杂度:这个算法里有两次遍历,遍历数组的时间复杂度是O(n),遍历哈希表的时间复杂度是O(n),所以最后的时间复杂度就是O(n)。
102+
103+
空间复杂度:额外只用了哈希表,哈希表中最多有 n 个键值对,所以空间复杂度是 O(n)。
104+
105+
106+
![](../../Pictures/qrcode.jpg)

‎0202-happy-number/Animation/202.mp4‎

13.5 MB
Binary file not shown.

0 commit comments

Comments
(0)

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