说的trie算法原文见
http://www.cnblogs.com/yeerh/archive/2011/08/24/2152607.html
回复中有说到一个很快的方法 。原文见的博客
仅测试,这种方法确定很快,但已不是单纯的hash算法了。
文章中基本上只有代码,只有很少的说明。
经过本人的认真阅读,终于搞懂了优化的关键所在。
现在对这种做了一个小的改动,不敢独独享,特分享给大家。
这种优化基于我们本常所用的大部分词或是字并不是脏字的前提。
有些字符即使在脏字单词中出现,他们出现地位置也不一样。
根据这个特点。我们先建立一个用于快速过滤的数组。
Uint16[] m_fastCheck = new Uint16[char.MaxValue];
fastCheck的索引号用来表示对应的字符。
里面的值 表示这个字符在单词中出现的位置,我们用Uint16来表示,也就可以保存1-16。。
比如:马这个字符, 脏字有 "你马", '马的' , '草泥马'
则 fastCheck[39532] = 0x07; //'马'转化为数字为39532;
因为字符可以隐式转换为整数。也可以写作 fastCheck['马'] = 0x07;
至于这个0x07是怎么来的。因为马出现的位置有 1,2,3
对这些位置做位运算就可以得出7.
这个我们在添加关键中的时候就可以初始化。
public void AddKey(string word)
{.................
for(int i=0;i<word.Length;i++)
{
m_fastCheck[word[i]] |= (UInt16)(1<<i);
}
}
有了这个,我们可以快速排除一些不是脏字字符。还可以更快吗?
可以,我们再加一个首尾字符的判断。
Uint16[] m_startCheck = new Uint16[char.MaxValue];
Uint16[] m_endCheck = new Uint16[char.MaxValue];
而这种我们保存的不是字符出现的位置。而是保存词的长度。
public void AddKey(string word)
{.................
for(int i=0;i<word.Length;i++)
{
m_fastCheck[word[i]] |= (UInt16)(1<<i);
}
Uint16 mask = (UInt16)(1<<word.Length-1);
m_startCheck[word[0]] |= mask;
m_endCheck[word[word.Length-1]] |= mask;
}
就这样,在进行查找的时候。我们先判断对应位置的字符出现地位置是否正确,
再判断首尾字符的长度是否要求。如果这些条件都满足,再进行hash查找。
方法如下:
public string FindOne(string text)
{
for (int index = 0; index < text.Length; index++)
{
int count = 0;
int maxIndex = Math.Min(maxWordLength + index, text.Length);
char begin = text[index];
for (int j = index; j < maxIndex; j++)
{
char current = text[j];
UInt16 mask = (UInt16)(1 << count);
//先判断字符出现的位置是否正确
if ((m_fastCheck[current] & mask) == 0)
{
index += count;
break;
}
++count;
//再判断首字符和尾字符的长度是否正确.
if ((m_startLength[begin] & mask) > 0 && (m_endLength[current] & mask) > 0)
{
//进行hash比较
.........................
}
}
}
return string.Empty;
}
至此,整个查找方法就完成了。脏字替换跟查找的方法差不多。
为了减少字符串的截取,我重写了一个HashSet, 支持bool Contains(string text,int offset,int coung)的方式进行调用.
完整的代码下载: