```
public boolean isMatch(String s, String p) {
int m = s.length();
int n = p.length();
if (s == null || p == null) {
return false;
}
boolean[][] OPT = new boolean[m+1][n+1];
OPT[0][0] = true;
for (int i = 1; i <= m; i++) {
OPT[i][0] = false;
}
for (int j = 1; j <= n; j++) {
OPT[0][j] = (p.charAt(j-1) == '*') && (j-2 >= 0) && OPT[0][j-2];
}
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
OPT[i][j] = ((OPT[i-1][j-1]) && equals(s, p, i-1, j-1))
|| ((OPT[i-1][j] || OPT[i][j-1])
&& (p.charAt(j-1) == '*')
&& equals(s, p, i-1, j-2))
|| ((p.charAt(j-1) == '*') && (j-2 >= 0) && OPT[i][j-2]);
}
}
return OPT[m][n];
}
private boolean equals(String s, String p, int si, int pi) {
return (s.charAt(si) == p.charAt(pi) || p.charAt(pi) == '.');
}
```

Basically, the OPT[i][j] means preceding substring of length i of s and length j of p. For any two substrings, the value of OPT[i][j] can be from one of following four cases:

- case 1: OPT[i-1][j-1] is true, and ith character of s is equal to j th character of p. Or j th character of p is '.'
- case 2: OPT[i-1][j] is true, then my pattern now is '*' and preceding character is equal to incoming character of s
- case 3: OPT[i][j-1] is true, then my pattern now is '*' which can match an empty string
- case 4: OPT[i][j-2] is true, and the pattern like (a*) matches an empty string

base case is the OPT[0][0], OPT[i][0], OPT[0][j].