实现一个高效的算法来找到两个字符串的交集

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

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

  1. 将两个字符串转换为char数组
  2. 获取较小的数组并为其生成哈希表,其中键为字符,值为0
  3. 现在循环遍历另一个数组,并在散列表中增加计数(如果该字符存在于其中)。
  4. 现在取出值为> 0的哈希表的所有char。
  5. 这些是交叉值。

这是一个O(n)解决方案,但使用额外的空间,2个char数组和一个哈希表

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

这个怎么样 …

var s1 = "aabbccccddd"; var s2 = "aabc"; var ans = s1.Intersect(s2); 

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

  1. 快速排列两个字符串,因此您有一个有序的字符序列
  2. 将索引保存到两个字符串中,比较每个字符串中的“下一个”字符,选择并输出第一个字符串,递增该字符串的索引。
  3. 继续,直到你到达其中一个字符串的末尾,然后从剩余的字符串中拉出唯一值。

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

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

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

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

O(N)和额外的空间只有26个整数。

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

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

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