解题思路:

  • 方案有三种
  • 正则表达式直接判断
  • 递归比较
  • 动态规划
  • 递归和动态规划的思路是相似的:
  • 通配符 * 和 ?
  • 都只能匹配到 数字或者字符 其他不可以匹配
  • ?只能匹配一位
  • * 号 可以匹配多位
  • 通配符对比的字符串中出现*,和被比较字符串去比较的情况: 有三种比较方式
  • 一对一匹配,则跳到下一个
  • 一对多的匹配, 则 *不变,原字符串 后移一位;
  • 一对零的情况,则*后移一位,原字符串指针不变。
  • 动态规划思想中:
  • dp数组的含义:
  • 原字符串的前i-1位和 通配符字符串 j-1对比,是否匹配。 如果当前匹配,则根据情况:
  • 如果是 ? 或者 相等,取上一位的值;
  • 如果是 * 号,则取 一下三种的或值。
  • 不包含i :即包含j不包含i的比较,一堆N匹配成功的情况
  • 不包含j :包含i 不包含j的比较, 一对零匹配成功的情况
  • i j都不包含情况的:一对一匹配成功的情况

import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        while (in.hasNext()) { // 注意 while 处理多个 case
            String str1 = in.next();
            String str2 = in.next();

            // 考点:递归
            // 8-12 首攻失败
            // 8-20 完成

            // 不区分大小写
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < str1.length(); i++) {
                if (Character.isUpperCase(str1.charAt(i))) {
                    sb.append(Character.toLowerCase(str1.charAt(i)));
                } else sb.append(str1.charAt(i));
            }
            StringBuilder sb2 = new StringBuilder();
            for (int i = 0; i < str2.length(); i++) {
                if (Character.isUpperCase(str2.charAt(i))) {
                    sb2.append(Character.toLowerCase(str2.charAt(i)));
                } else sb2.append(str2.charAt(i));
            }

            System.out.println(isMatch(sb2.toString(), sb.toString()));
        }

    }


    // 动态规划
    public static boolean isMatch(String s, String p) {
        int m = s.length();
        int n = p.length();
        boolean[][] dp = new boolean[m + 1][n + 1];
        dp[0][0] = true;
        for (int i = 1; i <= n; i++) {
            if (p.charAt(i - 1) == '*') {
                dp[0][i] = true;
            } else {
                break;
            }
        }

        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                char ch = s.charAt(i - 1);
                // 必须是字符或者数字
                boolean flag = Character.isLetter(ch) || Character.isDigit(ch);
                if (p.charAt(j - 1) == '*' && flag) {
                    // *时,三种情况,两个都跳过,或者跳过其中一个
                    dp[i][j] = dp[i][j - 1] || dp[i - 1][j] || dp[i - 1][j - 1];
                } else if ((p.charAt(j - 1) == '?' && flag) || s.charAt(i - 1) == p.charAt(j - 1)) {
                    // 等于问号 或者相等  true
                    dp[i][j] = dp[i - 1][j - 1];
                }
            }
        }

        return dp[m][n];
    }


    // 双指针 + 递归,完成俩个字符串的对比 (最后一个用例会超时)
    private static boolean helper(String s1, String s2, int p1, int p2) {
        // 终止条件
        // 指针都到两个字符串的末尾+1
        if (p1 == s1.length() && p2 == s2.length()) {
            return true;
        } else if (p1 == s1.length() || p2 == s2.length()) {
            // 其中一个已经超过末尾
            return false;
        }

        //  能被*和?匹配的字符仅由英文字母和数字0到9组成
        if ((s1.charAt(p1) == '*' || s1.charAt(p1) == '?') &&
                !Character.isLetterOrDigit(s2.charAt(p2))) return false;

        //遇到'*',各跳过一个比较后面,或者 s2继续往后跳先不比较 或者 s1往后走与当前的s2对比
        if (s1.charAt(p1) == '*') {
            return helper(s1, s2, p1, p2 + 1) || helper(s1, s2, p1 + 1, p2 + 1) ||
                   helper(s1, s2, p1 + 1, p2);
            //遇到'?'跟两个一样操作一样,直接指针都往后移一个继续比较
        } else if (s1.charAt(p1) == '?' || s1.charAt(p1) == s2.charAt(p2)) {
            return helper(s1, s2, p1 + 1, p2 + 1);
        } else {
            return false;
        }
    }

}