Leetcode - Regular Expression Matching

**
Question:

'.' Matches any single character.
'*' Matches zero or more of the preceding element.

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", "a*") → true
isMatch("aa", ".*") → true
isMatch("ab", ".*") → true
isMatch("aab", "c*a*b") → true

**

这是我写过的最恶心的代码!!!我连续写了四个小时,针对不同的细节。及其繁琐。
最后实在扛不住了,放弃了。
现在很累。实在看不动分析了。明天再说吧


Referenced Code:

public class Solution {
    public boolean isMatch(String s, String p) {
        if (p.isEmpty()) {
            return s.isEmpty();
        }

        if (p.length() == 1 || p.charAt(1) != '*') {
            if (s.isEmpty() || (p.charAt(0) != '.' && p.charAt(0) != s.charAt(0))) {
                return false;
            } else {
                return isMatch(s.substring(1), p.substring(1));
            }
        }

        //P.length() >=2
        while (!s.isEmpty() && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.'))  {  
            if (isMatch(s, p.substring(2))) { 
                return true;                     
            }                                    
            s = s.substring(1);
        }

        return isMatch(s, p.substring(2));
    }
}

Test result:

Paste_Image.png

思路:
这次作业挺难的,很难的,对于我来说。。。
是一种模式识别,要考虑较多的情况。每次碰到这样的编程题,程序员之间,或者说,码农和程序员之间的区别就会体现出来了。这是一种思路的区别。码农想到的一定是用if将各种情况考虑到,比如我。而真正的程序员,想到的一定是,以一种更加宏观的思路,来解决这个问题,而不拘泥于细节。这种宏观的思路,就是,递归。
Recursion has helped code become simple.
首先说下我犯下的错误吧。
最严重的错误,给出的两个字符串,s , p, 其实只会在p中出现 "." and "". 因为是表达式匹配,所以s中不会出现这些符号。
然后就是对 '
'含义的理解。"*"表示的是,前面那个字符出现的次数。
比如,

aa = a*
aaa = a*
ac = ab*c
....

这里会有几个问题。
1.前面如果没有字符,即他是字符串的首位,那么,他会想普通字符一样与s中字符进行比较。
2.
若连续的出现,******,我们仍将它两个两个的分组,前面一个默认为有意义的字符,后面一个才表示其出现的次数。所以前面一个*与普通字符作比较时永远是不相等的。所以一定是false。

  1. ".*" 的情况比较特殊。
ab = .*
abc = .*
abcdefghijkldsadf = .*
abcd != .*c
abcd = .*abcd
abcd = .*bcd
abcd = .*cd
abcd = .*d
abcd = .*

可以看出,如果 .* 后面没有尾巴,他与任何的s相匹配。如果有尾巴,他的尾巴必须与s中的尾巴相匹配。
4.该用何种思路来处理 * 的比较。
我一开始的思路,或者大多数人的思路,一定是贪婪算法。就是将*表示的次数刷到最大。比如:

aaa = a*
aaaaaab = a*b

这个时候我们会将*刷到最大。然后进行下一组比较。但是,有些情况是特殊的,也是我最后发现的,然后决定放弃自己的思路看答案。

aaaaa = a*a

在这样一个匹配中,如果用最大化 * 思路的话,那么就是不匹配的了, *会一直刷到s结束。然后p最后还有一个 a。那就不匹配了。所以这个时候就得判断, * 到底代表多少次。这并不是越大越好。我当时也想了一些方法来处理这种情况。比如统计相同字符的个数。。。。后来觉得太复杂了。网上一看,果不其然,
用递归!

下来说下思路吧。
应该分成两种情况。
1.p中第二个字符不是""。 那么,该怎么比就怎么比。
2.p中第二个字符是"
"。那么。就需要用递归。
如果 s.charAt(0) == p.charAt(0) || p.charAt(0) == '.' ----> .* (万能匹配)
那么,就会判断, s是否与p.substring(2),即p字符串剩下的部分匹配。如果匹配,那么就返回true。如果不匹配。
s = s.substring(1).
再判断 s.charAt(0) == p.charAt(0) || p.charAt(0) == '.'
如果满足,继续判断 此时的s是否与p.substring(2),即p字符串剩下的部分匹配。如果匹配,那么就返回true。如果不匹配。

aaa = a*a

比较次序是

s = aaa;
s != a;
s = aa;
s != a;
s = a;
s = a;
return true;

这就是这个代码的基本思路。用递归来实现这种判断,判断s剩下的部分(尾部)是否与p的尾部相匹配。已经,何处开始是s的尾部。

**
总结:
递归,感觉有种人工智能的感觉。其实也只是一种遍历,但是这种遍历,又包含了人的思维。因为他的每次执行,不仅仅是简单的遍历,还会将我们的思维再次重复执行一遍。这就是递归。我的实力还远远达不到这个水平。。
而且这次写代码,我再次可以感受到,自己思维中的那些琐碎的,冗余的想法。
而且以后有什么思路,一定要写在纸上,尤其对于这么复杂的题目。情况一多,很多东西脑子里想到过,但后来又忘了。
**

Anyway, Good luck, Zhidong Liu!

My code:

public class Solution {
    public boolean isMatch(String s, String p) {
        if (p.length() == 0) {
            return s.length() == 0;
        }
        else if (p.length() == 1) {
            if (s.length() == 1 && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.')) {
                return true;
            }
            else {
                return false;
            }
        }
        else if (p.charAt(1) != '*') {
            if (s.length() == 0) {
                return false;
            }
            else if (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.') {
                return isMatch(s.substring(1), p.substring(1));
            }
            else {
                return false;
            }
        }
        else {
            if (isMatch(s, p.substring(2))) {
                return true;
            }
            while (s.length() > 0 && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.')) {
                 s = s.substring(1);
                if (isMatch(s, p.substring(2))) {
                    return true;
                }
            }
            return false;
        }
    }
}

看的同学的代码,觉得逻辑很清楚。
dfs
首先,p每次移动的单位是2位。所有,如果p中含有 *,那么,一定在index = 1 的位置。
所以,
首先判断, p.length() == 0
如果等于 0, 那么s也必须等于0,否则就是false

然后,如果 p.length() == 1
那么,p不可能含有 *
所以可以简单地比较,
s.charAt(0) == p.charAt(0) || p.charAt(0)
此时可以返回true,否则返回false

如果p.length() >= 2 && p.charAt(1) != '*'
此时也可以放心的比较。
首先判断s 长度。如果已经是0了,因为 p第二位不是 , 所以一定不match,返回false
如果s.length() > 0, 并且s.charAt(0) == p.charAt(0) || p.charAt(0)
那么,就可以比较, isMatch(s.substring(1), p.substring(1))
有人会问,这里不是只移动了一格而没有移动两格吗?
因为第二位不是 '
'啊。所以我们移动一格并不会破坏顺序。

如果p.length() >= 2 && p.charAt(1) == '*'

这个时候,* 可以代表0个或多个他前面的字符。
所以,首先,如果*代表0个,那么我直接匹配s and p.substring(2)
即,
if (isMatch(s, p.substring(2))) {...}

然后如果匹配不成功,这个时候我就需要移动 s 了
首先看 s.charAt(0) 是否与 p.charAt(0) 匹配。
如果匹配,ok,移动一位,
s = substring(1);
然后匹配此时的 s and p
if (isMatch(s, p.substring(2))) {...}

如果不匹配。再判断此刻的s(注意,这里的s已经向右移动了一位。)
if s.charAt(0) 和 p.charAt(0), 是否匹配。

如果不匹配,退出循环,直接返回 false 就行。

注意这里,只有 p 第二位是 '*',我每次都移动两位。

差不多就这样了。这个解法比较通俗易懂,很不错。

一直记录生活,计划,的小笔记本丢了。很可惜。里面的东西也很复原了。幸亏上周把所有的日程记录到了 Mac Calendar上,减少了些损失。是啊,如果我的小笔记本是放在云端的,怎么会丢呢?
但是电子设备记录笔记,还是没有纸质笔记本的感觉好。
不知如何选。
这周经历了许多机会,许多失去。到现在,总体来说,还是收获更多。下周,下下周,真正的挑战就要来临了。现在还只是玩玩。
加油吧,多刷题,把简单medium 刷熟练了,高频hard也刷熟练了。

Anyway, Good luck, Richardo! -- 09/16/2016

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容