正则表达式用于错过字母的单词以及按顺序颠倒的两个字母

时间:2018-03-20 02:09:41

标签: c# regex

我想详细说明一个涵盖以下场景的正则表达式:

搜索到的字是"马铃薯"。

如果用户搜索" potaot"它匹配(他打字很快," o"手指比手指更快。(完成)

如果用户搜索" ptato"它匹配(他忘了一封信)。 (完成)

凭借我对正则表达式的了解,我可以进一步发展:

 (?=[potato]{5,6})p?o?t?a?t?o?

这个问题在于它匹配像#34; otatop"这样的反转词,这有点聪明但是有点bezarre,而且#34; ooooo",这是完全不受欢迎的。所以我没有描述我不想要的东西。

我不想要重复的字母来匹配" ooooo"," ooopp"等等(无法)

顺便说一句,我使用C#。

3 个答案:

答案 0 :(得分:6)

不要使用正则表达式。

最好的解决方案是简单的解决方案。只有十一种可能的不精确匹配,所以只需枚举它们:

List<string> inexactMatches = new List<string> {
  "otato", "ptato", "poato", "potto", "potao", "potat",
  "optato", "ptoato", "poatto", "pottao", "potaot"};
...
bool hasInexactMatch = inexactMatches.Contains(whatever);

输入它们不到一分钟;使用简单的特定解决方案,而不是尝试做一些疯狂的正则表达式,这将花费你几个小时来查找和调试。

如果您要坚持使用正则表达式,那么这是有效的:

otato|ptato|poato|potto|potao|potat|optato|ptoato|poatto|pottao|potaot

再次:更简单更好。

现在,有人可能会认为你想要解决这个问题,而不是“马铃薯”。在这种情况下,您可能已经这么说了 - 但无论如何,我们可以提出一些简单的解决方案。

首先,让我们枚举从目标字符串中省略一个字母的所有字符串。字符串是IEnumerable<char>所以让我们解决一般问题:

static IEnumerable<T> OmitAt<T>(this IEnumerable<T> items, int i) =>
  items.Take(i).Concat(items.Skip(i + 1));

这有点粗略地列举了两次序列,但我不打算强调它。现在让我们为字符串制作一个特定的版本:

static IEnumerable<string> Omits(this string s) =>
  Enumerable
    .Range(0, s.Length)
    .Select(i => new string(s.OmitAt(i).ToArray()));

大。现在我们可以说"frob".Omits()并返回rob, fob, frb, fro

现在让我们进行交换。再次,首先解决一般问题:

static void Swap<T>(ref T x, ref T y)
{
    T t = x;
    x = y;
    y = t;
}

static T[] SwapAt<T>(this IEnumerable<T> items, int i)
{
    T[] newItems = items.ToArray();
    Swap(ref newItems[i], ref newItems[i + 1]);
    return newItems;
}

现在我们可以解决它的问题:

static IEnumerable<string> Swaps(this string s) =>
    Enumerable
      .Range(0, s.Length - 1)
      .Select(i => new string(s.SwapAt(i)));

现在我们已经完成了:

string source = "potato";
string target = whatever;
bool match = 
  source.Swaps().Contains(target) || 
  source.Omits().Contains(target);

容易腻。 使用简单,直接,正确的算法解决一般问题,这些算法可以组成更大的解决方案。我的算法中没有一行长度超过三行,很容易看出它们是正确的。

答案 1 :(得分:2)

这里选择的武器是相似(或距离)匹配算法。 Compare similarity algorithms概述了最常见的距离指标/算法。

问题在于没有单一的最佳指标。选择取决于,例如,关于输入类型,准确度要求,速度,资源可用性等。然而,comparing algorithms can be messy

最常见的两个指标是Levenshtein距离和Jaro-Winkler:

  • Levenshtein距离,它提供了两个字符串之间的相似性度量,可以说比其他一些指标更不宽容,更直观。 (Levenshtein距离的修改版本就像Damerau-Levenshtein距离,其中包括换位,这可能更适合您的使用案例。)

  • Some声称Jaro-Winkler,它提供两个字符串之间的相似性度量,允许字符转置到一定程度调整公共前缀的权重,距离是#34;性能最高的一个目前可用的精确近似字符串匹配算法[Cohen,et al。],[Winkler]。&#34;然而,选择仍然在很大程度上取决于使用案例,并且不能从具体研究中得出一般性结论,例如:名称匹配Cohen, et al. 2003

您可以在NuGet上找到大量的软件包,它们为您提供各种相似度算法(abc),fuzzy matches,拼音等等。将此功能添加到您的网站或应用中。

模糊匹配也可以直接在数据库层上使用。可以为大多数数据库系统找到implementation of the Levenshtein distance(例如MySQLSQL Server)或已内置(OraclePostgreSQL)。

根据您的确切用例,您还可以使用基于云的解决方案(即使用基于AWSAzure等的微服务或roll-your-own)来获取自动提示 - 像模糊搜索/匹配。

答案 2 :(得分:0)

这样做最简单:

static void Main(string[] args)
{
    string correctWord = "Potato";

    string incorrectSwappedWord = "potaot";
    string incorrectOneLetter = "ptato";

    // Returns true
    bool swapped = SwappedLettersMatch(correctWord, incorrectSwappedWord);
    // Returns true
    bool oneLetter = OneLetterOffMatch(correctWord, incorrectOneLetter);
}

public static bool OneLetterOffMatch(string str, string input)
{
    int ndx = 0;

    str = str.ToLower();
    input = input.ToLower();

    if (string.IsNullOrWhiteSpace(str) || string.IsNullOrWhiteSpace(input))
    {
        return false;
    }

    while (ndx < str.Length)
    {
        string newStr = str.Remove(ndx, 1);

        if (input == newStr)
        {
            return true;
        }

        ndx++;
    }
    return false;
}

public static bool SwappedLettersMatch(string str, string input)
{
    if (string.IsNullOrWhiteSpace(str) || string.IsNullOrWhiteSpace(input))
    {
        return false;
    }

    if (str.Length != input.Length)
    {
        return false;
    }

    str = str.ToLower();
    input = input.ToLower();

    int ndx = 0;

    while (ndx < str.Length)
    {
        if (ndx == str.Length - 1)
        {
            return false;
        }
        string newStr = str[ndx + 1].ToString() + str[ndx];
        if (ndx > 0)
        {
            newStr = str.Substring(0, ndx) + newStr;
        }
        if (str.Length > ndx + 2)
        {
            newStr = newStr + str.Substring(ndx + 2);
        }

        if (newStr == input)
        {
            return true;
        }

        ndx++;
    }

    return false;
}

OneLetterOffMatch将返回true,如果只有一个字符丢失,则匹配关闭。如果只交换了两个字母,那么SwappedLettersMatch将返回true。这些函数不区分大小写,但如果您需要区分大小写的版本,只需删除对.ToLower()的调用。