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

**

這是我寫過的最惡心的代碼!!!我連續(xù)寫了四個小時,針對不同的細節(jié)。及其繁瑣。
最后實在扛不住了,放棄了。
現(xiàn)在很累。實在看不動分析了。明天再說吧


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

思路:
這次作業(yè)挺難的,很難的,對于我來說。。。
是一種模式識別,要考慮較多的情況。每次碰到這樣的編程題,程序員之間,或者說,碼農(nóng)和程序員之間的區(qū)別就會體現(xiàn)出來了。這是一種思路的區(qū)別。碼農(nóng)想到的一定是用if將各種情況考慮到,比如我。而真正的程序員,想到的一定是,以一種更加宏觀的思路,來解決這個問題,而不拘泥于細節(jié)。這種宏觀的思路,就是,遞歸。
Recursion has helped code become simple.
首先說下我犯下的錯誤吧。
最嚴重的錯誤,給出的兩個字符串,s , p, 其實只會在p中出現(xiàn) "." and "". 因為是表達式匹配,所以s中不會出現(xiàn)這些符號。
然后就是對 '
'含義的理解。"*"表示的是,前面那個字符出現(xiàn)的次數(shù)。
比如,

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

這里會有幾個問題。
1.前面如果沒有字符,即他是字符串的首位,那么,他會想普通字符一樣與s中字符進行比較。
2.
若連續(xù)的出現(xiàn),******,我們仍將它兩個兩個的分組,前面一個默認為有意義的字符,后面一個才表示其出現(xiàn)的次數(shù)。所以前面一個*與普通字符作比較時永遠是不相等的。所以一定是false。

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

可以看出,如果 .* 后面沒有尾巴,他與任何的s相匹配。如果有尾巴,他的尾巴必須與s中的尾巴相匹配。
4.該用何種思路來處理 * 的比較。
我一開始的思路,或者大多數(shù)人的思路,一定是貪婪算法。就是將*表示的次數(shù)刷到最大。比如:

aaa = a*
aaaaaab = a*b

這個時候我們會將*刷到最大。然后進行下一組比較。但是,有些情況是特殊的,也是我最后發(fā)現(xiàn)的,然后決定放棄自己的思路看答案。

aaaaa = a*a

在這樣一個匹配中,如果用最大化 * 思路的話,那么就是不匹配的了, *會一直刷到s結束。然后p最后還有一個 a。那就不匹配了。所以這個時候就得判斷, * 到底代表多少次。這并不是越大越好。我當時也想了一些方法來處理這種情況。比如統(tǒng)計相同字符的個數(shù)。。。。后來覺得太復雜了。網(wǎng)上一看,果不其然,
用遞歸!

下來說下思路吧。
應該分成兩種情況。
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) == '.'
如果滿足,繼續(xù)判斷 此時的s是否與p.substring(2),即p字符串剩下的部分匹配。如果匹配,那么就返回true。如果不匹配。

aaa = a*a

比較次序是

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

這就是這個代碼的基本思路。用遞歸來實現(xiàn)這種判斷,判斷s剩下的部分(尾部)是否與p的尾部相匹配。已經(jīng),何處開始是s的尾部。

**
總結:
遞歸,感覺有種人工智能的感覺。其實也只是一種遍歷,但是這種遍歷,又包含了人的思維。因為他的每次執(zhí)行,不僅僅是簡單的遍歷,還會將我們的思維再次重復執(zhí)行一遍。這就是遞歸。我的實力還遠遠達不到這個水平。。
而且這次寫代碼,我再次可以感受到,自己思維中的那些瑣碎的,冗余的想法。
而且以后有什么思路,一定要寫在紙上,尤其對于這么復雜的題目。情況一多,很多東西腦子里想到過,但后來又忘了。
**

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 長度。如果已經(jīng)是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已經(jīng)向右移動了一位。)
if s.charAt(0) 和 p.charAt(0), 是否匹配。

如果不匹配,退出循環(huán),直接返回 false 就行。

注意這里,只有 p 第二位是 '*',我每次都移動兩位。

差不多就這樣了。這個解法比較通俗易懂,很不錯。

一直記錄生活,計劃,的小筆記本丟了。很可惜。里面的東西也很復原了。幸虧上周把所有的日程記錄到了 Mac Calendar上,減少了些損失。是啊,如果我的小筆記本是放在云端的,怎么會丟呢?
但是電子設備記錄筆記,還是沒有紙質筆記本的感覺好。
不知如何選。
這周經(jīng)歷了許多機會,許多失去。到現(xiàn)在,總體來說,還是收獲更多。下周,下下周,真正的挑戰(zhàn)就要來臨了。現(xiàn)在還只是玩玩。
加油吧,多刷題,把簡單medium 刷熟練了,高頻hard也刷熟練了。

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,002評論 6 542
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,400評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,136評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,714評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,452評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,818評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,812評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,997評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,552評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,292評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,510評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,721評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,121評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,429評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,235評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,480評論 2 379

推薦閱讀更多精彩內容