2
2
3
3
public class Problem_0010_RegularExpressionMatching {
4
4
5
- public static boolean isMatch1 (String s , String p ) {
6
- if (s == null || p == null ) {
7
- return false ;
8
- }
9
- char [] str = s .toCharArray ();
10
- char [] pattern = p .toCharArray ();
11
- return isValid (str , pattern ) && process (str , pattern , 0 , 0 );
12
- }
13
-
14
5
public static boolean isValid (char [] str , char [] pattern ) {
15
6
for (char cha : str ) {
16
7
if (cha == '.' || cha == '*' ) {
@@ -25,15 +16,26 @@ public static boolean isValid(char[] str, char[] pattern) {
25
16
return true ;
26
17
}
27
18
19
+ // 课堂现场写
20
+ public static boolean isMatch1 (String s , String p ) {
21
+ if (s == null || p == null ) {
22
+ return false ;
23
+ }
24
+ char [] str = s .toCharArray ();
25
+ char [] pattern = p .toCharArray ();
26
+ return isValid (str , pattern ) && process1 (str , pattern , 0 , 0 );
27
+ }
28
+
29
+ // 课堂现场写
28
30
// str[si.....] 能否被 pattern[pi...] 变出来
29
31
// 潜台词:pi位置,pattern[pi] != '*'
30
- public static boolean process (char [] str , char [] pattern , int si , int pi ) {
32
+ public static boolean process1 (char [] str , char [] pattern , int si , int pi ) {
31
33
if (si == str .length ) { // si越界了
32
34
if (pi == pattern .length ) {
33
35
return true ;
34
36
}
35
37
if (pi + 1 < pattern .length && pattern [pi + 1 ] == '*' ) {
36
- return process (str , pattern , si , pi + 2 );
38
+ return process1 (str , pattern , si , pi + 2 );
37
39
}
38
40
return false ;
39
41
}
@@ -43,25 +45,26 @@ public static boolean process(char[] str, char[] pattern, int si, int pi) {
43
45
}
44
46
// si 没越界 pi 没越界
45
47
if (pi + 1 >= pattern .length || pattern [pi + 1 ] != '*' ) {
46
- return ((str [si ] == pattern [pi ]) || (pattern [pi ] == '.' )) && process (str , pattern , si + 1 , pi + 1 );
48
+ return ((str [si ] == pattern [pi ]) || (pattern [pi ] == '.' )) && process1 (str , pattern , si + 1 , pi + 1 );
47
49
}
48
50
// si 没越界 pi 没越界 pi+1 *
49
51
if (pattern [pi ] != '.' && str [si ] != pattern [pi ]) {
50
- return process (str , pattern , si , pi + 2 );
52
+ return process1 (str , pattern , si , pi + 2 );
51
53
}
52
54
// si 没越界 pi 没越界 pi+1 * [pi]可配[si]
53
- if (process (str , pattern , si , pi + 2 )) {
55
+ if (process1 (str , pattern , si , pi + 2 )) {
54
56
return true ;
55
57
}
56
58
while (si < str .length && (str [si ] == pattern [pi ] || pattern [pi ] == '.' )) {
57
- if (process (str , pattern , si + 1 , pi + 2 )) {
59
+ if (process1 (str , pattern , si + 1 , pi + 2 )) {
58
60
return true ;
59
61
}
60
62
si ++;
61
63
}
62
64
return false ;
63
65
}
64
66
67
+ // 课堂现场写
65
68
public static boolean isMatch2 (String s , String p ) {
66
69
if (s == null || p == null ) {
67
70
return false ;
@@ -80,6 +83,7 @@ public static boolean isMatch2(String s, String p) {
80
83
return isValid (str , pattern ) && process2 (str , pattern , 0 , 0 , dp );
81
84
}
82
85
86
+ // 课堂现场写
83
87
// str[si.....] 能否被 pattern[pi...] 变出来
84
88
// 潜台词:pi位置,pattern[pi] != '*'
85
89
public static boolean process2 (char [] str , char [] pattern , int si , int pi , int [][] dp ) {
@@ -136,56 +140,102 @@ public static boolean process2(char[] str, char[] pattern, int si, int pi, int[]
136
140
return false ;
137
141
}
138
142
139
- public static boolean isMatch3 (String str , String exp ) {
140
- if (str == null || exp == null ) {
143
+ // 以下的代码是课后的优化
144
+ // 有代码逻辑精简的优化,还包含一个重要的斜率优化,请先理解课上的内容
145
+ public static boolean isMatch3 (String s , String p ) {
146
+ if (s == null || p == null ) {
147
+ return false ;
148
+ }
149
+ char [] str = s .toCharArray ();
150
+ char [] pattern = p .toCharArray ();
151
+ return isValid (str , pattern ) && process3 (str , pattern , 0 , 0 );
152
+ }
153
+
154
+ // 举例说明斜率优化:
155
+ // 求状态(si = 3, pi = 7)时,假设状况如下
156
+ // str : a a a b ...
157
+ // si : 3 4 5 6 ...
158
+ // pat : a * ? ...
159
+ // pi : 7 8 9 ...
160
+ // 状态(si = 3, pi = 7)的底层会依赖:
161
+ // 状态(si = 3, pi = 9)
162
+ // 状态(si = 4, pi = 9)
163
+ // 状态(si = 5, pi = 9)
164
+ // 状态(si = 6, pi = 9)
165
+ //
166
+ // 求状态(si = 2, pi = 7)时,假设状况如下
167
+ // str : a a a a b ...
168
+ // si : 2 3 4 5 6 ...
169
+ // pat : a * ? ...
170
+ // pi : 7 8 9 ...
171
+ // 状态(si = 2, pi = 7)的底层会依赖:
172
+ // 状态(si = 2, pi = 9)
173
+ // 状态(si = 3, pi = 9)
174
+ // 状态(si = 4, pi = 9)
175
+ // 状态(si = 5, pi = 9)
176
+ // 状态(si = 6, pi = 9)
177
+ //
178
+ // 注意看状态(si = 2, pi = 7)底层依赖的后4个,其实就是状态(si = 3, pi = 7)
179
+ // 所以,状态(si = 2, pi = 7)的底层依赖可以化简为:
180
+ // 状态(si = 2, pi = 9)、状态(si = 3, pi = 7)
181
+ // 这样枚举行为就被化简成了有限两个状态,详细情况看代码
182
+ public static boolean process3 (char [] str , char [] pattern , int si , int pi ) {
183
+ if (si == str .length && pi == pattern .length ) {
184
+ return true ;
185
+ }
186
+ if (si == str .length ) {
187
+ return (pi + 1 < pattern .length && pattern [pi + 1 ] == '*' ) && process3 (str , pattern , si , pi + 2 );
188
+ }
189
+ if (pi == pattern .length ) {
190
+ return false ;
191
+ }
192
+ if (pi + 1 >= pattern .length || pattern [pi + 1 ] != '*' ) {
193
+ return ((str [si ] == pattern [pi ]) || (pattern [pi ] == '.' )) && process3 (str , pattern , si + 1 , pi + 1 );
194
+ }
195
+ // 此处为斜率优化,含义看函数注释
196
+ if ((str [si ] == pattern [pi ] || pattern [pi ] == '.' ) && process3 (str , pattern , si + 1 , pi )) {
197
+ return true ;
198
+ }
199
+ return process3 (str , pattern , si , pi + 2 );
200
+ }
201
+
202
+ // 以下的代码是斜率优化后的尝试函数,改成动态规划的解
203
+ // 请先理解基础班中"暴力递归改动态规划"的内容
204
+ // 如果str长度为N,pattern长度为M,最终时间复杂度为O(N*M)
205
+ public static boolean isMatch4 (String str , String pattern ) {
206
+ if (str == null || pattern == null ) {
141
207
return false ;
142
208
}
143
209
char [] s = str .toCharArray ();
144
- char [] e = exp .toCharArray ();
145
- if (!isValid (s , e )) {
210
+ char [] p = pattern .toCharArray ();
211
+ if (!isValid (s , p )) {
146
212
return false ;
147
213
}
148
- boolean [][] dp = initDPMap (s , e );
149
- for (int i = s .length - 1 ; i > -1 ; i --) {
150
- for (int j = e .length - 2 ; j > -1 ; j --) {
151
- if (e [j + 1 ] != '*' ) {
152
- dp [i ][j ] = (s [i ] == e [j ] || e [j ] == '.' ) && dp [i + 1 ][j + 1 ];
214
+ int N = s .length ;
215
+ int M = p .length ;
216
+ boolean [][] dp = new boolean [N + 1 ][M + 1 ];
217
+ dp [N ][M ] = true ;
218
+ for (int j = M - 1 ; j >= 0 ; j --) {
219
+ dp [N ][j ] = (j + 1 < M && p [j + 1 ] == '*' ) && dp [N ][j + 2 ];
220
+ }
221
+ // dp[0..N-2][M-1]都等于false,只有dp[N-1][M-1]需要讨论
222
+ if (N > 0 && M > 0 ) {
223
+ dp [N - 1 ][M - 1 ] = (s [N - 1 ] == p [M - 1 ] || p [M - 1 ] == '.' );
224
+ }
225
+ for (int i = N - 1 ; i >= 0 ; i --) {
226
+ for (int j = M - 2 ; j >= 0 ; j --) {
227
+ if (p [j + 1 ] != '*' ) {
228
+ dp [i ][j ] = ((s [i ] == p [j ]) || (p [j ] == '.' )) && dp [i + 1 ][j + 1 ];
153
229
} else {
154
- int si = i ;
155
- while (si != s .length && (s [si ] == e [j ] || e [j ] == '.' )) {
156
- if (dp [si ][j + 2 ]) {
157
- dp [i ][j ] = true ;
158
- break ;
159
- }
160
- si ++;
161
- }
162
- if (dp [i ][j ] != true ) {
163
- dp [i ][j ] = dp [si ][j + 2 ];
230
+ if ((s [i ] == p [j ] || p [j ] == '.' ) && dp [i + 1 ][j ]) {
231
+ dp [i ][j ] = true ;
232
+ } else {
233
+ dp [i ][j ] = dp [i ][j + 2 ];
164
234
}
165
235
}
166
236
}
167
237
}
168
238
return dp [0 ][0 ];
169
239
}
170
240
171
- public static boolean [][] initDPMap (char [] s , char [] e ) {
172
- int slen = s .length ;
173
- int elen = e .length ;
174
- boolean [][] dp = new boolean [slen + 1 ][elen + 1 ];
175
- dp [slen ][elen ] = true ;
176
- for (int j = elen - 2 ; j > -1 ; j = j - 2 ) {
177
- if (e [j ] != '*' && e [j + 1 ] == '*' ) {
178
- dp [slen ][j ] = true ;
179
- } else {
180
- break ;
181
- }
182
- }
183
- if (slen > 0 && elen > 0 ) {
184
- if ((e [elen - 1 ] == '.' || s [slen - 1 ] == e [elen - 1 ])) {
185
- dp [slen - 1 ][elen - 1 ] = true ;
186
- }
187
- }
188
- return dp ;
189
- }
190
-
191
241
}
0 commit comments