2014-10-28 3 views
2

Я хотел бы разделить, группировать и подсчитывать вхождения отдельных фраз в большой строке в C#.Сплит, группа и строка подсчета

Следующий псевдо-код должен указывать на то, чего я пытаюсь достичь.

var my_string = "In the end this is not the end"; 
my_string.groupCount(2); 

==> 
    [0] : {Key: "In the", Count:1} 
    [1] : {Key: "the end", Count:2} 
    [2] : {Key: "end this", Count: 1} 
    [3] : {Key: "this is", Count: 1} 
    [4] : {Key: "is not", Count: 1} 
    [5] : {Key: "not the", Count: 1} 

Как вы заметили, это не так просто, как просто разбиение строки и подсчет каждой подстроки. Пример группирует каждые 2 слова, но в идеале он должен иметь возможность обрабатывать любое число.

+0

Благодаря @dasblinkenlight, я пропустил это. – tribe84

+0

@GrantWinney - нет, два вопроса похожи, но не то же самое. – tribe84

+0

Как вы хотите разделить 'input'? –

ответ

1

Вот схема того, как вы можете подойти к этому:

  • использовать обычный Split метод string, чтобы получить отдельные слова
  • Составьте словарь для графов
  • пройти через все пары последовательных слов, построение составных клавиш и приращений

Вот как вы можете это реализовать:

var counts = new Dictionary<string,int>(); 
var tokens = str.Split(' '); 
for (var i = 0 ; i < tokens.Length-1 ; i++) { 
    var key = tokens[i]+" "+tokens[i+1]; 
    int c; 
    if (!counts.TryGetValue(key, out c)) { 
     c = 0; 
    } 
    counts[key] = c + 1; 
} 

Demo.

+0

Что происходит в случае действительно больших строк? – gabba

+0

@gabba То же самое происходит в случае не столь больших строк :-) Задача является линейной по количеству времени и требуемой памяти. – dasblinkenlight

+0

, когда вы разделяете строку 2gb на потоки небольших строк, вы получаете более двухкратное потребление памяти. Нам это не нужно. Все, что нам нужно сделать, - это сканирование и небольшой словарь. – gabba

0

Вот моя реализация. Я обновил его, чтобы переместить работу в функцию и позволить вам указать произвольный размер группы.

public static Dictionary<string,int> groupCount(string str, int groupSize) 
{ 
    string[] tokens = str.Split(new char[] { ' ' }); 

    var dict = new Dictionary<string,int>(); 
    for (int i = 0; i < tokens.Length - (groupSize-1); i++) 
    { 
     string key = ""; 
     for (int j = 0; j < groupSize; j++) 
     { 
      key += tokens[i+j] + " "; 
     } 
     key = key.Substring(0, key.Length-1); 

     if (dict.ContainsKey(key)) { 
      dict[key]++; 
     } else { 
      dict[key] = 1; 
     } 
    } 

    return dict; 
} 

Используйте это так:

string str = "In the end this is not the end"; 
int groupSize = 2; 
var dict = groupCount(str, groupSize); 

Console.WriteLine("Group Of {0}:", groupSize); 
foreach (string k in dict.Keys) { 
    Console.WriteLine("Key: \"{0}\", Count: {1}", k, dict2[k]); 
} 

.NET Fiddle

+0

Отмечу, что он очень похож на взятие dasblinkenlight. Он использует Split для получения отдельных слов, использует цикл for для получения токенов и словарь для поддержания количества токенов для получения. –

0

Вы можете создать метод, который строит фразу из заданных слов.Не очень эффективно (из-за Скип), но простую реализация:

private static IEnumerable<string> CreatePhrases(string[] words, int wordsCount) 
{ 
    for(int i = 0; i <= words.Length - wordsCount; i++) 
     yield return String.Join(" ", words.Skip(i).Take(wordsCount)); 
} 

Отдыха прост - разделить вашу строку на слова, строить фразы, и получить вхождение каждой фразы в исходной строке:

var my_string = "In the end this is not the end"; 
var words = my_string.Split(); 
var result = from p in CreatePhrases(words, 2) 
      group p by p into g 
      select new { g.Key, Count = g.Count()}; 

Результата :

[ 
    Key: "In the", Count: 1, 
    Key: "the end", Count: 2, 
    Key: "end this", Count: 1, 
    Key: "this is", Count: 1, 
    Key: "is not", Count: 1, 
    Key: "not the", Count: 1 
] 

Более эффективный способ создать последовательные группы элементов (работает с любым I Перечислимых):

public static IEnumerable<IEnumerable<T>> ToConsecutiveGroups<T>(
    this IEnumerable<T> source, int size) 
{ 
    // You can check arguments here    
    Queue<T> bucket = new Queue<T>(); 

    foreach(var item in source) 
    { 
     bucket.Enqueue(item); 
     if (bucket.Count == size) 
     { 
      yield return bucket.ToArray(); 
      bucket.Dequeue(); 
     } 
    } 
} 

И все расчеты можно было бы сделать в одной строке:

var my_string = "In the end this is not the end"; 
var result = my_string.Split() 
       .ToConsecutiveGroups(2) 
       .Select(words => String.Join(" ", words)) 
       .GroupBy(p => p) 
       .Select(g => new { g.Key, Count = g.Count()}); 
+1

Yeeaahh, Regex в последней строке лучшее решение здесь – gabba

+1

@gabba yep, лучше всего от меня в 1 AM :) Вместо того, чтобы просто возвращать счет, я делаю отчетливый :) –

+0

Также ваша душа была бы beter, если вы напишете метод SplitToConsecutiveGroups, который будет проходить через исходная строка и возвращаемая перечислимая группа слов – gabba

1

Вот другой подход, используя ILookup<string, string[]> сосчитать вхождение каждого массива:

var my_string = "In the end this is not the end"; 
int step = 2; 
string[] words = my_string.Split(); 
var groupWords = new List<string[]>(); 
for (int i = 0; i + step <= words.Length; i++) 
{ 
    string[] group = new string[step]; 
    for (int ii = 0; ii < step; ii++) 
     group[ii] = words[i + ii]; 
    groupWords.Add(group); 
} 
var lookup = groupWords.ToLookup(w => string.Join(" ", w)); 

foreach(var kv in lookup) 
    Console.WriteLine("Key: \"{0}\", Count: {1}", kv.Key, kv.Count()); 

Выход:

Key: "In the", Count: 1 
Key: "the end", Count: 2 
Key: "end this", Count: 1 
Key: "this is", Count: 1 
Key: "is not", Count: 1 
Key: "not the", Count: 1 
+1

Ницца! Взгляд здесь хорош – gabba

0

Предполагая, что вам нужно иметь дело с LARGE-строкой, я бы не рекомендовал вам разделить целую строку. Вы должны пройти через это, помните последние слова groupCount и сосчитать комбинации в dictinary:

var my_string = "In the end this is not the end"; 

    var groupCount = 2; 

    var groups = new Dictionary<string, int>(); 
    var lastGroupCountWordIndexes = new Queue<int>(); 

    for (int i = 0; i < my_string.Length; i++) 
    { 
     if (my_string[i] == ' ' || i == 0) 
     { 
      lastGroupCountWordIndexes.Enqueue(i); 
     } 

     if (lastGroupCountWordIndexes.Count >= groupCount) 
     { 
      var firstWordInGroupIndex = lastGroupCountWordIndexes.Dequeue(); 

      var gruopKey = my_string.Substring(firstWordInGroupIndex, i - firstWordInGroupIndex); 

      if (!groups.ContainsKey(gruopKey)) 
      { 
       groups.Add(gruopKey, 1); 
      } 
      else 
      { 
       groups[gruopKey]++; 
      } 
     } 

    } 
Смежные вопросы