实现字符串中重复字符删除的最快方法(C#)

实现字符串中重复字符删除的最快方法(C#),c#,.net,string,C#,.net,String,在C#中,检测字符串中的重复字符并将其删除(删除包括重复字符的第一个实例)的最快方法是什么 输入示例:nbHHkRvrXbvkn 示例输出:RrX最快,代码行数最少: var s = "nbHHkRvrXbvkn"; var duplicates = s.Where(ch => s.Count(c => c == ch) > 1); var result = new string(s.Except(duplicates).ToArray()); // = "RrX" 最快的性

在C#中,检测字符串中的重复字符并将其删除(删除包括重复字符的第一个实例)的最快方法是什么

输入示例:
nbHHkRvrXbvkn


示例输出:
RrX

最快,代码行数最少:

var s = "nbHHkRvrXbvkn";
var duplicates = s.Where(ch => s.Count(c => c == ch) > 1);
var result = new string(s.Except(duplicates).ToArray()); // = "RrX"
最快的性能可能是这样的(不保持顺序):

var h1=newhashset();
var h2=新的HashSet();
foreach(在“nbHHkRvrXbvkn”中的var ch)
{
如果(!h1.添加(ch))
{
h2.添加(ch);
}
}
h1.除(h2);//删除重复项
var chars=新字符[h1.Count];
h1.副本(chars);
var结果=新字符串(字符);//=“RrX”

性能测试

当有疑问时--测试它:)

尤里·法克托罗维奇的回答00:00:00.2360900 卢克的回答00:00:00.2225683 我的“几行”回答00:00:00.5318395 我的“快速”答案00:00:00.1842144
该算法具有通用性,可以应用于任何语言

  • 创建一个map(HashTable)char->int,其中保存找到的每个字符的计数,最初为空
  • 扫描字符串一次以填充地图
  • 创建一个新的空字符串来保存输出,可能需要使用StringBuilder
  • 扫描字符串(或映射,以较短者为准),仅将出现1的字符复制到输出字符串/StringBuilder

  • 这是一个很快就能维持秩序的。但我会有点担心LINQ是如何工作的,在哪里:

    string s = "nbHHkRvrXbvkn";
    Console.WriteLine( 
        s.ToCharArray()
            .GroupBy(c => c)
            .Where(g => g.Count() == 1)
            .Aggregate(new StringBuilder(), (b, g) => b.Append(g.Key)));
    
    编辑:这个在某些情况下比Luke的慢,但它保留了顺序

    private static string MyMethod(string s)
    {
        StringBuilder sb = new StringBuilder(s.Length);
        foreach (var g in s.ToCharArray().GroupBy(c => c))
            if (g.Count() == 1) sb.Append(g.Key);
    
        return sb.ToString();
    }
    

    这一个应该很快(并且它保留了原始顺序):

    publicstaticstringremoveduplicates(字符串源)
    {
    HashSet found=新的HashSet();
    HashSet dupes=新HashSet();
    foreach(源代码中的字符c)
    {
    如果(!found.Add(c))
    {
    添加(c);
    }
    }
    StringBuilder sb=新的StringBuilder(source.Length);
    foreach(源代码中的字符c)
    {
    如果(!dups.Contains(c))
    {
    sb.附加(c);
    }
    }
    使某人返回字符串();
    }
    
    这保留了顺序,并且根据我的测试,比使用哈希集快4倍。这假设您的字符范围为0-255,但您可以轻松扩展该范围。如果您计划在循环中使用此选项,请移动
    int[]c=new int[255]在函数中执行一个
    数组。清除(c,0255)

    
    私有静态字符串移除重复(字符串s)
    {
    int[]c=新的int[255];
    对于(int i=0;i
    非常好。性能比较也很棒。对于非常大的字符串,性能变化可能更为明显。我已经使用分离的调试器(但输入字符串相同)在发布版本中重复了性能测试。我对尤里回答的表现感到惊讶;它相当快@dtb:与你的答案相比,我的答案慢了很多,因为我保留了输出字符串中的原始顺序,这就需要通过输入字符串进行额外的循环。你和我用来查找复制品的技术完全相同。请参阅下面我的解决方案。。。您的想法是正确的,但是在我的testsc#has var?…中使用已知数据的数组比HashSet快4倍。。。我以前从未见过在c#中使用var。。。顺便说一句,这段代码很棒+1为什么你认为创建一个可能太大的StringBuilder比让它在运行中获得空间所需的时间要少?@Yuri:我做了基准测试!我使用数百万个随机字符串进行了测试,在大多数情况下,
    StringBuilder
    的预调整速度更快。当然,在现实世界中,字符串可能不是完全随机的。在这种情况下,性能差异将取决于源字符串中重复与非重复的比率。@Yuriy:我刚刚在另一台机器上进行了基准测试(Vista64与XP32),结果更接近。在64位机器上,
    StringBuilder
    是否预先分配似乎没有什么实际区别。(在这种情况下,不必预先分配并保存一些RAM可能是有意义的。)此外,我不知道编译器是否会为您展开这些循环,但您也可以尝试一下,您的测试计时/秒表结果是什么,与其他方法相比,此方法将是1/4或更少。@Yuriy:数组大小是否正确设置为65536?在我的测试中,一旦数组大小正确,此方法与使用
    哈希集
    之间没有太大区别。不,一旦数组设置正确,平均使用100个字符的字符串,情况会差60%。
    string s = "nbHHkRvrXbvkn";
    Console.WriteLine( 
        s.ToCharArray()
            .GroupBy(c => c)
            .Where(g => g.Count() == 1)
            .Aggregate(new StringBuilder(), (b, g) => b.Append(g.Key)));
    
    private static string MyMethod(string s)
    {
        StringBuilder sb = new StringBuilder(s.Length);
        foreach (var g in s.ToCharArray().GroupBy(c => c))
            if (g.Count() == 1) sb.Append(g.Key);
    
        return sb.ToString();
    }
    
    public static string RemoveDuplicates(string source)
    {
        HashSet<char> found = new HashSet<char>();
        HashSet<char> dupes = new HashSet<char>();
    
        foreach (char c in source)
        {
            if (!found.Add(c))
            {
                dupes.Add(c);
            }
        }
    
        StringBuilder sb = new StringBuilder(source.Length);
        foreach (char c in source)
        {
            if (!dupes.Contains(c))
            {
                sb.Append(c);
            }
        }
        return sb.ToString();
    }
    
    
            private static string RemoveDuplicates(string s)
            {
                int[] c = new int[255];
                for (int i = 0; i < s.Length; i++)
                {
                    c[s[i]]++;
                }
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < s.Length; i++)
                {
                    if (c[s[i]] == 1) sb.Append(s[i]);
                }
                return sb.ToString();
            }