算法笔记6:模式匹配

136 阅读1分钟

面试题16.18 模式匹配

这个题目一言难尽。一开始写了半天,感觉情况特别复杂,还以为是有规律我自己没找到,或者说分类的依据错了。但是看了一下题解发现也是这种强行分类的方法,于是就接着继续完善自己的思路了。

代码如下:

const patternMatching = (pattern, value) => {
    // edge case, if pattern len is 1 then it would always matches
    if (pattern.length === 1) {
        return true;
    }

    // there are only `a`s and `b`s in the pattern
    // so it would be easire to just treat the first char as our `A`
    const charA = pattern[0];
    const charB = charA === 'a' ? 'b' : 'a';
    const charANum = pattern
        .split('')
        .reduce((p, char) => char === charA ? p + 1 : p, 0);
    const charBNum = pattern.length - charANum;
    const valLen = value.length;

    // edge case, if pattern is normal yet value is falsy
    // then it would always be false
    if (charANum !== 0 && charBNum !== 0 && !value) {
        return false;
    }

    // category 1
    // if there is only one type of char in the pattern
    // then we don't need to figure out which char represents which word.
    if (charANum === 0 || charBNum === 0) {
        const usableNum = charANum || charBNum;
        if (valLen % usableNum !== 0) {
            return false;
        }

        const wordLen = valLen / usableNum;
        const word = value.slice(0, wordLen);
        let j = 0;
        for (let i = 0; i < pattern.length; i++) {
            if (value.slice(j, j + wordLen) !== word) {
                return false;
            }
            j += wordLen;
        }

        return j === valLen;
    }

    // category 2
    // most common cases, `a`s and `b`s appear multiple times.
    // yet we can figure out an interval to limit the possible combinations.
    const maxNum = Math.min(charANum, charBNum);
    // the max lenght of one word would be less or equal than
    // total value length devided by the number of char
    // which appears less than another
    const maxWordLen = Math.floor(valLen / maxNum);
    let result = false;
    // work out a word;
    for (let charALen = maxWordLen; charALen >= 0; charALen--) {
        if (result) {
            break;
        }

        const totalCharALen = charALen * charANum;
        const totalCharBLen = valLen - totalCharALen;
        const possibleCharBLen = totalCharBLen / charBNum;
        
        const charBLen = Math.floor(possibleCharBLen);
        if (possibleCharBLen !== charBLen) {
            // this combination does not work
            continue;
        }

        // then it it just the tedious work of checking if our assumption works
        let wordA = null;
        let wordB = null;
        let j = 0;
        for (let i = 0; i < pattern.length; i++) {
            if (wordA === wordB && wordA !== null && wordB !== null) {
                break;
            }

            const currChar = pattern[i];
            if (currChar === charA) {
                const word = value.slice(j, j + charALen);
                if (wordA === null) {
                    wordA = word;
                } else if (wordA !== word) {
                    break;
                }
                j += charALen;
            } else {
                const word = value.slice(j, j + charBLen);
                if (wordB === null) {
                    wordB = word;
                } else if (wordB !== word) {
                    break;
                }
                j += charBLen;
            }
        }

        result = j === valLen;
    }

    return result;
};