52
52
53
53
### 方法一:记忆化搜索
54
54
55
- $dfs(i)$ 表示从数组从下标 $i$ 开始到结尾,是否至少存在一个有效的划分 。
55
+ 我们设计一个函数 $dfs(i)$,表示从下标 $i$ 开始是否存在一种有效划分。那么答案就是 $dfs(0)$ 。
56
56
57
- 时间复杂度 $O(n)$,空间复杂度 $O(n)$。
57
+ 函数 $dfs(i)$ 的执行过程如下:
58
+
59
+ - 如果 $i \ge n$,返回 $true$。
60
+ - 如果 $i$ 和 $i+1$ 下标的元素相等,那么可以选择将 $i$ 和 $i+1$ 作为一个子数组,递归调用 $dfs(i+2)$。
61
+ - 如果 $i$, $i+1$ 和 $i+2$ 下标的元素相等,那么可以选择将 $i$, $i+1$ 和 $i+2$ 作为一个子数组,递归调用 $dfs(i+3)$。
62
+ - 如果 $i$, $i+1$ 和 $i+2$ 下标的元素依次递增 $1$,那么可以选择将 $i$, $i+1$ 和 $i+2$ 作为一个子数组,递归调用 $dfs(i+3)$。
63
+ - 如果上述情况都不满足,返回 $false$,否则返回 $true$。
64
+
65
+ 即:
66
+
67
+ $$
68
+ dfs(i) = \text{OR}
69
+ \begin{cases}
70
+ true,&i \ge n\\
71
+ dfs(i+2),&i+1 < n\ \text{and}\ \textit{nums}[i] = \textit{nums}[i+1]\\
72
+ dfs(i+3),&i+2 < n\ \text{and}\ \textit{nums}[i] = \textit{nums}[i+1] = \textit{nums}[i+2]\\
73
+ dfs(i+3),&i+2 < n\ \text{and}\ \textit{nums}[i+1] - \textit{nums}[i] = 1\ \text{and}\ \textit{nums}[i+2] - \textit{nums}[i+1] = 1
74
+ \end{cases}
75
+ $$
76
+
77
+ 为了避免重复计算,我们使用记忆化搜索的方法。
78
+
79
+ 时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 为数组的长度。
58
80
59
81
<!-- tabs:start -->
60
82
61
83
``` python
62
84
class Solution :
63
85
def validPartition (self , nums : List[int ]) -> bool :
64
86
@cache
65
- def dfs (i ) :
66
- if i = = n:
87
+ def dfs (i : int ) -> bool :
88
+ if i > = n:
67
89
return True
68
- res = False
69
- if i < n - 1 and nums[i] == nums[i + 1 ]:
70
- res = res or dfs(i + 2 )
71
- if i < n - 2 and nums[i] == nums[i + 1 ] and nums[i + 1 ] == nums[i + 2 ]:
72
- res = res or dfs(i + 3 )
73
- if (
74
- i < n - 2
90
+ a = i + 1 < n and nums[i] == nums[i + 1 ]
91
+ b = i + 2 < n and nums[i] == nums[i + 1 ] == nums[i + 2 ]
92
+ c = (
93
+ i + 2 < n
75
94
and nums[i + 1 ] - nums[i] == 1
76
95
and nums[i + 2 ] - nums[i + 1 ] == 1
77
- ):
78
- res = res or dfs(i + 3 )
79
- return res
96
+ )
97
+ return (a and dfs(i + 2 )) or ((b or c) and dfs(i + 3 ))
80
98
81
99
n = len (nums)
82
100
return dfs(0 )
@@ -85,63 +103,58 @@ class Solution:
85
103
``` java
86
104
class Solution {
87
105
private int n;
88
- private int [] f;
89
106
private int [] nums;
107
+ private Boolean [] f;
90
108
91
109
public boolean validPartition (int [] nums ) {
92
- this . nums = nums;
93
110
n = nums. length;
94
- f = new int [n] ;
95
- Arrays . fill(f, - 1 ) ;
111
+ this . nums = nums ;
112
+ f = new Boolean [n] ;
96
113
return dfs(0 );
97
114
}
98
115
99
116
private boolean dfs (int i ) {
100
- if (i = = n) {
117
+ if (i > = n) {
101
118
return true ;
102
119
}
103
- if (f[i] != - 1 ) {
104
- return f[i] == 1 ;
105
- }
106
- boolean res = false ;
107
- if (i < n - 1 && nums[i] == nums[i + 1 ]) {
108
- res = res || dfs(i + 2 );
109
- }
110
- if (i < n - 2 && nums[i] == nums[i + 1 ] && nums[i + 1 ] == nums[i + 2 ]) {
111
- res = res || dfs(i + 3 );
120
+ if (f[i] != null ) {
121
+ return f[i];
112
122
}
113
- if (i < n - 2 && nums[i + 1 ] - nums[i] == 1 && nums[i + 2 ] - nums[i + 1 ] == 1 ) {
114
- res = res || dfs(i + 3 );
115
- }
116
- f[i] = res ? 1 : 0 ;
117
- return res;
123
+ boolean a = i + 1 < n && nums[i] == nums[i + 1 ];
124
+ boolean b = i + 2 < n && nums[i] == nums[i + 1 ] && nums[i + 1 ] == nums[i + 2 ];
125
+ boolean c = i + 2 < n && nums[i + 1 ] - nums[i] == 1 && nums[i + 2 ] - nums[i + 1 ] == 1 ;
126
+ return f[i] = ((a && dfs(i + 2 )) || ((b || c) && dfs(i + 3 )));
118
127
}
119
128
}
120
129
```
121
130
122
131
``` cpp
123
132
class Solution {
124
133
public:
125
- vector<int > f;
126
- vector<int > nums;
127
- int n;
128
-
129
134
bool validPartition(vector<int >& nums) {
130
135
n = nums.size();
131
136
this->nums = nums;
132
137
f.assign(n, -1);
133
138
return dfs(0);
134
139
}
135
140
141
+ private:
142
+ int n;
143
+ vector<int > f;
144
+ vector<int > nums;
145
+
136
146
bool dfs(int i) {
137
- if (i == n) return true;
138
- if (f[ i] != -1) return f[ i] == 1;
139
- bool res = false;
140
- if (i < n - 1 && nums[ i] == nums[ i + 1] ) res = res || dfs(i + 2);
141
- if (i < n - 2 && nums[ i] == nums[ i + 1] && nums[ i + 1] == nums[ i + 2] ) res = res || dfs(i + 3);
142
- if (i < n - 2 && nums[ i + 1] - nums[ i] == 1 && nums[ i + 2] - nums[ i + 1] == 1) res = res || dfs(i + 3);
143
- f[ i] = res ? 1 : 0;
144
- return res;
147
+ if (i >= n) {
148
+ return true;
149
+ }
150
+ if (f[i] != -1 ) {
151
+ return f[i] == 1;
152
+ }
153
+ bool a = i + 1 < n && nums[i] == nums[i + 1];
154
+ bool b = i + 2 < n && nums[i] == nums[i + 1] && nums[i + 1] == nums[i + 2];
155
+ bool c = i + 2 < n && nums[i + 1] - nums[i] == 1 && nums[i + 2] - nums[i + 1] == 1;
156
+ f[i] = ((a && dfs(i + 2)) || ((b || c) && dfs(i + 3))) ? 1 : 0;
157
+ return f[i] == 1;
145
158
}
146
159
};
147
160
```
@@ -161,21 +174,14 @@ func validPartition(nums []int) bool {
161
174
if f[i] != -1 {
162
175
return f[i] == 1
163
176
}
164
- res := false
177
+ a := i+1 < n && nums[i] == nums[i+1 ]
178
+ b := i+2 < n && nums[i] == nums[i+1 ] && nums[i+1 ] == nums[i+2 ]
179
+ c := i+2 < n && nums[i+1 ]-nums[i] == 1 && nums[i+2 ]-nums[i+1 ] == 1
165
180
f[i] = 0
166
- if i < n-1 && nums[i] == nums[i+1] {
167
- res = res || dfs(i+2)
168
- }
169
- if i < n-2 && nums[i] == nums[i+1] && nums[i+1] == nums[i+2] {
170
- res = res || dfs(i+3)
171
- }
172
- if i < n-2 && nums[i+1]-nums[i] == 1 && nums[i+2]-nums[i+1] == 1 {
173
- res = res || dfs(i+3)
174
- }
175
- if res {
181
+ if a && dfs (i+2 ) || b && dfs (i+3 ) || c && dfs (i+3 ) {
176
182
f[i] = 1
177
183
}
178
- return res
184
+ return f[i] == 1
179
185
}
180
186
return dfs (0 )
181
187
}
@@ -184,95 +190,75 @@ func validPartition(nums []int) bool {
184
190
``` ts
185
191
function validPartition(nums : number []): boolean {
186
192
const n = nums .length ;
187
- const vis = new Array (n ).fill (false );
188
- const queue = [0 ];
189
- while (queue .length !== 0 ) {
190
- const i = queue .shift () ?? 0 ;
191
-
192
- if (i === n ) {
193
+ const f: number [] = Array (n ).fill (- 1 );
194
+ const dfs = (i : number ): boolean => {
195
+ if (i >= n ) {
193
196
return true ;
194
197
}
195
-
196
- if (! vis [i + 2 ] && i + 2 <= n && nums [i ] === nums [i + 1 ]) {
197
- queue .push (i + 2 );
198
- vis [i + 2 ] = true ;
199
- }
200
-
201
- if (
202
- ! vis [i + 3 ] &&
203
- i + 3 <= n &&
204
- ((nums [i ] === nums [i + 1 ] && nums [i + 1 ] === nums [i + 2 ]) ||
205
- (nums [i ] === nums [i + 1 ] - 1 && nums [i + 1 ] === nums [i + 2 ] - 1 ))
206
- ) {
207
- queue .push (i + 3 );
208
- vis [i + 3 ] = true ;
198
+ if (f [i ] !== - 1 ) {
199
+ return f [i ] === 1 ;
209
200
}
210
- }
211
- return false ;
201
+ const a = i + 1 < n && nums [i ] == nums [i + 1 ];
202
+ const b = i + 2 < n && nums [i ] == nums [i + 1 ] && nums [i + 1 ] == nums [i + 2 ];
203
+ const c = i + 2 < n && nums [i + 1 ] - nums [i ] == 1 && nums [i + 2 ] - nums [i + 1 ] == 1 ;
204
+ f [i ] = (a && dfs (i + 2 )) || ((b || c ) && dfs (i + 3 )) ? 1 : 0 ;
205
+ return f [i ] == 1 ;
206
+ };
207
+ return dfs (0 );
212
208
}
213
209
```
214
210
215
211
<!-- tabs:end -->
216
212
217
213
### 方法二:动态规划
218
214
219
- 设 $dp [ i ] $ 表示数组前 $i$ 个元素是否至少存在一个有效的划分。初始时 $dp [ 0 ] =true$, $dp [ 1 ] =false$ 。
215
+ 我们可以将方法一中的记忆化搜索转换为动态规划 。
220
216
221
- 根据题意,当 $i \ge 2$ 时,有
217
+ 设 $f[ i] $ 表示数组的前 $i$ 个元素是否存在一种有效划分,初始时 $f[ 0] = true$,答案就是 $f[ n] $。
218
+
219
+ 状态转移方程如下:
222
220
223
221
$$
224
- dp [i] = \text{OR}
222
+ f [i] = \text{OR}
225
223
\begin{cases}
226
- dp[i-2]\ \text{AND}\ \textit{nums}[i-1] = \textit{nums}[i-2],&i>1\\
227
- dp[i-3]\ \text{AND}\ \textit{nums}[i-1] = \textit{nums}[i-2] = \textit{nums}[i-3],&i>2\\
228
- dp[i-3]\ \text{AND}\ \textit{nums}[i-1] = \textit{nums}[i-2]+1 = \textit{nums}[i-3]+2,&i>2
224
+ true,&i = 0\\
225
+ f[i-2],&i-2 \ge 0\ \text{and}\ \textit{nums}[i-1] = \textit{nums}[i-2]\\
226
+ f[i-3],&i-3 \ge 0\ \text{and}\ \textit{nums}[i-1] = \textit{nums}[i-2] = \textit{nums}[i-3]\\
227
+ f[i-3],&i-3 \ge 0\ \text{and}\ \textit{nums}[i-1] - \textit{nums}[i-2] = 1\ \text{and}\ \textit{nums}[i-2] - \textit{nums}[i-3] = 1
229
228
\end{cases}
230
229
$$
231
230
232
- 答案为 $dp[ n] $。
233
-
234
- 时间复杂度 $O(n)$,空间复杂度 $O(n)$。
231
+ 时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 为数组的长度。
235
232
236
233
<!-- tabs:start -->
237
234
238
235
``` python
239
236
class Solution :
240
237
def validPartition (self , nums : List[int ]) -> bool :
241
238
n = len (nums)
242
- dp = [False ] * (n + 1 )
243
- dp[0 ] = True
244
- for i in range (2 , n + 1 ):
245
- if nums[i - 1 ] == nums[i - 2 ]:
246
- dp[i] = dp[i] or dp[i - 2 ]
247
- if i > 2 and nums[i - 1 ] == nums[i - 2 ] == nums[i - 3 ]:
248
- dp[i] = dp[i] or dp[i - 3 ]
249
- if (
250
- i > 2
251
- and nums[i - 1 ] - nums[i - 2 ] == 1
252
- and nums[i - 2 ] - nums[i - 3 ] == 1
253
- ):
254
- dp[i] = dp[i] or dp[i - 3 ]
255
- return dp[- 1 ]
239
+ f = [True ] + [False ] * n
240
+ for i, x in enumerate (nums, 1 ):
241
+ a = i - 2 >= 0 and nums[i - 2 ] == x
242
+ b = i - 3 >= 0 and nums[i - 3 ] == nums[i - 2 ] == x
243
+ c = i - 3 >= 0 and x - nums[i - 2 ] == 1 and nums[i - 2 ] - nums[i - 3 ] == 1
244
+ f[i] = (a and f[i - 2 ]) or ((b or c) and f[i - 3 ])
245
+ return f[n]
256
246
```
257
247
258
248
``` java
259
249
class Solution {
260
250
public boolean validPartition (int [] nums ) {
261
251
int n = nums. length;
262
- boolean [] dp = new boolean [n + 1 ];
263
- dp[0 ] = true ;
264
- for (int i = 2 ; i <= n; ++ i) {
265
- if (nums[i - 1 ] == nums[i - 2 ]) {
266
- dp[i] = dp[i] || dp[i - 2 ];
267
- }
268
- if (i > 2 && nums[i - 1 ] == nums[i - 2 ] && nums[i - 2 ] == nums[i - 3 ]) {
269
- dp[i] = dp[i] || dp[i - 3 ];
270
- }
271
- if (i > 2 && nums[i - 1 ] - nums[i - 2 ] == 1 && nums[i - 2 ] - nums[i - 3 ] == 1 ) {
272
- dp[i] = dp[i] || dp[i - 3 ];
273
- }
252
+ boolean [] f = new boolean [n + 1 ];
253
+ f[0 ] = true ;
254
+ for (int i = 1 ; i <= n; ++ i) {
255
+ boolean a = i - 2 >= 0 && nums[i - 1 ] == nums[i - 2 ];
256
+ boolean b = i - 3 >= 0 && nums[i - 1 ] == nums[i - 2 ] && nums[i - 2 ] == nums[i - 3 ];
257
+ boolean c
258
+ = i - 3 >= 0 && nums[i - 1 ] - nums[i - 2 ] == 1 && nums[i - 2 ] - nums[i - 3 ] == 1 ;
259
+ f[i] = (a && f[i - 2 ]) || ((b || c) && f[i - 3 ]);
274
260
}
275
- return dp [n];
261
+ return f [n];
276
262
}
277
263
}
278
264
```
@@ -282,55 +268,47 @@ class Solution {
282
268
public:
283
269
bool validPartition(vector<int >& nums) {
284
270
int n = nums.size();
285
- vector<bool > dp(n + 1);
286
- dp[ 0] = true;
287
- for (int i = 2; i <= n; ++i) {
288
- if (nums[ i - 1] == nums[ i - 2] ) dp[ i] = dp[ i] || dp[ i - 2] ;
289
- if (i > 2 && nums[ i - 1] == nums[ i - 2] && nums[ i - 2] == nums[ i - 3] ) dp[ i] = dp[ i] || dp[ i - 3] ;
290
- if (i > 2 && nums[ i - 1] - nums[ i - 2] == 1 && nums[ i - 2] - nums[ i - 3] == 1) dp[ i] = dp[ i] || dp[ i - 3] ;
271
+ vector<bool > f(n + 1);
272
+ f[ 0] = true;
273
+ for (int i = 1; i <= n; ++i) {
274
+ bool a = i - 2 >= 0 && nums[ i - 1] == nums[ i - 2] ;
275
+ bool b = i - 3 >= 0 && nums[ i - 1] == nums[ i - 2] && nums[ i - 2] == nums[ i - 3] ;
276
+ bool c = i - 3 >= 0 && nums[ i - 1] - nums[ i - 2] == 1 && nums[ i - 2] - nums[ i - 3] == 1;
277
+ f[ i] = (a && f[ i - 2] ) || ((b || c) && f[ i - 3] );
291
278
}
292
- return dp [ n] ;
279
+ return f [ n] ;
293
280
}
294
281
};
295
282
```
296
283
297
284
```go
298
285
func validPartition(nums []int) bool {
299
286
n := len(nums)
300
- dp := make([]bool, n+1)
301
- dp[0] = true
302
- for i := 2; i <= n; i++ {
303
- if nums[i-1] == nums[i-2] {
304
- dp[i] = dp[i] || dp[i-2]
305
- }
306
- if i > 2 && nums[i-1] == nums[i-2] && nums[i-2] == nums[i-3] {
307
- dp[i] = dp[i] || dp[i-3]
308
- }
309
- if i > 2 && nums[i-1]-nums[i-2] == 1 && nums[i-2]-nums[i-3] == 1 {
310
- dp[i] = dp[i] || dp[i-3]
311
- }
287
+ f := make([]bool, n+1)
288
+ f[0] = true
289
+ for i := 1; i <= n; i++ {
290
+ x := nums[i-1]
291
+ a := i-2 >= 0 && nums[i-2] == x
292
+ b := i-3 >= 0 && nums[i-3] == nums[i-2] && nums[i-2] == x
293
+ c := i-3 >= 0 && x-nums[i-2] == 1 && nums[i-2]-nums[i-3] == 1
294
+ f[i] = (a && f[i-2]) || ((b || c) && f[i-3])
312
295
}
313
- return dp [n]
296
+ return f [n]
314
297
}
315
298
```
316
299
317
300
``` ts
318
301
function validPartition(nums : number []): boolean {
319
302
const n = nums .length ;
320
- const dp = new Array (n + 1 ).fill (false );
321
- dp [0 ] = true ;
322
- for (let i = 2 ; i <= n ; ++ i ) {
323
- if (nums [i - 1 ] == nums [i - 2 ]) {
324
- dp [i ] = dp [i ] || dp [i - 2 ];
325
- }
326
- if (i > 2 && nums [i - 1 ] == nums [i - 2 ] && nums [i - 2 ] == nums [i - 3 ]) {
327
- dp [i ] = dp [i ] || dp [i - 3 ];
328
- }
329
- if (i > 2 && nums [i - 1 ] - nums [i - 2 ] == 1 && nums [i - 2 ] - nums [i - 3 ] == 1 ) {
330
- dp [i ] = dp [i ] || dp [i - 3 ];
331
- }
303
+ const f: boolean [] = Array (n + 1 ).fill (false );
304
+ f [0 ] = true ;
305
+ for (let i = 1 ; i <= n ; ++ i ) {
306
+ const a = i - 2 >= 0 && nums [i - 1 ] === nums [i - 2 ];
307
+ const b = i - 3 >= 0 && nums [i - 1 ] === nums [i - 2 ] && nums [i - 2 ] === nums [i - 3 ];
308
+ const c = i - 3 >= 0 && nums [i - 1 ] - nums [i - 2 ] === 1 && nums [i - 2 ] - nums [i - 3 ] === 1 ;
309
+ f [i ] = (a && f [i - 2 ]) || ((b || c ) && f [i - 3 ]);
332
310
}
333
- return dp [n ];
311
+ return f [n ];
334
312
}
335
313
```
336
314
0 commit comments