给定一个字符串序列的单词,检查它是否与模式匹配

时间:2016-03-23 22:03:56

标签: string algorithm pattern-matching

我在一次采访中遇到了这个问题而且我一直坚持最好的方式去做。问题如下:

给定字符串序列和字符串序列模式,如果单词序列与模式匹配则返回true,否则返回false。

匹配的定义:替换变量的单词必须始终遵循该替换。例如,如果“f”被替换为“猴子”,那么任何时候我们看到另一个“f”然后它必须匹配“猴子”,任何时候我们再次看到“猴子”它必须匹配“f”。

实施例
输入:“蚂蚁狗猫狗”,“a d c d”
输出:真实 这是真的,因为每个变量都只映射一个单词,反之亦然 a - >蚂蚁
d - >狗
c - >猫
d - >狗

输入:“蚂蚁狗猫狗”,“a d c e”
输出:假
这是错误的,因为如果我们将“d”替换为“狗”,那么你也不能将“e”替换为“狗”。
a - >蚂蚁
d,e - >狗(d和e都不能映射到狗这么假)
c - >猫

输入:“猴子狗鳗鱼”,“e f c c”
输出:真实 这是真的,因为每个变量都只映射一个单词,反之亦然 e - >猴子
f - >狗
c - >鳗鱼

最初,我想要做如下的事情......

function matchPattern(pattern, stringToMatch) {
  var patternBits = pattern.split(" ");
  var stringBits = stringToMatch.split(" ");
  var dict = {};

  if (patternBits.length < 0 
      || patternBits.length !== stringBits.length) {
    return false;
  }

  for (var i = 0; i < patternBits.length; i++) {
    if (dict.hasOwnProperty(patternBits[i])) {
      if (dict[patternBits[i]] !== stringBits[i]) {
        return false;
      }
    } else {
      dict[patternBits[i]] = stringBits[i];
    }
  }
  return true;
}

var ifMatches = matchPattern("a e c d", "ant dog cat dog");
console.log("Pattern: " + (ifMatches ? "matches!" : "does not match!"));

然而,我意识到这不起作用,并且失败了示例#2,因为它错误地返回true。处理这个问题的一种方法是使用双向字典或两个字典,即存储{“a”:“ant”}和 {“ant”:“a”}并检查if检查中的两个场景。然而,这似乎浪费了空间。有没有更好的方法来解决这个问题而不使用正则表达式?

2 个答案:

答案 0 :(得分:0)

对于这种特殊情况,我假设模式指的是匹配第一个字符。如果是这样,你可以简单地压缩和比较。

# python2.7

words   = "ant dog cat dog"
letters = "a d c d"
letters2 = "a d c e"

def match(ws, ls):
    ws = ws.split()
    ls = ls.split()
    return all(w[0] == l for w, l in zip(ws + [[0]], ls + [0]))

print match(words, letters)
print match(words, letters2)

有趣的[[0]]和[0]最终是为了确保模式和单词具有相同的长度。

答案 1 :(得分:0)

我认为,单词列表长度中二次方的简单选择是验证列表索引的每个配对在两个列表中具有相同的相等特征。我假设你已经将“单词”和“模式”作为列表,并且不需要解析空格和其他任何东西 - 无论如何,这应该是一个单独的函数。

function matchesPatternReference(words, pattern) {
    if(words.length !== pattern.length) return false;
    for(var i = 0; i < words.length; i++)
        for(var j = i+1; j < words.length; j++)
            if((words[i] === words[j]) !== (pattern[i] === pattern[j]))
                return false;
    return true;
}

稍微好一点的方法是规范化两个列表,然后比较规范化列表的相等性。要规范化列表,请将每个列表元素替换为列表中第一次出现之前出现的唯一列表元素的数量。这将是较长列表长度的线性,假设您认为散列查找和列表追加需要恒定时间。我不知道足够的Javascript知道这些是否有必要;当然在最坏的情况下,这个算法背后的想法可以在n * log(n)时间内使用合适的数据结构实现,即使不相信散列查找是恒定时间(无论语言如何,这都是一个有点可疑的假设)。

function normalize(words) {
    var next_id = 0;
    var ids = {};
    var result = [];
    for(var i = 0; i < words.length; i++) {
        if(!ids.hasOwnProperty(words[i])) {
            ids[words[i]] = next_id;
            next_id += 1;
        }
        result.push(ids[words[i]]);
    }
    return result;
}

function matchesPatternFast(words, pattern) {
    return normalize(words) === normalize(pattern);
}

注意:正如评论中指出的那样,应该手动检查规范化数组的深度相等,因为数组上的===在Javascript中进行身份比较而不进行元素比较。另请参阅How to check if two arrays are equal with Javascript?

附录:下面我认为matchesPatternFastmatchesPatternReference计算相同的函数 - 但是使用错误的假设===对数组进行逐点比较元素而不是指针比较。

我们可以定义以下功能:

function matchesPatternSlow(words, pattern) {
    return matchesPatternReference(normalize(words), normalize(pattern));
}

当且仅当normalize(x).length === x.length时,我才会发现normalize(x)[i] === normalize(x)[j]x[i] === x[j];因此matchesPatternSlow计算与matchesPatternReference相同的功能。

我现在要争辩matchesPatternSlow(x,y) === matchesPatternFast(x,y)。当然,如果normalize(x) === normalize(y),我们将拥有此属性。 matchesPatternFast将明显返回true。另一方面,matchesPatternSlow通过对其两个输入进行大量查询并验证这些查询始终为两个列表返回相同的结果来进行操作:在循环外,查询为function(x) { return x.length },并在内部循环,查询是function(x, i, j) { return x[i] === x[j]; }。由于相等对象对任何查询的响应都相同,因此两个规范化列表上的所有查询都会对齐,matchesPatternSlow也将返回true

如果normalize(x) !== normalize(y)怎么办?然后matchesPatternFast将明显返回false。但如果它们不相等,那么它们的长度不匹配 - 在这种情况下,matchesPatternSlow也将从我们希望的false中的第一次检查中返回matchesPatternReference - 或者某些指数的元素不相等。假设最小的不匹配指数为inormalize的属性是索引i处的元素将等于索引j<i处的元素,否则它将比索引{{1}处的最大元素大一通过0。所以我们现在要考虑四个案例:

  • 我们i-1j1<i j2<inormalize(x)[j1] === normalize(x)[i]。但是,自normalize(y)[j2] === normalize(y)[i]以后我们知道normalize(x)[i] !== normalize(y)[i]。因此,当normalize(x)[j1] !== normalize(y)[i]选择索引matchesPatternReferencej1时,我们会发现inormalize(x)[j1] === normalize(x)[i]truenormalize(y)[j1] === normalize(y)[i]且我们试图展示时立即返回false
  • 我们有false j<inormalize(x)[j] === normalize(x)[i]不等于normalize(y)[i]的任何先前元素。然后normalize(y)会在选择索引matchesPatternReferencefalse时返回j,因为i匹配这些索引但normalize(x)不匹配。
  • 我们有normalize(y) j<inormalize(y)[j] === normalize(y)[i]不等于normalize(x)[i]的任何先前元素。基本上与前一种情况相同。
  • 我们认为normalize(x)normalize(x)[i]中最大的早期元素大一个,normalize(x)normalize(y)[i]中最大的早期元素大一个。但由于normalize(y)normalize(x)对所有先前的元素达成一致,这意味着normalize(y),这与我们假设规范化列表在此索引上不同的说法相矛盾。

因此,在所有情况下,normalize(x)[i] === normalize(y)[i]matchesPatternFast都同意 - 因此matchesPatternSlowmatchesPatternFast会计算相同的功能。