2009-08-27 10 views

答えて

21

:最速のパフォーマンスのように最速

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 = new HashSet<char>(); 
var h2 = new HashSet<char>(); 

foreach (var ch in "nbHHkRvrXbvkn") 
{ 
    if (!h1.Add(ch)) 
    { 
     h2.Add(ch); 
    } 
} 

h1.ExceptWith(h2); // remove duplicates 

var chars = new char[h1.Count]; 
h1.CopyTo(chars); 
var result = new string(chars); // = "RrX" 

性能試験

場合疑問に - それをテスト:)

 
Yuriy Faktorovich's answer  00:00:00.2360900 
Luke's answer      00:00:00.2225683 
My 'few lines' answer    00:00:00.5318395 
My 'fast' answer     00:00:00.1842144 
+1

非常にいいです。素晴らしいパフォーマンスの比較も。非常に大きな文字列では、パフォーマンスのばらつきがさらに目立つことがあります。 – Alex

+1

デタッチされたデバッガ(同じ入力文字列)を使ってリリースビルドでパフォーマンステストを繰り返しました。私はユリの答えの演技に賛成です。それはかなり速いです! – dtb

+1

@dtb:あなたの答えに比べて私の答えが遅くなるのは、出力文字列の元の順序を保持しているため、入力文字列のループが余分に必要になるということです。あなたと私が二重引用符を実際に見つけるために使用する手法は、まったく同じ*です。 – LukeH

0

このアルゴリズムは一般的である、(任意の言語

  1. マップを作成するために適用することができますHashTable)最初に空の文字の数を保持するchar-> int
  2. strinをスキャンするgを1回押して地図を入力します。
  3. 出力を保持する新しい空の文字列を作成します。StringBuilderを使用する必要があります。
  4. 文字のみをコピーする文字列(いずれか短い方ortheマップを、)スキャンここでは、出力文字列/ StringBuilderの
  5. から1の発生
9

が注文を保存かなり速い一つです。

string s = "nbHHkRvrXbvkn"; 
Console.WriteLine( 
    s.ToCharArray() 
     .GroupBy(c => c) 
     .Where(g => g.Count() == 1) 
     .Aggregate(new StringBuilder(), (b, g) => b.Append(g.Key))); 

編集:この1はDTB年代よりもルークのいくつかのケースでは、まだ遅く打つが、それはこの

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(); 
} 
+1

+1。非常にきれいな解決策。それは驚くほど高速です! – dtb

4

順序を保持しかし、私はLINQのグループとどうするかについて少し心配するだろう一つは(と、それは元の順序を維持する)かなり速い必要があります:

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(); 
} 
+0

おそらく大きすぎるStringBuilderを作成すると、その場でスペースを取得するよりも時間がかかります。 –

+0

@ユリ:私はベンチマークしました!私は何百万ものランダムな文字列でテストし、 'StringBuilder'のサイズをあらかじめ設定することは、ほとんどの場合に高速でした。もちろん、現実世界では、文字列はおそらく純粋にランダムではありません。その状況では、パフォーマンスの違いは、ソース文字列内のダブと非ダブの比率に依存します。 – LukeH

+0

@Yuriy:私はちょうど別のマシン(Vista64対XP32)でベンチマークして、結果ははるかに近かった。 64ビットマシンでは、 'StringBuilder'があらかじめ割り当てられているかどうかに違いはありません。 (その場合は、あらかじめ割り振っておき、RAMを節約しておくのが賢明ではないでしょうか?) – LukeH

2

これは私のテストに基づいて、順序を保持して、HashSetのを使用するよりも4倍高速です。これはあなたのキャラクタの範囲が0-255であることを前提としていますが、それを簡単に拡張することができます。これをループで使用する予定がある場合は、int[] c = new int[255];を移動し、関数内でArray.Clear(c,0,255)を実行します。


     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(); 
     } 
+0

コンパイラがそれらのループをアンロールするかどうかわかりませんが、それも試すことができますhttp:// en .wikipedia.org/wiki/Loop_unwinding – gabe

+1

'char.MaxValue'は65535です – dtb

+0

サンプル文字列のテストタイミング/ストップウォッチの結果は? – Alex

関連する問題