Key ideas here:

Backtracking, optimized with dynamic programming.

Iterate through the simple tokens to minimize recursive calls.
public class Solution {
char[] s, p;
int sLength, pLength, noWildcard;
Boolean[][] dp;
public boolean isMatch(String ss, String ps) {
s = ss.toCharArray();
p = ps.toCharArray();
sLength = s.length;
pLength = p.length;
noWildcard = pLength  1;
dp = new Boolean[pLength + 1][sLength + 1];
return isMatch(0, 0);
}
boolean isMatch(int pi, int si){
if(dp[pi][si] != null) return dp[pi][si];
while(true){
if(pi < noWildcard && p[pi+1] == '*'){
char c = p[pi];
pi += 2;
while(true){
boolean isMatch = isMatch(pi, si);
dp[pi][si] = isMatch;
if(isMatch){
return true;
}
if(si == sLength) return pi == pLength;
if(c != '.' && c != s[si]){
return false;
}
si++;
}
}
if(pi == pLength  si == sLength) return pi == pLength && si == sLength;
if(p[pi] != '.' && p[pi] != s[si]){
return false;
}
pi++;
si++;
}
}
}