@author stormma
@date 2018/03/24
生命不息,奮鬥不止
題目1
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
DP思路分析
首先我們選取狀態, 用boolean[][] dp
數組來標記s中i個字符串是否匹配於p中j個字符串, 當然如果匹配則爲True
, else False
。
下面我們來分析一下狀態是怎麼變化的。
且看圖:
通過上面的狀態轉移圖, 我們很容易可以得到:
1. 邊界初始化, 當si = 0;
(即s字符串取0個字符), ps 0 -> p.length()
的初始化爲: 如果此時的p[pi]
這個字符是*
的話,
那麼它的值應該是上一個的值, 即dp[0][pi] = dp[0][pi - 1]
, 如果此時p[pi] != '*'
呢, 那麼肯定是False
. 當pi = 0;
,
初始化dp[si][0] = False && si >= 1
(p中取出0個字符, s中取出任意個(>0), 都是不匹配的), dp[0][0] = True
(此時s中取0個字符和p中取0個字符, 無疑是匹配的).
2. 邊界初始化完成之後, 我們分析一下其他地方怎麼轉移. 分爲以下兩種情況來討論:
- if p[pi] == '*'
, 無非是從它的左邊和上邊轉移而來的。
- if p[pi] == '?' || p[pi] == s[si]
, 那麼它肯定是從它左上角轉移而來。
狀態轉移分析完成之後, 顯然答案便是dp[s.length()][p.length()]
AC代碼
/**
* dp解決
* time running time: O(n*m)
* extra space: O(n*m)
*/
static class Solution2 {
public boolean isMatch(String s, String p) {
if (s == null && p == null) return true;
if (s == null || p == null) return false;
// dp[i][j]表示s字符串中前i個和p中前j個字符是否匹配
boolean[][] dp = new boolean[s.length() + 1][p.length() + 1];
dp[0][0] = true;
// 初始化邊界
for (int i = 1; i <= p.length(); i++) {
dp[0][i] = p.charAt(i - 1) == '*' && dp[0][i - 1];
}
for (int i = 1; i <= s.length(); i++) {
for (int j = 1; j <= p.length(); j++) {
if (p.charAt(j - 1) == '?' || s.charAt(i - 1) == p.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
} else if (p.charAt(j - 1) == '*') {
// ab abcd
// ab* ab*
dp[i][j] = dp[i][j - 1] || dp[i - 1][j];
}
}
}
return dp[s.length()][p.length()];
}
}
解法2
我們可以用一個指針來記錄*
出現的位置, 再用兩個指針標記s
和p
的位置: sp
和pp
指針, 再定義一個
輔助變量match
, 分析如下:
1. 如果此時s[sp] == p[pp] || p[pp] == '?'
, 那麼我們只需讓sp
和pp
指針走一步即可。
2. 如果此時的p[pp] == '*'
, 我們用star
來記錄一下此時*
出現的位置, 用輔助變量match
來記錄一下
此時的s
走到了哪個地方, 然後讓pp
再走一步。
3. 如果此時star
存在有效的值, 並且都不滿足以上兩種情況, 那麼我們讓pp
回到上一次出現*
的下一個位置, 讓p
回到上次出現*
的時候, s
的位置的下一個位置。
爲什麼這樣做呢? 我們來看一個例子:
s = bbaec
p = *c
sp = 0, pp = 0, match = 0, star = pp = 0, match = sp = 0 => pp++ => pp = 1
sp = 0, pp = 1, match = 0, star = 0; 此時b != c :
然後s繼續走: pp = star + 1 = 1, match++ => match = 1, sp = 1
sp = 1, pp = 1, match = 1, star = 0, 此時 b != c:
s繼續走: pp = star + 1 = 1, match++ => match = 2, sp = 2
sp = 2, pp = 1, match = 2, star = 0, 此時 a != c:
s繼續走: pp = star + 1 = 1, match++ => match = 3, sp = 3
sp = 3, pp = 1, match = 3, star = 0, 此時 e != c:
s繼續走: pp = star + 1 = 1, match++ => match = 4, sp = 4
sp = 4, pp = 1, match = 4, star = 0, 此時 c == c:
sp++ => sp = 5, pp++ => pp = 2
退出循環, 說明`s`走到了頭, 但是不確定`p`是否走到了頭, 外加一個判斷p的循環即可解決。
說到底, 如果遇到不符合字符串不相等並且不存在*
的時候, 就回退到上一個star出現的地方, 然後跳過可能被*
匹配的字符。
AC代碼
static class Solution1 {
/**
* time running: O(n)
* bbarc
* *c
* @param s
* @param p
* @return
*/
public boolean isMatch(String s, String p) {
if (s == null && p == null) return true;
if (s == null || p == null) return false;
int sp = 0, pp = 0, star = -1, match = 0;
while (sp < s.length()) {
if (pp < p.length() && (s.charAt(sp) == p.charAt(pp) || p.charAt(pp) == '?')) {
sp++;
pp++;
} else if (pp < p.length() && p.charAt(pp) == '*') {
star = pp;
match = sp;
pp++;
} else if (star != -1) {
pp = star + 1;
match++;
sp = match;
} else {
return false;
}
}
while (pp < p.length() && p.charAt(pp) == '*') pp++;
return pp == p.length();
}
}
題目2
Implement regular expression matching with support for '.' and '*'.
'.' 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
DP思路分析
此題和上一個題很類似, 我們還是採用dp
來解決, 狀態選擇情況一樣, boolean [][] dp = new boolean[s.length() + 1][p.length() + 1]
狀態轉移也很簡單, 在這就不畫圖分析了, 直接給出AC代碼。
AC代碼
static class Solution {
public boolean isMatch(String s, String p) {
if (p.isEmpty()) {
return s.isEmpty();
}
boolean[][] dp = new boolean[s.length() + 1][p.length() + 1];
dp[0][0] = true;
for (int j = 1; j <= p.length(); j++) {
if (p.charAt(j - 1) == '*') {
dp[0][j] = dp[0][j - 2];
}
}
for (int i = 1; i <= s.length(); i++) {
for (int j = 1; j <= p.length(); j++) {
if (p.charAt(j - 1) == '.' || s.charAt(i - 1) == p.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
} else if (p.charAt(j - 1) == '*') {
if (p.charAt(j - 2) == s.charAt(i - 1) || p.charAt(j - 2) == '.') {
dp[i][j] = dp[i][j - 2] || dp[i - 1][j];
} else {
dp[i][j] = dp[i][j - 2];
}
}
}
}
return dp[s.length()][p.length()];
}
}
博客來自於我的個人博客轉載請註明出處