腾讯编程题

记一次腾讯编程题,写了3个半小时,感觉还挺好。代表我现在的水平了。希望以后每次看都会有新的想法。

笔试题目说明

名词说明

单词
由大小写英文字母组成,不含其它字符。

摘要
由多个单词和语句结束符组成。
一条语句内的单词间用一个空格分隔;
摘要中若语句结束,以英文逗号或句号结尾。

搜索次数
标识该摘要被搜索次数。
搜索次数大于等于0。

搜索关键词
由一个关键单词组成,不包含2个及其以上的单词.

全词匹配
搜索的关键词与摘要中的单词完全一致,不存在被包含关系。

包含匹配
关键词与摘要中的单词为包含关系,如关键单词operation,被cooperation包含。

搜索规则
1、搜索出所有包含关键单词的摘要。
2、每次搜索时,若该摘要被搜索到关键词,则其搜索次数自动加1。
3、输出的摘要间以#符号间隔。
4、收索次数最高的摘要排在最前面。
5、若几份摘要的搜索次数相同,则输出规则按以下方式进行排序:
     A、比较全词匹配的关键字次数,按次数由大到小输出摘要。
     B、若全词匹配的关键字次数相同,则比较 包含匹配次数,次数多的优先输出。
     C、若全词匹配与包含匹配次数相同,则对摘要按由小到大进行排序输出。
6、进行搜索时,不区分大小写。

待实现接口
1、boolean addAbstract(String strAbstract,integer iCount)
    strAbstract: 摘要输入入参,同一份摘要不会重复输入。
     iCount:摘要的搜索次数。
    若入参为摘要空,直接返回false。


2、String searchAbstract(String strKeyWord)
     strKeyWord:搜索的关键单词。
    返回值为搜索符合关键字的摘要,每份摘要间以”#”号间隔。

举例
输入内容:
addAbstract(“These are good books .You can choose one book from them.”,9);
addAbstarct(“You can search books from Google.”,8);
addAbstarct(“Search and preview millions of books from libraries and publishers worldwide using Google Book Search.”,8);
addAbstarct(“Go to Google Books Home. Advanced Book Search, About Google ... All books.”,6)
addAbstarct(“Bookshelf provides free access to books and documents in life science and healthcare.”,7)

第一次搜索 关键词为BOOK

输出:
These are good books .You can choose one book from them.# Search and preview millions of books from libraries and publishers worldwide using Google Book Search.# You can search books from Google.# Bookshelf provides free access to books and documents in life science and healthcare.# Go to Google Books Home. Advanced Book Search, About Google ... All books.
 
第二次搜索 关键词为Google 

输出:
You can search books from Google .# Search and preview millions of books from libraries and publishers worldwide using Google Book Search .#Go to Google Books Home. Advanced Book Search, About Google ... All books.


规格
0<=摘要个数范围<=200
1<=摘要所含单词个数<=50
1<=单词所含字母数<=50
超出如上约束的输入认为是错误的,对应接口返回失败


其他要求:

已经提供初步的代码框架及测试用例,请在此框架上继续完成代码,并保证基本用例通过。

可以根据实际需要自由增加类等数据结构定义

要求考虑并发场景保证数据安全。

不得修改原有的接口定义。

不限制其他类库的使用。

注意遵从编程规范,圈复杂度不超过10。

尽善尽美地发挥,将工程师的思想发挥出来

实现接口:

package com.tencent.ied.bk;

public class CSearchAbstract {

    /**
     * @param strAbstract
     *            标识输入的摘要。
     * @param iCount
     *            标识该摘要搜索的次数。
     * @return 成功返回true,异常返回FALSE。
     */
    public boolean addAbstract(String strAbstract, int iCount) {
        return true;
    }

    /**
     * @param strKeyWord
     *            搜索关键词。
     * @return 返回搜索到的摘要。
     */
    public String searchAbstract(String strKeyWord) {
        return  "";
    }
}
 

 

测试用例:

package com.tencent.ied.bk.unittest;

import static org.junit.Assert.assertEquals;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.tencent.ied.bk.CSearchAbstract;

public class CTestSearchAbstract {

    @Before
    public void setUp() throws Exception {
    }

    @After
    public void tearDown() throws Exception {
    }

    @Test
    public void testFindFlushPattern() {
        CSearchAbstract objSA = new CSearchAbstract();
        objSA.addAbstract("These are good books .You can choose one book from them.", 9);
        objSA.addAbstract("You can search books from Google.", 8);
        objSA.addAbstract("Search and preview millions of books from libraries and publishers worldwide using Google Book Search.", 8);
        objSA.addAbstract("Go to Google Books Home. Advanced Book Search, About Google ... All books.", 6);
        objSA.addAbstract("Bookshelf provides free access to books and documents in life science and healthcare.", 7);

        String strRst = objSA.searchAbstract("BOOK");
        String rstExpt = "These are good books .You can choose one book from them.# Search and preview millions " +
        "of books from libraries and publishers worldwide using Google Book Search.# You can search books from Google.# Bookshelf provides" +
        " free access to books and documents in life science and healthcare.# Go to Google Books Home. Advanced Book Search, About " +
        "Google ... All books.";
        assertEquals(strRst,rstExpt);
        strRst = objSA.searchAbstract("google");
        rstExpt = "You can search books from Google.# Search and preview millions of books from libraries and publishers worldwide using "
       + "Google Book Search.# Go to Google Books Home. Advanced Book Search, About Google ... All books.";

       assertEquals(strRst,rstExpt);
    }
}
 

 个人编写的程序:

package com.insightfullogic.java8;


import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

public class CSearchAbstract{
    //0<=摘要个数范围<=200, 缓存摘要ID对应摘要对象
    private static LRUCache ABSTRACT = new LRUCache(200);
    //全局摘要ID
    private static AtomicInteger INC = new AtomicInteger(1);
    //静态分隔符
    private static String SEPA = "# ";

    //关键词map,内部记录包含摘要ID的集合
    private static ConcurrentHashMap> WORD_MAP = new ConcurrentHashMap<>();

    /**
     * @param strAbstract 标识输入的摘要。
     * @param iCount      标识该摘要搜索的次数。
     * @return 成功返回true, 异常返回FALSE。
     */
    public boolean addAbstract(String strAbstract, int iCount){
        if (strAbstract == null || strAbstract.length() < 1){
            return false;
        }
        return statWordConut(strAbstract, iCount);
    }

    /**
     * @param strKeyWord 搜索关键词。
     * @return 返回搜索到的摘要。
     */
    public String searchAbstract(String strKeyWord){
        if (strKeyWord == null || strKeyWord.length() < 1){
            return null;
        }
        //获取摘要ID集合,setFull全词匹配集合,setInclude包含匹配集合
        Set setAll = Sets.newHashSet();
        strKeyWord = strKeyWord.toLowerCase();
        for (Map.Entry> entry : WORD_MAP.entrySet()){
             if (entry.getKey().contains(strKeyWord)){
                 setAll.addAll(entry.getValue());
            }
        }

        //获取摘要包含匹配的list
        LinkedList listAll = Lists.newLinkedList();
        setAll.forEach(id -> {
            if (ABSTRACT.get(id) != null){
                listAll.add(ABSTRACT.get(id));
                //增加摘要搜索次数
                ABSTRACT.get(id).incr();
            }
        });

        //返回包含匹配的结果排序
        // TODO LRUCache会把最新操作数据放到前面,如果搜索规则结果排序规则都未命中,最后写入的会优先输出。如果要求先入优先输出,这里可以倒转下
        // Collections.reverse(listAll);
        listAll.sort(getAbstractBeanComparator(strKeyWord));

        //构建返回内容
        StringBuffer sb = new StringBuffer();
        listAll.stream().forEach(o -> sb.append(o.getContent()).append(SEPA));
        sb.delete(sb.length() - SEPA.length(), sb.length());
        return sb.toString();
    }

    /**
     * 搜索规则结果排序规则
     *

  • 1.搜索次数最高的摘要排在最前面。

  •      *
  • 2.比较全词匹配的关键字次数,按次数由大到小输出摘要。

  •      *
  • 3.若全词匹配的关键字次数相同,则比较 包含匹配次数,次数多的优先输出。

  •      *
  • 4.若全词匹配与包含匹配次数相同,则对摘要按由小到大进行排序输出。

  •      * @param strKeyWord
         * @return
         */
        private Comparator getAbstractBeanComparator(String strKeyWord){
            return (o1, o2) -> {
                AbstractBean abstractBean1 = ABSTRACT.get(o1.getId());
                AbstractBean abstractBean2 = ABSTRACT.get(o2.getId());

                //1.搜索次数最高的摘要排在最前面
                int count1 = abstractBean1.getCount().intValue();
                int count2 = abstractBean2.getCount().intValue();
                if (count1 != count2){
                    return count2 - count1;
                }

                //2.比较全词匹配的关键字次数,按次数由大到小输出摘要。
                int fullCount1 = abstractBean1.wordCountMap.entrySet()
                        .stream()
                        .filter(o -> o.getKey().equals(strKeyWord))
                        .mapToInt(Map.Entry :: getValue)
                        .sum();
                int fullCount2 = abstractBean2.wordCountMap.entrySet()
                        .stream()
                        .filter(o -> o.getKey().equals(strKeyWord))
                        .mapToInt(Map.Entry :: getValue)
                        .sum();
                if (fullCount1 != fullCount2){
                    return fullCount2 - fullCount1;
                }

                //3.若全词匹配的关键字次数相同,则比较 包含匹配次数,次数多的优先输出。
                int includeCount1 = abstractBean1.wordCountMap.entrySet()
                        .stream()
                        .filter(o -> o.getKey().contains(strKeyWord) && !o.getKey().equals(strKeyWord))
                        .mapToInt(Map.Entry :: getValue)
                        .sum();
                int includeCount2 = abstractBean2.wordCountMap.entrySet()
                        .stream()
                        .filter(o -> o.getKey().contains(strKeyWord) && !o.getKey().equals(strKeyWord))
                        .mapToInt(Map.Entry :: getValue)
                        .sum();
                if (includeCount1 != includeCount2){
                    return includeCount2 - includeCount1;
                }

                //4.若全词匹配与包含匹配次数相同,则对摘要按由小到大进行排序输出。
                return abstractBean1.getContent().length() - abstractBean2.getContent().length();
            };
        }

        /**
         * 统计输入摘要中关键单词,按单词出现次数从前到后统计
         *
         * @param str
         */
        public static boolean statWordConut(String str, int iCount){
            //统计摘要关键词和出现次数map
            HashMap map = new HashMap<>(str.length(), 0.75f);
            //删除标点字符,\W意思是非单词字符;
            String[] array = str.split("\\W+");

            Integer tmp;
            for (String s : array){
                //1<=单词所含字母数<=50
                if (s.length() > 50){
                    continue;
                }
                s = s.toLowerCase();
                tmp = map.get(s);
                map.put(s, tmp == null ? 1 : tmp + 1);
            }

            //1<=摘要所含单词个数<=50 TODO 这里也可以只保留摘要按数量统计的前50个单词
            if (map.isEmpty() || map.size() > 50){
                return false;
            }

            List> list = map.entrySet()
                    .stream()
                    .collect(Collectors.toList());
            list.sort((o1, o2) -> o2.getValue() - o1.getValue());

            //分配摘要ID,摘要记录到LRU缓存中
            Integer id = INC.getAndIncrement();
            ABSTRACT.put(id, new AbstractBean(id, new AtomicInteger(iCount), str, map));

            list.stream().forEach(obj -> {
                String key = obj.getKey().toLowerCase();
                if (WORD_MAP.containsKey(key)){
                    String finalKey = key;
                    //TODO 这里要直接操作value对象,时间有限没有找到底层api
                    synchronized (WORD_MAP){
                        WORD_MAP.put(key, new HashSet(){{
                            add(id);
                            addAll(WORD_MAP.get(finalKey));
                        }});
                    }

                }else{
                    WORD_MAP.put(key, Sets.newHashSet(id));
                }
            });

            return true;
        }

        /**
         * LRU(Least recently used最近最少使用)缓存,支持get和put操作,并且两者的时间复杂度为O(1)
         * 实现:参考LinkedHashMap
         */
        public static class LRUCache{
            private LinkedHashMap map;
            private final int capacity;

            public LRUCache(int capacity){
                this.capacity = capacity;
                //accessOrder = true,按访问顺序排序,访问后会移到链尾(tail)
                map = new LinkedHashMap(capacity, 0.75f, true){
                    //LinkedHashMap封装的淘汰方法,当put新值方法返回true时,就移除该map中最老的键和值
                    protected boolean removeEldestEntry(Map.Entry eldest){
                        //如果Map的size大于设定的最大长度,返回true,再新加入对象时删除最少使用对象(head)
                        return size() > capacity;
                    }
                };
            }

            public AbstractBean get(int key){
                return map.getOrDefault(key, null);
            }

            public void put(int key, AbstractBean value){
                map.put(key, value);
            }

        }

        /**
         * 摘要对象
         */
        static class AbstractBean{
            /**
             * 摘要ID
             */
            private Integer id;

            /**
             * 摘要搜索次数
             */
            private AtomicInteger count;

            /**
             * 摘要内容
             */
            private String content;

            /**
             * 单词在摘要中出现次数,key为小写字母
             */
            private Map wordCountMap;

            public AbstractBean(Integer id, AtomicInteger count, String content, Map wordCountMap){
                this.id = id;
                this.count = count;
                this.content = content;
                this.wordCountMap = wordCountMap;
            }

            /**
             * 自旋锁增加摘要搜索次数
             */
            public void incr(){
                for(;;){
                    int i = count.get();
                    boolean flag = count.compareAndSet(i, ++i);
                    if(flag){
                        break;
                    }
                }
            }

            public Integer getId(){
                return id;
            }

            public void setId(Integer id){
                this.id = id;
            }

            public AtomicInteger getCount(){
                return count;
            }

            public void setCount(AtomicInteger count){
                this.count = count;
            }

            public String getContent(){
                return content;
            }

            public void setContent(String content){
                this.content = content;
            }

            public Map getWordCountMap(){
                return wordCountMap;
            }

            public void setWordCountMap(Map wordCountMap){
                this.wordCountMap = wordCountMap;
            }
        }

    }
     

    你可能感兴趣的:(java,面试)