• 字典树的基础,以及在实际项目中对于敏感词的替换的应用


    最近刷题时连续遇到两道字典树的题目,所以做一下这个数据结构的总结。

    首先什么叫做字典树?

          叫     

          是  

    我   想  

          看  

          听 

    这种树结构并且把文字或者英文放在里面组成的叫做字典树。

    那么字典树有什么用呢?

    通过几道题目的练习我发现,字典树主要应用在,对于字符串的分级匹配和查询。

    比如在我们如果有三句话,1:我是人,2:我是男人,3:我是中国人

    如果一般的我们用三个字符串去存放他们,然后当我们要寻找在这些字符串中是否存在我是中国人的时候,那么就需要一句句匹配过来,如果有1000条这样的数据,那么匹配的速度可想而知。

    当我们用字典树去存放的时候,我们可以存放成这样

               人

    我  是   男   人

               中   国   人

    这样我们从树的根部一直寻找下去,一个个字去寻找,如果有,那么就找下去,如果没有就不存在,这样寻找速度也是可想而知的。

    然后下面是字典树在C中的定义,以及简单的应用

    typedef struct Node
    {
        int number;
        char word[120];
        Node *next[26];
        void init()
        {
            number = 0;
            memset(next,NULL,sizeof(next));
        }
    }NODE,*PNODE;

    当然一般的题目都是英文的,所以存放的是英文的字符串,所以其中的next用了26长度的数组

    PNODE setNode()
    {
        PNODE pNew = (PNODE) malloc(sizeof(NODE));
        pNew->init();
        return pNew;
    }

    创建一个新的节点

    void insertNode(char st[120], int number)
    {
        char temp[120];
        PNODE pNew = pHead;
        int length = strlen(st);
        int i,j;
        for (i = 0; i < length; i++)
        {
            j = st[i] - 'a';
            if(pNew->next[j] == NULL)
            {
                pNew->next[j] = setNode();
            }
            pNew = pNew->next[j];
            pNew->number += number;
            temp[i] = st[i];/*把每一段到这个节点为止的字符串赋值,并在最后赋值、0否则打印时会出错*/
            temp[i+1] = '';
            strcpy(pNew->word , temp);
        }
    }

    插入新的节点(简单的描述一下,就是先把根节点找到,然后去要加入字典树的字符串的第一个字符,减去‘a’使它变成数字,然后寻找根节点下面是否存在对应数字的节点,没有就新建一个节点,有就在新的节点上面赋值,以此类推)

    最后顺便说一句的就是,我虽然掌握了字典树的构造,却没有办法解题的原因是,字典树只是数据结构,而真正题目往往需要dfs或者dp加以运用和搜索才能得到结果,所以之后还是要对搜索上面的算法加以掌握。

    还有就是,在实际项目中可以运用字典树和DFA算法实现对于敏感词的替换,因为脏活数据库本来数据量就及其庞大,如果没有好的数据结构和算法的话,对于脏活的替换会非常的耗时耗力。下面是替换的实际中可以使用的工具类(一个是数据结构,一个是算法部分,两者都要导入)。

    package dfaWord;
    
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
    
    /**
     * 脏话匹配替换工具类(数据结构部分)
     * @author XEX
     * 
     */
    public class DFAwordInit {
        
        @SuppressWarnings("rawtypes")
        private HashMap wordsMap;
        
        /**
         * 初始化数据结构
         * @param dirtyWordsSet 脏话的数据集合(要把收到的json数组转换成集合的形式)
         */
        @SuppressWarnings({ "rawtypes", "unchecked" })
        private void DFAwordMapInit(Set<String> dirtyWordsSet)
        {
            wordsMap = new HashMap(dirtyWordsSet.size());//根据脏话的数据集合大小来动态创建,减小空间复杂度
            
            Iterator<String> dirtyWordsIterator = dirtyWordsSet.iterator();//生成一个迭代器,用于循环取出集合中的数据
            
            String wordKeyTemp = null;
            Map wordsMapTemp_old = null;//当前的树的节点,或者是头节点
            Map<String, String> wordsMapTemp_new = null;//如果有新的词,生成的新的节点
            
            while (dirtyWordsIterator.hasNext()) {
                wordKeyTemp = dirtyWordsIterator.next();//获取字符串
                wordsMapTemp_old = wordsMap;//头结点
                
                for(int i=0;i<wordKeyTemp.length();i++)//循环每一个字
                {
                    char charTemp =  wordKeyTemp.charAt(i);//取出一个字
                    
                    Object wordsMapTemp = wordsMapTemp_old.get(charTemp);//查询当前各个子节点是否存在这个字
                    if(wordsMapTemp != null)//存在节点
                    {
                        wordsMapTemp_old = (Map) wordsMapTemp;//存在节点的话就把当前节点指针指向存在的那个节点(表示这个字在这个数据结构中了)
                    }
                    else
                    {
                        wordsMapTemp_new = new HashMap<String, String>();//如果不存在的话就新建一个节点(表示这个字在这个数据结构中还不存在)
                        wordsMapTemp_new.put("EoF", "0");//利用这个标识是否是最终节点
                        wordsMapTemp_old.put(charTemp, wordsMapTemp_new);//存放新节点
                        wordsMapTemp_old = wordsMapTemp_new;//把当前节点指针指向存在的那个新节点
                    }
                    if(i == wordKeyTemp.length() - 1){//如果字符串没了,那么把当前节点的标识的值变成1,表明这个已经是最终节点了
                        wordsMapTemp_old.put("EoF", "1");
                    }
                }
            }
        }
        
        /**
         * 初始化方法
         * @param dirtyWordsSet 脏话数据集合(要把收到的json数组转换成集合的形式)
         * @return 脏话数据结构
         */
        @SuppressWarnings("rawtypes")
        public Map init(Set<String> dirtyWordsSet)
        {
            DFAwordMapInit(dirtyWordsSet);
            return wordsMap;
        }
    }
    package dfaWord;
    
    import java.util.HashSet;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
    
    import org.apache.commons.lang3.RandomStringUtils;
    
    /**
     * 脏话匹配替换工具类(算法部分)
     * @author XEX
     * 
     */
    public class DFAwordFilter {
    
        /**
         * 测试用例(运行此方法即可看见效果,在项目别的地方之间按照本方法操作即可)
         */
        public static void main(String[] args) throws Exception
        {
            long beginTime = System.currentTimeMillis();
            
            Set<String> set = new HashSet<String>();//把数据放入集合中,下面是我随便放置的数据
            
            set.add("中国人民");//这个是敏感词
            for(int i=0;i<1000;i++)//这个是随机的数据,模拟脏活数据库的庞大
            {
                set.add("我"+RandomStringUtils.randomAlphanumeric(10)+"呀呀呀呀呀呀呀呀呀呀");
            }
            
            DFAwordFilter a = new DFAwordFilter(set);//构造数据结构
            
            System.out.println(a.wordsFilter("啊啊啊啊啊啊中国人民啊啊啊啊啊啊    啊啊啊", 1));//通过主方法替换字符串
            
            long endTime = System.currentTimeMillis();
            
            System.out.println(endTime - beginTime);
        }
        /**
         * 用来存放脏话数据结构
         */
        @SuppressWarnings("rawtypes")
        private Map wordsMap;
        
        /**
         * 构造方法
         * @param dirtyWordsSet 脏话数据集合(要把收到的json数组转换成集合的形式)
         */
        public DFAwordFilter(Set<String> dirtyWordsSet){
            wordsMap = new DFAwordInit().init(dirtyWordsSet);//初始化数据结构
        }
        
        /**
         * 替换字符串主方法
         * @param words 要进行替换的字符串
         * @param index 从这个位置开始替换
         * @return 替换后的字符串
         */
        public String wordsFilter(String words, int index)
        {
            Set<String> set = getDirtyWord(words);//获取这个字符串的所有脏字
            String resultWords = words;//用来保存替换后的字符串
            Iterator<String> iterator = set.iterator();//创建一个迭代器
            String wordTemp = null;
            while(iterator.hasNext())//循环这个字符串的所有脏字的集合
            {
                wordTemp = iterator.next();
                resultWords = resultWords.replaceAll(wordTemp, "*");//替换原字符串中的脏字为“*”
            }
            return resultWords;//返回替换后的字符串
        }
        
        /**
         * 获取用户输入字符串中的所有带的脏字
         * @param words 输入字符串
         * @return 所有带的脏字集合
         */
        public Set<String> getDirtyWord(String words)
        {
            Set<String> resultSet = new HashSet<String>();
            
            for(int i = 0; i < words.length(); i++)//循环这个字符串的每一个字
            {
                int resultLength = lengthOfWordCheck(words,i);//把这个字所在的位置传入,如果这个字和后面的字一起为脏话的话,返回这个脏话的长度
                
                if(resultLength > 0)//如果脏话长度大于0,则存在脏话
                {
                    resultSet.add(words.substring(i, i + resultLength));//在结果集中加入这个脏话,通过原始字符串截断得到
                    i += resultLength;//因为后面“resultLength”的长度已经是脏话了,跳过这脏话,继续检查后面的
                    i--;
                }
            }
            return resultSet;//返回所有带的脏字集合
        }
        
        /**
         * 判断一个字符串是不是脏话
         * @param words 字符串
         * @param index 开始位置
         * @return 返回脏话长度
         */
        @SuppressWarnings("rawtypes")
        public int lengthOfWordCheck(String words,int index)
        {
            boolean flag = false;//立一个flag,如果是脏话那么true,不是为false
            int resultFlag = 0;//返回的脏话长度
            char wordTemp;
            Map wordsMap_old = wordsMap;
            
            for(int i=index; i<words.length(); i++)//循环这个字符串中的所有的字
            {
                wordTemp = words.charAt(i);//取出一个字
                wordsMap_old = (Map)wordsMap_old.get(wordTemp);//从当前子节点中查询是否存在脏字map中
                
                if(wordsMap_old != null)//如果存在
                {
                    resultFlag++;//脏话长度加1
                    if(wordsMap_old.get("EoF").equals("1"))//如果当前节点已经为最后一个节点的话
                    {
                        flag = true;//存在脏话
                        break;//跳出循环。这个之后可以做修改,如果优先匹配长的字符串就把这个break删除,继续比较
                    }
                }
                else
                {
                    break;//不存在脏字
                }
            }
            
            if(!flag)
            {
                return 0;//不存在脏字返回脏字长度为0
            }
            else
            {
                return resultFlag;//存在脏字返回脏字长度
            }
        }
    }
  • 相关阅读:
    Python——方法
    Python——类和对象(二)
    Python——类和对象(一)
    Python——函数的高级应用
    Python——函数入门(三)
    Python——函数入门(二)
    Python——函数入门(一)
    Python——序列封包与序列解包
    min(T)方法
    max(T)方法
  • 原文地址:https://www.cnblogs.com/linkstar/p/5252270.html
Copyright © 2020-2023  润新知