高效的关键字过滤及查找算法

标签: 关键字 过滤 算法 | 发表时间:2011-08-24 22:27 | 作者:边城浪 kongshanzhanglao
出处:http://www.cnblogs.com/

最近做游戏服务器需要使用到关键字查找和过滤的功能

首先想到的是使用HashSet<string> 的方式.

实现如下:

 

View Code
 public class FilterWord
    {
        
int m_maxLen=8; //关键字最大长度
        HashSet<string> m_keys = new HashSet<string>();
        
public FilterWord(string path)
        {
            init(path);
        }
        FilterWord()
        {
            
string path = System.AppDomain.CurrentDomain.BaseDirectory;
            path 
= Path.Combine(path, "Config""FilterWord.txt");
            init(path);
        }

        
void init(string path)
        {
            
using (StreamReader sw = new StreamReader(File.OpenRead(path)))
            {
                
string key = sw.ReadLine();
                
while (key != null)
                {
                    
if (key != string.Empty && m_keys.Add(key) && key.Length > m_maxLen)
                    {
                        m_maxLen 
= key.Length;
                    }
                    key 
= sw.ReadLine();
                }
            }
        }

        
/// <summary>
        
/// 插入新的Key.
        
/// </summary>
        
/// <param name="name"></param>
        public void InsertKey(string key)
        {
            
if ((!string.IsNullOrEmpty(key)) && m_keys.Add(key) && key.Length > m_maxLen)
            {
                m_maxLen 
= key.Length;
            }
        }


        
/// <summary>
        
/// 检查是否包含非法字符
        
/// </summary>
        
/// <param name="name"></param>
        
/// <returns>找到的第1个非法字符.没有则返回string.Empty</returns>
        public string GetBadWord(string name)
        {
            
for (int len = 1; len <= name.Length; len++)
            {
                
int maxIndex = name.Length - len;
                
for (int index = 0; index <= maxIndex; index++)
                {
                    
string key = name.Substring(index, len);
                    
if (m_keys.Contains(key))
                    {
                        
return key;
                    }
                }
            }
            
return string.Empty;
        }

        
/// <summary>
        
/// 替换非法字符
        
/// </summary>
        
/// <param name="name"></param>
        
/// <returns></returns>
        public string ReplacetBadWord(string name)
        {
            
int maxLen = Math.Min(m_maxLen, name.Length);
            
for (int len = 1; len <= maxLen; len++)
            {
                
int maxIndex = name.Length - len;
                
for (int index = 0; index <= maxIndex; index++)
                {
                    
string key = name.Substring(index, len);
                    
if (m_keys.Contains(key))
                    {
                        
int l = key.Length;
                        name 
= name.Replace(key, new string('*', l));
                        index 
+= (l - 1);
                    }
                }
            }
            
return name;
        }
    }

这个方式有个缺点.就是使用 String.Substring会创建大量临时对象.即便是使用了最大长度进行分割字符串的控件.在需要过滤的字符串较长时,还是不见得高效.

有没有一种方法如避免创建大量字符串呢?有,就是下面要给大家推荐的Trie树..

 

Trie,又称单词查找树键树,是一种形结构,是一种哈希树的变种。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。

更多介绍见维基百科: http://zh.wikipedia.org/wiki/Trie

维基百科上有一个C++的实现..

在这里我给出一个C#的实现.

 

 public class TrieFilter
    {
        
private Char m_key;
        
private Dictionary<Char, TrieFilter> m_values;

        
public TrieFilter()
        {
            m_values 
= new Dictionary<Char, TrieFilter>();
        }

        TrieFilter(Char key)
        {
            m_key 
= key;
            m_values 
= new Dictionary<Char, TrieFilter>();
        }

        
/// <summary>
        
/// 添加关键字
        
/// </summary>
        
/// <param name="key"></param>
        public void AddKey(String key)
        {
            
if (String.IsNullOrWhiteSpace(key))
            {
                
throw new ArgumentException("key 不能为空");
            }
            TrieFilter node 
= this;
            
foreach (var c in key)
            {
                TrieFilter subnode;
                
if (!node.m_values.TryGetValue(c, out subnode))
                {
                    subnode 
= new TrieFilter(c);
                    node.m_values.Add(c, subnode);
                }
                node 
= subnode;
            }
            node.m_values[
'\0'= null;
        }

        
public String FindOne(String s)
        {
            
for (int i = 0; i < s.Length; i++)
            {
                TrieFilter subnode;
                
if (m_values.TryGetValue(s[i], out subnode))
                {
                    
for (int j = i + 1; j < s.Length; j++)
                    {
                        
if (subnode.m_values.TryGetValue(s[j], out subnode))
                        {
                            
if (subnode.m_values.ContainsKey('\0'))
                            {
                                
return s.Substring(i, (j + 1 - i));
                            }
                        }
                        
else
                        {
                            
break;
                        }
                    }
                }
            }
            
return string.Empty;
        }

        
public IEnumerable<string> FindAll(String s)
        {
            
for (int i = 0; i < s.Length; i++)
            {
                TrieFilter subnode;
                
if (m_values.TryGetValue(s[i], out subnode))
                {
                    
for (int j = i + 1; j < s.Length; j++)
                    {
                        
if (subnode.m_values.TryGetValue(s[j], out subnode))
                        {
                            
if (subnode.m_values.ContainsKey('\0'))
                            {
                                
yield return s.Substring(i, (j + 1 - i));
                            }
                        }
                        
else
                        {
                            
break;
                        }
                    }
                }
            }
        }

        
public string Replace(string name, char c)
        {
            
char[] chars = null//name.ToArray();
            for (int i = 0; i < name.Length; i++)
            {
                TrieFilter subnode;
                
if (m_values.TryGetValue(name[i], out subnode))
                {
                    
for (int j = i + 1; j < name.Length; j++)
                    {
                        
if (subnode.m_values.TryGetValue(name[j], out subnode))
                        {
                            
if (subnode.m_values.ContainsKey('\0'))
                            {
                                
if (chars == null) chars = name.ToArray();
                                
for (int t = i; t <= j; t++)
                                {
                                    chars[t] 
= c;
                                }
                                i 
= j;
                            }
                        }
                        
else
                        {
                            
break;
                        }
                    }
                }
            }
            
return chars == null ? name : new string(chars);
        }
    }

 

 在游戏或聊天系统中,消息过滤的开销经常占到了系统总开销的一半.

使用这种方式基本上杜绝了新对象的创建,有效的减少了垃圾回收的负担,节约了资源.

测试代码:

    //添加关键字

TrieFilter filter = new TrieFilter();

filter.AddKey("SB");

filter.AddKey("fuck");

filter.AddKey("日本");

 //过滤

string x = filter.FindOne("名字加fuck日本人妖成"); 

var xs= filter.FindAll("名字加fuck日本人妖成"); 

{

 Console.WriteLine(v);

}

//替换

string y= filter.Replace("名字加fuck日本人妖成",'*');foreach(var v in xs)

作者: 边城浪 发表于 2011-08-24 22:27 原文链接

评论: 0 查看评论 发表评论


最新新闻:
· 15个基于jQuery开发的网站,非常有创意!(2011-08-24 22:25)
· 移动社交游戏平台木瓜移动用户数突破2500万(2011-08-24 22:24)
· 分析称Groupon中国业务是一场灾难(2011-08-24 22:21)
· Facebook手机HTC Status销量不佳 AT&T或停售(2011-08-24 21:49)
· 三星诺基亚将效仿苹果推廉价智能手机(2011-08-24 21:46)

编辑推荐:Hack, Everything!

网站导航:博客园首页  我的园子  新闻  闪存  小组  博问  知识库

相关 [关键字 过滤 算法] 推荐:

高效的关键字过滤及查找算法

- kongshanzhanglao - 博客园-首页原创精华区
最近做游戏服务器需要使用到关键字查找和过滤的功能. 首先想到的是使用HashSet 的方式..         int m_maxLen=8; //关键字最大长度.         /// 插入新的Key.         /// 检查是否包含非法字符.         /// 找到的第1个非法字符.没有则返回string.Empty.

协同过滤算法

- - CSDN博客推荐文章
今天要讲的主要内容是 协同过滤,即Collaborative Filtering,简称 CF.    关于协同过滤的一个最经典的例子就是看电影,有时候不知道哪一部电影是我们喜欢的或者评分比较高的,那.    么通常的做法就是问问周围的朋友,看看最近有什么好的电影推荐. 在问的时候,都习惯于问跟自己口味差不.

为 Google+ 按照关键字过滤信息流的 Chrome 扩展 Stream Filter

- 安得米 - 谷奥——探寻谷歌的奥秘
随着Google+的流行,你可能发现自己的timeline越来越混乱了,但又不忍心丢掉那些圈养的好友,怎么办呢. 你可以选择Stream Filter这枚Chrome扩展,它可以帮助你设置设置关键字. 比如有人老是爱发点阿猫阿狗的萌图,非常无聊,但为了不错过此人的重要言论,你可以针对这个特点加入“猫”和“狗”的关键字,这样整个世界就清净了.

[Java Web]敏感词过滤算法

- - CSDN博客推荐文章
DFA算法的原理可以参考 这里,简单来说就是通过Map构造出一颗敏感词树,树的每一条由根节点到叶子节点的路径构成一个敏感词,例如下图:. LOG.error("sensitiveWordMap 未初始化!");. LOG.error("敏感词库文件转码失败!");. LOG.error("敏感词库文件不存在!");.

协调过滤算法之ALS

- - 标点符
ALS是交替最小二乘的简称. 在机器学习中,ALS特指使用交替最小二乘求解的一个协同推荐算法. 如:将用户(user)对商品(item)的评分矩阵分解成2个矩阵:. user对item 潜在因素的偏好矩阵(latent factor vector). item潜在因素的偏好矩阵. 假设有m个user和n个item,所以评分矩阵为R.

推荐算法之基于用户的协同过滤算法

- - CSDN博客综合推荐文章
协同过滤是推荐算法中最基本的算法,主要分为基于用户的协同过滤算法和基于物品的协同过滤算法. 这篇文章主要介绍基于用户的协同过滤算法,简单来说,要给用户u作推荐,那么只要找出那些和u之前的行为类似的用户,即和u比较像的用户,把他们的行为推荐给用户u即可. 所以基于用户的系统过滤算法包括两个步骤:1)找到和目标用户兴趣相似的用户集合  2)找到这个集合中的用户喜欢的,且目标用户没有听说过的物品推荐给目标用户.

基于综合兴趣度的协同过滤推荐算法

- - IT技术博客大学习
标签:   兴趣   协同过滤   推荐. 电子商务推荐系统最大的优点在于它能收集用户的兴趣资料和个人信息,根据用户兴趣偏好主动为用户做出个性化推荐. 推荐技术指的是如何找出用户感兴趣的商品并列出推荐清单,在用户信息获取差别不大的情况下,推荐技术成为决定一个推荐系统性能的关键,其中推荐算法是推荐技术的核心[1].

推荐算法之协同过滤实战

- - 互联网 - ITeye博客
协同过滤(Collective Filtering)可以说是推荐系统的标配算法. 在谈推荐必谈协同的今天,我们也来谈一谈基于KNN的协同过滤在实际的推荐应用中的一些心得体会. 我们首先从协同过滤的两个假设聊起. 用户一般会喜欢与自己喜欢物品相似的物品. 用户一般会喜欢与自己相似的其他用户喜欢的物品.

基于Item的时序协同过滤算法

- - 冰火岛
基于Item的时序协同过滤算法技术方案包括两个步骤:. (1)提取用户商品点击日志、搜索点击日志和商品基本信息等基本数据. 然后,去除噪音数据(譬如每天点击商品数达到数以万计的用户)和缺失值数据,构建时序点击流数据,即记录用户每天按照点击时间先后顺序排序的商品行为数据. 从而得到如下数据结构:<用户id,商品id,点击时间,点击日期>;.