leetcode 44 通配符匹配

题目

给定一个字符串 (s) 和一个字符模式 § ,实现一个支持 ? 和 * 的通配符匹配
匹配规则:
相同字符可以匹配
‘?’ 可以匹配任何单个字符。
‘*’ 可以匹配任意字符串(包括空字符串)。

解题思路

动态规划

递推函数
定义dp[i][j]表示字符串s的前i个字符和匹配字符串p的前j个字符的匹配情况,
初始情况dp[i][0]全为false,dp[0][j]由p[j]得出,如果p的前j个字符都为’*'则dp[0][j]为true。

状态转移方程
当p[j]为字母,则判断p[j]和s[i]是否相等,相等则dp[i][j]=dp[i-1][j-1],不相等则dp[i][j]为false
当p[j]为’?’,则dp[i][j]=dp[i-1][j-1]
当p[j]为’’,则p[i][j]=p[i-1][j] || p[i][j-1],即p[i-1][j]和p[i][j-1]两种情况有一种情况为真都可以推导出p[i][j]为真,p[i-1][j]代表’‘匹配了s[i]这个字母的情况,p[i][j-1]则代表’*'没有匹配s[i]这个字母的情况。
代码:

bool isMatch(string s, string p) {
	int l1=s.length();
	int l2=p.length();
	bool dp[1000][1000];
	for(int i=0;i<=l1;i++){
		dp[i][0]=false;//当p长度为空的时候,空模式不能匹配任何字符串 
	}
	for(int i=1;i<=l2;i++){
        if(p[i-1]=='*'){
        	dp[0][i]=true;
		}else{
			for(;i<=l2;i++){
				dp[0][i]=false;
			}
			break;
		}
	} 
	dp[0][0]=true;
    for(int i=1;i<=l1;i++){
    	for(int j=1;j<=l2;j++){
    		if(p[j-1]=='*'){
    		    dp[i][j]=dp[i-1][j]||dp[i][j-1];
			}else if(p[j-1]=='?'){
				dp[i][j]=dp[i-1][j-1];
			}else{
				if(s[i-1]==p[j-1]){
					dp[i][j]=dp[i-1][j-1];
				}else{
					dp[i][j]=false;//提交代码没加这句话导致错误 
				} 
			}
		}
	}
    return dp[l1][l2];
}
遇到问题:

1) 在提交时后显示超时,去掉代码中的cout后解决
2) 自己测试输出正确,提交后输出却错误,经测试发现新建dp的时候,测试环境好像默认dp为1了,所以提交代码在当p[j]为字母且p[j]不等于s[i]的情况没有做任何处理的话,会导致出错。

贪心算法
自己的做法

稍微看了一下题解就开始自己写了,思路大致为因为匹配字符串p中可以根据*分成若干个子字符串,对于 * a * b *这种情况,我们只要遍历s,依次找到匹配这些字符串,如果都找到了则表示匹配成功,因为 * 可以匹配任何字符串,但是对于a * b * 、 * a * b和a * b这种情况,就要单独测试了,下面是我自己写的代码(写的很烂、勿喷)

bool sub_match(string s,string p,int l){
	for(int i=0;i<l;i++){
		if(p[i]=='?'){
			continue;
		}else if(s[i]!=p[i]){
			cout<<s[i]<<' '<<p[i]<<' '<<(p[i]=='?')<<endl;
			return false;
		}
	}
	return true;
}
bool isMatch(string s, string p){
	string orip = p;
    int l1=s.length();
    int l2=p.length();
    if(l1==0&&l2==0)return true;
    if(l2==0)return false;//空模式不能匹配任何字符串 
    for(int i=0;i<l2;i++){
    	if(p[i]=='*'){
    		p[i]=' ';
		}
	}
	stringstream ss(p);
	string p_list[5000];
	int index=0;
	while(ss>>p_list[index]){
		index++;
	}
	//处理特殊情况 
	if(index==0){//表示p为全* 
		return true;
	}
	if(orip[0]!='*'){//p开头不是*则需要匹配第一个子字符串是否匹配s对应的开头子字符串
		if(!sub_match(s.substr(0,p_list[0].length()),p_list[0],p_list[0].length())){
			return false;
		}
	}
	int index2=0,lastindex=0;
	for(int i=0;i<l1;i++){
		if(p_list[index2][0]==s[i]||p_list[index2][0]=='?'){
			int tl=p_list[index2].length();
			string ts;
			if(i+tl-1<=s.length()){
			    ts=s.substr(i,tl);
			}else{
				return false;
			}
			if(tl!=ts.length())return false;
			if(sub_match(ts,p_list[index2],tl)){
				index2++;
				i=i+tl-1;
				lastindex=i;//记录最后一次s匹配子字符串的结尾位置
			}
		}
	}
	if(index2==index&&lastindex==(l1-1)||index2==index&&orip[l2-1]=='*'){//所有子字符串都匹配成功,同时s末尾没有元素未匹配或者p末尾为*
		return true;
	}else if(index2==index&&(orip[0]=='*'||index2!=1)){//所有字符串都匹配成功,但是s末尾有字符未匹配同时p末尾不是*,则需要判断最后一个子字符串是否能匹配s的末尾对应子字符串
		int tl = p_list[index2-1].length();
		if(sub_match(s.substr(l1-tl,tl),p_list[index2-1],tl)){
			return true;
		}
	} 
	return false;
}
遇到的问题

主要就是不是* a * b *这种情况的p的处理,以及p的子字符串中含有?的处理,对应leetcode测试环境上的各种样例。

官方做法

官方做法的思路是类似的,不过代码更精巧
详细看https://leetcode-cn.com/problems/wildcard-matching/solution/tong-pei-fu-pi-pei-by-leetcode-solution/
这里贴上官方代码:

bool isMatch(string s, string p) {
        auto allStars = [](const string& str, int left, int right) {
            for (int i = left; i < right; ++i) {
                if (str[i] != '*') {
                    return false;
                }
            }
            return true;
        };
        auto charMatch = [](char u, char v) {
            return u == v || v == '?';
        };
        while (s.size() && p.size() && p.back() != '*') {//s不为空,p不为空且p的末尾不为*,这里是用来判断p末尾没有*的情况,如果p末尾没有*,则先把末尾子字符串匹配 
            if (charMatch(s.back(), p.back())) {//如果s和p的尾部字符匹配,直到p的尾部字符为* 
                s.pop_back();
                p.pop_back();
            }
            else {
                return false;
            }
        }
        if (p.empty()) {//如果p为空,则只有s为空时才为真 
            return s.empty();
        }

        int sIndex = 0, pIndex = 0;
        int sRecord = -1, pRecord = -1;
        while (sIndex < s.size() && pIndex < p.size()) {//然后就当做首尾都为*的情况操作了 
            if (p[pIndex] == '*') {
                ++pIndex;
                sRecord = sIndex;
                pRecord = pIndex;
            }
            else if (charMatch(s[sIndex], p[pIndex])) {
                ++sIndex;
                ++pIndex;
            }
            else if (sRecord != -1 && sRecord + 1 < s.size()) {//这里的sRecord! = -1就是用来判断p开头没有*的情况,p开头没有*号的话 
                ++sRecord;
                sIndex = sRecord;
                pIndex = pRecord;
            }
            else {
                return false;
            }
        }
        return allStars(p, pIndex, p.size());
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值