【编程题 动态规划】HJ71 字符串通配符(详细注释 易懂)

题目描述:

 题目链接: 字符串通配符_牛客题霸_牛客网 (nowcoder.com)    

在计算机中,通配符一种特殊语法,广泛应用于文件搜索、数据库、正则表达式等领域。现要求各位实现字符串通配符的算法。
要求:
实现如下2个通配符:
*:匹配0个或以上的字符(注:能被*和?匹配的字符仅由英文字母和数字0到9组成,下同)
?:匹配1个字符

注意:匹配时不区分大小写。

输入:

通配符表达式;
一组字符串。

输出:

返回不区分大小写的匹配结果,匹配成功输出true,匹配失败输出false

数据范围:字符串长度:1\le s\le 100\1≤s≤100 

进阶:时间复杂度:O(n^2),空间复杂度:O(n)

输入描述:

先输入一个带有通配符的字符串,再输入一个需要匹配的字符串

输出描述:

返回不区分大小写的匹配结果,匹配成功输出true,匹配失败输出false

示例1

输入:

te?t*.*
txt12.xls

输出:

false

示例2

输入

**Z
0QZz

输出:

true

题目理解:

    先给一个字符串,里面含有 * 和 ?两种通配符,再给一个常规字符串,里面有各种字符,但都没有通配符的作用,让你给出两个字符是否相等的结论。

     题目比较容易理解,但是要做起来还是很麻烦的。我们通过眼睛去看,都要看一会儿才能得到是否相等的结论。那写成代码,让程序判断,确实有点强程(人)所难了。

   这个题的解题思路,和  最长公共子序列 是几乎一模一样的。

思路讲解:

     我们的思路是运用动态规划。 首先设一个布尔型的 二维数组,用它来记录两个字符串比较的结果, 设 有通配符的字符串长度为 m ,没有通配符的字符串长度为 n, 二维数组的长度设为[ m+1 ][ n+1 ] ,因为两个字符比较时,不能一相等就设为 true,那样的话会有两个问题,一个是,重复的字符会被多次计算,就比如 字符串 str0 中的 t 和 str1 中 tt ,就会被视为相等,导致错误结论; 还有一个问题是 如果前面的字符都不想等了,后面的字符即使相等,也不应该被赋值为 true 。所以我们给两个字符串前面添加一个空字符。

    可能这样空说着 有点抽象,我们看下面图来说,上面那一行是有通配符的字符串,左边那一列是没有通配符的常规字符串, 添加了空字符后,要给 二维数组(布尔型的)的[ 0 ][ 0 ]坐标赋值为true  ,因为空字符和空字符肯定相等。

        然后我们看一下,是如何比较的, 首先是左边那一列的 t 字符 和 通配符的每一个进行比较,如果相等就看看它们前面的两个字符是否相等, 它们前面都是空字符,所以 左边那一列(以下称为 str1)的 t字符 ,和 通配符字符串(以下称为 str0)的 t字符就是相等的 ,赋值为true 。

         然后str1的 t 继续与 str0 的* 比较,我们看到它们的前面一个字符 ,str1的是null ,str0 的是t,它俩的值是 false,按道理t 和 * 也是 false, 但是* 它是通配符,所以 我们就设定 ,如果 遇到* ,除了看 它们前面的位置,也就是 [ i-1 ][ j-1 ] ,还要看 [ i ][ j-1 ] ,以及 [ i-1 ][ j ] 这两个位置,如果这两个位置也都是false,那么才是false,我们发现  [ i ][ j-1 ] 处是 true,所以  [ i ][ j ] 处也是true ,后面比较,不一样的都是false 。

         然后是str1 的 l 字符串,它遇到 * 也是同理,所以结果也是 true。最后比较完所有的字符串,得到最终结果。   这里可能会有人有疑问,为什么只返回[ m][ n ]处的布尔值, 因为我们是用 str1的每个字符与str0 比较的, 中间有一个匹配不了,最后的结果就是两个字符不相等,所以 只有返回 [ m][ n ]处的 布尔值,才表示两个字符串是否匹配。

   
 

    这里解释一下  当通配符是 * 时,为什么要看 布尔数组中 [ i ][ j-1 ] ,以及 [ i-1 ][ j ] 位置的布尔值 ,举例说明 ,如果通配符字符串是 ab* ,常规字符串是 abcdd ,我们知道 c 和 * 是由于 前面的 b 和 b相等,所以它俩为 true ,但是d 和* 怎么得出正确结果,那我们就让它看 前面的 c ,如果 c和* 为true ,那么 d 和 * 也就能得到true 的结果,这个就是看 [ i -1 ][ j ] 位置的作用,它用于应对 * 可以匹配一个以上的多个字符的情况。      

      再举例说明一个,如果 通配符字符串是 ab* c,常规字符串是 abc,那这个时候 常规字符串的 字符 b 遇到通配符字符串的 * 怎么处理呢,还是看下图吧。 这个时候,就要看           [ i ][ j-1 ] 了,由于[ i ][ j-1 ] 是true ,所以b 和 * 结果也是 true ,那我们能发现,[ i ][ j-1 ] 的作用就是应对 当 * 匹配0个字符的情况。

       上面情况看完了,再看一下,下面的情况,这个通配符比较特殊,它的开头就是*,*是可以匹配 0个或以上的,它放在开头,理论上是可以直接匹配完 一整个常规字符串的,所以为了应对它这种特殊情况,我们设定,如果通配符的开头是*,或者连着是* ,就把对应的常规字符串的行首赋值为 true ,比如 下面,通配符字符串的第一个字符是 *, 就把常规字符串的 第一个字符 r 那里,即 [ i ][ 0 ] 设为true ,这样之后,常规字符串的每一个与* 匹配,都是true ,就符合题意了 。

  代码注释:

    

import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);
        String str0 = scan.nextLine().toLowerCase();
        String str1 = scan.nextLine().toLowerCase();
        int m = str0.length();
        int n = str1.length();
        // 设一个 布尔类型的二维数组
        boolean[][] flag = new boolean[m+1][n+1];
        flag[0][0]= true;
       // 用于应对 开头一个或连续多个 * 的情况
        for(int i=1;i< m;i++){
            if(str0.charAt(i-1)=='*'){
                flag[i][0] = true;
            }else{
                break;
            }
        }
        for(int i=1;i<= m;i++){
            for(int j=1;j<=n;j++){
            // 要注意 二维数组中的下标是比 字符串中字符的下标多1 的
                if(str0.charAt(i-1)== str1.charAt(j-1)){
                   flag[i][j] = flag[i-1][j-1];
                }
         // ?只能匹配英文字母,和数字
                if(str0.charAt(i-1) == '?' &&((str1.charAt(j-1)>='a'&& str1.charAt(j-1)<='z')
                    || (str1.charAt(j-1)>='1'&& str1.charAt(j-1)<='9'))){
                    flag[i][j] = flag[i-1][j-1];
                    
                }
                  // * 也只能匹配英文字母,和数字
                if(str0.charAt(i-1) == '*' &&((str1.charAt(j-1)>='a'&& str1.charAt(j-1)<='z')
                    || (str1.charAt(j-1)>='1'&& str1.charAt(j-1)<='9'))){
                    flag[i][j] = flag[i-1][j-1] || flag[i-1][j] || flag[i][j-1];
                    
                }
                
            }
        }
        System.out.println(flag[m][n]);
    }
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值