动态规划 | 带有通配符的字符串匹配(浅显易懂)

带有通配符的字符串匹配

一、Leetcode | 44 Wildcard Matching(只有一个字符串包含通配符)



题目很简单,就是说两个字符串,一个含有通配符,去匹配另一个字符串;输出两个字符串是否一致。

注意:’?’表示匹配任意一个字符,’*’表示匹配任意字符0或者多次

首先,我们想到暴力破解。如果从头到尾的破解,到第二个字符时,是否匹配成功取决于第一个字符是否匹配成功! 所以我们想到应该要用到动态规划;

既然用到动态规划,最重要的是设置初值 和找到递推式:

于是,我们开始分析初值怎么设;其实很简单,把这个匹配问题可以想象成一个矩阵dp,纵轴代表含有通配符的匹配字符串s2, 横轴代表要匹配的字符串s1。假设现在s2=”a*b”, s1=”abc” 如图:

对应空位就是截止到当前的 (i,j) 位置,两字符串是否匹配。匹配为 T(true),不匹配为 F(false),最后返回最右下角的值,就是当前两个字符串是否匹配的最终值;

现在我们要做的设置初值,所以我们大可多加一行和一列,来填充初值;s1既然是要匹配的,我们都设为 F(即dp[0][1]=F,dp[0][2]=F,dp[0][3]=F),表示当前还未开始匹配。而s2的初值,我们发现如果星号和a调换位置,星号可以匹配任意字符串,所以dp[i][0]的值取决于该位置是否为星号和上一个位置d[i-1][0]是否为T(其实就是上一个位置是否也是星号),所以我们设置dp[0][0]为 T。所以形成下图:

此时初值已经设置完毕,我们要找到递推式;经局部推算,我们发现递推式应该有两种,一种是当s2的字符是星号,另一种是s2的字符是非星号。

先看星号的情况:当要计算dp[2][1](即要匹配a*和a时),我们发现是取决于dp[1][1](即a和a是否匹配),当要计算dp[2][2] (即要匹配a*和ab时),是取决于dp[2][1] (即a*和a是否匹配)。抽象一下,星号和任意字符(0或多个)都匹配。所以字符串截止到星号匹配的情况,取决于当前位置向上和向左的情况(即可以为0个字符,也可以为多个字符)。所以此时递推式为dp[i][j]=dp[i?1][j]||dp[i][j?1] 如图:

再看非星号的情况:当要计算dp[3][2] (即要匹配a*b和ab时),则取决于dp[2][1]和a[3][2] (即a*和a是否匹配,同时b和b是否匹配);所以可以得到递推式 dp[i][j] = dp[i-1][j-1]&&a[i][j]。如图:

最后我们得到了初值和两个递推式,就可以上代码了;

//isMatch: s1无通配符,s2有通配符, ‘?‘表示匹配任意一个字符,‘*‘表示匹配任意字符0或者多次
    public static boolean isMatch(String s1, String s2) {
        int countXing = 0;
        for(char c : s2.toCharArray())
            countXing++;
        if(s2.length() - countXing > s1.length() ) //说明s2去掉通配符,长度也长于s1
            return false;

        //动态规划设置初值
        boolean[][] dp = new boolean[s2.length()+1][s1.length()+1];
        dp[0][0] = true;

        for(int i=1; i<=s2.length(); i++) {
            char s2_char = s2.charAt(i-1);
            dp[i][0] = dp[i-1][0] && s2_char==‘*‘; //设置每次循环的初值,即当星号不出现在首位时,匹配字符串的初值都为false
            for(int j=1; j<=s1.length(); j++) {
                char s1_char = s1.charAt(j-1);
                if(s2_char == ‘*‘)
                    dp[i][j] = dp[i-1][j] || dp[i][j-1]; //动态规划递推式(星号) 表示星号可以匹配0个(决定于上次外循环的结果)或者多个(决定于刚才内循环的结果)
                else
                    dp[i][j] = dp[i-1][j-1] && (s2_char==‘?‘ || s1_char == s2_char); //动态规划递推式(非星号) 表示dp值取决于上次的状态和当前状态
            }
        }
        return dp[s2.length()][s1.length()];
    }

二、两个字符串都包含通配符的解法

通过上面那个列子,其实就这个问题就很容易想了。

首先就是初值的设置,两个字符串都按上题中的包含通配符的字符串设置初值的方法,根据是否为星号和上一个的状态。

其次就是递推式,它不用变,只是需要同时判断两个字符串中是否都包含通配符。

直接上代码:

public static boolean isMatchByBoth(String s1, String s2) {

            //动态规划设置初值
            boolean[][] dp = new boolean[s2.length()+1][s1.length()+1];
            dp[0][0] = true;

            for(int i=1; i<=s2.length(); i++) {
                char s2_char = s2.charAt(i-1);
                dp[i][0] = dp[i-1][0] && s2_char==‘*‘; //设置每次循环的初值,即当星号不出现在首位时,匹配字符串的初值都为false
                for(int j=1; j<=s1.length(); j++) {
                    char s1_char = s1.charAt(j-1);
                    dp[0][j] = dp[0][j-1] && s1.charAt(j-1)==‘*‘;
                    if(s2_char == ‘*‘ || s1_char == ‘*‘) {
                        dp[i][j] = dp[i-1][j] || dp[i][j-1]; //动态规划递推式(星号) 表示星号可以匹配0个(决定于上次外循环的结果)或者多个(决定于刚才内循环的结果)
                    } else {
                        dp[i][j] = dp[i-1][j-1] && (s1_char==‘?‘ || s2_char==‘?‘ || s1_char == s2_char);
                    }
                }
            }
            return dp[s2.length()][s1.length()];
        }


以上只是自己的理解,希望大家多多交流!

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-11 09:21:28

动态规划 | 带有通配符的字符串匹配(浅显易懂)的相关文章

[OpenJudge]带有通配符的字符串匹配

#include <iostream> #include <stdlib.h> #include <string.h> using namespace std; /* 字符串匹配 ?代表一个字符(不能没有),*可以代表任意多个字符(可以为空) ?表示任意字符,也就是说?永远可以匹配成功,本质上,只要遇到?就一定匹配 *的本质,是分割字符串,即如果P=P1*P2,即P得匹配条件是匹配P1串之后,再匹配P2子串 */ //@param  src[], 带匹配的字符串 //@

COJN 0558 800600带通配符的字符串匹配

800600带通配符的字符串匹配 难度级别:B: 运行时间限制:1000ms: 运行空间限制:51200KB: 代码长度限制:2000000B 试题描述 通配符是一类键盘字符,当我们不知道真正字符或者不想键入完整名字时,常常使用通配符代替一个或多个真正字符.通配符有问号(?)和星号(*)等,其中,“?”可以代替一个字符,而“*”可以代替零个或多个字符.你的任务是,给出一个带有通配符的字符串和一个不带通配符的字符串,判断他们是否能够匹配.例如,1?456 可以匹配 12456.13456.1a45

openjudge6252 带通配符的字符串匹配

描述 通配符是一类键盘字符,当我们不知道真正字符或者不想键入完整名字时,常常使用通配符代替一个或多个真正字符.通配符有问号(?)和星号(*)等,其中,“?”可以代替一个字符,而“*”可以代替零个或多个字符. 你的任务是,给出一个带有通配符的字符串和一个不带通配符的字符串,判断他们是否能够匹配. 例如,1?456 可以匹配 12456.13456.1a456,但是却不能够匹配23456.1aa456: 2*77?8可以匹配 24457798.237708.27798. 输入输入有两行,每行为一个不

含有通配符的字符串匹配

字符串匹配问题,给定两个字符串.求字符串2.在字符串1中的最先匹配结果.字符串2中能够存在'*'符号,且该符号能够代表随意字符,即字符串2中存在通配符. e.g. 输入:abcdefghabef, a*f 输出:abcdef #include <iostream> #include <string> using namespace std; bool Match(const string &s1,const string &s2,string &result

带通配符的字符串匹配(动态规划)

OJ地址:http://noi.openjudge.cn/ch0206/6252/ 1 #include<string> 2 #include<cstdio> 3 #include<iostream> 4 using namespace std; 5 string A,B; 6 bool Judge(int a,int b); 7 void Init(); 8 int main() 9 { 10 // cin>>A>>B;//A with ? o

带通配符的字符串匹配问题

1 /* 2 不使用c,c++库,?表示任意一个,*表示>=0任意,匹配规则要求匹配最大的字符子串,例如a*d ,匹配abbdd而非abbd,即最大匹配字符串 3 input :abcadefg 4 reule : a?c 5 ouput : abc 6 7 input : newsadfanewfdsdsf 8 rule :new 9 output: new new(最后一个不带空格,中间用空格分隔) 10 11 input : breakfastfood 12 rule : f*d 13 o

含通配符的字符串匹配问题

题目: 给定两个字符串,求字符串2,在字符串1中的最先匹配结果.字符串2中 可以存在'*'符号,且该符号可以代表任意字符,即字符串2中存在通配符. 例如:输入:abcdefghabef,a*f 输出:abcdef #include<iostream> #include<string> using namespace std; int main() { int i=0,len=0,len1,len2,j=0,begin=0; string s,s1,s2,s3; getline(ci

【python cookbook】【字符串与文本】3.利用shell通配符做字符串匹配

问题:当工作在Linux shell下时,使用常见的通配符模式(即,*.py.Dat[0-9]*.csv等)来对文本做匹配 解决方案:fnmatch模块提供的两个函数fnmatch().fnmatchcase() #fnmatch()的匹配模式所采用的大小写区分规则和底层文件系统相同(根据操作系统的不同 而不同) #fnmatchcase()的匹配模式区分大小写 >>> from fnmatch import fnmatch,fnmatchcase >>> fnmatc

Python实用技法第22篇:利用Shell通配符做字符串匹配

1.需求 当工作在UNIX Shell下时,我们想使用常见的通配符模式(即:.py,Dat[0-9].csv等)来对文本做匹配. 2.解决方案 fnmatch模块提供了两个函数:fnmatch()和fnmatchcase(),可用来执行这样的匹配,使用起来非常简单. 实例: from fnmatch import fnmatch,fnmatchcase print(fnmatch('mark.txt','*.txt')) print(fnmatch('mark.txt','?ark.txt'))