参见 http://www.cnblogs.com/xingd/archive/2008/01/23/1050443.html。
感谢sumtech的回复和讨论,原本的效率已经足够网站实用了,虽然也想到一些改进方法,但是一直懒得去做。sumtech通过邮件跟我讨论,我也终于抽了时间做了改进,改进后的算法效率比原先的算法提高了400%,也就是仅需要原来的1/5时间。
算法关键是将两个BitArray合并成了byte[char.MaxValue],其中7个bit用来判断前7个字符,另一个bit判断其他字符。并且增加了minWordLength和charCheck,用来过滤过短的判断,以及仅有一个字符时的快速判断。
使用的数据:
初始化数据的代码:
判断是否包含脏字的代码:
2008-02-01修订:发现Bug, 一个字符的charCheck应该放到for循环外,去掉j == 1的判断,外层的判断改为if (j + 1 > minWordLength)。
最后介绍下自己,我现任大众点评网(dianping.com)的系统架构师,自己的个人博客是www.stevenxu.com,点评网的整个开发团队即将推出团队博客,我的一些文章一般会先发到博客园和团队博客,跟据反馈修订后再发到个人博客。
PS: 现在匹配的是最小长度,并且是区分大小写的,这部分功能在脏字替换时需要实现。
感谢sumtech的回复和讨论,原本的效率已经足够网站实用了,虽然也想到一些改进方法,但是一直懒得去做。sumtech通过邮件跟我讨论,我也终于抽了时间做了改进,改进后的算法效率比原先的算法提高了400%,也就是仅需要原来的1/5时间。
算法关键是将两个BitArray合并成了byte[char.MaxValue],其中7个bit用来判断前7个字符,另一个bit判断其他字符。并且增加了minWordLength和charCheck,用来过滤过短的判断,以及仅有一个字符时的快速判断。
使用的数据:
private HashSet<string> hash = new HashSet<string>();
private byte[] fastCheck = new byte[char.MaxValue];
private BitArray charCheck = new BitArray(char.MaxValue);
private int maxWordLength = 0;
private int minWordLength = int.MaxValue;
private byte[] fastCheck = new byte[char.MaxValue];
private BitArray charCheck = new BitArray(char.MaxValue);
private int maxWordLength = 0;
private int minWordLength = int.MaxValue;
初始化数据的代码:
foreach (string word in badwords)
{
maxWordLength = Math.Max(maxWordLength, word.Length);
minWordLength = Math.Min(minWordLength, word.Length);
for (int i = 0; i < 7 && i < word.Length; i++)
{
fastCheck[word[i]] |= (byte)(1 << i);
}
for (int i = 7; i < word.Length; i++)
{
fastCheck[word[i]] |= 0x80;
}
if (word.Length == 1)
{
charCheck[word[0]] = true;
}
else
{
hash.Add(word);
}
}
{
maxWordLength = Math.Max(maxWordLength, word.Length);
minWordLength = Math.Min(minWordLength, word.Length);
for (int i = 0; i < 7 && i < word.Length; i++)
{
fastCheck[word[i]] |= (byte)(1 << i);
}
for (int i = 7; i < word.Length; i++)
{
fastCheck[word[i]] |= 0x80;
}
if (word.Length == 1)
{
charCheck[word[0]] = true;
}
else
{
hash.Add(word);
}
}
判断是否包含脏字的代码:
public bool HasBadWord(string text)
{
int index = 0;
while (index < text.Length)
{
if ((fastCheck[text[index]] & 1) == 0)
{
while (index < text.Length - 1 && (fastCheck[text[++index]] & 1) == 0) ;
}
if (minWordLength == 1 && charCheck[text[index]])
{
return true;
}
for (int j = 1; j <= Math.Min(maxWordLength, text.Length - index - 1); j++)
{
if ((fastCheck[text[index + j]] & (1 << Math.Min(j, 7))) == 0)
{
break;
}
if (j + 1>= minWordLength)
{
string sub = text.Substring(index, j + 1);
if (hash.Contains(sub))
{
return true;
}
}
}
index++;
}
return false;
}
{
int index = 0;
while (index < text.Length)
{
if ((fastCheck[text[index]] & 1) == 0)
{
while (index < text.Length - 1 && (fastCheck[text[++index]] & 1) == 0) ;
}
if (minWordLength == 1 && charCheck[text[index]])
{
return true;
}
for (int j = 1; j <= Math.Min(maxWordLength, text.Length - index - 1); j++)
{
if ((fastCheck[text[index + j]] & (1 << Math.Min(j, 7))) == 0)
{
break;
}
if (j + 1>= minWordLength)
{
string sub = text.Substring(index, j + 1);
if (hash.Contains(sub))
{
return true;
}
}
}
index++;
}
return false;
}
2008-02-01修订:发现Bug, 一个字符的charCheck应该放到for循环外,去掉j == 1的判断,外层的判断改为if (j + 1 > minWordLength)。
最后介绍下自己,我现任大众点评网(dianping.com)的系统架构师,自己的个人博客是www.stevenxu.com,点评网的整个开发团队即将推出团队博客,我的一些文章一般会先发到博客园和团队博客,跟据反馈修订后再发到个人博客。
PS: 现在匹配的是最小长度,并且是区分大小写的,这部分功能在脏字替换时需要实现。