4

入力として次の文字列の配列があるとします。

foo-139875913
foo-aeuefhaiu
foo-95hw9ghes
barbazabejgoiagjaegioea
barbaz8gs98ghsgh9es8h
9a8efa098fea0
barbaza98fyae9fghaefag
bazfa90eufa0e9u
bazgeajga8ugae89u
bazguea9guae
aifeaufhiuafhe

ここでは、"foo-"、"barbaz"、"baz" の 3 つの異なるプレフィックスが使用されていますが、これらのプレフィックスは前もって知られていません (まったく異なるものである可能性があります)。

さまざまな一般的なプレフィックスが何であるかをどのように確立して、それらをグループ化できるようにするにはどうすればよいでしょうか? 私が提供したデータには、"bazg" で始まる 2 つと "bazf" で始まる 1 つがあり、もちろん "baz" がプレフィックスであるため、これは少し注意が必要です。

私がこれまでに試したのは、それらをアルファベット順に並べ替えてから、順番にループして、前の文字と同じ行の文字数を数えることです。番号が異なる場合、または同じ文字が 0 文字の場合は、新しいグループを開始します。これに関する問題は、前述の「bazg」と「bazf」の問題に陥り、それらを 2 つの異なるグループ (1 つの要素のみを含むグループ) に分けることです。

編集:さて、さらにいくつかのルールを追加しましょう:

  • 長さの差が X 文字未満の密接に一致するグループがない限り、一般的に、潜在的なグループが長い方が短いグループよりも優先されます。(つまり、X が 2 の場合、bazg よりも baz が優先されます)
  • グループには、少なくとも Y 個の要素が含まれている必要があります。そうでない場合、グループではありません。
  • 上記のルールの範囲内で、「グループ」のいずれにも一致しない要素を単純に破棄しても問題ありません。

2 番目のルールとの関連で最初のルールを明確にするために、X が 0 で Y が 2 の場合、2 つの「bazg」エントリはグループになり、「bazf」は単独であるため破棄されます。

4

3 に答える 3

5

さて、これはおそらく簡単なハックですO(something_bad)

IEnumerable<Tuple<String, IEnumerable<string>>> GuessGroups(IEnumerable<string> source, int minNameLength=0, int minGroupSize=1)
{
    // TODO: error checking
    return InnerGuessGroups(new Stack<string>(source.OrderByDescending(x => x)), minNameLength, minGroupSize);
}

IEnumerable<Tuple<String, IEnumerable<string>>> InnerGuessGroups(Stack<string> source, int minNameLength, int minGroupSize)
{
    if(source.Any())
    {
        var tuple = ExtractTuple(GetBestGroup(source, minNameLength), source);
        if (tuple.Item2.Count() >= minGroupSize)
            yield return tuple;
        foreach (var element in GuessGroups(source, minNameLength, minGroupSize))
            yield return element;   
    }
}

Tuple<String, IEnumerable<string>> ExtractTuple(string prefix, Stack<string> source)
{
    return Tuple.Create(prefix, PopWithPrefix(prefix, source).ToList().AsEnumerable());
}

IEnumerable<string> PopWithPrefix(string prefix, Stack<string> source)
{
    while (source.Any() && source.Peek().StartsWith(prefix))
        yield return source.Pop();
}

string GetBestGroup(IEnumerable<string> source, int minNameLength)
{
    var s = new Stack<string>(source);
    var counter = new DictionaryWithDefault<string, int>(0);
    while(s.Any())
    {
        var g = GetCommonPrefix(s);
        if(!string.IsNullOrEmpty(g) && g.Length >= minNameLength)
            counter[g]++;
        s.Pop();
    }
    return counter.OrderBy(c => c.Value).Last().Key;
}

string GetCommonPrefix(IEnumerable<string> coll)
{
    return (from len in Enumerable.Range(0, coll.Min(s => s.Length)).Reverse()
            let possibleMatch = coll.First().Substring(0, len)
            where coll.All(f => f.StartsWith(possibleMatch))
            select possibleMatch).FirstOrDefault();
}

public class DictionaryWithDefault<TKey, TValue> : Dictionary<TKey, TValue>
{
  TValue _default;
  public TValue DefaultValue {
    get { return _default; }
    set { _default = value; }
  }
  public DictionaryWithDefault() : base() { }
  public DictionaryWithDefault(TValue defaultValue) : base() {
    _default = defaultValue;
  }
  public new TValue this[TKey key]
  {
    get { return base.ContainsKey(key) ? base[key] : _default; }
    set { base[key] = value; }
  }
}

使用例:

string[] input = {
    "foo-139875913",
    "foo-aeuefhaiu",
    "foo-95hw9ghes",
    "barbazabejgoiagjaegioea",
    "barbaz8gs98ghsgh9es8h",
    "barbaza98fyae9fghaefag",
    "bazfa90eufa0e9u",
    "bazgeajga8ugae89u",
    "bazguea9guae",
    "9a8efa098fea0",
    "aifeaufhiuafhe"
};

GuessGroups(input, 3, 2).Dump();

ここに画像の説明を入力

于 2013-05-06T13:16:36.030 に答える
0

あなたの要件が外れていないかどうか疑問に思っています。特定のキー サイズ要件ではなく、特定のグループ化サイズを探しているようです。指定されたグループサイズに基づいて、指定されたグループサイズを含め、文字列を可能な限り最大のグループに分割するプログラムを以下に示します。したがって、グループ サイズを 5 に指定すると、サイズ 5 のグループを作成できる最小のキーで項目がグループ化されます。この例では、foo-fとしてグループ化します。より複雑なキーを作成する必要がないためです。識別子。

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

namespace ConsoleApplication2
{
    class Program
    {
        /// <remarks><c>true</c> in returned dictionary key are groups over <paramref name="maxGroupSize"/></remarks>
        public static Dictionary<bool,Dictionary<string, List<string>>> Split(int maxGroupSize, int keySize, IEnumerable<string> items)
        {
            var smallItems = from item in items
                             where item.Length < keySize
                             select item;
            var largeItems = from item in items
                             where keySize < item.Length
                             select item;
            var largeItemsq = (from item in largeItems
                               let key = item.Substring(0, keySize)
                               group item by key into x
                               select new { Key = x.Key, Items = x.ToList() } into aGrouping
                               group aGrouping by aGrouping.Items.Count() > maxGroupSize into x2
                               select x2).ToDictionary(a => a.Key, a => a.ToDictionary(a_ => a_.Key, a_ => a_.Items));
            if (smallItems.Any())
            {
                var smallestLength = items.Aggregate(int.MaxValue, (acc, item) => Math.Min(acc, item.Length));
                var smallItemsq = (from item in smallItems
                                   let key = item.Substring(0, smallestLength)
                                   group item by key into x
                                   select new { Key = x.Key, Items = x.ToList() } into aGrouping
                                   group aGrouping by aGrouping.Items.Count() > maxGroupSize into x2
                                   select x2).ToDictionary(a => a.Key, a => a.ToDictionary(a_ => a_.Key, a_ => a_.Items));
                return Combine(smallItemsq, largeItemsq);
            }
            return largeItemsq;
        }

        static Dictionary<bool, Dictionary<string,List<string>>> Combine(Dictionary<bool, Dictionary<string,List<string>>> a, Dictionary<bool, Dictionary<string,List<string>>> b) {
            var x = new Dictionary<bool,Dictionary<string,List<string>>> {
                { true, null },
                { false, null }
            };
            foreach(var condition in new bool[] { true, false }) {
                var hasA = a.ContainsKey(condition);
                var hasB = b.ContainsKey(condition);
                x[condition] = hasA && hasB ? a[condition].Concat(b[condition]).ToDictionary(c => c.Key, c => c.Value)
                    : hasA ? a[condition]
                    : hasB ? b[condition]
                    : new Dictionary<string, List<string>>();
            }
            return x;
        }

        public static Dictionary<string, List<string>> Group(int maxGroupSize, IEnumerable<string> items, int keySize)
        {
            var toReturn = new Dictionary<string, List<string>>();
            var both = Split(maxGroupSize, keySize, items);
            if (both.ContainsKey(false))
                foreach (var key in both[false].Keys)
                    toReturn.Add(key, both[false][key]);
            if (both.ContainsKey(true))
            {
                var keySize_ = keySize + 1;
                var xs = from needsFix in both[true]
                         select needsFix;
                foreach (var x in xs)
                {
                    var fixedGroup = Group(maxGroupSize, x.Value, keySize_);
                    toReturn = toReturn.Concat(fixedGroup).ToDictionary(a => a.Key, a => a.Value);
                }
            }
            return toReturn;
        }

        static Random rand = new Random(unchecked((int)DateTime.Now.Ticks));
        const string allowedChars = "aaabbbbccccc"; // "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ";
        static readonly int maxAllowed = allowedChars.Length - 1;

        static IEnumerable<string> GenerateText()
        {
            var list = new List<string>();
            for (int i = 0; i < 100; i++)
            {
                var stringLength = rand.Next(3,25);
                var chars = new List<char>(stringLength);
                for (int j = stringLength; j > 0; j--)
                    chars.Add(allowedChars[rand.Next(0, maxAllowed)]);
                var newString = chars.Aggregate(new StringBuilder(), (acc, item) => acc.Append(item)).ToString();
                list.Add(newString);
            }
            return list;
        }

        static void Main(string[] args)
        {
            // runs 1000 times over autogenerated groups of sample text.
            for (int i = 0; i < 1000; i++)
            {
                var s = GenerateText();
                Go(s);
            }
            Console.WriteLine();
            Console.WriteLine("DONE");
            Console.ReadLine();
        }

        static void Go(IEnumerable<string> items)
        {
            var dict = Group(3, items, 1);
            foreach (var key in dict.Keys)
            {
                Console.WriteLine(key);
                foreach (var item in dict[key])
                    Console.WriteLine("\t{0}", item);
            }
        }

    }
}
于 2013-05-06T18:36:37.323 に答える