【问题标题】:Need help expanding on an anagram regex需要帮助扩展字谜正则表达式
【发布时间】:2013-02-15 21:03:26
【问题描述】:

我正在尝试扩展此正则表达式以列出给定字母集的所有可能的字谜:

^(?!.*([aer]).*\1)(?!(.*d){4})([aerd]*|[a-z])$

到目前为止,基于这个正则表达式,我可以接收到由字母“dadder”组成的单词和子词的任意组合的匹配,例如“adder”、“add”、“ad”、“red”等等。 正则表达式复杂而不是简单的[dadder]* 的原因是因为显然每个字母可以匹配无限次,这很糟糕,如果提供了两个 d,我希望每个字母只匹配测试字符串一次,它最多只能匹配两次或更少。如果有人当然可以简化正则表达式以匹配指定 X 次的任何字母组合,请随时提供它:)

但是我的主要问题是,我现在想加入一个句号字符“.”。如果在字符列表中遇到句号,它将充当通配符并且可以匹配任何字符 a-z。所以dadd.r可以匹配daddzrdaddordaddprrpdadd等。

有人可以帮我解决这个问题吗?

【问题讨论】:

  • Check if string is subset of a bunch of characters? (RegEx)? 的可能副本。有两种类型的解决方案:从要测试的单词中形成正则表达式,或者形成单个正则表达式来匹配所有单词。披露:我是不受欢迎且疯狂的“形成单个正则表达式”的作者。
  • 感谢您的链接,将检查它 nhahtdh
  • 除非您知道自己在做什么,否则请按照帖子中的说明进行操作...
  • 我做了,伙计,我知道是你生成的。我读过你的帖子。我只是想先让它在 Regex Buddy 中工作。我认为这应该适用于我的 or 条件,但它不是 :( 关于如何支持我的通配符的任何想法?^(?!(?:[^a]*+a){3})(?!(?:[^b]*+b){2})(?!(?:[^\w]*+\w){1})(?!(?:[^c]*+c){2})(?!(?:[^d]*+d){2})(?!(?:[^e]*+e){2})(?!(?:[^f]*+f){2})(?!(?:[^g]*+g){2})([abcdefg]+|\w)$

标签: regex anagram


【解决方案1】:

这不是一个应该用正则表达式解决的问题,nhahtdh's amusing answer 应该会让你信服。

正则表达式擅长匹配模式。它们不是解决基于集合的问题的工具,而这正是您尝试使用它们的目的。

您确实需要一种算法方法,因为这就是问题的本质。 This question 就是这样一个话题。

【讨论】:

  • @nhahtdh,我的假设是匹配的 用于生成字谜。即匹配作为潜在字谜一部分的字典单词。 OP 说这是给“字谜创造者”的。
  • 哎呀,让我澄清一下。这是为了找到所有的字谜。我称它为字谜“创造者”,因为我的工具将创建所有可能字谜的列表并为用户列出它们。
  • 用户可以输入任意数量的随机字母。我正在迭代 200K+ 字典单词,我希望测试是否可以使用任何或所有用户提供的字母来生成任何单词。
  • @GONeale:链接的算法可以被认为是在字典中查找字谜(这是你的情况)。所以再想一想,我认为它可能适用。不过,我不知道你会如何修改它。
  • 我真的很喜欢你的正则表达式 :) 我确信我可以改变它来支持通配符。如果你有时间,你能试着修复我目前所拥有的东西吗? ^(?!(?:[^a]*+a){3})(?!(?:[^b]*+b){2})(?!(?:[^c]*+c){2})(?!(?:[^d]*+d){2})(?!(?:[^e]*+e){2})(?!(?:[^f]*+f){2})(?!(?:[^g]*+g){2})([abcdefg]+|\w{1})$ 这将匹配 'abcdefg' 但不匹配 'abcdefgz' 即使我有 or \w{1}
【解决方案2】:

问题的第一部分是这个问题的副本:Check if string is subset of a bunch of characters? (RegEx)?


此答案专门用于解决您面临的实际问题(问题的第二部分)。

一个非常简单的解决方案是使用 2 个映射:一个映射原始集中字符的频率,并记下 . 的数量,另一个映射每个输入字符串的字符频率.

伪代码:

// I assume the maps return 0 for non existent entries
// Depending on the input, the map can simply be an array, or a tree/hash map

function checkAnagramExtended(originalString, inputString):
    if (inputString.length > originalString.length):
        return false

    // The frequency mapping for original string (ref stands for reference)
    // Ideally, refMap should be filled up once instead of every call
    // to this function
    var refMap = countFrequency(originalString)
    // The frequency mapping for input string
    var inpMap = empty map

    foreach (character c in inputString):

        if (inpMap[c] >= refMap[c]):
            // You may want to check that c is a character allowed
            // to be substituted by dot .
            // if (!canBeSubstitutedByDot(c)):
            //     return false

            if (inpMap['.'] >= refMap['.']):
                return false
            else:
                inpMap['.'] += 1

        else:
            inpMap[c] += 1

    return true

附录:扩展正则表达式解决方案?

您的点 . 扩展名允许匹配来自 a-z 的任何字符,这使得正则表达式解决方案变得更加不切实际。

在我解决另一个问题的方法中,我严重依赖负前瞻来断言特定字符的计数小于多字符集中的最大字符数。

. 扩展名可以改变任何字符允许的最大字符数,因此破坏了我上面的解决方案。如果你强制正则表达式完成这项工作,如果只有 1 个.,则有可能生成正则表达式,但是当你将其增加到 2 个时,事情就会爆炸。

【讨论】:

  • 感谢您的帮助 nhahtdh。我正在查看 dan1111 建议的 FogleBird 的答案。
  • @GONeale:等等。你想匹配还是生成?
  • 匹配。我已经有了我的单词表。这就是为什么我相信 Regex 可以完成这项工作。我的意思是,它已经是,并且在 200K+ 单词上表现出色,我只是想扩展它以支持通配符。
  • (我应该说,我显然在我的 C# 程序中构建了我的正则表达式,取决于输入的字母,它并不总是看起来像那个特定的“爸爸”示例。
  • 我对生成没有兴趣,我已经有一个单词字典,如果在迭代我的字典单词列表时可以从提供的字母中生成任何单词,我只想要一个 TRUE 或 FALSE。跨度>
【解决方案3】:

好的,在尝试将其作为正则表达式进行大量工作之后,由于不完整的通配符支持和缓慢的处理时间,我放弃了。

我现在已经将我的需求转换为 C# 函数,实际上我现在更舒服、更快乐了,因为它也快了 400%,这很棒。

这将检查给定单词是否是一组字母的字谜或子字谜,通过 (.) 支持通配符。

letters 是用于测试字谜的字母。

其中dictionaryData 是要测试的单词List<string>

var letterCounts = letters.Select(x => x)
  .GroupBy(x => x)
  .ToDictionary(x => x.Key, x => x.Count());

var containsWildcards = letters.IndexOf('.') >= 0;
foreach (var dictWord in dictionaryData)
{
    var matches = 0;
    var dictWordLength = dictWord.Length;
    if (dictWordLength > letters.Length)
        continue;
    var addedChars = new List<char>();
    foreach (var dictLetter in dictWord)
    {
        var foundLetter = false;
        if (letterCounts.ContainsKey(dictLetter) &&
            addedChars.Count(x => x == dictLetter) < letterCounts[dictLetter])
        {
            if (letters.IndexOf(dictLetter) >= 0)
                foundLetter = true;
        }
        else if (containsWildcards &&
            addedChars.Count(x => x == '.') < letterCounts['.'])
        {
            addedChars.Add('.');
            foundLetter = true;
        }
        if (foundLetter)
        {
            addedChars.Add(dictLetter);
            matches++;
        }
        if (dictWordLength == matches)
            break;
    }

    if (dictWordLength <= matches)
    {
        // We have a match!
    }
}

希望它也可以帮助其他人。

【讨论】:

  • 您说. 是任何字符的通配符,但在您的正则表达式中,. 不能是集合中已经存在的字符之一。对于A.DDER,您的正则表达式将排除deerrare 的情况。 (注意deer多了个e,可以用点,rare多了个r,可以用点)。 regex101.com/r/rS8hU5。 (您可以使用输入,正则表达式是从您的帖子中复制粘贴的)。
  • 哦,这是个问题,很好的发现 :) 是的,通配符显然也需要是您现有的字母之一 :(
  • 好吧,搞定了,但是放弃了正则表达式,不得不创建一个 C# 函数,但我现在真的更开心了,我终于放弃了正则表达式,因为它快了大约 400%,这是很棒。
  • 看看你的想法@nhahtdh,随意做一些测试,但检查了一堆单词,一切都在检查。再次感谢您在这个问题上对我的坚持:)
  • dictWordLength == matches 会在您遍历整个单词时发生。所以break没用。 letterCounts[dictLetter] 大于某个值的事实已经暗示它至少为 1,因此内部的额外检查是无用的。我将在 pastebin 中发表更多评论...
猜你喜欢
  • 2012-11-30
  • 2023-03-19
  • 1970-01-01
  • 2021-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-01
相关资源
最近更新 更多