【发布时间】:2016-11-03 04:34:30
【问题描述】:
我有一个问题要解决,在给定字符串source 和搜索条件集合criteria 的情况下,算法必须返回包含criteria 的所有项的source 的最短子字符串。
==================================
更新
- 相同的搜索条件可能在多个源字符串中 次。在这种情况下,需要返回子字符串 包含搜索条件的特定实例,使得 它是所有可能的子字符串中最短的。
- 搜索项中可以包含空格,例如
hello world - 搜索条件的顺序无关紧要,只要它们都在结果子字符串中
===================================
String source = "aaa wwwww fgffsd ththththt sss sgsgsgsghs bfbfb hhh sdfg kkk dhdhtrherhrhrthrthrt ddfhdetehehe kkk wdwd aaa vcvc hhh zxzx sss nbnbn";
List<String> criteria = new List<string> { "kkk", "aaa", "sss", "hhh" };
上面的输入应该返回以下子字符串:kkk wdwd aaa vcvc hhh zxzx sss
不幸的是,我花了很多时间尝试编写这样的算法,但我无法做到恰到好处。以下是我目前得到的代码:
public struct Extraction
{
public int Start { get; set; }
public int End { get; set; }
public int Length
{
get
{
var length = this.End - this.Start;
return length;
}
}
public Extraction(int start, int end)
{
this.Start = start;
this.End = end;
}
}
public class TextExtractor
{
private String _source;
private Dictionary<String, List<Int32>> _criteriaIndexes;
private Dictionary<String, int> _entryIndex;
public TextExtractor(String source, List<String> searchCriteria)
{
this._source = source;
this._criteriaIndexes = this.ExtractIndexes(source, searchCriteria);
this._entryIndex = _criteriaIndexes.ToDictionary(x => x.Key, v => 0);
}
public String Extract()
{
List<Extraction> possibleExtractions = new List<Extraction>();
int index = 0;
int min = int.MaxValue;
int max = 0;
bool shouldStop = false;
while (index < _criteriaIndexes.Count && !shouldStop)
{
Boolean compareWithAll = index == _criteriaIndexes.Count - 1;
if (!compareWithAll)
{
var current = _criteriaIndexes.ElementAt(index);
this.CalculateMinMax(current, ref min, ref max);
index++;
}
else
{
var entry = _criteriaIndexes.Last();
while (_entryIndex[entry.Key] < entry.Value.Count)
{
int a = min;
int b = max;
this.CalculateMinMax(entry, ref a, ref b);
_entryIndex[entry.Key]++;
Extraction ext = new Extraction(a, b);
possibleExtractions.Add(ext);
}
int k = index - 1;
while (k >= 0)
{
var prev = _criteriaIndexes.ElementAt(k);
if (prev.Value.Count - 1 > _entryIndex[prev.Key])
{
_entryIndex[prev.Key]++;
break;
}
else
{
k--;
}
}
shouldStop = _criteriaIndexes.All(x => x.Value.Count - 1 <= _entryIndex[x.Key]);
_entryIndex[entry.Key] = 0;
index = 0;
min = int.MaxValue;
max = 0;
}
}
Extraction shortest = possibleExtractions.First(x => x.Length.Equals(possibleExtractions.Min(p => p.Length)));
String result = _source.Substring(shortest.Start, shortest.Length);
return result;
}
private Dictionary<String, List<Int32>> ExtractIndexes(String source, List<String> searchCriteria)
{
Dictionary<String, List<Int32>> result = new Dictionary<string, List<int>>();
foreach (var criteria in searchCriteria)
{
Int32 i = 0;
Int32 startingIndex = 0;
var indexes = new List<int>();
while (i > -1)
{
i = source.IndexOf(criteria, startingIndex);
if (i > -1)
{
startingIndex = i + 1;
indexes.Add(i);
}
}
if (indexes.Any())
{
result.Add(criteria, indexes);
}
}
return result;
}
private void CalculateMinMax(KeyValuePair<String, List<int>> current, ref int min, ref int max)
{
int j = current.Value[_entryIndex[current.Key]];
if (j < min)
{
min = j;
}
int indexPlusWordLength = j + current.Key.Length;
if (indexPlusWordLength > max)
{
max = indexPlusWordLength;
}
}
}
如果有人能指出我的算法哪里出错了,我将不胜感激。此外,我觉得这是一个非常幼稚的实现。也许有比尝试索引组合更好的方法来解决这个问题?
谢谢!
【问题讨论】:
-
你的算法很复杂。代码应该相当简单。 1)在空格周围分割字符串。 2) 从第一个单词开始,直到找到所有四个条件。保存结果 3) 从单词二开始,直到找到所有找到的条件。如果新长度比第一个长度短,则用新结果替换旧结果。 4) 在单词 3 重复,然后是单词 4,然后是单词 5。
-
是的,我知道我的算法很复杂。这就是我在这里问的原因之一:我想看到解决这个问题的不同方法。我将尝试您描述的方法,但请注意,相同的搜索条件可能会多次出现在源字符串中,您在建议的解决方案中是否考虑过这一点?
-
另外,搜索条件中是否有可能包含空格?即
{ "hello there", "foo", "bar" }; -
找到的项目在源字符串中的顺序是否必须与它们在条件列表中的顺序相同?即,如果您的标准有
{"one", "two", "three"},并且您的字符串是"two three one two five three",那么正确的响应是"two three one"还是"one two five three"? -
@RufusL 是的,实际上,搜索条件中可以有一个空格。并且搜索项的顺序无关紧要,只要它们都在子字符串中找到即可。
标签: c# string algorithm search