在字符串列表中查找有1个字符差异的字符串

本文关键字:字符串 字符 1个 列表 查找 | 更新日期: 2023-09-27 18:03:54

是否有可能在LINQ中找到字符串列表中只有1个字符差异的字符串?

s_Str = "XXX_P_P1";
l_str = {"XXX_N_P1", "XX1_Z_P1","XX2_A_P1","DXX_P_P1"};

,结果应该返回:

f_Str = {"XXX_N_P1","DXX_P_P1"}

列表中的字符串将具有不同的字符串长度。我的主要要求是找到XXX_N_P1

主要要求不同,这就是为什么我只需要找到有1个字符差异的字符串

在字符串列表中查找有1个字符差异的字符串

我们可以利用匿名对象自动生成的相等性和GetHashCode实现来连接表示字符其位置的值对。我们期望结果集比原字符串的长度和原字符串的长度少一个元素。

var numMatches = str1
                  .Select((s,i) => new {s, i})
                  .Join(str2.Select((s, i) => new{s, i}), 
                                    a => a, 
                                    b => b, 
                                    (a, b) => 0) //what we select is unimportant
                  .Count(); //because we're only after a count
var singleCharIsDifferent=
                 (str1.Length == str2.Length)
                 && (str1.Length - 1) == numMatches;

您要查找的是edit distance:

在计算机科学中,编辑距离是通过计算将一个字符串转换为另一个字符串所需的最小操作数来量化两个字符串(如单词)之间的差异程度的一种方法。

一个流行的方法是使用Levenshtein距离

可以在这里找到一个示例实现,它会产生您想要的结果。

var s_Str = "XXX_P_P1";
var l_str = new string[]{"XXX_N_P1", "XX1_Z_P1","XX2_A_P1","DXX_P_P1"};
var f_str = l_str.Where(l => LevenshteinDistance(s_Str, l) == 1).ToArray();

f_str现在是{"XXX_N_P1","DXX_P_P1"}


尽管如此,如果这太多了,如果您正在寻找LINQ一行代码,您可以使用LINQ查询获得仅在一个字符上不同的所有字符串(忽略插入和删除),如下所示:

l_str.Where(l => l.Zip(s_Str, (a, b) => a != b).Count(t => t) == 1);

我以前用扩展方法解决过类似的问题:

/// <summary>
/// String extensions methods
/// </summary>
public static class StringExtensionsClass
{
    /// <summary>
    /// Check if two strings has only one "difference"
    /// </summary>
    /// <param name="BaseString"></param>
    /// <param name="StringToCountDiff"></param>
    /// <returns></returns>
    public static bool HasOneDiff(this string BaseString, string StringToCountDiff)
    {
        int _diffCount = 0;
        if (BaseString.Length == StringToCountDiff.Length)
        {
            for (int i = 0; i < BaseString.Length; i++)
            {
                if (BaseString[i] != StringToCountDiff[i])
                {
                    _diffCount++;
                }
            }
            if (_diffCount == 1)
            {
                return true;
            }
        }
        return false;
    }
}

和LINQ:

var matches = l_str.Where(l => l.HasOneDiff(s_Str) == true).ToArray();

需要levenshteinstance比较算法:

string s_Str = "XXX_P_P1";
var l_str = new List<string> { "XXX_N_P1", "XX1_Z_P1", "XX2_A_P1", "DXX_P_P1"};
var result = l_str.Where(x => LevenshteinDistance.Compute(s_Str, x) <= 1);

static class LevenshteinDistance
{
    /// <summary>
    /// Compute the distance between two strings.
    /// </summary>
    public static int Compute(string s, string t)
    {
        int n = s.Length;
        int m = t.Length;
        int[,] d = new int[n + 1, m + 1];
        // Step 1
        if (n == 0)
        {
            return m;
        }
        if (m == 0)
        {
            return n;
        }
        // Step 2
        for (int i = 0; i <= n; d[i, 0] = i++)
        {
        }
        for (int j = 0; j <= m; d[0, j] = j++)
        {
        }
        // Step 3
        for (int i = 1; i <= n; i++)
        {
            //Step 4
            for (int j = 1; j <= m; j++)
            {
                // Step 5
                int cost = (t[j - 1] == s[i - 1]) ? 0 : 1;
                // Step 6
                d[i, j] = Math.Min(
                    Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1),
                    d[i - 1, j - 1] + cost);
            }
        }
        // Step 7
        return d[n, m];
    }
}

我假设您想比较由_分隔的令牌。因此使用String.Split('_')。然后可以使用Enumerable.Intersect检查匹配的令牌数量。只取最多有一个标记不匹配的字符串:

var l_str = new List<string>{"XXX_N_P1", "XX1_Z_P1","XX2_A_P1","DXX_P_P1"};
string s_Str = "XXX_P_P1";
string[] findTokens = s_Str.Split('_');
List<string> f_Str = l_str
    .Select(str => new { str, split = str.Split('_') })
    .Where(x => x.split.Length - findTokens.Intersect(x.split).Count() >= x.split.Length - 1)
    .Select(x => x.str)
    .ToList();

Result: "XXX_N_P1","DXX_P_P1"