题解 | #字符串通配符#

字符串通配符

https://www.nowcoder.com/practice/43072d50a6eb44d2a6c816a283b02036

HJ71 字符串通配符--dp求解

解题步骤:
  1. 先将目标串s中的大写字母转化为小写字母
  2. 定义动态规划方程意义,dp[i][j]表示表示p(包含*?的模式串)的前i个字符是否能与s的前j个字符匹配
  • 定义动态规划方程的边界
            空字符串与空字符串一定匹配,所以 dp[0][0] = true
            当p的起始字符(可能连续多个)为*时,由于*表示可以匹配任意字符任意多个字符空字符, 可以将其看作空字符,那么dp[i][0] = true, 其中i=[1, p.length()]
  • 找出动态规划方程递推公式
            存在三种情况(实际上只需要处理前两种即可,不处理的情况下的dp[i][j]表示不匹配):
            1. p的第i个字符与s的第j个字符相同,或p的第i个字符为'?', '?'可以表示任意一个字符。 即p[i] == s[j] || p[i] == '?', 此时dp[i][j]的状态取决于他们前面的字符串是否匹配(因为当前已经匹配了),即dp[i][j] = dp[i-1][j-1]
            2. p的第i个字符为'*', 由于*表示可以匹配任意字符任意多个字符空字符, 可以将其分为两种情况,使用这个'*'字符或不使用这个’*‘字符,当不使用’*‘字符时,dp[i][j] = dp[i-1][j], 即将p的第i个字符当成空字符。
                当使用'*'字符时,只要p[i]与s的前j-1个字符中任意一个匹配,即可表示匹配(这表示的是s匹配的字符之后未匹配的字符用'*'来代替)。
import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        while (in.hasNextLine()) { // 注意 while 处理多个 case
            String p = in.nextLine();
            String s = in.nextLine();
            // 检验s中是否存在除字母和数字以外的字符
            char[] arr_s = s.toCharArray();
            for(int i=0;i<arr_s.length;i++){
                char c = arr_s[i];
                if(Character.isUpperCase(c)) arr_s[i] = Character.toLowerCase(c);
            }
            s = String.valueOf(arr_s);
            System.out.println(isMatch(p, s));
        }
    }
    
    public static boolean isMatch(String p, String s){
        int m = p.length(), n = s.length();
        // dp[i][j]表示p的前i个字符是否能与s的前j个字符匹配
        boolean[][] dp = new boolean[m+1][n+1];
        // 初始化边界
        dp[0][0] = true; // 空字符串可以匹配
        for(int i=1;i<=m;i++){
            if(p.charAt(i-1) == '*'){  // *可以匹配0个任意字符,当p的前面连续为*时,可以表示为不存在
                dp[i][0] = true;
            }else{
                break;
            }
        }
        for(int i=1;i<=m;i++){
            char pc = p.charAt(i-1);
            if(Character.isUpperCase(pc)) pc = Character.toLowerCase(pc);
            for(int j=1;j<=n;j++){
                char sc = s.charAt(j-1);
                if(pc == sc || (pc == '?' && (Character.isDigit(sc) || Character.isLetter(sc)))){ //当前字符与s中第j个字符相同
                    dp[i][j] = dp[i-1][j-1];
                }else if(pc == '*'){
                    // 不使用'*',或 使用‘*’
                    // dp[i][j-1]是由 dp[i][0] || dp[i][1] ||... || dp[i][j-1]而来的, 而dp[i][j-2] 又是由dp[i][0] || ... ||dp[i][j-2]而来的, 所以此处直接可以由dp[i][j-1]递推而来
                    /*  该循环与下面的dp[i][j] = dp[i-1][j] || dp[i][j-1]是等价的,只不过使用该循环易于理解
                    for(int k=0;k<j;k++){
                        dp[i][j] = dp[i-1][j] || dp[i][k];
                    }*/
                    dp[i][j] = dp[i-1][j] || dp[i][j-1];
                }else{
                    dp[i][j] = false;
                }
            }
        }
        return dp[m][n];
    }
    
}


全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务