|
| 1 | +constZERO_OR_MORE_CHARS='*'; |
| 2 | +constANY_CHAR='.'; |
| 3 | + |
| 4 | +/** |
| 5 | + * Dynamic programming approach. |
| 6 | + * |
| 7 | + *@param {string} string |
| 8 | + *@param {string} pattern |
| 9 | + *@return {boolean} |
| 10 | + */ |
| 11 | +exportdefaultfunctionregularExpressionMatching(string,pattern){ |
| 12 | +/* |
| 13 | + * Let's initiate dynamic programming matrix for this string and pattern. |
| 14 | + * We will have pattern characters on top (as columns) and string characters |
| 15 | + * will be placed to the left of the table (as rows). |
| 16 | + * |
| 17 | + * Example: |
| 18 | + * |
| 19 | + * a * b . b |
| 20 | + * - - - - - - |
| 21 | + * a - - - - - - |
| 22 | + * a - - - - - - |
| 23 | + * b - - - - - - |
| 24 | + * y - - - - - - |
| 25 | + * b - - - - - - |
| 26 | + */ |
| 27 | +constmatchMatrix=Array(string.length+1).fill(null).map(()=>{ |
| 28 | +returnArray(pattern.length+1).fill(null); |
| 29 | +}); |
| 30 | + |
| 31 | +// Let's fill the top-left cell with true. This would mean that empty |
| 32 | +// string '' matches to empty pattern ''. |
| 33 | +matchMatrix[0][0]=true; |
| 34 | + |
| 35 | +// Let's fill the first row of the matrix with false. That would mean that |
| 36 | +// empty string can't match any non-empty pattern. |
| 37 | +// |
| 38 | +// Example: |
| 39 | +// string: '' |
| 40 | +// pattern: 'a.z' |
| 41 | +// |
| 42 | +// The one exception here is patterns like a*b* that matches the empty string. |
| 43 | +for(letcolumnIndex=1;columnIndex<=pattern.length;columnIndex+=1){ |
| 44 | +constpatternIndex=columnIndex-1; |
| 45 | + |
| 46 | +if(pattern[patternIndex]===ZERO_OR_MORE_CHARS){ |
| 47 | +matchMatrix[0][columnIndex]=matchMatrix[0][columnIndex-2]; |
| 48 | +}else{ |
| 49 | +matchMatrix[0][columnIndex]=false; |
| 50 | +} |
| 51 | +} |
| 52 | + |
| 53 | +// Let's fill the first column with false. That would mean that empty pattern |
| 54 | +// can't match any non-empty string. |
| 55 | +// |
| 56 | +// Example: |
| 57 | +// string: 'ab' |
| 58 | +// pattern: '' |
| 59 | +for(letrowIndex=1;rowIndex<=string.length;rowIndex+=1){ |
| 60 | +matchMatrix[rowIndex][0]=false; |
| 61 | +} |
| 62 | + |
| 63 | +// Not let's go through every letter of the pattern and every letter of |
| 64 | +// the string and compare them one by one. |
| 65 | +for(letrowIndex=1;rowIndex<=string.length;rowIndex+=1){ |
| 66 | +for(letcolumnIndex=1;columnIndex<=pattern.length;columnIndex+=1){ |
| 67 | +// Take into account that fact that matrix contain one extra column and row. |
| 68 | +conststringIndex=rowIndex-1; |
| 69 | +constpatternIndex=columnIndex-1; |
| 70 | + |
| 71 | +if(pattern[patternIndex]===ZERO_OR_MORE_CHARS){ |
| 72 | +/* |
| 73 | + * In case if current pattern character is special '*' character we have |
| 74 | + * two options: |
| 75 | + * |
| 76 | + * 1. Since * char allows it previous char to not be presented in a string we |
| 77 | + * need to check if string matches the pattern without '*' char and without the |
| 78 | + * char that goes before '*'. That would mean to go two positions left on the |
| 79 | + * same row. |
| 80 | + * |
| 81 | + * 2. Since * char allows it previous char to be presented in a string many times we |
| 82 | + * need to check if char before * is the same as current string char. If they are the |
| 83 | + * same that would mean that current string matches the current pattern in case if |
| 84 | + * the string WITHOUT current char matches the same pattern. This would mean to go |
| 85 | + * one position up in the same row. |
| 86 | + */ |
| 87 | +if(matchMatrix[rowIndex][columnIndex-2]===true){ |
| 88 | +matchMatrix[rowIndex][columnIndex]=true; |
| 89 | +}elseif( |
| 90 | +( |
| 91 | +pattern[patternIndex-1]===string[stringIndex]|| |
| 92 | +pattern[patternIndex-1]===ANY_CHAR |
| 93 | +)&& |
| 94 | +matchMatrix[rowIndex-1][columnIndex]===true |
| 95 | +){ |
| 96 | +matchMatrix[rowIndex][columnIndex]=true; |
| 97 | +}else{ |
| 98 | +matchMatrix[rowIndex][columnIndex]=false; |
| 99 | +} |
| 100 | +}elseif( |
| 101 | +pattern[patternIndex]===string[stringIndex]|| |
| 102 | +pattern[patternIndex]===ANY_CHAR |
| 103 | +){ |
| 104 | +/* |
| 105 | + * In case if current pattern char is the same as current string char |
| 106 | + * or it may be any character (in case if pattern contains '.' char) |
| 107 | + * we need to check if there was a match for the pattern and for the |
| 108 | + * string by WITHOUT current char. This would mean that we may copy |
| 109 | + * left-top diagonal value. |
| 110 | + * |
| 111 | + * Example: |
| 112 | + * |
| 113 | + * a b |
| 114 | + * a 1 - |
| 115 | + * b - 1 |
| 116 | + */ |
| 117 | +matchMatrix[rowIndex][columnIndex]=matchMatrix[rowIndex-1][columnIndex-1]; |
| 118 | +}else{ |
| 119 | +/* |
| 120 | + * In case if pattern char and string char are different we may |
| 121 | + * treat this case as "no-match". |
| 122 | + * |
| 123 | + * Example: |
| 124 | + * |
| 125 | + * a b |
| 126 | + * a - - |
| 127 | + * c - 0 |
| 128 | + */ |
| 129 | +matchMatrix[rowIndex][columnIndex]=false; |
| 130 | +} |
| 131 | +} |
| 132 | +} |
| 133 | + |
| 134 | +returnmatchMatrix[string.length][pattern.length]; |
| 135 | +} |