引子
思路:看到兩個(gè)序列去匹配的問題鹦牛,最自然的想法是雙層循環(huán)嘗試對(duì)齊匹配,我們假設(shè)表格數(shù)字為1代表匹配成功勇吊,0代表匹配失敗曼追。
分析:分別遍歷s和p兩個(gè)字符串,如果p[i] == s[j]汉规,則表示匹配成功礼殊,否則直接退出循環(huán)遍歷。
用代碼也很好實(shí)現(xiàn)
bool isMatch(char * s, char * p){
int n = strlen(s);
int m = strlen(p);
int dp[m][n];
for(int i = 0; i < m; I++)
for(int j = 0; j < n; j++)
dp[i][j] = 0;
for (int i = 0; i < m; i++){
for (int j = 0; j < n; j++){
if (s[j] == p[I])
dp[i][j] = 1;
else
return false;
}
}
return dp[m][n];
}
但是针史,事實(shí)并不會(huì)這樣簡(jiǎn)單晶伦,因?yàn)橛刑厥夥?hào)星號(hào)('*')和問號(hào)('?')的加入,讓事情變得復(fù)雜啄枕。細(xì)致分析的話可能情況很多已經(jīng)無從下手了婚陪。
1、s 和 p 都為空
既然無從下手射亏,我們就用最笨的辦法近忙,從最簡(jiǎn)單的方法逐步去逼近自然的情況竭业,自然智润,最簡(jiǎn)單的情況當(dāng)然是 p 和 s 均為空了。
分析:兩個(gè)都為空未辆,是匹配成功的窟绷。
結(jié)論:匹配成功
2、p為空
假設(shè) p = ''咐柜, s = 'abcd'兼蜈,將情況1中融合進(jìn)去
分析:當(dāng) p 為空時(shí)攘残,
1、如果 s 為空的時(shí)候为狸,匹配成功
2歼郭、如果 s 非空的時(shí)候,匹配失敗
結(jié)論:匹配失敗
3辐棒、s為空
如果拿上例來類推的話病曾,會(huì)理所當(dāng)然的認(rèn)為:
當(dāng) s 為空時(shí),
1漾根、如果 p 為空的時(shí)候泰涂,匹配成功
2、如果 p 非空的時(shí)候辐怕,匹配失敗
對(duì)于情況1逼蒙,當(dāng)然沒有疑問了;對(duì)于情況2寄疏,如果沒有星號(hào)('*')是牢、問號(hào)('?')特殊符號(hào)搗亂的話,確實(shí)也會(huì)匹配失敗赁还,但如果考慮星號(hào)('*')可以匹配空的情況的時(shí)候妖泄,如下圖:
3.1 p = '*'
同樣將情況1融合進(jìn)來
結(jié)論:匹配成功
3.2、p為'**' 時(shí)
結(jié)論:匹配成功
也就是說艘策,當(dāng) s 為空時(shí)蹈胡,
1、如果 p 為空或者全部由星號(hào)('*')組成朋蔫,匹配成功罚渐;
2、p 除了星號(hào)('*')之外還有其他的字符或者問號(hào)('?')的時(shí)候驯妄,還是需要分析一下:
3.3荷并、 p包含星號(hào)('*')外的其他字符
3.3.1、p = '**a' 時(shí)
分析:星號(hào)('*')可以匹配空字符沒有錯(cuò)青扔,所以dp[0]源织、dp[1]都可以表示匹配成功,這個(gè)在上例中已經(jīng)分析過了微猖,可是到匹配p[2]的時(shí)候谈息,匹配失敗
結(jié)論:匹配失敗
我們?cè)僬{(diào)整一下星號(hào)('*')與'a'出現(xiàn)的先后次序
3.3.2、p = '*a*' 時(shí)
分析:同樣的凛剥,dp[0] = 1侠仇,可是dp[1] = a,依然無法匹配空字符,所以dp[1] = 0逻炊,這里的區(qū)別在于雖然p[2] = '*'互亮,可是dp[2]無法延續(xù)上述的情況,因?yàn)橹虚g被p[1]隔斷了余素,所以這里的dp[2] = 0豹休。如果從物理意義上解釋的話,p = '*a*'應(yīng)該去匹配一個(gè)字符串桨吊,字符串的格式為中間含有一個(gè)字符'a'慕爬,這個(gè)字符'a'的左右兩邊包含任意多個(gè)由空格和任意字母組成的序列,比如 s = 'cd ef a we'屏积,但是假如s 為空医窿,那肯定是匹配不上的。
結(jié)論:匹配失敗
再調(diào)換一下星號(hào)('*')與'a'出現(xiàn)的先后次序
3.3.3炊林、 p = 'a**' 時(shí)
分析:同上例一樣姥卢, p = 'a**' 是用來匹配以字符'a'開頭的,后面跟著包含任意多個(gè)由空格和任意字母組成的序列渣聚,比如 s = 'ad fwe'独榴,而s 為空的情況,也是匹配不上的奕枝。
結(jié)論:匹配失敗
3.3.4棺榔、 p 只包含星號(hào)('*')和 '?'
同樣的分析可以將上述的字符'a'換成'?',結(jié)論也是一樣的
分析:我們?cè)诳偨Y(jié)星號(hào)('*')與字母出現(xiàn)的順序可以發(fā)現(xiàn):只有從第一位開始連續(xù)出現(xiàn)星號(hào)('*')的時(shí)候隘道,才會(huì)連續(xù)的出現(xiàn)1症歇,否則就意味著1的連續(xù)性被打斷,后續(xù)均為0谭梗。知道這一點(diǎn)忘晤,對(duì)將來的一般性模式拓展及總結(jié)很有用,我們甚至可以用代碼來實(shí)現(xiàn)在s為空p非空情況下激捏,匹配結(jié)果究竟是0或者1的規(guī)律设塔。很明顯p有下圖三種情況,而且远舅,這個(gè)情況很簡(jiǎn)單闰蛔,我們可以直接畫出結(jié)果。
總結(jié)图柏,當(dāng) s 為空時(shí)序六,
1、如果 p 為空或者全部由星號(hào)('*')組成爆办,匹配成功难咕;
2课梳、p 除了星號(hào)('*')之外還有其他的字符或者問號(hào)('?')的時(shí)候距辆,匹配失敗余佃,失敗點(diǎn)在p第一次其他字母'a-z'或者問號(hào)('?'),接下來無論出現(xiàn)任何字符跨算,如果p的第一個(gè)字符不是星號(hào)('*')爆土,則從第一個(gè)位置開始均匹配失敗。
如果要將3.3節(jié)的所有情況用代碼來實(shí)現(xiàn)的話诸蚕,假設(shè) p 的長度為m時(shí)步势,定義一個(gè)數(shù)組 dp[m + 1],用來記錄 p 與空字符串 s 匹配情況背犯,其中的dp[0] = 1坏瘩,代表 p 也為空的時(shí)候,p與s匹配成功的狀態(tài)漠魏,同時(shí)也將dp[0] = 1作為我們的初始狀態(tài)倔矾。
int m = strlen(p);
int dp[m + 1];
dp[0] = 1;
for (int i = 1; i <= m; I++)
dp[i] = dp[i - 1] && p[i - 1] == '*';
4、s p 均不為空
有了上面的鋪墊柱锹,我們來總結(jié)最后一種情況哪自。其實(shí),對(duì)于s禁熏、p均不為空的情況(假設(shè)s長度為n壤巷,p長度為m),可以將以上所述的情況整合在一起瞧毙,整合的方法就是新初始化一個(gè)數(shù)組dp[m + 1][n + 1]胧华,其中的第0行和地0列的值其實(shí)就是上述所有情況的整合,而且宙彪,第0行和第0列的所有數(shù)字撑柔,可以直接算出來。我們依然從最簡(jiǎn)單的情況開始:
4.1您访、s = 'a'铅忿,p = 'b'
分析:因?yàn)閜[0] != s[0], 所以dp[1][1] = 0
結(jié)論:匹配失敗
4.2、p = 'a' 灵汪,s = 'a'
分析:因?yàn)閜[0] == s[0], 所以dp[1][1] = 1
結(jié)論:匹配成功
4.3檀训、p = '*' ,s = 'a'
分析:因?yàn)閜[0] = '*' , 可以匹配任意字符享言,所以無論s[0]為任何字母均可以匹配, 所以dp[1][1] = 1
結(jié)論:匹配成功
4.4峻凫、p = '?' ,s = 'a'
分析:因?yàn)閜[0] = '?' , 可以匹配任意非空字符览露,所以無論s[0]為任何字母均可以匹配, 所以dp[1][1] = 1
結(jié)論:匹配成功
以上四種情況荧琼,是最簡(jiǎn)單的情況,我們當(dāng)然可以通過四個(gè)if條件判斷就可以得出結(jié)果,我們接下來再升級(jí)一下難度命锄,看看能不能總結(jié)出一個(gè)規(guī)律堰乔,可以覆蓋最一般的情況。
4.5脐恩、p = 'ab' 镐侯,s = 'ab'
dp的初始化結(jié)果并結(jié)合4.2分析的dp[1][1] = 1的結(jié)論可知:
分析:本例其實(shí)就是引子的簡(jiǎn)化版,但也并沒有什么不同∈幻埃現(xiàn)在還剩下三個(gè)空格需要我們補(bǔ)充苟翻,針對(duì)本案例,我們只希望知道dp[2][2]的值骗污,因?yàn)閜[1] == s[1] == 'b' , 所以dp[2][2] = 1崇猫。
結(jié)論:匹配成功
現(xiàn)在問題來了,如果我們每次遇到p[i] == s[j], 就設(shè)定dp[i][j] = 1需忿,這個(gè)是不是我們要找的規(guī)律呢邓尤?
答案當(dāng)然是不能,請(qǐng)看下例
4.6贴谎、p = 'cb' 汞扎,s = 'ab'
dp的初始化結(jié)果并結(jié)合4.1分析的dp[1][1] = 0的結(jié)論可知:
分析:我們直接來看dp[2][2],我們知道擅这,我們定義的dp數(shù)組澈魄,就是來記錄s和p對(duì)應(yīng)位置字符的匹配情況的,針對(duì)本例仲翎,很明確痹扇,是不匹配的,雖然p[1] == s[1]溯香,但是p[0] != s[0]鲫构,所以dp[2][2] 和 dp[1][1] 均等于0。
結(jié)論:匹配失敗
規(guī)律一:
當(dāng)p[i] == s[j] 時(shí)判定dp[i + 1][j + 1]的值時(shí)
1玫坛、如果dp[i][j] == 0结笨, 則dp[i + 1][j + 1] = 0,表達(dá)的意思是如果s和p前一個(gè)對(duì)應(yīng)位置匹配失敗湿镀,當(dāng)前位置的字母也跟著匹配失敗
2炕吸、如果dp[i][j] == 1, 則dp[i + 1][j + 1] = 1勉痴,表達(dá)的意思是如果s和p前一個(gè)對(duì)應(yīng)位置匹配成功赫模,當(dāng)前位置的字母也跟著匹配成功
這里需要解釋一下的是,因?yàn)槲覀兎謩e在s和p之前多加了一行和一列的用于概括s或者p為空的情況蒸矛,所以當(dāng)記錄p[i] 和 s[j]匹配的時(shí)候瀑罗,dp相應(yīng)p[i] 和 s[j]的位置不是dp[i][j] 而是dp[i + 1][j + 1]胸嘴,dp[i][j]對(duì)應(yīng)的記錄的是p[i - 1] 和 s[j - 1]
上面的規(guī)律也很容易用代碼實(shí)現(xiàn)
if(p[i] == s[j]){
if(dp[i][j] == 1)
dp[i + 1][j + 1] = 1;
else
dp[i + 1][j + 1] = 0;
}
簡(jiǎn)化一下也就是這樣
if(p[i] == s[j])
dp[i + 1][j + 1] = dp[i][j];
4.7、 p = 'a?c' 斩祭,s = 'abc'
dp的初始化結(jié)果并結(jié)合4.2分析的dp[1][1] = 0的結(jié)論可知:
分析:因?yàn)閱柼?hào)('?')可以匹配任何非空的字母劣像, 不難得出,當(dāng)p[i] == '?'時(shí)停忿,不管s[j]為任何非空的字母,其實(shí)都可以直接認(rèn)為p[i] == s[j] 蚊伞,對(duì)dp的處理都可以沿用規(guī)律一的總結(jié)席赂,我們就暫且稱這樣的發(fā)現(xiàn)為規(guī)律二吧。
結(jié)論:匹配成功
規(guī)律二:
當(dāng)p[i] == '?' 時(shí)判定dp[i + 1][j + 1]的值時(shí)
1时迫、如果dp[i][j] == 0颅停, 則dp[i + 1][j + 1] = 0,表達(dá)的意思是如果s和p前一個(gè)對(duì)應(yīng)位置匹配失敗掠拳,當(dāng)前位置的字母也跟著匹配失敗
2癞揉、如果dp[i][j] == 1, 則dp[i + 1][j + 1] = 1溺欧,表達(dá)的意思是如果s和p前一個(gè)對(duì)應(yīng)位置匹配成功喊熟,當(dāng)前位置的字母也跟著匹配成功
規(guī)律二也很容易用代碼表示出來
if(p[i] == '?')
dp[i + 1][j + 1] = dp[i][j];
將規(guī)律一和規(guī)律二合并之后就變成
if(p[i] == s[j] || p[i] == '?')
dp[i + 1][j + 1] = dp[i][j];
4.8、s = 'abc'姐刁, p = 'a*c'
dp的初始化結(jié)果并結(jié)合4.2分析的dp[1][1] = 1的結(jié)論可知:
分析:dp[1][1] = 1的結(jié)論我們已經(jīng)反復(fù)看了幾遍了芥牌,下面我們來確定dp[2][2]的值,如果參考規(guī)律一和規(guī)律二的結(jié)論聂使,星號(hào)('*')可以匹配任何的字母壁拉,針對(duì)本例中dp[1][1] = 1的前提,dp[2][2]也應(yīng)該等于1柏靶,同理dp[3][3] = 1弃理,完美匹配。
結(jié)論:我們?nèi)搜劭隙ㄊ悄芸闯鍪瞧ヅ涞?br>
上面的分析屎蜓,可能會(huì)讓人誤認(rèn)為星號(hào)('*')沒有特殊的地方痘昌?錯(cuò)!
我們繼續(xù)往下看炬转。
4.9控汉、s = 'dec', p = 'a*c'
dp的初始化結(jié)果并結(jié)合4.2分析的dp[1][1] = 0的結(jié)論可知:
分析:如果參考規(guī)律一的結(jié)論返吻,dp[1][1] = 0姑子,那么dp[2][2] 也等于0嗎?是的测僵,dp[2][2] = 0街佑,那么dp[3][3] 呢谢翎?看到p[2] == s[2] == 'c',同樣根據(jù)規(guī)律一遞推dp[3][3] = 0沐旨,事實(shí)上dp[3][3] 也確實(shí)應(yīng)該等于 0森逮。別著急去翻4.8,再耐心往下看兩步磁携。
結(jié)論:人眼知道是匹配失敗的褒侧,但是如何讓機(jī)器代碼來計(jì)算還需要進(jìn)一步的分析
4.10、s = 'ac'谊迄, p = 'a*c'
dp的初始化結(jié)果并結(jié)合4.8分析的結(jié)論可知:
分析:我們先說結(jié)論吧闷供,s = 'ac', p = 'a*c' 是可以匹配成功的统诺,p[0] 匹配 s[0]歪脏, p[1] 匹配空,p[2] 匹配 s[2]粮呢⌒鍪В可是當(dāng)我們用代碼來推導(dǎo)dp[3][2]的時(shí)候,是應(yīng)該依據(jù)dp[2][2]的值嗎啄寡?
結(jié)論:還是往下看吧
現(xiàn)在豪硅,是時(shí)候來總結(jié)一下星號(hào)('*')的真正規(guī)律了,在之前的所有討論中挺物,我們只關(guān)心dp數(shù)組對(duì)角的元素舟误,原因也是因?yàn)椋覀兇竽X更愿意先看到最理想或者最容易思考的情況姻乓,也就是s 和 p長度相等的情況嵌溢,其實(shí)我們也應(yīng)該關(guān)注對(duì)角線外的元素,因?yàn)轭}目的設(shè)計(jì)星號(hào)('*')的可以匹配任意字母甚至包括非空字符蹋岩,所以這就會(huì)導(dǎo)致即使p和s不等長赖草,依然可能匹配成功。
敲黑板剪个!星號(hào)規(guī)律('*')總結(jié)開始了
星號(hào)('*')的出現(xiàn)把問題的復(fù)雜度升級(jí)了許多秧骑,開始短路了。
我們?cè)賮碇厣暌幌滦翘?hào)('*')的定義:可以匹配任意字符串(包括空字符串)扣囊。
雖然星號(hào)('*')看似很強(qiáng)大乎折,但是也要考慮束縛條件,比如在例4.9中侵歇,s = 'dec'骂澄, p = 'a*c' ,p[0] != s[0]惕虑,當(dāng)要用p[1]也就是星號(hào)('*')來做匹配的時(shí)候坟冲,自然是要考慮dp[1][1]的值磨镶。再看一個(gè)星號(hào)('*')匹配空字符的情況,鞏固一下p和s不等長健提,依然可能匹配成功的例子琳猫。
4.11、s = 'abdc'私痹, p = 'a*c'
dp的初始化結(jié)果并結(jié)合4.8分析的結(jié)論可知:
分析:依然先說結(jié)論脐嫂,本例中的 s 和 p 確實(shí)是匹配成功的,p[0] 匹配 s[0]紊遵, p[1] 同時(shí)匹配 s[1] 和 s[2]账千,p[2] 匹配 s[3]。但是我們dp[3][3]的時(shí)候癞蚕,由于p[2] = 'c', s[2] = 'd'蕊爵,那么dp[3][3] = 0辉哥,本例特殊的點(diǎn)在于 m != n桦山,我們最終是要求d[m][n],對(duì)于本例則是要求出d[3][4]醋旦,可是d[3][4]怎么求呢恒水,比照規(guī)律一,我們要知道他的上一個(gè)的匹配結(jié)果饲齐,也就是需要知道d[2][3]钉凌,物理上的解釋就是首先要判斷它的前一個(gè)字符對(duì)是否能匹配成功∥嫒耍看來我們真的需要關(guān)注dp數(shù)組的所有元素的值了御雕。
對(duì)于第一行的值,也就是用p[0]來匹配 s 各個(gè)位置的值滥搭,p[0] = a酸纲,很自然,當(dāng)遍歷s的時(shí)候瑟匆,遇到s[i] == 'a'闽坡,則dp[1][i + 1] = 1,否則dp[1][i + 1] = 0愁溜。補(bǔ)全之后如下圖所示:
但是對(duì)于第二行的值就難辦了疾嗅,因?yàn)閜[1] = '*',所以當(dāng)p[1] 與 s[0] = 'a'匹配的時(shí)候應(yīng)該等于1呢還是應(yīng)該等于0呢冕象?也許有人要說了當(dāng)然等于1了代承,因?yàn)樾翘?hào)('*')的定義:可以匹配任意字符串(包括空字符串)。如果真的是這樣的話渐扮,那么是不是說只要星號(hào)('*')所在的行次泽,都應(yīng)該為1穿仪?
大寫的STOP
我們先跳回例4.9,按照上述方法意荤,我們也很容易補(bǔ)全第一行和“第二行”如下圖所示:
細(xì)心的同學(xué)發(fā)現(xiàn)了啊片,我們補(bǔ)全第一行是沒有問題的,第一行的數(shù)據(jù)值全部為0玖像,但是我們把第二行加了雙引號(hào)紫谷,權(quán)當(dāng)假設(shè)之前的結(jié)論是準(zhǔn)確的,那我們是不是可以繼續(xù)把第三行補(bǔ)全呢捐寥?
震驚笤昨!dp[3][4] = 0
可是明明dp[3][3] = 1!
這下握恳,我們是不是已經(jīng)猜到了瞒窒,雖然星號(hào)('*')可以匹配任意字符,但是武斷的把星號(hào)('*')根據(jù)規(guī)律一來判斷乡洼,應(yīng)該是不對(duì)的崇裁。
我們把4.9 和4.11的圖合并在一起看吧
兩個(gè)圖合并在一起之后,我們同步來看p[1] = '*'束昵,而且我們專注于求例dp[2][1]拔稳,對(duì)于左右兩個(gè)圖,他們唯一不同的點(diǎn)在于dp[1][1]的值锹雏,左圖的dp[1][1] = 0巴比,而右側(cè)的dp[1][1] = 1,原因也是因?yàn)樽髨D的p[0] 和 s[0]確實(shí)沒有匹配成功礁遵,而右圖的p[0] 和 s[0]匹配成功了轻绞。
從物理意義上理解的話,當(dāng)遇到星號(hào)('*')的時(shí)候:
1佣耐、如果星號(hào)('*')前面的元素(針對(duì)本例星號(hào)('*')前面的元素是'a')匹配成功(比如右圖的情況)政勃,那么星號(hào)('*')所在的對(duì)應(yīng)位置(本例中的dp[2][1])也可以假設(shè)是匹配成功,即右圖dp[2][1] = 1晰赞;
2稼病、如果星號(hào)('*')前面的元素(針對(duì)本例星號(hào)('*')前面的元素是'a')(比如左圖的情況)那么星號(hào)('*')所在的對(duì)應(yīng)位置(本例中的dp[2][1])也可以假設(shè)是匹配失敗,即左圖dp[2][1] = 0掖鱼。
再把圖更新一下啊
這樣是不是說:
如果p[i] == '*'然走,當(dāng)計(jì)算dp[i + 1][j + 1]的值時(shí),只需要去查看dp[i][j + 1]戏挡。用代碼表示就是
if (p[i] == '*')
dp[i + 1][j + 1] = dp[i][j + 1];
先把假設(shè)再完善一下
應(yīng)該已經(jīng)發(fā)現(xiàn)不對(duì)了芍瑞,對(duì)于左圖,在星號(hào)('*')所在的行全部置0其實(shí)沒問題褐墅,因?yàn)樽髨D本身確實(shí)匹配失敗了拆檬,可是右圖呢洪己,我們本來就知道右圖是匹配成功了呀,我們把回去看 圖4.9和4.11合并圖_b 竟贯,我們現(xiàn)在重點(diǎn)關(guān)注一下左右兩圖dp[2][2]的位置
先說相同點(diǎn):兩個(gè)圖中dp[2][2]正上方也就是dp[1][2]的位置均為0
再說不同點(diǎn):
左圖中答捕,dp[2][2]的左方dp[2][1]和左上方dp[1][1]均為0;
右圖中屑那,dp[2][2]的左方dp[12][1]和左上方dp[1][1]均為1拱镐。
會(huì)不會(huì)有疑問,星號(hào)('*')左方和左上方的位置一定相等呢持际?或者說當(dāng)遇到星號(hào)('*')的時(shí)候沃琅,是不是還要考慮星號(hào)('*')左方和左上方的位置的值呢?因?yàn)檫@兩個(gè)位置確實(shí)會(huì)現(xiàn)實(shí)星號(hào)('*')之前的匹配情況啊蜘欲。針對(duì)左右兩圖中的dp[2][2]益眉,其實(shí)他們左方dp[2][1]本身就是由左上方dp[1][1]值決定的,所以左方dp[2][1]已經(jīng)反應(yīng)了左上方dp[1][1]的匹配情況了姥份。
我們先把dp[2][2]的值究竟是1 還是0 先擱置下來郭脂,我們來看看dp[2][2]右側(cè)的dp[2][3]的值
先說相同點(diǎn):兩個(gè)圖中dp[2][3]正上方也就是dp[1][3]的位置均為0,左上方也就是位置dp[1][2]均為0殿衰,dp[2][3]的左方dp[2][2]的值還不好確定
再說不同點(diǎn):
完了朱庆,沒有不同點(diǎn)盛泡,唯一的不同點(diǎn)就是左右兩圖后面的列數(shù)不一致闷祥。
可是實(shí)際上呢,我們應(yīng)該隱隱約約可以看到傲诵,這兩個(gè)位置的值應(yīng)該也是不同的凯砍。可是這個(gè)不同怎么遞推出來呢拴竹?依然是隱隱約約可以察覺悟衩,對(duì)于位置dp[2][3],其實(shí)左側(cè)dp[2][2]的值可以反應(yīng)出其之前的匹配情況的栓拜,同樣的座泳,dp[2][2]也是可以通過dp[2][1]來反應(yīng)區(qū)別不同。
現(xiàn)在我們先把dp[2][2]的值分別填上
也就是說幕与,
如果p[i] == '*'挑势,當(dāng)計(jì)算dp[i + 1][j + 1]的值時(shí),不僅需要去查看dp[i][j + 1]啦鸣,也需要看dp[i + 1][j]潮饱。經(jīng)過上述一系列的分析,只需要
dp[i][j + 1] 或者dp[i + 1][j]其中一個(gè)為1诫给,那么dp[i + 1][j + 1] 就等于 1香拉。用代碼表示如下:
if (p[i] == '*')
dp[i + 1][j + 1] = dp[i][j + 1] || dp[i + 1][j];
可是上面只是我們的推測(cè)啦扬,事實(shí)真的是這樣嗎?我們?cè)侔炎畛R?guī)的情況擺出來
4.12凫碌、當(dāng) p = '*a*b'扑毡,s = 'adceb' 時(shí)的初始化結(jié)果:
綜合以上的分析,全部填滿之后
4.13盛险、p = 'ab'僚楞,s = 'adceb'
dp的初始化結(jié)果并結(jié)合4.8分析的結(jié)論可知:
至此,所有的分析都結(jié)束了枉层,我們開始正式設(shè)計(jì)代碼泉褐。
1、首先要獲取p 和 s 的長度鸟蜡,假設(shè)分別為m膜赃、n;
2揉忘、聲明一個(gè)數(shù)組dp[m + 1][n + 1]跳座,并對(duì)其第零行和第零列進(jìn)行初始化;
3泣矛、分別對(duì)p 和 s進(jìn)行遍歷疲眷,將dp數(shù)組的每個(gè)位置進(jìn)行賦值
有三種情況:
3.1、 如果s[i]==p[j] or p[j]=='?' 則 dp[i+1][j+1] = dp[i][j]
3.2您朽、 如果p[j]=='*' 則dp[i+1][j+1] = dp[i][j+1] || dp[i+1][j]
3.3狂丝、 s[i] != p[j] 則 dp[i+1][j+1]=0;
4、返回結(jié)果即dp[m][n]即可
其實(shí),這個(gè)圖就是將圖3.6和圖2整合了一下而已。
好了启泣,到了這一步恬涧,我們就可以來分析計(jì)算剩下來的所有空格應(yīng)該對(duì)應(yīng)的值了,也就是從這一步開始,我們要分析動(dòng)態(tài)規(guī)劃里最難找的最優(yōu)子狀態(tài)了。
bool isMatch(char * s, char * p){
int n = strlen(s);
int m = strlen(p);
bool dp[m + 1][n + 1];
for (int i = 0; i < m + 1; i++)
for(int j = 0; j < n + 1; j++)
dp[i][j] = 0;
dp[0][0] = true;
for (int i = 1; i <= m; i++)
dp[i][0] = dp[i - 1][0] && p[i - 1] == '*';
for (int i = 0; i < m; i++){
for (int j = 0; j < n; j++){
if (s[j] == p[i] || p[i] == '?'){
dp[i + 1][j + 1] = dp[i][j];
}
else if (p[i] == '*'){
dp[i + 1][j + 1] = dp[i][j + 1] || dp[i + 1][j];
}
else{
dp[i + 1][j + 1] = 0;
}
}
}
return dp[m][n];
}