Я вижу, что вы используете HashSet
, но вы не используете ни одного из его преимуществ, поэтому вам следует просто использовать List
.
Требуется время, чтобы просмотреть все документы и найти строки в других строках, поэтому вам следует постараться как можно больше устранить их.
Одна из возможностей - установить индексы того, какие документы содержат какие пары символов. Если строка query
содержит Hello
, вы будете искать документы, содержащие He
, el
, ll
и lo
.
Вы можете установить Dictionary<string, List<int>>
, где ключом словаря являются комбинации символов, а список содержит индексы к документам в вашем списке документов. Конечно, настройка словаря займет некоторое время, но вы можете сосредоточиться на комбинациях символов, которые встречаются реже. Если комбинация символов существует в 80% документов, это довольно бесполезно для устранения документов, но если комбинация символов существует только в 2% документов, это привело к уменьшению 98% вашей работы.
Если вы перебираете документы в списке и добавляете вложения в списки в словаре, списки индексов будут отсортированы, так что потом будет очень легко присоединиться к спискам. Когда вы добавляете индексы в список, вы можете выбрасывать списки, когда они становятся слишком большими, и вы видите, что они не будут полезны для устранения документов. Таким образом, вы будете хранить только короткие списки, и они не будут занимать так много памяти.
Edit:
Это небольшой пример:
public class IndexElliminator<T> {
private List<T> _items;
private Dictionary<string, List<int>> _index;
private Func<T, string> _getContent;
private static HashSet<string> GetPairs(string value) {
HashSet<string> pairs = new HashSet<string>();
for (int i = 1; i < value.Length; i++) {
pairs.Add(value.Substring(i - 1, 2));
}
return pairs;
}
public IndexElliminator(List<T> items, Func<T, string> getContent, int maxIndexSize) {
_items = items;
_getContent = getContent;
_index = new Dictionary<string, List<int>>();
for (int index = 0;index<_items.Count;index++) {
T item = _items[index];
foreach (string pair in GetPairs(_getContent(item))) {
List<int> list;
if (_index.TryGetValue(pair, out list)) {
if (list != null) {
if (list.Count == maxIndexSize) {
_index[pair] = null;
} else {
list.Add(index);
}
}
} else {
list = new List<int>();
list.Add(index);
_index.Add(pair, list);
}
}
}
}
private static List<int> JoinLists(List<int> list1, List<int> list2) {
List<int> result = new List<int>();
int i1 = 0, i2 = 0;
while (i1 < list1.Count && i2 < list2.Count) {
switch (Math.Sign(list1[i1].CompareTo(list2[i2]))) {
case 0: result.Add(list1[i1]); i1++; i2++; break;
case -1: i1++; break;
case 1: i2++; break;
}
}
return result;
}
public List<T> Find(string query) {
HashSet<string> pairs = GetPairs(query);
List<List<int>> indexes = new List<List<int>>();
bool found = false;
foreach (string pair in pairs) {
List<int> list;
if (_index.TryGetValue(pair, out list)) {
found = true;
if (list != null) {
indexes.Add(list);
}
}
}
List<T> result = new List<T>();
if (found && indexes.Count == 0) {
indexes.Add(Enumerable.Range(0, _items.Count).ToList());
}
if (indexes.Count > 0) {
while (indexes.Count > 1) {
indexes[indexes.Count - 2] = JoinLists(indexes[indexes.Count - 2], indexes[indexes.Count - 1]);
indexes.RemoveAt(indexes.Count - 1);
}
foreach (int index in indexes[0]) {
if (_getContent(_items[index]).Contains(query)) {
result.Add(_items[index]);
}
}
}
return result;
}
}
Тест:
List<string> items = new List<string> {
"Hello world",
"How are you",
"What is this",
"Can this be true",
"Some phrases",
"Words upon words",
"What to do",
"Where to go",
"When is this",
"How can this be",
"Well above margin",
"Close to the center"
};
IndexElliminator<string> index = new IndexElliminator<string>(items, s => s, items.Count / 2);
List<string> found = index.Find("this");
foreach (string s in found) Console.WriteLine(s);
Выход:
What is this
Can this be true
When is this
How can this be