LeetCode Wildcard Matching 优化动态规划法和迭代算法

Implement wildcard pattern matching with support for'?'and'*'.

'?' Matches any single character.
'*' Matches any sequence of characters (including the empty sequence).

The matching should cover the entire input string (not partial).

The function prototype should be:
bool isMatch(const char *s, const char *p)

Some examples:
isMatch("aa","a") → false
isMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "*") → true
isMatch("aa", "a*") → true
isMatch("ab", "?*") → true
isMatch("aab", "c*a*b") → false

实现通配符的功能。特殊的串匹配。这道题的迭代法会更加简单,运行速度更加快。还是相当有难度的题目。
参考了Leetcode上的程序,然后自己优化了一下。

下面是动态规划法的优化程序:

优化地方:增加一个bool tream标志。如果当前行全部为false,那么就可以判断最终结果也为false。如果字符串很长,又是不匹配的话可以加快很多速度。

这样可以让速度提高差不多100ms,有图:

原算法是300多ms,我修改之后就变为200多ms了。

修改:

vector<vector<bool> > r(2, vector<bool>(ms_max, false));

修正为

vector<vector<bool> > r(2, vector<bool>(ms_max+1, false));

参考程序:http://discuss.leetcode.com/questions/222/wildcard-matching

bool isMatch(const char *s, const char *p) {
		if (!*s && !*p) return true;

		int ms_max = 1;//size of *s
		const char* ss = s;
		while(*ss){ ++ms_max;++ss;}//ms_max = strlen(s)+1;
		int np_max = 1;
		const char* pp = p;
		while(*pp){if(*pp!='*')++np_max;++pp;}//计算不带非*的字符长度+1
		if(ms_max < np_max) return false;
		//这里写vector<bool>(ms_max, false)居然Leetcode上AC了
		//但是在vs上是下标溢出的,正确应该+1.
		vector<vector<bool> > r(2, vector<bool>(ms_max+1, false));
		bool flag = 1;
		r[0][0] = true;//最右上角初始化为true
		do{//*p
			//增加标志,提前判断匹配串,返回;当前行全部false,结果肯定false
			bool tream = false;
			int ms = 1;
			ss = s;//每次从表的1的位置开始填表
			if (*p == '*'){//3.
				while(*p == '*') ++p;//处理掉重复的*
				--p;//返回填表位置
				//因为*可以和空""匹配,所有把当前行0列置真
				r[flag][0] = r[!flag][0];//例如特殊情况:"aa", "*"
				for( ;ms <= ms_max; ++ms){//up and left判断两格就可以了,没有所有格都判断
					if (r[!flag][ms] || r[flag][ms-1])//这里没有从0开始
					{
						tream = true;
						break;
					}
					else r[flag][ms] = false;
				}
				for(;ms <= ms_max; ++ms){
					r[flag][ms] = true;
				}
			}
			else{
				do{//填写行
					bool r_flag = false;//1.
					if (*ss == *p || *p == '?'){//2.
						r_flag = r[!flag][ms-1];//diagnal
					}
					r[flag][ms]=r_flag;
					if (r_flag) tream = true;
					++ms;++ss;
				}while(*ss);//*s
				r[flag][0] = false;//第二行开始的最右边格填false
			}
			if (tream == false) return false;
			++p;
			flag = !flag;//利用flag来交替使用两个数组代表一个二维表
		}while(*p);
		return r[!flag][ms_max-1];
	}


下面是迭代程序,要熟悉这个思维:记录上一次开始比较的位置,如图:

下面程序是直接使用指针记录位置

bool isMatch(const char *s, const char *p) {
		const char *backtrack_s = NULL, *backtrack_s = NULL;
		while (*s) {
			if (*p == '?' || *s == *p) {
				++s;
				++p;
			}
			else {
				if (*p == '*') {
					while (*p == '*')
						++p;
					if (*p == '\0') 
						return true;
					backtrack_s = s;
					backtrack_s = p;
				}
				else {//backtrack_s是为了判断是否到尾,注意:还有判断是否出现了*
					if (backtrack_s) {
//注意:在当前位置往后判断出现不相等的时候,再重新回到下一个位置重新往后比较
						s = ++backtrack_s;
						p = backtrack_s;//恢复p的位置
					}
					//s的恢复位置backtrack_s已经试完,那么就不匹配
					else return false;
				}
			}
		}
		while (*p == '*')//处理p末端的*
			++p;
		return (*s == '\0' && *p == '\0');
	}
};

//2014-2-24 update AC
	bool isMatch(const char *s, const char *p) 
	{
		int sn = strlen(s);
		int pn = strlen(p);
		for (int i = 0, c = 0; i < pn; i++)
		{
			if (p[i] != '*') c++;
			if (c > sn) return false;
		}
		vector<vector<bool> > tbl(2, vector<bool>(sn+1));
		tbl[0][0] = true;
		bool idx = true;
		bool finished = false;
		for (int i = 0; i < pn && !finished; i++)
		{
			if (p[i] == '*')
			{
				while (i < pn && p[i] == '*') i++;
				i--;
			}
			finished = true;
			if (p[i] == '*')
			{
				int j = 0;
				for (; j < sn && !tbl[!idx][j]; j++) tbl[idx][j] = false;
				if (j <= sn) finished = false;
				for (; j <= sn; j++) tbl[idx][j] = true;
			}
			else
			{
				tbl[idx][0] = false;
				int j = 0;
				for ( ; j < sn; j++)
				{
					if (s[j] == p[i] || p[i] == '?') 
						tbl[idx][j+1] = tbl[!idx][j];
					else tbl[idx][j+1] = false;
					if (tbl[idx][j+1]) finished = false;
				}
			}
			idx = !idx;
		}
		return finished? false : tbl[!idx][sn];
	}

好像增加和不增加一个结束标志,其实效率也没多大影响。

本题关键的一个考点:判断p字符串中的总长字符减去*号数,得到的总长是否长于s字符串,如果长过,那么就返回假。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值