簡體   English   中英

正則表達式用於錯過字母的單詞以及按順序顛倒的兩個字母

[英]Regex for words that miss a letter and also two letters that are inverted in order

我想詳細說明一個涵蓋以下場景的正則表達式:

搜索到的單詞是“馬鈴薯”。

如果用戶搜索“potaot”(他快速鍵入並且“o”手指比“t”手指快,則匹配。(完成)

如果用戶搜索“ptato”(他忘了一個字母),它會匹配。 (完成)

憑借我對正則表達式的了解,我能夠進一步發展:

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

這個問題是它匹配像“otatop”這樣的反向詞,這是一個有點聰明但有點bezarre,和“ooooo”,這是完全不受歡迎的。 所以我沒有描述我不想要的東西。

我不希望重復的字母與“ooooo”,“ooopp”等相匹配(無法)

順便說一句,我正在使用C#。

不要使用正則表達式。

最好的解決方案就是簡單。 只有十一種可能不精確的匹配,所以只需枚舉它們:

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);

十分簡單。 使用簡單,直接,正確的算法解決一般問題,這些算法可以組成更大的解決方案 我的算法都不超過三行,很容易看出它們是正確的。

這里選擇的武器是相似(或距離)匹配算法。 比較相似度算法可以很好地概述最常見的距離度量/算法。

問題是沒有單一的最佳指標。 選擇取決於例如輸入類型,精度要求,速度,資源可用性等。然而, 比較算法可能是混亂的

兩個最常建議的指標是Levenshtein距離和Jaro-Winkler:

  • Levenshtein距離提供了兩個字符串之間的相似性度量,可以說比其他一些指標更不寬容,更直觀。 (Levenshtein距離的修改版本就像Damerau-Levenshtein距離,其中包括換位,這可能更適合您的使用案例。)

  • 一些人聲稱Jaro-Winkler,它提供兩個字符串之間的相似性度量,允許字符轉置到一定程度調整公共前綴的權重,距離是“目前可用的最高性能和准確的近似字符串匹配算法之一[Cohen,et al 。],[溫克勒]。“ 然而,選擇仍然在很大程度上取決於用例,並且無法從特定研究中得出一般性結論,例如名稱匹配Cohen等。 2003年

您可以在NuGet上找到大量的軟件包,它們為您提供各種相似度算法( abc ), 模糊匹配 ,語音等,以便將此功能添加到您的站點或應用程序中。

模糊匹配也可以直接在數據庫層上使用。 對於大多數數據庫系統(例如MySQLSQL Server )或者已經內置( OraclePostgreSQL ),可以找到Levenshtein距離實現

根據您的具體用例,您還可以使用基於雲的解決方案(即使用基於AWSAzure等的微服務或自行滾動 )來獲得類似於自動提取的模糊搜索/匹配。

這樣做最簡單:

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()的調用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM