Каков наилучший способ разбить список строк, чтобы соответствовать первым и последним буквам?
У меня длинный список слов в C#, и я хочу найти все слова в этом списке, которые имеют одинаковые первые и последние буквы и имеют длину, скажем, от 5 до 7 символов. Например, список может иметь:
"впустую было вымыто мытье было мытье смотрело часы варит с отходами мытье умов"
Вернется
Длина: 5-7, Первая буква: w, Последняя буква: d, "впустую, вымыл, смотрел" Длина: 5-7, Первая буква: w, Последняя буква: s, "моет, часы, увядает, тратит"
Тогда я мог бы изменить спецификацию на длину 3-4 символа, которая будет возвращать
Длина: 3-4, Первая буква: w, Последняя буква: s, "was, wits"
Я нашел этот метод разбиения, который очень быстрый, сделал каждый элемент уникальным, использовал длину и дал отличный старт: расщепление строки в списки на основе длины слов C#
Есть ли способ изменить / использовать это, чтобы учесть первые и последние буквы?
РЕДАКТИРОВАТЬ
Первоначально я спросил о "самом быстром" способе, потому что обычно я решаю подобные проблемы с помощью большого количества строковых массивов (которые работают медленно и требуют много кода). LINQ и поиски являются новыми для меня, но я вижу, что ILookup, использованный в решении, с которым я связан, удивителен своей простотой и очень быстр. На самом деле мне не нужно минимальное время процессора. Любой подход, который позволяет избежать создания отдельных массивов для этой информации, был бы фантастическим.
5 ответов
Этот лайнер даст вам группы с одинаковыми первой / последней буквой в вашем диапазоне
int min = 5;
int max = 7;
var results = str.Split()
.Where(s => s.Length >= min && s.Length <= max)
.GroupBy(s => new { First = s.First(), Last = s.Last()});
Просто в LINQPad я создал это:
void Main()
{
var words = new []{"wasted", "was", "washed", "washing", "was", "washes", "watched", "watches", "wilts", "with", "wastes", "wits", "washings"};
var firstLetter = "w";
var lastLetter = "d";
var minimumLength = 5;
var maximumLength = 7;
var sortedWords = words.Where(w => w.StartsWith(firstLetter) && w.EndsWith(lastLetter) && w.Length >= minimumLength && w.Length <= maximumLength);
sortedWords.Dump();
}
Если это не достаточно быстро, я бы создал таблицу поиска:
Dictionary<char, Dictionary<char, List<string>> lookupTable;
и делать:
lookupTable[firstLetter][lastLetter].Where(<check length>)
var minLength = 5;
var maxLength = 7;
var firstPart = "w";
var lastPart = "d";
var words = new List<string> { "washed", "wash" }; // so on
var matches = words.Where(w => w.Length >= minLength && w.Length <= maxLength &&
w.StartsWith(firstPart) && w.EndsWith(lastPart))
.ToList();
по большей части это должно быть достаточно быстро, если вы не имеете дело с десятками тысяч слов и не беспокоитесь о мс. тогда мы можем смотреть дальше.
Вот метод, который делает именно то, что вы хотите. Вам дан только список строк и минимальная / максимальная длина, правильно? Вам не дают первые и последние буквы для фильтрации. Этот метод обрабатывает все первые / последние буквы в строках.
private static void ProcessInput(string[] words, int minLength, int maxLength)
{
var groups = from word in words
where word.Length > 0 && word.Length >= minLength && word.Length <= maxLength
let key = new Tuple<char, char>(word.First(), word.Last())
group word by key into @group
orderby Char.ToLowerInvariant(@group.Key.Item1), @group.Key.Item1, Char.ToLowerInvariant(@group.Key.Item2), @group.Key.Item2
select @group;
Console.WriteLine("Length: {0}-{1}", minLength, maxLength);
foreach (var group in groups)
{
Console.WriteLine("First letter: {0}, Last letter: {1}", group.Key.Item1, group.Key.Item2);
foreach (var word in group)
Console.WriteLine("\t{0}", word);
}
}
Просто подумав, я понятия не имею, будет ли это быстрее или эффективнее, чем опубликованные решения linq, но это также можно сделать довольно легко с помощью регулярных выражений.
Например, если вы хотите получить слова длиной 5-7 букв, которые начинаются с "w" и заканчиваются на "s", вы можете использовать шаблон вдоль строк:
\bw[A-Za-z]{3,5}s\b
(и это может быть довольно легко сделано, чтобы быть более переменным - например, иметь переменную для первой буквы, минимальной длины, максимальной длины, последней буквы и подключить их к шаблону, чтобы заменить w, 3, 5 & s)
Таким образом, используя библиотеку RegEx, вы можете просто взять свои захваченные группы в свой список.
Опять же, я не знаю, как это сравнивает по эффективности с linq, но я подумал, что это может заслуживать упоминания.
Надеюсь это поможет!!