日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

每天一道LeetCode-----只可能有'.'和'*'的字符串正则匹配

發(fā)布時間:2024/4/19 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 每天一道LeetCode-----只可能有'.'和'*'的字符串正则匹配 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Regular Expression Matching

原題鏈接Regular Expression Matching

意思是模擬正則表達式的. *操作,判斷給定的目標字符串p是否匹配源字符串s。
規(guī)則

  • ‘.’匹配任意一個字符
  • ‘*’匹配前一個字符0次或者多次,即可以刪除它前面的字符

注:例子中isMatch(“ab”, “.“) == true說明”.“可以匹配的字符可以是任意組合,比如說此題中”.”匹配成”a”,那么因為 ” * ” 的前面是”.”,說明” * “可以匹配任意字符,不是說必須是前面”.”匹配的字符,也就是說”*”匹配的可以不是”.”匹配的”a”


假設s的長度為s_len,p的長度為p_len,那么可以分成比較s[0]和p[0],以及s[1 : s_len)和p[1 : p_len),再簡化一點,如果p中沒有” * “,可能會有” . “時
分析題目時可以分解成幾種情況,

  • (s[0] == p[0]) || (p[0] == ‘.’)。此時只需要比較s[1 : s_len)和p[1 : p_len)是否相等,遞歸即可實現(xiàn)
  • (s[0] != p[0]) && (p[0] != ‘.’)。此時無需繼續(xù)比較,返回false
  • 如果p中有” * “,那么情況增多

  • ((s[0] == p[0]) || (p[0] == ‘.’)) &&p[1] != ‘*’;此時和上面情況相同,比較下一個位置即可
  • ((s[0] == p[0]) || (p[0] == ‘.’)) && p[1] == ‘‘;根據(jù)” “的規(guī)則,p[0]可以被刪掉然后從p[2]開始和s[0]比較,這是一種情況。另一種是重復p[0]多次,多次的意思是p[0]后面可能有多個”p[0] * p[0] * p[0] * “,而模擬多次重復最好的方法就是不改變p的索引,仍然從p[0]開始比較s的下一個位置s[1]。
  • ((s[0] != p[0]) && (p[0] != ‘.’)) && p[1] == ‘*’;顯然直接刪除p[0],從p[2]開始比較s[0]
  • ((s[0] != p[0]) && (p[0] != ‘.’)) && p[1] != ‘*’;直接return fales,沒得比了,當前這個位置就匹配不了
  • 所以很多時候把所有情況列出來會很好的理清思路,這種當前狀態(tài)可能會影響后續(xù)狀態(tài)的問題是動態(tài)規(guī)劃的典型問題,所以直接套用動態(tài)規(guī)劃即可。
    需要注意的就是一些邊界條件,比方說i == s.size()的情況和j == p.size()的情況,又或者是j + 1 >= p.size()的情況,這種情況p[j + 1]不存在,就沒辦法判斷后面的字符是否是’*’。


    首先是遞歸的動態(tài)規(guī)劃,dp[i][j]的意思是s[i : s_len)是否和p[j : p_len)匹配,遞歸時需要有三種狀態(tài)

  • 1,true
  • 0,false
  • -1,還沒有被考慮
  • class Solution { public:bool isMatch(string s, string p) {/* 創(chuàng)建時數(shù)量多創(chuàng)建一個,即s.size() + 1和p.size() + 1* 原因在于遞歸時i, j可能等于s.size()和p.size()*/std::vector<std::vector<int> > dp(s.size() + 1, std::vector<int>(p.size() + 1, -1));return judge_match(0, 0, s, p, dp) == 1;}private:int judge_match(int i, int j, const std::string& s, const std::string& p, std::vector<std::vector<int> >& dp){/* 如果dp[i][j] != -1,說明s[i : s_len)是否和p[j : p_len)匹配已經判斷過,直接返回 * 這個位置就容易越界,是上面創(chuàng)建vector時數(shù)量加1的原因*/if(dp[i][j] != -1)return dp[i][j];/* 如果s和p都已經到達尾端,s沒有字符需要匹配,p也沒有可匹配的字符,直接返回true * 為什么需要判斷p也到尾端?* 如果p未到尾端,說明p可能字符比s多,同樣可能是無法匹配的,所以不能直接返回* 只是可能無法匹配的原因* p后面的每一個字符都可以被后面的'*'刪掉,從而和s完全匹配*/if(j >= p.size() && i >= s.size())return 1;/* 如果p到達尾端而s未到,說明s剩余的字符p已經沒有字符可以匹配了,返回false */else if(j >= p.size() && i < s.size())return 0;bool ans;bool first_match = (i < s.size()) && ((s[i] == p[j]) || (p[j] == '.'));/* 判斷p[j]后面的字符是否是'*',從而有刪掉p[j]和重復p[j]兩種可能* 注:重復p[j]的前提是s[i] == p[j] || p[j] == '.',否則s[i]無法匹配,只能刪除p[j]*/if(j + 1 < p.size() && p[j + 1] == '*')ans = (first_match && judge_match(i + 1, j, s, p, dp) == 1) || (judge_match(i, j + 2, s, p, dp) == 1);elseans = (first_match && judge_match(i + 1, j + 1, s, p, dp) == 1);dp[i][j] = ans ? 1 : 0;return dp[i][j];} };

    然后是非遞歸的,非遞歸的動態(tài)規(guī)劃很重要,真…的…很重要
    逐層向目標接近,有個選擇兩層for循環(huán)的界限的技巧,初始時是離目標最遠的狀態(tài)
    比如說如果目標是dp[0][0],那么初始時就是dp[s_len - 1][p_len - 1],如果目標是dp[0][p_len],那么初始時就是dp[s_len][0]
    但是,需要考慮邊界情況,比如說本題就需要考慮dp[s_len][0],dp[s_len][1]…dp[s_len][p_len - 1],原因在遞歸中也體現(xiàn)了,就是如果s到了尾端而p沒有到,也是有可能匹配成功的(p后面的字符全可以刪除時),所以初始時是dp[s_len][p_len - 1]

    class Solution { public:bool isMatch(string s, string p) {/* 數(shù)量加1的原因仍然是防止越界 */std::vector<std::vector<bool> > dp(s.size() + 1, std::vector<bool>(p.size() + 1, false));/* 上面遞歸時的邊界判斷,都到尾端時代表匹配 */dp[s.size()][p.size()] = true;/* i是從s.size()開始的,原因是dp[s_len][0] ... dp[s_len][p_len - 1]仍然可能是true */for(int i = s.size(); i >= 0; --i){for(int j = p.size() - 1; j >= 0; --j){bool first_match = (i < s.size()) && (s[i] == p[j] || p[j] == '.');if(j + 1 < p.size() && p[j + 1] == '*')dp[i][j] = (first_match && dp[i + 1][j]) || (dp[i][j + 2]);elsedp[i][j] = (first_match && dp[i + 1][j + 1]);}}return dp[0][0];} };

    Wildcard Matching

    原題鏈接Wildcard Matching

    這道題和上面的基本一樣了,不同點就是’*’的規(guī)則有改變,代碼如下
    沒什么特別的,也分遞歸和非遞歸,判斷的時候多了一個條件,就是’*’可以表示空字符

    class Solution { public:bool isMatch(string s, string p) {std::vector<std::vector<bool> > dp(s.size() + 1, std::vector<bool>(p.size() + 1, false));dp[s.size()][p.size()] = true;for(int i = s.size(); i >= 0; --i){for(int j = p.size() - 1; j >= 0; --j){bool first_match = (i < s.size()) && (s[i] == p[j] || p[j] == '?' || p[j] == '*');/* dp[i][j + 1]是讓'*'表示空字符 */if(p[j] == '*')dp[i][j] = (first_match && dp[i + 1][j]) || (first_match && dp[i + 1][j + 1]) || (dp[i][j + 1]);elsedp[i][j] = (first_match && dp[i + 1][j + 1]);}}return dp[0][0];} /*bool isMatch(string s, string p) {std::vector<std::vector<int> > dp(s.size() + 1, std::vector<int>(p.size() + 1, -1));return judge_match(0, 0, s, p, dp);}private:int judge_match(int i, int j, const std::string& s, const std::string& p, std::vector<std::vector<int> >& dp){if(dp[i][j] != -1)return dp[i][j];if(j >= p.size() && i >= s.size())return 1;else if(j >= p.size() && i < s.size())return 0;bool ans;bool first_match = (i < s.size()) && (s[i] == p[j] || p[j] == '?' || p[j] == '*');if(p[j] == '*')ans = (first_match && judge_match(i + 1, j, s, p, dp) == 1) || (first_match && judge_match(i + 1, j + 1, s, p, dp) == 1) || (judge_match(i, j + 1, s, p, dp) == 1);elseans = (first_match && judge_match(i + 1, j + 1, s, p, dp) == 1);dp[i][j] = ans ? 1 : 0;return dp[i][j];}*/ };

    總結

    以上是生活随笔為你收集整理的每天一道LeetCode-----只可能有'.'和'*'的字符串正则匹配的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。