解题思路:
- 方案有三种
- 正则表达式直接判断
- 递归比较
- 动态规划
- 递归和动态规划的思路是相似的:
- 通配符 * 和 ?
- 都只能匹配到 数字或者字符 其他不可以匹配
- ?只能匹配一位
- * 号 可以匹配多位
- 通配符对比的字符串中出现*,和被比较字符串去比较的情况: 有三种比较方式
- 一对一匹配,则跳到下一个
- 一对多的匹配, 则 *不变,原字符串 后移一位;
- 一对零的情况,则*后移一位,原字符串指针不变。
- 动态规划思想中:
- 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;
}
}
}