算法57----字符串匹配問題【動態規划】


 一、題目:交錯字符串

給定三個字符串 s1, s2, s3, 驗證 s3 是否是由 s1 和 s2 交錯組成的。

示例 1:

輸入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac"
輸出: true

示例 2:

輸入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc"
輸出: false

思路:動態規划:時間O(M*N ),空間O(M*N)

構造一個(M+1)*(N+1)的矩陣dp:dp[i][j] 代表是s1的前i個字符與s3中匹配,s2中前j個字符與s3中匹配.

  • 初始化:首行首列則是假設其中一個字符串為空時,另一個字符串是否與目標字符串一一對應。
  1. dp[0][0]=true.   # s3為空時可以由str1和str2的空字符串組成
  2. dp[i][0]:表示s3[0...i-1]能否由str1[0.....i-1]組成,若可以則dp[i][0]=true,反之則為false
    • dp[i][0] = dp[i-1][0] and s1[i-1][0] == s3[i-1][0]
  3. dp[0][j]:表示s3[0...j-1]能否由str2[0.....j-1]組成,若可以則dp[0][j]=true,反之則為false
    • dp[0][j] = dp[0][j-1] and s2[j-1] == s3[j-1]
  • 狀態方程:其他位置(i,j),dp[i][j]的值:

dp[i][j] = (dp[i-1][j] == True and s1[i-1] == s3[i+j-1]) or (dp[i][j-1] ==True and s2[j-1] == s3[i+j-1])

  • dp[i-1][j]:代表s3[i+j-2]能否被str1[0...i-2]和str2[0...j-1]交錯組成,若可以,以及str1[i-1]等於s3[i+j-1],則dp[i][j]=true,反之則為false
  • dp[i][j-1]:代表s3[i+j-2]能否被str1[0...i-1]和str2[0...j-2]交錯組成,若可以,以及str2[j-1]等於s3[i+j-1],則dp[i][j]=true,反之則為false
  • 若前兩種情況都不滿足,則dp[i][j]=false

代碼:

def isInterleave(s1, s2, s3):
    if len(s3) != len(s2) + len(s1):
        return False
    dp = [[False] * (len(s2)+1) for i in range(len(s1)+1)]
#初始化 dp[0][0]
= True for j in range(1,len(s2)+1): dp[0][j] = dp[0][j-1] and s2[j-1] == s3[j-1] for i in range(1,len(s1)+1): dp[i][0] = dp[i-1][0] and s1[i-1][0] == s3[i-1][0]
#狀態方程
for i in range(1,len(s1)): for j in range(1,len(s2)): dp[i][j] = (dp[i-1][j] == True and s1[i-1] == s3[i+j-1]) or (dp[i][j-1] ==True and s2[j-1] == s3[i+j-1]) return dp[-1][-1] s1 = "aabcc" s2 = "dbbca" s3 = "aadbbcbcac" isInterleave(s1, s2, s3)

 

二、題目:正則化表達式匹配【含通配符】

給定一個正則字符串p,一個字符串s。要求驗證s和p是否能匹配。

特別的,正則字符串中僅由兩個特殊字符:'.'表示任意的單個字符,'*'表示其前方緊鄰元素連續出現0個或者更多個。要求匹配需要覆蓋整個輸入字符串,而不是部分的匹配。

s 可能為空,且只包含從 a-z 的小寫字母。
p 可能為空,且只包含從 a-z 的小寫字母,以及字符 . 和 *。

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

思路:動態規划:

狀態轉移數組 f [i] [j] 表示利用 p 的前 i 個字符匹配 s 的前 j 個字符的匹配結果(成功為true,失敗為false)。

邊界:

  • dp[0][0] = True,s和p都是空格。
  • i = 0,只有p為這種情況 p = ‘a*b*c*'才能True,別的都為False。

        for i in range(1,len(p)+1):           

if p[i-1] == '*': if i >= 2:

dp[0][i] = dp[0][i-2]

  • j = 0,全部為False。即p為空,都False。

非邊界:

如果s[i] == p[j] 或者 p[j] == '.':【如果s的最后一位和p的最后一位相同,則只需要判斷前面的】

dp[i][j] = dp[i - 1][j - 1]

如果p[j] == '*':【dp[i][j-2]:*前面一位為0個即可,Sx~P】【dp[i][j-1]:*前面一位為1個,Sx~Pz】【dp[i-1][j]:*匹配x(x==z或者z=='.'), S~Pzy】

  dp[i][j] = dp[i][j-2] || dp[i][j-1] || (dp[i-1][j] and (s[i-1]==p[j-2] or p[j-2]=='.'))

解釋:

對於s和p,設各個最后一個字符為x, y,p的倒數第二字符為z,除此外前面字符設為S,P,則:

s = Sx

p = Pzy

  • 如果x == y或y == '.',則如果S和Pz匹配,則s和p匹配,因為最后兩字字母是匹配的。這就縮減了問題規模。
  • 而對於y ==  '*'的情況,需要考慮z:

    如果x != z,則只有在s和P匹配的情況下,s和p才匹配。

    如果x == z,設匹配符號為~吧,方便,則如果S~PzySx~PSx~Pz,【S~P,S~Pz也匹配】都可得出s和p匹配。

 代碼:

def isMatch(self, s, p):
        """
        :type s: str
        :type p: str
        :rtype: bool
        """
        dp = [[False] * (len(p) + 1) for i in range(len(s) + 1)]
        dp[0][0] = True

        for i in range(1,len(p)+1):
            if p[i-1] == '*':
                if i >= 2:
                    dp[0][i] = dp[0][i-2]
        for i in range(1,len(s)+1):
            for j in range(1,len(p)+1):
                if p[j-1]=='.' or s[i-1] == p[j-1]:
                    dp[i][j] = dp[i-1][j-1]
                elif p[j-1]=='*':
                    dp[i][j] = dp[i][j-2] or dp[i][j-1] or (dp[i-1][j] and (s[i-1]==p[j-2] or p[j-2]=='.'))
    return dp[len(s)][len(p)]   

 三、題目:字符串匹配【含通配符?*】

請你寫個程序判斷對於給定字符串s,字符串p是否能與其匹配。s串僅包含字母,p串可以包含字母和字符'?'、'*'。匹配的規則如下:
'?'可以匹配任意一位英文字母(不區分大小寫)
'*'用於表示任意多位英文字母(不區分大小寫,可以是0位)
例如,字符串“ab*ba*a*”和“a?b*abbbb”都可匹配字符串“abbababbbb”。

 思路:動態規划:時間O(M*N),空間O(M*N)

bp[i][j]表示s的前i個字符和p的前j個字符是否匹配。

邊界:

  • bp[0][0] = True
  • b[0][j]的值取決於p前一個位置是否為‘*’以及前一情況是否匹配。

非邊界:

  • 當p[j]等於‘?’或者s[i] == p[j]時,則 bp[i][j] 的值取決於 bp[i-1][j-1],即為s的前一位置和p的前一位置是否匹配;
  • 當p[j]等於‘*’時,如果該‘*’可以匹配s中的0個或者1個字符,分別對應bp[i][j-1],即s的當前位置和p的前一位置是否匹配,以及bp[i-1][j-1],即s的前一位置和p的前一位置是否匹配。

代碼:

 

def isMatch(s, p):
    dp = [[False] * (len(p) + 1) for i in range(len(s) + 1)]
    #邊界
    dp[0][0] = True
    for j in range(1,len(p)+1):
        #邊界
        dp[0][j] = dp[0][j-1] and p[j-1] == '*'
        for i in range(1,len(s)+1):
            if p[j-1] == '?' or s[i-1] == p[j-1]:
                dp[i][j] = dp[i-1][j-1]
            elif p[j-1] == '*':
                dp[i][j] = bp[i][j-1] or dp[i-1][j-1]
    return dp[len(s)][len(p)]  
s = 'a'
p = 'a***b*a'
isMatch(s, p)

 四、題目:leetcode[115] Distinct Subsequences

給定字符串S和T,S通過刪除某些位置的字符得到T的話,就記作一種subSequence。返回總共有幾種。

Here is an example:
S = "rabbbit", T = "rabbit"

思路:動態規划

 dp[i][j]表示T的從0開始長度為i的子串和S的從0開始長度為j的子串的匹配的個數。那么最后目標就是dp[len(S)][len(T)];

比如, dp[2][3]表示T中的ra和S中的rab的匹配情況。

邊界:

 

dp[0][0] = 1; // T和S都是空串.
dp[0][ 1 ... len(S) ] = 1; // T是空串,S只有一種子序列匹配。
dp[1 ... len(T) ][0] = 0; // S是空串,T不是空串,S沒有子序列匹配。

非邊界:

  • 顯然,至少有dp[i][j] = dp[i][j - 1]

比如, 因為T 中的"ra" 匹配S中的 "ra", 所以dp[2][2] = 1 。 顯然T 中的"ra" 也匹配S中的 "rab",所以s[2][3] 至少可以等於dp[2][2]。

  •  如果T[i-1] == S[j-1], 那么dp[i][j] = dp[i][j - 1] + (T[i - 1] == S[j - 1] ? dp[i - 1][j - 1] : 0);

比如, T中的"rab"和S中的"rab"顯然匹配,
根據(1), T中的"rab"顯然匹配S中的“rabb”,所以dp[3][4] = dp[3][3] = 1,
根據(2),   T中的"rab"中的b等於S中的"rab1b2"中的b2, 所以要把T中的"rab"和S中的"rab1"的匹配個數累加到當前的dp[3][4]中。 所以dp[3][4] += dp[2][3] = 2;

代碼:

class Solution {
public:
    int numDistinct(string S, string T) {
        vector<vector<int> > dp(T.length() + 1, vector<int>(S.length() + 1, 0));
        dp[0][0] = 1;
        for (int i = 1; i < S.length() + 1; ++i) 
            dp[0][i] = 1;
        for (int i = 1; i < T.length() + 1; ++i) 
            dp[i][0] = 0;
        for (int i = 1; i < T.length() + 1; ++i) {
            for (int j = 1; j < S.length() + 1; ++j) {
                dp[i][j] = dp[i][j - 1];
                if (S[j - 1] == T[i - 1])
                    dp[i][j] += dp[i - 1][j - 1];
            }
        }
        return dp[T.length()][S.length()];
    }
};

 

 

 

五、題目:字符串匹配

有一個字符串"BBC ABCDAB ABCDABCDABDE",里面是否包含另一個字符串"ABCDABD"?如果匹配則返回True,否則FALSE。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM