题目描述:
题目链接: 字符串通配符_牛客题霸_牛客网 (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]);
}
}