基础算法(Leetcode)刻意练习第三十天——贪心算法

引言

由 “LSGO软件技术团队” 组织的 “基础算法刻意练习” 采用分类别练习的模式,即选择了五个知识点(数组、链表、字符串、树、贪心算法),每个知识点选择了 三个简单、两个中等、一个困难 等级的题目,共计三十道题,利用三十天的时间完成这组刻意练习。以下是我的每日打卡记录:


Task30.通配符匹配

  • Leetcode第44题

  • 难度:困难

  • 题目概述:

      给定一个字符串 s 和一个字符模式 p ,实现一个支持 '?' 和 '*' 的通
      配符匹配。
      
      '?' 可以匹配任何单个字符。
      '*' 可以匹配任意字符串(包括空字符串)。
      两个字符串完全匹配才算匹配成功。
    
      说明:
      s 可能为空,且只包含从 a-z 的小写字母。
      p 可能为空,且只包含从 a-z 的小写字母,以及字符 ? 和 *。
      
      示例 1:
      输入:
      s = "aa"
      p = "a"
      输出: false
      解释: "a" 无法匹配 "aa" 整个字符串。
      
      示例 2:
      输入:
      s = "aa"
      p = "*"
      输出: true
      解释: '*' 可以匹配任意字符串。
      
      示例 3:
      输入:
      s = "cb"
      p = "?a"
      输出: false
      解释: '?' 可以匹配 'c', 但第二个 'a' 无法匹配 'b'。
      
      示例 4:
      输入:
      s = "adceb"
      p = "*a*b"
      输出: true
      解释: 第一个 '*' 可以匹配空字符串, 第二个'*'可以匹配字符串"dce"
      
      示例 5:
      输入:
      s = "acdcb"
      p = "a*c?b"
      输入: false
    

题解思路


  • 1.超时的递归

看到这道题,第一反应就是用递归,大笔一挥,代码出来了,顺便也超时了。因为输入的字符串很长的时候,递归深度太大,就会引起超时。

class Solution {
    public boolean isMatch(String s, String p) {
        if(s.isEmpty())
            return p.matches("\\**"); 
        else if(p.isEmpty())
            return false;
        char s0=s.charAt(0);
        char p0=p.charAt(0);
        boolean first= p0=='*' || p0=='?' ||s0==p0;
        if(p0=='*')
            return isMatch(s,p.substring(1)) || //*代表零个字符
                   isMatch(s.substring(1),p.substring(1)) || //*代表一个字符
                   isMatch(s.substring(1),p); //*代表多个字符
        else
            return first && isMatch(s.substring(1),p.substring(1));
    }
}

提交记录

超时啦!


  • 2.回溯(参考题解)

整个问题中让人头疼的其实只是 *号的匹配个数问题,那么我们可以依次“尝试”,对于 *号(如果是连续的 *****号,只取最后一个进行假设),先假设它匹配 0 个字符,匹配剩下的 pattern,如果错误,回到假设的 *号处,假设它匹配 1 个字符,以此类推,直到 s 匹配完成。

时间复杂度:O( s*log( p ) )

class Solution {
        public boolean isMatch(String s, String p) {
        int pLen = p.length();
        int pS=0, pP=0;
        int pStar = -1, pStr = -1;
        while (pS<s.length()) {
            if (pP < pLen && (p.charAt(pP) == '?' || p.charAt(pP) == s.charAt(pS))){
                ++pS;
                ++pP;
            }
            //如果有*,则记录可回溯的位置并让它代表零个字符
            else if (pP < pLen && p.charAt(pP) == '*') { 
                pStar = pP;
                pStr = pS;
                ++pP;
            }
            //不匹配且没有可回溯的*,返回false
            else if (pStar==-1) 
                return false;
            //不匹配但是具有可回溯的*,则让*匹配更多的字符
            else { 
                pP = pStar + 1;
                pS = pStr + 1;
                pStr = pS;
            }
        }
        //偷懒使用了正则表达式,速度会变慢
        return p.substring(pP).matches("\\**"); 
    }
}

提交记录

在这里插入图片描述


  • 3.动态规划

说实话,接触了这么多动态规划的题目,虽然我看得出来这道题可以用 “动态规划” 解决,自己上手的时候就一头雾水,所以就不丢人现眼了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
KMP算法是一种字符串匹配算法,用于在一个文本串S内查找一个模式串P的出现位置。它的时间复杂度为O(n+m),其中n为文本串的长度,m为模式串的长度。 KMP算法的核心思想是利用已知信息来避免不必要的字符比较。具体来说,它维护一个next数组,其中next[i]表示当第i个字符匹配失败时,下一次匹配应该从模式串的第next[i]个字符开始。 我们可以通过一个简单的例子来理解KMP算法的思想。假设文本串为S="ababababca",模式串为P="abababca",我们想要在S中查找P的出现位置。 首先,我们可以将P的每个前缀和后缀进行比较,得到next数组: | i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | --- | - | - | - | - | - | - | - | - | | P | a | b | a | b | a | b | c | a | | next| 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 | 接下来,我们从S的第一个字符开始匹配P。当S的第七个字符和P的第七个字符匹配失败时,我们可以利用next[6]=4,将P向右移动4个字符,使得P的第五个字符与S的第七个字符对齐。此时,我们可以发现P的前五个字符和S的前五个字符已经匹配成功了。因此,我们可以继续从S的第六个字符开始匹配P。 当S的第十个字符和P的第八个字符匹配失败时,我们可以利用next[7]=1,将P向右移动一个字符,使得P的第一个字符和S的第十个字符对齐。此时,我们可以发现P的前一个字符和S的第十个字符已经匹配成功了。因此,我们可以继续从S的第十一个字符开始匹配P。 最终,我们可以发现P出现在S的第二个位置。 下面是KMP算法的C++代码实现:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值