Проверьте, содержит ли строка элемент из списка (строк)

100

Для следующего блока кода:

For I = 0 To listOfStrings.Count - 1
    If myString.Contains(lstOfStrings.Item(I)) Then
        Return True
    End If
Next
Return False

Вывод:

Случай 1:

myString: C:\Files\myfile.doc
listOfString: C:\Files\, C:\Files2\
Result: True

Случай 2:

myString: C:\Files3\myfile.doc
listOfString: C:\Files\, C:\Files2\
Result: False

Список (listOfStrings) может содержать несколько элементов (минимум 20), и его нужно проверять на тысячах строк (например, myString).

Есть ли лучший (более эффективный) способ написать этот код?

Теги:
performance
list
coding-style

10 ответов

234
Лучший ответ

С LINQ и с использованием С# (в наши дни я не знаю VB):

bool b = listOfStrings.Any(s=>myString.Contains(s));

или (более короткий и более эффективный, но, возможно, менее понятный):

bool b = listOfStrings.Any(myString.Contains);

Если вы тестируете равенство, стоит посмотреть HashSet и т.д., но это не поможет с частичными совпадениями, если вы не разделите его на фрагменты и не добавите порядок сложности.


update: если вы действительно имеете в виду "StartsWith", вы можете отсортировать список и поместить его в массив; затем используйте Array.BinarySearch, чтобы найти каждый элемент - проверьте по поиску, чтобы убедиться, что это полное или частичное совпадение.

  • 1
    Вместо Contains я бы использовал StartsWith на основе его примеров.
  • 0
    @tvanfosson - это зависит от того, являются ли примеры полностью инклюзивными, но да, я бы согласился. Легко изменить, конечно.
Показать ещё 10 комментариев
5

Был высказан ряд предложений по более раннему аналогичному вопросу "Лучший способ проверить существующую строку на большом списке сопоставимых данных.

Regex может быть достаточным для вашего требования. Выражение будет конкатенацией всех подстрок-кандидатов с оператором OR "|" между ними. Разумеется, вам придется следить за неэкранированными символами при построении выражения или не скомпилировать его из-за ограничений сложности или размера.

Другой способ сделать это - построить структуру trie data для представления всех подстрок-кандидатов (это может несколько дублировать то, что регулярное выражение совпадение делает). По мере прохождения каждого символа в тестовой строке вы создадите новый указатель на корень trie и продвигаете существующие указатели на соответствующий дочерний элемент (если есть). Вы получаете совпадение, когда любой указатель достигает листа.

3

когда вы создаете свои строки, это должно быть как

bool inact = new string[] { "SUSPENDARE", "DIZOLVARE" }.Any(s=>stare.Contains(s));
2

На основе ваших шаблонов одним из улучшений было бы изменение на использование StartsWith вместо Contains. StartsWith нужно только перебирать каждую строку до тех пор, пока не найдет первое несоответствие вместо необходимости перезапускать поиск в каждой позиции символа, когда он найдет его.

Кроме того, на основе ваших шаблонов, похоже, вы сможете извлечь первую часть пути для myString, а затем отменить сравнение - поиск начального пути myString в списке строк, а не в другом вокруг.

string[] pathComponents = myString.Split( Path.DirectorySeparatorChar );
string startPath = pathComponents[0] + Path.DirectorySeparatorChar;

return listOfStrings.Contains( startPath );

EDIT. Это будет еще быстрее, если использовать идею HashSet @Marc Gravell, поскольку вы можете изменить Contains на ContainsKey, и поиск будет O (1) вместо O (N). Вы должны убедиться, что пути точно совпадают. Обратите внимание, что это не общее решение, как @Marc Gravell, но оно соответствует вашим примерам.

Извините за пример С#. У меня не было достаточно кофе, чтобы перевести на VB.

  • 0
    Re начинается с; возможно предварительно отсортировать и использовать бинарный поиск? Это может быть быстрее снова.
1

Мне понравился ответ Marc, но мне нужно, чтобы Contains соответствовал CaSe InSenSiTiVe.

Это было решение:

bool b = listOfStrings.Any(s => myString.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0))
  • 0
    Разве это не должно быть> -1?
  • 0
    @CSharped Не имеет значения, поскольку> -1 (больше минус 1) и> = 0 (больше или равно нулю) - это одно и то же.
1

Вы тестировали скорость?

то есть. Вы создали образец данных и профилировали его? Это может быть не так плохо, как вы думаете.

Это может быть и то, что вы могли бы размножаться в отдельный поток и создавать иллюзию скорости!

1

Я не уверен, что это более эффективно, но вы могли бы подумать об использовании Lambda Expressions.

0
myList.Any(myString.Contains);
0

Если скорость критическая, вам может понадобиться искать алгоритм Aho-Corasick для наборов шаблонов.

Это trie с ошибками, то есть сложность O (n + m + k), где n - это длина входного текста, m кумулятивная длина шаблонов и k число совпадений. Вам просто нужно изменить алгоритм на завершение после того, как будет найдено первое совпадение.

0

Вместо списка строк вы могли бы использовать регулярные выражения? Даже если вам приходилось использовать несколько регулярных выражений, я бы подумал, что тестирование пары (сотни?) Было бы лучше, чем тысячи сравнений строк.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню