【LeetCode:(每日一题1023. 驼峰式匹配) -> 剑指 Offer II 097. 子序列的数目 | 暴力递归=>记忆化搜索=>动态规划】

在这里插入图片描述

🍎作者简介:硕风和炜,CSDN-Java领域新星创作者🏆,保研|国家奖学金|高中学习JAVA|大学完善JAVA开发技术栈|面试刷题|面经八股文|经验分享|好用的网站工具分享💎💎💎
🍎座右铭:人生如棋,我愿为卒,行动虽慢,可谁曾见我后退一步?🎯🎯🎯

在这里插入图片描述

题目链接

题目描述

给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。

字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是)

题目数据保证答案符合 32 位带符号整数范围。

示例 1:

输入:s = “rabbbit”, t = “rabbit”
输出:3
解释:
如下图所示, 有 3 种可以从 s 中得到 “rabbit” 的方案。
rabbbit
rabbbit
rabbbit
示例 2:

输入:s = “babgbag”, t = “bag”
输出:5
解释:
如下图所示, 有 5 种可以从 s 中得到 “bag” 的方案。
babgbag
babgbag
babgbag
babgbag
babgbag

提示:

0 <= s.length, t.length <= 1000
s 和 t 由英文字母组成

求解思路&实现代码&运行结果


暴力递归

求解思路
  1. 接下来我给大家分析一下我对整个题目的思考过程:
  2. 首先,我想到的解决方案是从字符串s的0位置开始,到最后一个位置结束,找到s的子序列在t出现的个数。那么这个过程开上去好像没有什么问题,但是仔细一研究就会发现整个过程和t脱离了联系,而且无从下手,所以我们直接pass掉。
  3. 其次想到的解决方案就是我们需要去比较此时s的某一个位置和t的某一个位置是否相等,所以我就想到了设置俩个指针,一个指向s的位置,一个指向t的位置,如果此时这俩个位置相同,有以下俩种的方案,1.我们可以继续向后进行判断,俩个指针同时向后移动,继续判断; 2.指向s的指针向后移动,可以和原来指向t的指针继续判断;然后就是如果此时这俩个位置是不相等的,那么和我们上面2的操作是一样的,继续判断即可。
实现代码
class Solution {
    public int numDistinct(String s, String t) {
        int m=s.length(),n=t.length();
        char[] arr1=s.toCharArray();
        char[] arr2=t.toCharArray();
        return process(0,arr1,0,arr2);
    }

    public int process(int index1,char[] arr1,int index2,char[] arr2){
        if(arr2.length-index2>arr1.length-index1) return 0;
        
        if(index2==arr2.length){
            return 1;
        }
        if(index1==arr1.length) return 0;
        if(arr1[index1]==arr2[index2]){
            return process(index1+1,arr1,index2+1,arr2)+process(index1+1,arr1,index2,arr2);
        }else{
            return process(index1+1,arr1,index2,arr2);
        }
    }
}
运行结果

在这里插入图片描述


记忆化搜索

求解思路
  1. 根据我们递归的分析,在递归的过程中会产生重复的子过程,所以我们想到了加一个缓存表,也就是我们的记忆化搜索。
实现代码
class Solution {
    
    public int numDistinct(String s, String t) {
        int m=s.length(),n=t.length();
        int[][] dp=new int[m][n];
        char[] arr1=s.toCharArray();
        char[] arr2=t.toCharArray();
        for(int i=0;i<m;i++) Arrays.fill(dp[i],-1);
        return process(0,arr1,0,arr2,dp);
    }

    public int process(int index1,char[] arr1,int index2,char[] arr2,int[][] dp){
        if(arr2.length-index2>arr1.length-index1) return 0;
        if(index2==arr2.length){
            return 1;
        }
        if(index1==arr1.length) return 0;
        if(dp[index1][index2]!=-1) return dp[index1][index2];
        if(arr1[index1]==arr2[index2]){
            return dp[index1][index2]=process(index1+1,arr1,index2+1,arr2,dp)+process(index1+1,arr1,index2,arr2,dp);
        }else{
            return dp[index1][index2]=process(index1+1,arr1,index2,arr2,dp);
        }
    }
}
运行结果

在这里插入图片描述


动态规划

求解思路
  1. 接下来我们根据之前的递归思路以及记忆化缓存改写动态规划。
实现代码
class Solution {
    
    public int numDistinct(String s, String t) {
        int m=s.length(),n=t.length();
        int[][] dp=new int[m+1][n+1];
        char[] arr1=s.toCharArray();
        char[] arr2=t.toCharArray();
        for(int i=0;i<=m;i++) dp[i][n]=1;
        for(int index1=m-1;index1>=0;index1--){
            for(int index2=n-1;index2>=0;index2--){
                if(arr1[index1]==arr2[index2]){
                    dp[index1][index2]=dp[index1+1][index2+1]+dp[index1+1][index2];
                }else{
                    dp[index1][index2]=dp[index1+1][index2];
                }
            }
        }
        return dp[0][0];
    }
}
运行结果

在这里插入图片描述


课后练习(LeetCode每日一题)

题目链接

题目描述

如果我们可以将小写字母插入模式串 pattern 得到待查询项 query,那么待查询项与给定模式串匹配。(我们可以在任何位置插入每个字符,也可以插入 0 个字符。)

给定待查询列表 queries,和模式串 pattern,返回由布尔值组成的答案列表 answer。只有在待查项 queries[i] 与模式串 pattern 匹配时, answer[i] 才为 true,否则为 false。

求解思路

  1. 思路1:和我们上面题目的思路一样(展示)。
  2. 思路2:双指针(自行实现)。

实现代码

class Solution {
    public List<Boolean> camelMatch(String[] queries, String pattern) {
        List<Boolean> ans=new ArrayList<>();
        for(int i=0;i<queries.length;i++){
            ans.add(process(0,queries[i],0,pattern));
        }
        return ans;
    }

    public boolean process(int i,String queries,int j,String pattern){
        // 如果此时j指针到达了pattern字符串的结尾位置
        if(j==pattern.length()){
            // 我们判断一下queries字符串中是否还有大写字母 如果有的话,直接返回false
            while(i<queries.length()){
                if(Character.isUpperCase(queries.charAt(i))) return false;
                i++;
            }
            // 如果遍历完成后,没有返回false 那就是true了
            return true;
        }
        // 如果j指针没有达到结束位置,但是此时i指针到达结束位置了,直接返回false
        if(i==queries.length()) return false;
        // 如果此时queries的某个位置的字符是大写的话
        if(Character.isUpperCase(queries.charAt(i))){
            // 我们需要判断此时俩个字符串是否匹配,如果匹配 直接递归下一个位置 否则 直接false
            if(queries.charAt(i)==pattern.charAt(j)) return process(i+1,queries,j+1,pattern);
            else return false;
        }else{
            // 如果当前queries某个位置是小写的话,那么我们继续判断
            // 此时字符串中俩个位置的值是否相等,如果相等,我们可以有多种选择
            // 1.i和j指针同时移动到下一个位置 2.queries的i指针继续向后移动
            if(queries.charAt(i)==pattern.charAt(j)){
                return process(i+1,queries,j+1,pattern)|| process(i+1,queries,j,pattern);
            }else{
                // 如果不想等,queries的i指针继续向后移动和j指针匹配
                return process(i+1,queries,j,pattern);
            }
        }
    }
}
  • 说明: 几乎所有的字符串匹配问题都是可以使用动态规划求解的.

共勉

最后,我想送给大家一句一直激励我的座右铭,希望可以与大家共勉!
在这里插入图片描述

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

硕风和炜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值