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

字符串通配符

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] 】。

全部评论

相关推荐

一共一个小时,面试难度以及自己的回答算是最近的面试压力比较大的,实习问了30分钟,中间穿插八股。1.redis数据结构2.redis持久化机制3.mysql索引底层4.聚簇索引与非聚簇索引5.索引优化6.索引失效7.mysql执行一条sql8.那么多索引mysql怎么选(不会)9.tcp与udp区别10.tcp为什么可靠11.消息队列作用12.kafka怎么保证消息有序性13.mcp是什么?14.skills是什么?15.jvm内存分配与回收过程(我讲了从创建对象到判断垃圾对象到垃圾回收我全说了一遍,是这个吗?)16.fullgc触发机制17.tcp的拥塞控制流程(不会了)18.分布式事务解决方案,说了2pc,3pc,tcc。算法是反转双向链表,没有按格式输出,但是面试官没让继续写了,面完以为挂了,结果晚上秒过,看看复试什么情况吧。今天百度打电话准备发offer了,业务跟在手子的差不多,很垂,并且说不分日常暑期,只看表现,会有转正机会,但是考虑再三还是拒绝了,百度实习薪资确实有点低,title也不如之前了,但是面试的二位业务老师我很喜欢,对我的评价也不错,希望之后能有机会共事。从三月份到现在一共面了六家,面试次数总共是8场,情况如下:脉脉二面(无答复,默认挂)百度二面已oc美团一面过,下周一二面shein一面过直接HR面游族一面过直接HR面腾讯一面过等待约二面滴滴明天一面面试通过率还是蛮高的,但是大部分都是日常,感觉对我现在的加成不大,大概率不会去,不知道暑期会是什么情况呢唉,希望能有面试吧,继续加油。字节被无hc直接取消了,现在还没人捞,有没有字节HR救救我
不管什么都不想跳动了:本人美团百度快手都待过,建议肯定是直接留快手多一点产出后转正or直接冲字节腾讯暑期吧。一是快手从福利到基建都吊打另外两家。美团现在这个业务比较惨,本来毛利就很低,亏损严重,今年很可能要优化人力降低成本,去了别说日常,就算暑期后面都很可能被优化。百度其实实习生权限挺高的,可以接触到一些含金量高的项目,但是现在的风评不如之前了,薪资也不高。二是转正概率和薪资是跟产出挂钩的,你都在手子已经积累产出了,去其他家日常实习产出都是从0开始,肯定不可能有你在手子转正可能性大啊,现在日常压根没必要去,而且我有两个师弟都是在快手日常转正的,不用太担心,安心留在手子一边多做一点产出然后一边冲字节腾讯暑期,字节腾讯今年实习岗位非常多的,不如好好把握这个,加油。
查看18道真题和解析
点赞 评论 收藏
分享
03-16 11:07
南开大学 Java
牛马人的牛马人生:快手卡实习经历的
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务