使用特定字符的正则表达式(如拼字游戏库)

时间:2013-07-22 15:44:27

标签: javascript regex

我希望能够创建一个正则表达式,我可以在其中识别出一组像[dgos]这样的字母,并在我的正则表达式中使用它......但每当使用该字母时,都会使用它将它带离银行。

因此,我们可以说\1能够代表那一组字母(dgos)。我可以写一个正则表达式:

^\1{1}o\1{2}$

它基本匹配:
\1{1} = [dgos]{1}
o
\1{2} = [dgos]{2}减去第一个中使用的内容

匹配可以包括gooddosgsogd等等,但不包含sods(因为s必须使用两次)或sooo(因为o必须使用两次)。

我还希望能够识别可以多次使用的字母。我自己开始写这篇文章,但后来意识到我甚至不知道从哪里开始,所以我也一直在寻找并且没有找到一种非常优雅的方法来做到这一点,或者一种方法来做到这一点<强大>足够灵活,可以很容易地以最少的输入生成正则表达式。

我在下面使用条件和多个正则表达式的组合解决方案(随意评论对该答案的想法 - 也许这是我必须这样做的方式?),但我更愿意使用单一的正则表达式解决方案......如果可能的话,还有更优雅和高效的东西。

请注意,更高级别的优雅和单一的正则表达式部分只是我的偏好,最重要的是它的工作原理和性能足够好。

5 个答案:

答案 0 :(得分:1)

假设你使用的是javascript 1.5+版(所以你可以使用lookaheads),这是我的解决方案:

^([dogs])(?!.*\1)([dogs])(?!.*\2)([dogs])(?!.*\3)[dogs]$

所以在每个字母匹配后,你会执行一个负面的预测,以确保这个匹配的字母永远不再出现。

如果你想让一些字母重复,那么这种方法是行不通的(或至少需要更复杂!)。 (例如,如果您要匹配的信件是“示例”。)

编辑:我只是稍微重新思考一下,这是一个更优雅的解决方案:

^(?:([dogs])(?!.*\1)){4}

答案 1 :(得分:1)

在考虑了一些之后,我想到了一种方法,这是可能的,虽然这个解决方案非常难看!例如,假设您要匹配字母“goods”(即包括两个“o”):

 ^(?=.*g)(?=.*o.*o)(?=.*d)(?=.*s).{5}$

- 所以,我使用前瞻性前瞻来检查所有这些字母是否都在文本中,然后只检查确切地说有5个字母。

或者作为另一个例子,假设我们想要匹配字母“banana”,在单词的第2位用字母“a”。然后你可以匹配:

 ^(?=.*b)(?=.*a.*a.*a)(?=.*n.*n).a.{4}$

答案 2 :(得分:1)

在@ TomLord的答案的基础上,考虑到您不一定需要耗尽信件,您可以使用negative lookahead assertions而不是正数。对于您的示例D<2 from bank>R<0-5 from bank>,该正则表达式将是

/^(?![^o]*o[^o]*o)(?![^a]*a[^a]*a)(?![^e]*e[^e]*e[^e]*e)(?![^s]*s[^s]*s)d[oaes]{2}r[oaes]{0,5}$/i

<强>说明:

^                        # Start of string
(?![^o]*o[^o]*o)         # Assert no more than one o
(?![^a]*a[^a]*a)         # Assert no more than one a
(?![^e]*e[^e]*e[^e]*e)   # Assert no more than two e
(?![^s]*s[^s]*s)         # Assert no more than one s
d                        # Match d
[oaes]{2}                # Match two of the letters in the bank
r                        # Match r
[oaes]{0,5}              # Match 0-5 of the letters in the bank
$                        # End of string

你也可以写(?!.*o.*o)而不是(?![^o]*o[^o]*o),但后者更快,更难阅读。写这个的另一种方法是(?!(?:[^o]*o){2})(如果重复次数增加,则很有用)。

当然,您需要考虑字符串“固定”部分中的字母数(在这种情况下(dr)不会干扰银行,但他们可能会在其他例子中这样做。)

答案 3 :(得分:0)

我想不出在正则表达式中完全做到这一点的方法,但我能够想出这个:http://jsfiddle.net/T2TMd/2/

由于jsfiddle仅限于帖子大小,我无法在那里做更大的字典。 Check here是一个使用180k字词的更好例子。

主要功能:

/*
* filter = regex to filter/split potential matches
* bank = available letters
* groups = capture groups that use the bank
* dict = list of words to search through
*/
var matchFind = function(filter, bank, groups, dict) {
    var matches = [];
    for(var i=0; i < dict.length; i++) {
        if(dict[i].match(filter)){
            var fail = false;
            var b = bank;
            var arr = dict[i].split(filter);
            //loop groups that use letter bank
            for(var k=0; k<groups.length && !fail; k++) {
                var grp = arr[groups[k]] || [];
                //loop characters of that group
                for(var j=0; j<grp.length && !fail; j++) {
                    var regex = new RegExp(b);
                    var currChar = grp.charAt(j);
                    if(currChar.match(regex)) {
                        //match found, remove from bank
                        b = b.replace(currChar,"");
                    } else { 
                        fail = true;
                    }
                }
            }
            if(!fail) {
                matches.push(dict[i]);
            }
        }
    }
    return matches;
}

用法:

$("#go").click( function() {
    var f = new RegExp($("#filter").val());
    var b = "["+$("#bank").val().replace(/[^A-Za-z]+/g,"").toUpperCase()+"]";
    var g = $("#groups").val().replace(/[^0-9,]+/g,"").split(",") || [];
    $("#result").text(matchFind(f,b,g,dict).toString());
});

为了更容易创建场景,我也创建了这个:

$("#build").click( function() {
    var bank = "["+$("#buildBank").val().replace(/[^A-Za-z]+/g,"").toUpperCase()+"]";
    var buildArr = $("#builder").val().split(",");
    var groups = [];
    var build = "^";
    for(var i=0; i<buildArr.length; i++) {
        var part = buildArr[i];
        if(/\</.test(part)) {
            part = "(" + bank + part.replace("<", "{").replace(">", "}").replace("-",",") + ")";
            build = build + part;
            groups.push(i+1);
        } else {
            build = build + "("+part+")";
        }
    }
    build = build + "$";
    $("#filter").val(build);
    $("#bank").val(bank);
    $("#groups").val(groups.toString());
    $("#go").click();
});

这在拼字游戏中很有用,所以假设你处在一个单词必须以“D”开头的位置,“D”和平行单词中的“R”之间有两个空格,您的信函银行有OAEES。在构建器中,我可以放D,<2>,R,<0-3>,因为它必须以D开头,然后它必须有来自银行的2个字母,然后有一个R,那么我最多可以使用3个字母(因为我' m在D和R之间使用2)。

构建器将使用字母组并将D,<2>,R,<0-3>转换为^(D)([OAEES]{2})(R)([OAEES]{0,5})$,用于过滤可能的匹配项。然后,通过这些可能的匹配,它将查看使用字母库的字符串逐字符的捕获组,当它找到它们时从该正则表达式中删除字母,以便如果使用的字母组字母数比那里多,则它将不匹配是在信银行。

测试上述方案here

答案 4 :(得分:0)

这样的东西很容易指定,只需很长时间

gdos|gdso|gods|gosd|....

您基本上指定了每个排列。只需编写一些代码来生成每个排列,与备用运算符结合,就完成了!

虽然......实际编码用于生成排列的决策树可能需要付出代价......

我发誓,我想我之前在stackoverflow上回答了这个问题。让我回复你......