算法:动态规划之字符串模式匹配

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

目录

一、问题描述

二、常规算法

三、动态规划算法

总结


提示:以下是本篇文章正文内容,下面案例可供参考

一、问题描述

给定一个字符串 (s) 和一个字符模式 (p) ,实现一个支持 '?' 和 '*' 的通配符匹配

'?' 可以匹配任何单个字符。

'*' 可以匹配任意字符串(包括空字符串)。

两个字符串完全匹配才算匹配成功。

s 可能为空,且只包含从 a-z 的小写字母。

p 可能为空,且只包含从 a-z 的小写字母,以及字符 ? 和 *。

输入:

s = "aa"

p = "a"

输出: false

解释: "a" 无法匹配 "aa" 整个字符串。

输入:

s = "aa"

p = "*"

输出: true

解释: '*' 可以匹配任意字符串。

输入:

s = "cb"

p = "?a"

输出: false

解释: '?' 可以匹配 'c', 但第二个 'a' 无法匹配 'b'。

输入:

s = "adceb"

p = "*a*b"

输出: true

解释: 第一个 '*' 可以匹配空字符串, 第二个 '*' 可以匹配字符串 "dce"。

输入:

s = "acdcb"

p = "a*c?b"

输出: false

二、常规算法

解题思路:

我们将这个题拆解一下,分为不包含*的场景和包含*的场景

1、不包含*的场景,我们逐位进行比对即可得出最终的结论

2、包含*的场景,我们无法确定起点位置,所以我们用s的每一个字符,去和p的每一个字符进行匹配,记录匹配的结果,最终相当于成了一道寻路题了,只不过限制了前进方向为向下或者向右,起点从首个字符能匹配到的地方开始算起。

代码示例:

public void test() {
    String s = "acdcb";
    String p = "a*c?b";
    // 模式匹配串中不包含*
    if (!p.contains("*")) {
        if (s.length() != p.length()) {
            System.out.println(false);
            return;
        }
        for (int i = 0; i < s.length(); i++) {
            if (p.charAt(i) == '?' || s.charAt(i) == p.charAt(i)) {
                continue;
            }
            System.out.println(false);
            return;
        }
        System.out.println(false);
        return;
    }
    // 模式匹配串中包含*, 我们用s中的每一个字符都去和p中的每个字符匹配, 结果保存在一个二维数组中, s中有几个字符, 就有几行数据
    int[][] array = new int[s.length()][p.length()];
    for (int i = 0; i < s.length(); i++) {
        for (int j = 0; j < p.length(); j++) {
            if (p.charAt(j) == '?' || p.charAt(j) == '*' || s.charAt(i) == p.charAt(j)) {
                // 单个字符匹配, 标记为1
                array[i][j] = 1;
                continue;
            }
            // 单个字符不匹配, 标记为0
            array[i][j] = 0;
        }
    }
    // 最后我们得到一个全部标记了0或1的二维数组, 起点是第一行中的某个元素, 要求从这个元素开始向下或者向右移动, 最终能抵达斜对角的位置
    for (int j = 0; j < p.length(); j++) {
        if (array[0][j] == 1) {
            boolean can = canReach(array, 0, j);
            if (!can) {
                continue;
            }
            System.out.println(true);
            return;
        }
    }
}

public boolean canReach(int[][] array, int row, int col) {
    // 如果当前位置已经到达右下角对角线,返回true
    if (row == array.length - 1 && col == array[0].length - 1) {
        return true;
    }

    // 如果当前位置超出矩阵范围或者元素为0,返回false
    if (row >= array.length || col >= array[0].length || array[row][col] == 0) {
        return false;
    }

    // 递归向下或向右移动
    return canReach(array, row + 1, col) || canReach(array, row, col + 1);
}

常规算法,胜于更易理解,将复杂问题拆解为简单的子问题,逐个破解。

三、动态规划算法

解题思路:

最终的匹配结果依赖于最后一个字符的匹配结果,最后一个字符的匹配结果又依赖于前一个字符的匹配结果,依此类推,直至首个字符匹配成功。那么前一个字符的匹配结果在该字符的匹配结果的什么位置呢,答案是上方或者左侧。由于p中包含*字符,这个*可能代表0 ~ N个字符,所以可能是跳过该位置,有可能是跨了多个位置。我们约定横跨是向右,跳过是向下。

所以当出现p中某位是*时,它取的结果来自它的上方和左侧,如果有一个是通过,本位置即为通过。

代码示例:

public void test() {
    String s = "aa";
    String p = "a";
    // 初始化动态规划数组
    boolean[][] dp = new boolean[s.length() + 1][p.length() + 1];
    // 当模式串为空时,只有当字符串也为空时才匹配成功
    dp[0][0] = true;
    // 处理模式串中的第一个字符
    for (int j = 1; j <= p.length(); j++) {
        if (p.charAt(j - 1) == '*') {
            dp[0][j] = dp[0][j - 1];
        }
    }
    // 动态规划过程
    for (int i = 1; i <= s.length(); i++) {
        for (int j = 1; j <= p.length(); j++) {
            if (p.charAt(j - 1) == '?' || s.charAt(i - 1) == p.charAt(j - 1)) {
                dp[i][j] = dp[i - 1][j - 1];
            } else if (p.charAt(j - 1) == '*') {
                dp[i][j] = dp[i - 1][j] || dp[i][j - 1];
            }
        }
    }
    System.out.println(JSON.toJSONString(dp));
    // 返回最终匹配结果
    System.out.println(dp[s.length()][p.length()]);
}

动态规划算法,要找一下规律,代码比较简洁。


总结

解题过程已奉上,快快练起来,简单到有手就行!

相关推荐
Kalika0-01 小时前
猴子吃桃-C语言
c语言·开发语言·数据结构·算法
sp_fyf_20241 小时前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-02
人工智能·神经网络·算法·计算机视觉·语言模型·自然语言处理·数据挖掘
我是哈哈hh3 小时前
专题十_穷举vs暴搜vs深搜vs回溯vs剪枝_二叉树的深度优先搜索_算法专题详细总结
服务器·数据结构·c++·算法·机器学习·深度优先·剪枝
Tisfy3 小时前
LeetCode 2187.完成旅途的最少时间:二分查找
算法·leetcode·二分查找·题解·二分
Mephisto.java3 小时前
【力扣 | SQL题 | 每日四题】力扣2082, 2084, 2072, 2112, 180
sql·算法·leetcode
robin_suli3 小时前
滑动窗口->dd爱框框
算法
丶Darling.3 小时前
LeetCode Hot100 | Day1 | 二叉树:二叉树的直径
数据结构·c++·学习·算法·leetcode·二叉树
labuladuo5204 小时前
Codeforces Round 977 (Div. 2) C2 Adjust The Presentation (Hard Version)(思维,set)
数据结构·c++·算法
jiyisuifeng19914 小时前
代码随想录训练营第54天|单调栈+双指针
数据结构·算法
꧁༺❀氯ྀൢ躅ྀൢ❀༻꧂4 小时前
实验4 循环结构
c语言·算法·基础题