leetcode第一刷_Wildcard Matching

又好又难的题。

我发现难题一般都包含了好几个简单题,必须对一些基本的算法熟练掌握才能快速准确的写出代码。这个题就用到了kmp,这个算法写过好多遍,还是不断的出错,哎。

题目中有两种符号,?可以匹配任意单个字符,*可以匹配任意0个或多个字符。?其实没什么,麻烦就麻烦在*上。一个*实际上代表了一段任意的字符串。很容易想到的一种解法是用递归,当p[i]不为*时,看p[i]是不是‘?‘或者p[i]==s[j],这样就把两个串都往下移一个位置,如果p[i]是*的话,一种情况是*代表的是0个字符,那么就只移动p,也可以代表多个字符,一步一步的移动s,代码如下,这种方式是正确的,小规模的数据是可以过得,但是大规模的会超时,因为有*之后支路实在是太多了。

class Solution {
public:
    bool isMatch(const char *s, const char *p)
    {
    	if (s == NULL || p == NULL) return false;
    	if (*p == ‘\0‘) return *s == ‘\0‘;
    	if (*p == ‘*‘)
    	{
        	while (*p == ‘*‘) ++p;
        	while (*s != ‘\0‘)
        	{
            		if (isMatch(s, p)) return true;
            		++s;
        	}
        	return isMatch(s, p);
    	}
    	else if ((*s != ‘\0‘ && *p == ‘?‘) || *p == *s)
    	{
        	return isMatch(s + 1, p + 1);
    	}
   	return false;
    }
};

下面介绍一种很巧妙的思路。*可以代表任意一个字符串,那么如果将p按照*来分割的话,剩下的每一段都应该是s的子串,这些子串是有先后顺序的,中间其实是由*来连接的。如果这些子串却是按照顺序匹配了s,那么加上*之后,p和s就会完全匹配。这里有两个边界情况,第一个是如果p的开头不是*,那么分割后p的第一串必须跟s从头开始匹配,不匹配直接返回false。第二个边界是最后一段,如果p的最后一段不是*,那么p的最后一段必须与s的后半部分匹配。

实现的时候,p按照*分割没什么,一个字符一个字符的加就行了,如果是java的话一个split函数就搞定了,注意多个*连在一起的话相当于一个。判断一个字符串是不是子串我用的是kmp,有一个地方特别注意,q[0]=-1而不是0,j的初值也是-1,这里总是搞错。kmp算法的思想是让子串不断的匹配长串,如果子串在位置i出现不匹配的情况,则子串回到前面的某个位置q[i],这个位置使得p[0, q[i]]依然与长串匹配,且q[i]最大。有关kmp可以找matrix67的那篇博客,很精彩的。再加上边界条件的讨论,就可以写出完整的代码了。

我在变量命名的时候犯了个地级错误,但是编译运行是没有问题的,一个算法写到将近100行。。

class Solution {
public:
    int p[100000];
    vector<string> mp;
    bool bs = false, be = false;
    void getp(string &c){
        p[0] = -1;
        int j=-1, i=1;
        while(i<c.length()){
            while(j>=0&&c[j+1]!=c[i]&&c[j+1]!=‘?‘)
                j=p[j];
            if(c[j+1] == c[i] || c[j+1] == ‘?‘)
                ++j;
            p[i] = j;
            ++i;
        }
    }
    int isSubstr(const char* a, string &b){
        getp(b);
        int i=0, j=-1;
        while(a[i]){
            while(j>=0&&b[j+1]!=a[i]&&b[j+1]!=‘?‘)
                j=p[j];
            if(b[j+1] == a[i]||b[j+1] == ‘?‘)
                ++j;
            if(j == b.length()-1)
                return i+1;
            ++i;
        }
        return -1;
    }
    void partS(const char *p){
        string tps;
        while(*p){
            while(*p == ‘*‘)  ++p;
            tps = "";
            for(;*p&&*p!=‘*‘;++p)
                tps += *p;
            if(tps != "")   mp.push_back(tps);
        }
    }
    bool fullcomp(const char *s, string &p, int len){
        for(int i=0;i<len;i++){
            if(s[i] != p[i] && p[i] != ‘?‘)
                return false;
        }
        return true;
    }
    bool isMatch(const char *s, const char *p){
        if(s==NULL && p==NULL)  return false;
        if(*p == ‘\0‘)  return *s == ‘\0‘;
        mp.clear();
        int i=0, j=0, l1=0, l2=0;
        while(s[i]) {++i; ++l1;}
        while(p[j]) {++j; ++l2;}
        partS(p);
        if(mp.size() == 0){
            return true;
        }
        bool firstmatch = false;
        int offset = 0, tpoff;
        if(*p != ‘*‘){
            if(mp.size()==1 && p[l2-1] != ‘*‘){
                if(l1 > l2 || l1==0)
                    return false;
            }
        	if(l1<mp[0].length()||!fullcomp(s, mp[0], mp[0].length()))
        		return false;
        	offset += mp[0].length();
        	firstmatch = true;
        }
        vector<string>::iterator it=mp.begin();
        if(firstmatch)	++it;
        for(;it!=mp.end()&&offset<l1;++it){
            if(l1-offset<(*it).length())    return false;
            tpoff = isSubstr(s+offset, *it);
            if(tpoff == -1)    return false;
            offset += tpoff;
        }
        if(it!=mp.end()){
            return false;
        }
        if(p[l2-1] != ‘*‘){
			for(int i=0;i<mp[mp.size()-1].length();i++){
				if(p[l2-i-1] != s[l1-i-1] && p[l2-i-1] != ‘?‘)
					return false;
			}
		}
        return true;
    }
};

leetcode第一刷_Wildcard Matching

时间: 2024-08-06 16:39:03

leetcode第一刷_Wildcard Matching的相关文章

leetcode第一刷_Regular Expression Matching

这道题跟有?和*的那道题很像,不过要简单一些.为什么会简单呢,因为*号只能匹配跟它前面相同的字符.需要注意一点,从aab可以用c*a*b来匹配可以看出,*号可以使他之前的那个字符出现次数变成0. 昨天实验室的同学正好在做这个题,说想用递归做,我想都没想就说用递归肯定超时了.她为什么,我跟人家说因为递归的分支太多了,可怎么也想不起当初自己是怎么写的,回来一看,居然用递归做的,打脸啊..这个题为什么用递归可以呢?我觉得主要是因为*号的匹配规则,比起可以匹配任意字符来说,只能匹配之前的那个字符显得严格

leetcode第一刷_Binary Tree Inorder Traversal

递归实现当然太简单,也用不着为了ac走这样的捷径吧..非递归实现还挺有意思的. 树的非递归遍历一定要借助栈,相当于把原来编译器做的事情显式的写出来.对于中序遍历,先要訪问最左下的节点,一定是进入循环后,不断的往左下走,走到不能走为止,这时候,能够从栈中弹出訪问的节点,相当于"左根右"过程的"根",然后应该怎么做呢?想一下中序遍历完根节点之后应该干嘛,对,是走到右子树中继续反复这个过程,可是有一点,假设这个节点不包括右子树怎么办?这样的情况下,下一个应该訪问的节点应该

leetcode第一刷_Sqrt(x)

这道题乍看下来非常简单,实际上要注意的问题非常多. 注意看给出来的函数的接口,返回的是int值,也就是计算结果是个近似值.怎样求呢?难道是从2开始往上算?直到某个值正好接近x?当然不行,肯定超时了.再仔细想一下,对了,有二分法,从最大的开始,每次计算一下平方,如果结果比x大,那么缩短上界,否则提高下界. 思想很正确,下面的问题是最大的那个值是多少?你会毫不犹豫的说出是x啊,x的平方根肯定比x小吧.好,那如果x是INT_MAX呢,你想用什么类型来存储这个平方的结果?而且这样每次减半,也得好一会儿才

leetcode第一刷_Subsets II

要求子集,有非常现成的方法.N个数,子集的个数是2^N,每个元素都有在集合中和不在集合中两种状态,这些状态用[0,pow(2,N)]中每个数来穷举,如果这个数中的第i位为1,说明当前集合中包含源数组中的第i个数. 至于有没有重复的元素,大部分有重复元素的问题,都可以借助一个vis集合,里面存放所有已经求得的集合或者其他形式的解,只有少数题目会超时,哪些问题具体的说. class Solution { public: vector<vector<int> > subsetsWithD

leetcode第一刷_Decode Ways

这道题还挺难的.递归的思路是好想,不过不出意料的超时了. dp嘛.想一下i-1的编码加上第i个编码会怎样,如果加上的这个编码不是0,那么这一位可以独立解码,那长为i的解码个数至少是长为i-1的解码个数.还有呢?如果i-1位是1,可以把i-1位和i位同时解码出来,还有呢?如果i-1位是2而i位是0-6中的数字,也可以同时解码这两位编码.满足这个条件的时候,当前长度的解码个数还要加上i-2时的解码个数. 完全可以用一个数组存放过去的结果,但是很明显,当前的结论只与前一个和前前一个结果有关系.只要用三

leetcode第一刷_Gray Code

说到格雷码,应该没人不知道,具体它有什么用,我还真不是很清楚,我室友应该是专家.生成的规律不是很明显,之前看到帖子讲的,这会儿找找不到了.. 思想是这样的,如果有n位,在第2^(n-1)个编码下面画一条水平线的话,你会发现除了第一位之外,其他位都是关于这条线对称的,如下,以三位格雷码举例: 000 001 011 010 --------------------- 110 111 101 100 很神奇吧,我以前是不知道这个规律的.从一开始的一位格雷码,0,1,开始,每次对称的在上一个前面添加上

leetcode第一刷_Permutation Sequence

这道题还挺好的,如果你的思路是每次生成一个全排列,然后累计到k次,那么停下来吧,肯定超时了亲.. 微软今年的笔试题里有一道类似的,我之前已经提到过了,是只有0和1的字符串,求第k个排列是什么样子的.这道题比那个要难一些,但是总体的思路是一样的.假设有n个数要组成排列,求第k个排列.像填表一样,从高位往地位,逐个填写.先考虑有n-1个数要组成排列,最多有(n-1)!种情况,当第n个数加入后,第n个数可以是从1增加到n的,没增加1,所包含的全排列数就会增加(n-1)!,因此,如果用k/(n-1)!,

leetcode第一刷_Spiral Matrix

我觉得这个题好无聊啊,好端端一个数组,干嘛要跟比巴卜一样转一圈输出呢.. 思想很简单,每次从左到右,再从上到下,在从右到左,再从下到上.问题是每次到什么时候该改变方向.我的做法是用一个变量保存当前在第几层,这个层是相对于从外向内有几圈来说的.注意想清楚边界的话这个题一点也不难.有个细节,我的循环退出条件是访问的数跟矩阵总个数之间的关系,如果有一次在判断进入循环是条件是满足的,但是在循环内部不满足了,我的策略是忽略这种情况,虽然这样会在结果集中多加一些重复的数据,但是以我的算法,一定是先访问没有访

leetcode第一刷_Scramble String

字符串的好题.题干解释的非常复杂,一下让人不知所措了. 这道题到底是什么意思呢?最终的结果是把一个字符串中字母的顺序打乱了,让你判断一个字符串能不能由另一个字符串打乱得到.那打乱这个过程是怎么做的呢,很简单,给你一个字符串,你必须先找一个点把它砍成两半,你可以通过交换这两半的顺序来打乱源字符串的顺序,也就是在两半中的字符与另一半中所有字符的相对顺序是统一的.对于每一半,都可以重复上面的过程. 那想一下,怎么知道打断的那个点在哪呢?穷举.怎么知道打断之后有没有做交换操作呢?两种情况递归,有一条走的