Skip to content

🌟 feat: add 10、1004、1006、1038、1052、115、119、1190、1206、1208、1233、1282、1… #791

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 71 additions & 23 deletions LeetCode/1-10/10. 正则表达式匹配(困难).md
Original file line number Diff line number Diff line change
Expand Up @@ -74,41 +74,28 @@ Tag : 「动态规划」、「序列 DP」
本题可以使用动态规划进行求解:

* 状态定义:`f(i,j)` 代表考虑 `s` 中以 `i` 为结尾的子串和 `p` 中的 `j` 为结尾的子串是否匹配。最终我们要求的结果为 `f[n][m]` 。

* 状态转移:也就是我们要考虑 `f(i,j)` 如何求得,前面说到了 `p` 有三种字符,所以这里的状态转移也要分三种情况讨论:

1. `p[j]` 为普通字符:匹配的条件是前面的字符匹配,同时 `s` 中的第 `i` 个字符和 `p` 中的第 `j` 位相同。

`f(i,j) = f(i - 1, j - 1) && s[i] == p[j]` 。

2. `p[j]` 为 `'.'`:匹配的条件是前面的字符匹配, `s` 中的第 `i` 个字符可以是任意字符。

`f(i,j) = f(i - 1, j - 1) && p[j] == '.'`。

3. `p[j]` 为 `'*'`:读得 `p[j - 1]` 的字符,例如为字符 `a`。 然后根据 `a*` 实际匹配 `s` 中 `a` 的个数是 $0$ 个、$1$ 个、$2$ 个 ...

3.1. 当匹配为 $0$ 个:`f(i,j) = f(i, j - 2)`

3.2. 当匹配为 $1$ 个:`f(i,j) = f(i - 1, j - 2) && (s[i] == p[j - 1] || p[j - 1] == '.')`

3.3. 当匹配为 $2$ 个:`f(i,j) = f(i - 2, j - 2) && ((s[i] == p[j - 1] && s[i - 1] == p[j - 1]) || p[j] == '.')`
1. `p[j]` 为普通字符:匹配的条件是前面的字符匹配,同时 `s` 中的第 `i` 个字符和 `p` 中的第 `j` 位相同。 即 `f(i,j) = f(i-1, j-1) && s[i] == p[j]` 。
2. `p[j]` 为 `'.'`:匹配的条件是前面的字符匹配, `s` 中的第 `i` 个字符可以是任意字符。即 `f(i,j) = f(i-1, j-1) && p[j] == '.'`。
3. `p[j]` 为 `'*'`:读得 `p[j-1]` 的字符,例如为字符 `a`。 然后根据 `a*` 实际匹配 `s` 中 `a` 的个数是 $0$ 个、$1$ 个、$2$ 个 ...
* 当匹配为 $0$ 个:`f(i,j) = f(i,j-2)`
* 当匹配为 $1$ 个:`f(i,j) = f(i-1,j-2) && (s[i] == p[j-1] || p[j-1] == '.')`
* 当匹配为 $2$ 个:`f(i,j) = f(i-2, j-2) && ((s[i] == p[j-1] && s[i-1] == p[j-1]) || p[j] == '.')`

**我们知道,通过「枚举」来确定 `*` 到底匹配多少个 `a` 这样的做法,算法复杂度是很高的。**

**我们需要挖掘一些「性质」来简化这个过程。**

![640.png](https://pic.leetcode-cn.com/1611397993-lmpHIZ-640.png)
![](https://pic.leetcode-cn.com/1611397993-lmpHIZ-640.png)

代码:
Java 代码:
```Java
class Solution {
public boolean isMatch(String ss, String pp) {
// 技巧:往原字符头部插入空格,这样得到 char 数组是从 1 开始,而且可以使得 f[0][0] = true,可以将 true 这个结果滚动下去
int n = ss.length(), m = pp.length();
ss = " " + ss;
pp = " " + pp;
char[] s = ss.toCharArray();
char[] p = pp.toCharArray();
ss = " " + ss; pp = " " + pp;
char[] s = ss.toCharArray(), p = pp.toCharArray();
// f(i,j) 代表考虑 s 中的 1~i 字符和 p 中的 1~j 字符 是否匹配
boolean[][] f = new boolean[n + 1][m + 1];
f[0][0] = true;
Expand All @@ -132,6 +119,67 @@ class Solution {
}
}
```
C++ 代码:
```C++
class Solution {
public:
bool isMatch(string s, string p) {
int n = s.length(), m = p.length();
s = " " + s; p = " " + p;
vector<vector<bool>> f(n + 1, vector<bool>(m + 1, false));
f[0][0] = true;
for (int i = 0; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (j + 1 <= m && p[j + 1] == '*' && p[j] != '*') continue;
if (i - 1 >= 0 && p[j] != '*') {
f[i][j] = f[i - 1][j - 1] && (s[i] == p[j] || p[j] == '.');
} else if (p[j] == '*') {
f[i][j] = (j - 2 >= 0 && f[i][j - 2]) || (i - 1 >= 0 && f[i - 1][j] && (s[i] == p[j - 1] || p[j - 1] == '.'));
}
}
}
return f[n][m];
}
};
```
Python 代码:
```Python
class Solution:
def isMatch(self, s: str, p: str) -> bool:
n, m = len(s), len(p)
s, p = " " + s, " " + p
f = [[False] * (m + 1) for _ in range(n + 1)]
f[0][0] = True
for i in range(n + 1):
for j in range(1, m + 1):
if j + 1 <= m and p[j + 1] == '*' and p[j] != '*':
continue
if i - 1 >= 0 and p[j] != '*':
f[i][j] = f[i - 1][j - 1] and (s[i] == p[j] or p[j] == '.')
elif p[j] == '*':
f[i][j] = (j - 2 >= 0 and f[i][j - 2]) or (i - 1 >= 0 and f[i - 1][j] and (s[i] == p[j - 1] or p[j - 1] == '.'))
return f[n][m]
```
TypeScript 代码:
```TypeScript
function isMatch(s: string, p: string): boolean {
let n: number = s.length, m: number = p.length
s = " " + s; p = " " + p;
let f: boolean[][] = new Array(n + 1).fill(false).map(() => new Array(m + 1).fill(false));
f[0][0] = true;
for (let i: number = 0; i <= n; i++) {
for (let j: number = 1; j <= m; j++) {
if (j + 1 <= m && p.charAt(j + 1) === '*' && p.charAt(j) !== '*') continue;
if (i - 1 >= 0 && p.charAt(j) !== '*') {
f[i][j] = f[i - 1][j - 1] && (s.charAt(i) === p.charAt(j) || p.charAt(j) === '.');
} else if (p.charAt(j) === '*') {
f[i][j] = (j - 2 >= 0 && f[i][j - 2]) || (i - 1 >= 0 && f[i - 1][j] && (s.charAt(i) === p.charAt(j - 1) || p.charAt(j - 1) === '.'));
}
}
}
return f[n][m];
};
```
* 时间复杂度:$n$ 表示 `s` 的长度,$m$ 表示 `p` 的长度,总共 $n \times m$ 个状态。复杂度为 $O(n \times m)$
* 空间复杂度:使用了二维数组记录结果。复杂度为 $O(n \times m)$

Expand Down
142 changes: 128 additions & 14 deletions LeetCode/1001-1010/1004. 最大连续1的个数 III(中等).md
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,27 @@ Tag : 「双指针」、「滑动窗口」、「二分」、「前缀和」



给定一个由若干 $0$$1$ 组成的数组 `A`,我们最多可以将 $K$ 个值从 $0$ 变成 $1$
给定一个由若干 01 组成的数组 `A`,我们最多可以将 `K` 个值从 0 变成 1

返回仅包含 $1$ 的最长(连续)子数组的长度。
返回仅包含 1 的最长(连续)子数组的长度。


示例 1:
```
输入:A = [1,1,1,0,0,0,1,1,1,1,0], K = 2

输出:6

解释:
[1,1,1,0,0,1,1,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 6。
```
示例 2:
```
输入:A = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3

输出:10

解释:
[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 10。
Expand Down Expand Up @@ -50,7 +54,7 @@ Tag : 「双指针」、「滑动窗口」、「二分」、「前缀和」
* 如果 $A[i]$ 本身就为 1 的话,无须消耗翻转次数,$f[i][j] = f[i - 1][j] + 1$。
* 如果 $A[i]$ 本身不为 1 的话,由于定义是必须以 $A[i]$ 为结尾,因此必须要选择翻转该位置,$f[i][j] = f[i - 1][j - 1] + 1$。

代码:
Java 代码:
```Java
class Solution {
public int longestOnes(int[] nums, int k) {
Expand Down Expand Up @@ -96,23 +100,19 @@ class Solution {

**因此,对于某个确定的「左端点/右端点」而言,以「其最远右端点/最远左端点」为分割点的前缀和数轴,具有「二段性」。可以通过二分来找分割点。**

代码:
Java 代码:
```Java
class Solution {
public int longestOnes(int[] nums, int k) {
int n = nums.length;
int ans = 0;
int n = nums.length, ans = 0;
int[] sum = new int[n + 1];
for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + nums[i - 1];
for (int i = 0; i < n; i++) {
int l = 0, r = i;
while (l < r) {
int mid = l + r >> 1;
if (check(sum, mid, i, k)) {
r = mid;
} else {
l = mid + 1;
}
if (check(sum, mid, i, k)) r = mid;
else l = mid + 1;
}
if (check(sum, r, i, k)) ans = Math.max(ans, i - r + 1);
}
Expand All @@ -124,6 +124,80 @@ class Solution {
}
}
```
C++ 代码:
```C++
class Solution {
public:
int longestOnes(vector<int>& nums, int k) {
int n = nums.size(), ans = 0;
vector<int> sum(n + 1, 0);
for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + nums[i - 1];
for (int i = 0; i < n; i++) {
int l = 0, r = i;
while (l < r) {
int mid = l + r >> 1;
if (check(sum, mid, i, k)) r = mid;
else l = mid + 1;
}
if (check(sum, r, i, k)) ans = max(ans, i - r + 1);
}
return ans;
}
bool check(const vector<int>& sum, int l, int r, int k) {
int tol = sum[r + 1] - sum[l];
int len = r - l + 1;
return len - tol <= k;
}
};
```
Python 代码:
```Python
class Solution:
def longestOnes(self, nums: List[int], k: int) -> int:
n, ans = len(nums), 0
sumv = [0] * (n + 1)
for i in range(1, n + 1):
sumv[i] = sumv[i - 1] + nums[i - 1]
for i in range(n):
l, r = 0, i
while l < r:
mid = l + r >> 1
if self.check(sumv, mid, i, k):
r = mid
else:
l = mid + 1
if self.check(sumv, l, i, k):
ans = max(ans, i - l + 1)
return ans

def check(self, sumv, l, r, k):
tol = sumv[r + 1] - sumv[l]
lenv = r - l + 1
return lenv - tol <= k
```
TypeScript 代码:
```TypeScript
function check(sum: number[], l: number, r: number, k: number): boolean {
const tol = sum[r + 1] - sum[l];
const len = r - l + 1;
return len - tol <= k;
}
function longestOnes(nums: number[], k: number): number {
let n = nums.length, ans = 0;
const sum = new Array(n + 1).fill(0);
for (let i = 1; i <= n; i++) sum[i] = sum[i - 1] + nums[i - 1];
for (let i = 0; i < n; i++) {
let l = 0, r = i;
while (l < r) {
let mid = l + r >> 1;
if (check(sum, mid, i, k)) r = mid;
else l = mid + 1;
}
if (check(sum, l, i, k)) ans = Math.max(ans, i - l + 1);
}
return ans;
};
```
* 时间复杂度:$O(n\log{n})$
* 空间复杂度:$O(n)$

Expand All @@ -143,12 +217,11 @@ class Solution {

右端点一直右移,左端点在窗口不满足「`len - tol <= k`」的时候进行右移,即可做到线程扫描的复杂度。

代码:
Java 代码:
```Java
class Solution {
public int longestOnes(int[] nums, int k) {
int n = nums.length;
int ans = 0;
int n = nums.length, ans = 0;
for (int i = 0, j = 0, tot = 0; i < n; i++) {
tot += nums[i];
while ((i - j + 1) - tot > k) tot -= nums[j++];
Expand All @@ -158,6 +231,47 @@ class Solution {
}
}
```
C++ 代码:
```C++
class Solution {
public:
int longestOnes(vector<int>& nums, int k) {
int n = nums.size(), ans = 0;
for (int i = 0, j = 0, tot = 0; i < n; i++) {
tot += nums[i];
while ((i - j + 1) - tot > k) tot -= nums[j++];
ans = max(ans, i - j + 1);
}
return ans;
}
};
```
Python 代码:
```Python
class Solution:
def longestOnes(self, nums: List[int], k: int) -> int:
n, ans = len(nums), 0
j, tot = 0, 0
for i in range(n):
tot += nums[i]
while (i - j + 1) - tot > k:
tot -= nums[j]
j += 1
ans = max(ans, i - j + 1)
return ans
```
TypeScript 代码:
```TypeScript
function longestOnes(nums: number[], k: number): number {
let n = nums.length, ans = 0;
for (let i = 0, j = 0, tot = 0; i < n; i++) {
tot += nums[i];
while ((i - j + 1) - tot > k) tot -= nums[j++];
ans = Math.max(ans, i - j + 1);
}
return ans;
};
```
* 时间复杂度:$O(n)$
* 空间复杂度:$O(1)$

Expand Down
Loading