题解 | #字符串通配符#动态规划

字符串通配符

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

一、正则匹配

我一开始想到的是使用正则匹配的方式去做。

* 按题意用正则表示则是[0-9a-z]*。

?按题意用正则表示则是[0-9a-z]。

另外特殊字符.,需要前面加上\\进行转移,这样把匹配串转化成了一个正则匹配串,就可以之间用String的方法直接获取结果。

import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        while (in.hasNextLine()) {
            String pattern = in.nextLine().toLowerCase();
            String s = in.nextLine().toLowerCase();

            char[] chars = pattern.toCharArray();
		  //sb就是正则匹配字符串
            StringBuilder sb = new StringBuilder();
		  //这个用来合并多个连续的*
             char lastc = ' ';
            for (int i = 0; i < chars.length;i++) {
                char c = chars[i];
                if (i != 0){
                    lastc = chars[i-1];
                }
                if (c == '*' ){
                    if (lastc != '*'){
                        sb.append("[0-9a-z]*");
                    }
                    continue;
                }
                if (c == '?'){
                    
                        sb.append("[0-9a-z]");
                
                   
                    continue;
                }
                if (c == '.'){
                    sb.append("\\.");
                    continue;
                }
                sb.append(c);
            }

            System.out.println(s.matches(sb.toString()));

        }
    }
}

二、动态规划

这个可以看看我之前两串最大子串的那个题解https://www.nowcoder.com/feed/main/detail/f952886975314dea8eafa66de153caa5

这题类似,首先定义dp[i][j],表示待匹配的字符串S的前i个字符组成的子串,和匹配串P的前j个字符组成的子串,之间的匹配状态。用true表示匹配成功。

由简至繁,i的数值是从1到S的长度值,j的数值范围是1到P的长度值,我们需要找出从1->length变化的规律。因为是判断S满不满足P的形式,我们先确定S,再动P。那么对每个场景的遍历方式为:

for (int i = 1; i <= s.length ; i++) {
  for (int j = 1; j <= p.length; j++) {
	//这里就是处理:S的前i个字符,和P的前j个字符的匹配结果
  }
}

循环体中的每个场景下,p的第j个字符p[j-1] (注意索引是0开始,所以要-1)都会遇到什么情况呢?就三种。

1、为通配符*

这个情况下,一是不匹配任何字符,直接把当前p的第j个字符忽略掉,那么dp[i][j]的值就取决于dp[i][j-1];

二是匹配字符,那么就需要把匹配的S的第i个字符忽略掉,dp[i][j]的值就取决于dp[i-1][j],但这需要有一个前提,就是S的第i个字符是字母或数字。

所以:dp[i][j] = dp[i][j-1] || ( s[i-1] = 字母或数字 && dp[i-1][j] )

2、为通配符?

当P第j个字符是?,那S想要匹配就必须是字母或数字,然后匹配上了,还要求S的前i-1个字符与P的前j-1个字符需要匹配。

所以: dp[i][j] = s[i-1] = 字母或数字 && dp[i-1][j]

3、为非通配符

这个是最好处理的,直接比较,但是需要注意忽略大小写,这个需要注意,我是先转成了小写统一比较

所以:dp[i][j] = (s[i-1] == p[j-1]) && dp[i-1][j-1]

for (int i = 1; i <= s.length ; i++) {
	for (int j = 1; j <= p.length; j++) {
		if (p[j - 1] == '*'  ) {
			//当匹配字符为*时,那么要不匹配:dp[i][j] = dp[i][j - 1],
			//或者匹配字符,但是前字符必须是字母或数字才可以
			dp[i][j] = dp[i][j - 1] || 
			  ( (Character.isLetter(s[i - 1]) || Character.isDigit(s[i - 1])) && dp[i - 1][j]) ;
		} else if (p[j - 1] == '?' ) {
			//如果是?,那么匹配的字符首先要是字母或数字,然后出去匹配的字符,前面的字符要能匹配
			dp[i][j] =  (Character.isLetter(s[i - 1]) || 
						 Character.isDigit(s[i - 1])) && dp[i - 1][j - 1] ;
		} else {
			//如果是其它字符,那忽略大小写,需要相等才能匹配成功
			dp[i][j] = Character.toLowerCase(s[i - 1]) == Character.toLowerCase(p[j - 1]) 
			  && dp[i - 1][j - 1];
		}
	}
}

这里有一个可能会有问题的就是:当p的第j个字符为*时,它转移方程是:

dp[i][j] = dp[i][j-1] || ( s[i-1] = 字母或数字 && dp[i-1][j] )

假设没有字母或数字的要求,*可以匹配任意一个字符,那么可以匹配0个,1个,2个。。。。。一直到匹配k个字符,可以为1到S长度直接的任意值。它的转移方程应当是:

dp[i][j] = dp[i][j-1] || dp[i-1][j] || dp[i-2][j] ....................dp[i-k][j]

这样子好像更符和我们思考的方式,匹配多个,但是用的动态规划,其实后面那个dp[i-k][j]是处理过的。就上面那个公式后面一部分【dp[i-1][j] || dp[i-2][j] ....................dp[i-k][j] 】可以直接表示为dp[i-1][j],因为我们在算dp[i-1][j]的时候,按上面的公式会算上【dp[i-1][j] || dp[i-2][j] ....................dp[i-k][j] 】。

全部评论

相关推荐

10-28 14:42
门头沟学院 Java
watermelon1124:因为嵌入式炸了
点赞 评论 收藏
分享
牛客868257804号:九个中铁八个中建
点赞 评论 收藏
分享
有没有经济学家能告诉我,三年后中国的就业市场会不会好转?我在校招中拿到了一份9k+的offer,还是行业的龙头企业,心里其实不想再考研了。但又总是担心,万一读研后薪资更高,我会不会后悔呢?
Fyhyuky:三年后肯定不会啊,只会比现在更烂,你自己看看现在有没有什么增长点,电车都是国家补贴兜底才发展出来的,已经比较违背市场自然规律了,互联网更不用说了,国家强力打压,传统制造业转型失败,现在苟延残喘中
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务