2017-12-24 9 views
0

私は文字列でいっぱいのテキストファイルを各行に1つずつ持っています。これらの文字列の一部には、未知数の「@」文字が含まれます。各 "@"は数字1,2,3、または4を表すことができます。これらの "@"のそれぞれについて、文字列のすべての可能な組み合わせ(順列?)を生成したいと思います。文字列ごとに設定された数の "@"があった場合、ネストされたforループ(素早く汚い)を使用するだけです。私は未知数の「@」でそれを行うよりエレガントな方法を見つけるのを助ける必要があります。未知数のスロットを含むすべての組み合わせを生成する

例1:入力文字列が[email protected]

出力文字列は次のようになります:

a1bc 
a2bc 
a3bc 
a4bc 

例2:

a1bc1d 
a1bc2d 
a1bc3d 
a1bc4d 
a2bc1d 
a2bc2d 
a2bc3d 
... 
a4bc3d 
a4bc4d 

:入力文字列が[email protected]@d

出力文字列は次のようになりあります誰もがこれを手伝うことができますか?私はC#を使用しています。

答えて

0

これは、実際には再帰関数にとってかなり良い場所です。私はC#を書いていませんが、文字列を受け取り、展開された文字列を含む配列を返す関数List<String> expand(String str)を作成します。

expand最初に@を見つけて文字列を検索し、文字列+展開の最初の部分を含むリストを作成できます。次に、文字列の最後の部分にexpandを呼び出して、最後の部分の展開の各要素に展開の各要素を追加できます。

実装例のJavaのArrayListを使用して:

ArrayList<String> expand(String str) { 

    /* Find the first "@" */ 
    int i = str.indexOf("@"); 

    ArrayList<String> expansion = new ArrayList<String>(4); 
    /* If the string doesn't have any "@" */ 
    if(i < 0) { 
     expansion.add(str); 
     return expansion; 
    } 

    /* New list to hold the result */ 
    ArrayList<String> result = new ArrayList<String>(); 

    /* Expand the "@" */ 
    for(int j = 1; j <= 4; j++) 
     expansion.add(str.substring(0,i-1) + j); 

    /* Combine every expansion with every suffix expansion */ 
    for(String a : expand(str.substring(i+1))) 
     for(String b : expansion) 
      result.add(b + a); 
    return result; 
} 
0

私はコンソールアプリケーションに以下のコードをテストしました。あなたの要件を満たしているかどうか確認してください。ここでの主なアイデアは、指定された文字列を分割してすべての可能な出力を構成することです。ここで

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      //string value = "[email protected]"; 
      string value = "[email protected]@d"; 
      //string value = "[email protected]@[email protected]"; 

      var array = Generate(value); 
      foreach (string str in array) 
      { 
       Console.WriteLine(str); 
      } 

      Console.ReadKey(); 
     } 

     // This method will initiate generation of 
     // all combinations for given value 
     static string[] Generate(string value) 
     { 
      string[] parts = value.Split(new string[] { "@" }, StringSplitOptions.None); 

      var count = (int)Math.Pow(4, parts.Length - 1); 

      string[] array = new string[count]; 

      int index = 0; 
      Generate(array, ref index, parts[0], 1, parts); 

      return array; 
     } 

     // Here we generate all possible combinations 
     // with recursion 
     static void Generate(string[] array, ref int index, string value, int level, string[] parts) 
     { 
      bool flag = level + 1 < parts.Length; 

      for (int i = 1; i < 5; i++) 
      { 
       string str = (value + i + parts[level]); 

       if (flag) 
       { 
        Generate(array, ref index, str, level + 1, parts); 
       } 
       else 
       { 
        array[index] = str; 
        index++; 
       } 

      } 
     } 

    } 
} 

これは再帰的な解決のために大声で叫んされてスクリーンショット

enter image description here

0

です。

まず、指定された値のセットから特定の長さのすべての組み合わせを生成するメソッドを作成します。文字列の生成にのみ関心があるので、stringは不変であるという事実を利用できます(P.D.2参照)。再帰関数を実装するのが非常に簡単になり、次の理由が考えられます。

static IEnumerable<string> GetAllCombinations<T>(
    ISet<T> set, int length) 
{ 
    IEnumerable<string> getCombinations(string current) 
    { 
     if (current.Length == length) 
     { 
      yield return current; 
     } 
     else 
     { 
      foreach (var s in set) 
      { 
       foreach (var c in getCombinations(current + s)) 
       { 
        yield return c; 
       } 
      } 
     } 
    } 

    return getCombinations(string.Empty); 
} 

このメソッドがどのように機能するかを慎重に検討します。それを理解するための小さな事例を手で手にしてください。

は今、かつて我々はすべての可能な組み合わせを生成する方法を知って、文字列を構築するのは簡単です:指定された文字列内のワイルドカードの数うち

  1. 図:これは私たちの組み合わせの長さになります。
  2. すべての組み合わせについて、各文字をワイルドカードが出現する文字列に挿入します。

    public static IEnumerable<string> GenerateCombinations<T>(
        this string s, 
        IEnumerable<T> set, 
        char wildcard) 
    { 
        var length = s.Count(c => c == wildcard); 
        var combinations = GetAllCombinations(set, length); 
        var builder = new StringBuilder(); 
    
        foreach (var combination in combinations) 
        { 
         var index = 0; 
    
         foreach (var c in s) 
         { 
          if (c == wildcard) 
          { 
           builder.Append(combination[index]); 
           index += 1; 
          } 
          else 
          { 
           builder.Append(c); 
          } 
         } 
    
         yield return builder.ToString(); 
         builder.Clear(); 
        } 
    } 
    

    そして、我々は終わった:

[OK]を、ちょうどそれを行うことができます。使用法は次のようになります。

var set = new HashSet<int>(new[] { 1, 2, 3, 4 }); 
Console.WriteLine(
    string.Join("; ", "[email protected]@d".GenerateCombinations(set, '@'))); 

そして案の定、出力は次のようになります。これは最もパフォーマンスや効率的な実装

a1bc1d; a1bc2d; a1bc3d; a1bc4d; a2bc1d; a2bc2d; a2bc3d; 
a2bc4d; a3bc1d; a3bc2d; a3bc3d; a3bc4d; a4bc1d; a4bc2d; 
a4bc3d; a4bc4d 

ですか?おそらくそれは読めるものではなく、保守可能なものです。あなたが会議ではない特定のパフォーマンス目標を持っていない限り、うまく動作し、理解しやすいコードを記述します。

P.D.私はすべてのエラー処理と引数の検証を省略しました。

P.D.2:組み合わせの長さが大きい場合は、GetAllCombinationsの連結文字列はお勧めできません。その場合、GetAllCombinationsIEnumerable<IEnumerable<T>>を返し、ImmutableStack<T>を実装し、それをstringの代わりに組み合わせバッファとして使用します。

0

私はここに問題のミニマルなアプローチを提供しています。 はい、他のように再帰はここに行く方法です。

再帰は入力の短い部分に対して解を提供することでこの問題を解決することができ、結果がマージされるまでもう一方の部分でもう一度やり直すことができます。

すべての再帰には停止条件が必要です。再帰が必要ないことを意味します。

ここで私のストップコンディションは、文字列に"@"がなくなったことです。 IEnumerable<char>なので、文字列を値のセット(1234)として使用しています。

他のすべてのソリューションは素晴らしいです。ちょっとしたアプローチをご紹介したいと思います。 LINQと

internal static IEnumerable<string> GetStrings(string input) 
{ 
    var values = "1234"; 
    var permutations = new List<string>(); 

    var index = input.IndexOf('@'); 
    if (index == -1) return new []{ input }; 

    for (int i = 0; i < values.Length; i++) 
    { 
     var newInput = input.Substring(0, index) + values[i] + input.Substring(index + 1); 
     permutations.AddRange(GetStrings(newInput)); 
    } 

    return permutations; 
} 

アンでも短くし、クリーンなアプローチ:

internal static IEnumerable<string> GetStrings(string input) 
{ 
    var values = "1234"; 

    var index = input.IndexOf('@'); 
    if (index == -1) return new []{ input }; 

    return 
     values 
     .Select(ReplaceFirstWildCardWithValue) 
     .SelectMany(GetStrings); 

    string ReplaceFirstWildCardWithValue(char value) => input.Substring(0, index) + value + input.Substring(index + 1); 
} 
関連する問題