最近在一个项目中需要针对上百万条(大约在800W条)的字符串进行相关的处理。该字符串是以文本的形式存放在本地硬盘,并且更新频率为20分钟一次。
具体需求:
1、判断某一个字符串是否存在这800W条字符串中,时间要求在5ms以内
2、根据字符串前缀,返回包含此前缀的10-100条字符串。时间要求在5ms以内
3、占用内存要小
本人接到此需求第一反应用List,或者二叉树啥的。结果效率慢得死,
后经过大量资料查阅,发现采用DAWG(Directed Acyclic Word Graph)和Trie可以很快速的查找到相应的字符串。
主要思路:将每个字符串的字符进行创建相应的Node,如果有相同的字符则放置在同一个节点中。
例如有6个字符串:Top, Tops, Tap, Tapc, Topa and Taps。根据DAWG和Trie的原理将创建成如下一个Tree
在添加字符串的时候,根据字符串的字符创建树形结构,每个节点代表一个字符。如果是相同字符时,则存储在同一个节点中。这样可少占用内存。
在进行判断字符串是否存在时,只需要依次判断字符串的节点是否存在。
首先创建一个字符节点类LetterNode. 该类主要用于存储相关字符节点的信息:子节点,父节点等等。
/// The Dawg's letter node
/// </summary>
internal sealed class LetterNode
{
private const int InitialSpreadCapacity = 4;
private Dictionary<char, LetterNode> _childNodes;
/// <summary>
/// Initializes a new instance of the <see cref="LetterNode"/> class.
/// </summary>
public LetterNode()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="LetterNode"/> class.
/// </summary>
/// <param name="letter">The letter.</param>
public LetterNode(char letter)
: this()
{
Letter = letter;
}
/// <summary>
/// The node's child nodes
/// </summary>
public Dictionary<char, LetterNode> ChildNodes
{
get
{
if (this._childNodes == null)
{
this._childNodes = new Dictionary<char, LetterNode>(InitialSpreadCapacity);
}
return this._childNodes;
}
}
/// <summary>
/// Describe the node is end of the word.
/// </summary>
public bool IsEndOfWord
{
get;
set;
}
/// <summary>
/// Gets or sets the letter.
/// </summary>
/// <value>The letter.</value>
public char Letter
{
get;
set;
}
/// <summary>
/// Gets or sets the parent of this node.
/// </summary>
/// <value>The parent.</value>
public LetterNode Parent
{
get;
set;
}
/// <summary>
/// Gets the word defined at this element.
/// </summary>
/// <value>The word ending here (if this is a word, or the empty string.</value>
public string Word
{
get
{
if (IsEndOfWord)
{
StringBuilder sb = new StringBuilder(20);
sb.Append(Letter);
var node = Parent;
while (node != null)
{
sb.Insert(0, node.Letter);
node = node.Parent;
}
return sb.ToString();
}
else
{
return string.Empty;
}
}
}
}
接下来实现Dawg针对字符串创建树形节点。思路是参照上图。
/// Adds the specified item.
/// </summary>
/// <param name="item">The item.</param>
public void Add(string item)
{
if (string.IsNullOrEmpty(item))
{
return;
}
item = item.ToLowerInvariant();
LetterNode node = null, parentNode = null;
if (!this._rootNodes.TryGetValue(item[0], out node))
{
node = new LetterNode(item[0]);
this._rootNodes[item[0]] = node;
}
for (int i = 1; i < item.Length; ++i)
{
parentNode = node;
if (!node.ChildNodes.TryGetValue(item[i], out node))
{
node = new LetterNode(item[i]);
node.Parent = parentNode;
parentNode.ChildNodes[item[i]] = node;
}
}
if (!node.IsEndOfWord)
{
node.IsEndOfWord = true;
this._count++;
}
}
判断字符串是否存在:
{
prefix = prefix.ToLowerInvariant();
Dictionary<char, LetterNode> nodes = this._rootNodes;
LetterNode node = null;
for (int i = 0; i < prefix.Length; ++i)
{
if (nodes.TryGetValue(prefix[i], out node))
{
nodes = node.ChildNodes;
}
else
{
return null;
}
}
return node;
}
{
var node = this.SearchPrefixLetterNode(item);
return node != null && node.IsEndOfWord;
}
作者:Terry
出处:http://foolishfox.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。