import java.util.*; /** * 动态规划 * 思路:我们先不考虑 * 的存在。假如没有 * 的话,我们判断两个字符串是否匹配就非常简单, * 即 s 和 t 是否匹配当且仅当 s 的每一个字符和 t 对应位置字符是否可以匹配即可,之后 * 再考虑加入 * 的情况,因为 * 的存在,导致其可以有不确定的情况,即匹配 0 || 1 || 多 * 个字符,因此这就导致我们需要进行动态规划了。 * 我们定义 dp[i][j] 为 s 的前 i 个字符和 t 的前 j 个字符是否可以匹配, * 其中状态转移方程为 * 当 t 的第 j 个字符为 * 时:dp[i][j] = dp[i][j-1] || dp[i-1][j] || dp[i-1][j-1] * 0 个 多个 1 个 * 当 t 的第 j 个字符不为 * ,且这两个对应字符可以匹配时:dp[i][j] = dp[i-1][j-1] * 原始初始化dp[0][0] = true , 其余均为 false * 根据错误改进后的初始化:初始化dp[0][0] = true, * 且若当 t 的第 j 个字符为 * 时 dp[0][j] = dp[0][j-1],其余均为 false * 最终返回dp[sLen][tLen]即可 * * 程序错误:(需要加入初始化中) * 可是这样子写出来的程序有一种情况无法处理,就是当 t 字符串开头为 * 时, * 比如 t:*h s:h 按理来说是可以匹配的,但是当我们判断到 s 的第1个字符 * 和 s 的2个字符相等的时候,我们执行的是 dp[i][j] = dp[i-1][j-1], * 而 dp[0][1] = false ,导致最终结果为 false,及我们需要先对dp的 * 第 0 行处理一下。 * 这个处理的实际含义就是当 t 中第一个为 * ,或者从开始一直为 * 的时候, * 尽管 s 没有字符,也是可以匹配的,因为 * 可以匹配 0 个字符。 */ public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); //匹配时不区分大小写,全转化为小写,方便处理 String t = scanner.nextLine().toLowerCase(); String s = scanner.nextLine().toLowerCase(); int tLen = t.length(); int sLen = s.length(); boolean[][] dp = new boolean[sLen + 1][tLen + 1]; //初始化 dp[0][0] = true; //初始化第一行 for (int i = 1; i < tLen + 1; i++) { if (t.charAt(i - 1) == '*') { dp[0][i] = dp[0][i - 1]; } } //状态转移 for (int i = 1; i < sLen + 1; i++) { for (int j = 1; j < tLen + 1; j++) { if (t.charAt(j - 1) == '*') { //可以匹配 if (canMatch(s.charAt(i - 1))) { dp[i][j] = dp[i - 1][j] || dp[i][j - 1] || dp[i - 1][j - 1]; } //不可以匹配就为false } else { if (t.charAt(j - 1) == '?' && canMatch(s.charAt(i - 1))) { dp[i][j] = dp[i - 1][j - 1]; } else if (s.charAt(i - 1) == t.charAt(j - 1)) { dp[i][j] = dp[i - 1][j - 1]; } } } } System.out.println(dp[sLen][tLen]); } private static Boolean canMatch(char ch) { return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z'); } }