实现一种有效的算法来查找两个字符串的交集

时间:2009-07-07 05:11:31

标签: c# algorithm

  
    

实现一个算法,该算法将两个字符串作为输入,并返回两者的交集,每个字母最多代表一次。

  

Algo :(考虑使用的语言是c#)

  1. 将两个字符串转换为字符数组
  2. 获取较小的数组并为其生成哈希表,其中key为字符,值为0
  3. 现在循环遍历另一个数组,并在哈希表中增加计数(如果该字符存在于其中)。
  4. 现在取出所有值为>的哈希表的char。 0
  5. 这些是交叉值。
  6. 这是一个O(n)解决方案但是使用了额外的空间,2个char数组和一个哈希表

    你能想到比这更好的解决方案吗?

5 个答案:

答案 0 :(得分:10)

这个怎么样......

var s1 = "aabbccccddd";
var s2 = "aabc";

var ans = s1.Intersect(s2);

答案 1 :(得分:2)

没有测试过这个,但这是我的想法:

  1. 将两个字符串定位到适当的位置,因此您有一个有序的字符序列
  2. 将索引保存到两个字符串中,比较每个字符串中的“next”字符,选择并输出第一个字符串,递增该字符串的索引。
  3. 继续,直到你到达其中一个字符串的末尾,然后从剩余的字符串中拉出唯一值。
  4. 不会使用额外的内存,只需要两个原始字符串,两个整数和一个输出字符串(或StringBuilder)。作为额外的奖励,输出值也将被排序!

    第2部分: 这就是我要写的内容(对于注释感到抱歉,对于stackoverflow是新的):

    private static string intersect(string left, string right)
    {
      StringBuilder theResult = new StringBuilder();
    
      string sortedLeft = Program.sort(left);
      string sortedRight = Program.sort(right);
    
      int leftIndex = 0;
      int rightIndex = 0;
    
      //  Work though the string with the "first last character".
      if (sortedLeft[sortedLeft.Length - 1] > sortedRight[sortedRight.Length - 1])
      {
        string temp = sortedLeft;
        sortedLeft = sortedRight;
        sortedRight = temp;
      }
    
      char lastChar = default(char);
      while (leftIndex < sortedLeft.Length)
      {
        char nextChar = (sortedLeft[leftIndex] <= sortedRight[rightIndex]) ? sortedLeft[leftIndex++] : sortedRight[rightIndex++];
    
        if (lastChar == nextChar) continue;
    
        theResult.Append(nextChar);
        lastChar = nextChar;
      }
    
      //  Add the remaining characters from the "right" string
      while (rightIndex < sortedRight.Length)
      {
        char nextChar = sortedRight[rightIndex++];
        if (lastChar == nextChar) continue;
    
        theResult.Append(nextChar);
        lastChar = nextChar;
      }
      theResult.Append(sortedRight, rightIndex, sortedRight.Length - rightIndex);
    
      return (theResult.ToString());
    }
    

    我希望这更有意义。

答案 2 :(得分:1)

您不需要2个char数组。 System.String数据类型有一个按位置的内置索引器,它从该位置返回char,因此您可以从0循环到(String.Length - 1)。如果您对速度比对优化存储空间更感兴趣,那么您可以为其中一个字符串创建一个HashSet,然后创建一个包含最终结果的第二个HashSet。然后迭代遍历第二个字符串,针对第一个HashSet测试每个char,如果它存在则将其添加到第二个HashSet。最后,您已经拥有一个包含所有交叉点的单个HashSet,并且自己保存在Hashtable中运行的通道,以查找具有非零值的HashSet。

编辑:我在关于根本不想使用任何内置容器的所有评论之前输入了这个

答案 3 :(得分:1)

这是我将如何做到这一点。它仍然是O(N)并且它不使用哈希表,而是使用长度为26的一个int数组。(理想情况下)

  1. 制作一个包含26个整数的数组,每个元素用于alphebet的一个字母。初始化为0。
  2. 迭代第一个字符串,在遇到字母时递减一个字符串。
  3. 迭代第二个字符串并获取与您遇到的任何字母对应的索引处的绝对值。 (编辑:感谢评论中的scwagner)
  4. 返回与保存值大于0的所有索引对应的所有字母。
  5. 仍为O(N)且仅有26个整数的额外空间。

    当然,如果您不仅限于较低或大写字符,则可能需要更改数组大小。

答案 4 :(得分:0)

  

“每个字母最多代表一次”

我假设这意味着你只需要知道交叉点,而不是它们发生了多少次。如果是这样,那么您可以通过使用yield来减少算法。而不是存储计数并继续迭代第二个字符串以寻找其他匹配,您可以在那里产生交集,并继续从第一个字符串开始下一个可能的匹配。