我想详细说明一个涵盖以下场景的正则表达式:
搜索到的字是"马铃薯"。
如果用户搜索" potaot"它匹配(他打字很快," o"手指比手指更快。(完成)
如果用户搜索" ptato"它匹配(他忘了一封信)。 (完成)
凭借我对正则表达式的了解,我可以进一步发展:
(?=[potato]{5,6})p?o?t?a?t?o?
这个问题在于它匹配像#34; otatop"这样的反转词,这有点聪明但是有点bezarre,而且#34; ooooo",这是完全不受欢迎的。所以我没有描述我不想要的东西。
我不想要重复的字母来匹配" ooooo"," ooopp"等等(无法)
顺便说一句,我使用C#。
答案 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上找到大量的软件包,它们为您提供各种相似度算法(a,b,c),fuzzy matches,拼音等等。将此功能添加到您的网站或应用中。
模糊匹配也可以直接在数据库层上使用。可以为大多数数据库系统找到implementation of the Levenshtein distance(例如MySQL,SQL Server)或已内置(Oracle,PostgreSQL)。
根据您的确切用例,您还可以使用基于云的解决方案(即使用基于AWS,Azure等的微服务或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()
的调用。