【问题标题】:Optimal way to find pairs in a list of strings在字符串列表中查找对的最佳方法
【发布时间】:2021-02-26 01:21:59
【问题描述】:

我有一个列表中唯一字符串的列表。我需要找到配对元素。配对元素是匹配以下任一条件的元素

  1. 如果字符串 A 和 B 以“_N”结尾且 B 以“_P”结尾,则字符串 A、B 是一对,反之亦然,例如:ABCD_N 和 ABCD_P 是一对
  2. 字符串 A、B 是一对,如果将出现的“N”替换为“P”,则会得到字符串 B,反之亦然。 例如:ABNX,ABPX 都是对

现在我正在循环列表并在列表的其余部分中搜索其对应的对并将它们配对,至少为 O(n^2),我的字符串列表可能高达一百万

for(int i = 0; i < list.Count; i++)
{
    for(int j=0;j<list.Count && j != i; j++)
    {
        if(list[i].EndsWith("_N") || list[j].EndsWith("_P"))
        {
            //Call method to find corresponding pair for this string;       O(n)
            //Call my processPairsmethod()
        }
    }
}

其他选项是正则表达式,我可以利用它来结束 _N 和 _P 条件,但不确定如何获得第二个条件的正则表达式。

PS:list中不存在重复项

任何指针都会有帮助

【问题讨论】:

  • 第二个条件不是已经覆盖第一个了吗?
  • 无论如何,如果保证列表没有重复项,最好使用HashSet。现在,元素是否可以包含多个Ns 或Ps?如果是这样,您是只替换一次还是全部替换?
  • 是的...元素可以包含多个N或P。这种情况下,我们应该将它们全部替换并检查残差列表中对应的字符串名称。
  • 不,第一个条件只是查看带有“_N”或“_P”的字符串结尾,如果字符串的其他部分有 N 或 P,则不会打扰。第二个条件仅涵盖第一个如果字符串不包含 N 除了最后一个 _N

标签: c# regex


【解决方案1】:

如果您知道字符串是唯一的,则可以创建一个创建唯一键的函数,例如将 N 和 P 都替换为不存在的字符(如 '*'):

// matches one single "N" or "P", that will be replaced by a single "*"
var rx = new Regex("N|P", RegexOptions.Compiled);

// pairs will be a list of string[2] arrays
var pairs = list.Select(s => new { Original = s, Key = rx.Replace(s, "*") })
  .ToLookup(obj => obj.Key)
  // each grp should be of length 2. In this select, validity checks should
  // be performed
  .Select(grp => grp.Select(obj => obj.Original).ToArray())
  .ToList();

使用ToLookup 应该会给你更好的性能:它大致应该为 O(n),因为查找搜索将是(大约)O(1)。

请记住,代码未经测试。

【讨论】:

  • ANNNBC 和 APBCP 怎么样?这将使它们匹配,而它们显然不匹配
【解决方案2】:

首先,您的算法有缺陷,因为您假设以 _N 和 _P 结尾的字符串会配对,但是 ABC_N 和 ABCDE_P 呢?他们不配对。 (长度不同)

你要做的就是在每个字符上迭代每个字符串,并且只有一个不同的字符,一个字符串是 N,另一个是 P。

for(int i = 0; i < list.Count; i++)
{
    for(int j=i + 1;j<list.Count && j != i; j++) // you dont need to start at j = 0
    {
        int a = 0;
        while (list[i][a] == list[j][a])
             a++;
        if ((list[i][a] == 'N' && list[j][a] == 'P') || (list[i][a] == 'P' && list[j][a] == 'N') {
             while (list[i][a] == list[j][a])
                 a++;
              if (list[i][a] == list[j][a] && list[j][a] == '\0')
                   // both list items match
                  //Call my 
         }
            
        }
    }
}

O(N) 算法

private static Dictionary<string, string> map = new Dictionary<string, string>();

for(int i = 0; i < list.Count; i++) {
     // replace N by P in the string
     if (map.ContainsKey(list[i]))
         processPairsmethod(map[list[i]], list[i]);
     else {
         int a = 0;
         while (list[i][a]) {
               if (list[i][a] == 'N' || 'P') {
                    StringBuilder sb = new StringBuilder(list[i]);
                    sb[a] = list[i][a] ^ 'N' ^ 'P'; // will replace N by P and P by N thanks to xoring operation as N ^ N ^ P = P
                    String key = sb.ToString();
                    map[key] = list[i]; // map["ANBC "] = "APBC", now you just need to check for the key.
               }
               a++;
           }
     }
}

【讨论】:

  • 我正在处理验证中的长度部分。在这里,我试图给你一个核心问题的要点。假设此逻辑仅在长度验证之后出现
  • 那么你无论如何都可以遵循这个算法,也可以从 i+1 开始使用 j 来节省时间
  • 这仍然是O(n^2)K K=字符串的最大长度
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-15
  • 2017-10-06
  • 2010-10-06
  • 2017-03-11
相关资源
最近更新 更多