java使用ac算法实现关键词高亮

本文介绍如何在Java中利用AC自动机算法实现关键词高亮。针对输入文本和关键词,如“我想买苹果手机,请问哪里可以买苹果手机”和“苹果”,“苹果手机”,“哪里”,成功在10w条数据中耗时41ms完成高亮标记,解决单词重叠问题,确保关键词只被标记一次。实现过程包括构建AC自动机,标记关键词位置,然后根据标记插入替换符完成高亮。

需求背景

标记出一句话中所有关键词
inpu:我想买苹果手机,请问哪里可以买苹果手机
keyword:“苹果”, “苹果手机”, “哪里”
result:我想买[[苹果手机]],请问[[哪里]]可以买[[苹果手机]]
10w条耗时:41ms

难点:需要考虑单词重叠问题(overlap),例如“苹果手机”同时包含两个关键词,只标记一次。

思路

通过ac自动机遍历得到所有关键词;

  • 新建一个byte[],长度等于原句子,根据ac算法结果将关键字位置设为1;
  • 将原句子转为char[],遍历char[]和byte[],如果byte[]前后位置不一致,则插入替换符;
    3.1 如果前一个为1,后一个为0,则插入“]]”;
    3.2 如果前一个为0,后一个为1,则插入“[[”;
  • 判断末尾是否插入替换符;

代码

pom文件引入hanlp,使用其中ac算法

<dependency>
    <groupId>com.hankcs</groupId>
    <artifactId>hanlp</artifactId>
    <version>portable-1.7.8</version>
</dependency>
package com.bincoder.StringUtils;

import com.hankcs.hanlp.collection.AhoCorasick.AhoCorasickDoubleArrayTrie;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.TreeMap;

public class KeywordMatch {

    /**
     * 构建ac自动机
     */
    public static AhoCorasickDoubleArrayTrie<String> buildAcdt(List<String> keywords){
        AhoCorasickDoubleArrayTrie<String> acdt = new AhoCorasickDoubleArrayTrie<>();
        TreeMap<String, String> map = new TreeMap<>();
        for(String keyword : keywords){
            map.put(keyword, keyword);
        }//加入Java开发交流君样:756584822一起吹水聊天
        acdt.build(map);
        return acdt;
    }

    public static String highLight(String originText, AhoCorasickDoubleArrayTrie<String> acdt){
        List<int[]> hitLocationList = new ArrayList<>();
        // ac算法匹配关键词
        acdt.parseText(originText, (begin, end, value)->{
           int[] indexPair = new int[2];
           indexPair[0] = begin;
           indexPair[1] = end-1;
           hitLocationList.add(indexPair);
        });
        // 构建bitmap
        byte[] posStatus = new byte[originText.length()];
        for(int[] item : hitLocationList){
            posStatus[item[0]] = 1;
            for(int i=item[0]; i<=item[1]; i++){
                posStatus[i] = 1;
            }
        }
        // 字符串拼接
        int lastStatus = 0;
        char[] charArray = originText.toCharArray();
        StringBuilder stringBuilder = new StringBuilder();
        for(int i=0; i<posStatus.length; i++){
            if(posStatus[i] == lastStatus){
                stringBuilder.append(charArray[i]);
            }else if(0 == lastStatus){
                stringBuilder.append("[[").append(charArray[i]);
                lastStatus = 1;
            }else if(1 == lastStatus){
                stringBuilder.append("]]").append(charArray[i]);
                lastStatus = 0;
            }//加入Java开发交流君样:756584822一起吹水聊天
            if(i == posStatus.length-1 && 1 == lastStatus){
                stringBuilder.append("]]");
            }
        }

        return stringBuilder.toString();
    }

    public static void main(String[] args) {
        String text = "我想买苹果手机,请问哪里可以买苹果手机";
        List<String> keywords = Arrays.asList("苹果", "苹果手机", "哪里");
//加入Java开发交流君样:756584822一起吹水聊天
        AhoCorasickDoubleArrayTrie<String> acdt = KeywordMatch.buildAcdt(keywords);
        String result = KeywordMatch.highLight(text, acdt);
        System.out.println("inpu:" + text);
        System.out.println("result:" + result);

        long start = System.currentTimeMillis();
        for(int i=0; i<100000; i++){
            result = KeywordMatch.highLight(text, acdt);
        }
        long end = System.currentTimeMillis();
        long total = end - start;
        System.out.println("耗时:" + total + "ms");

    }

}

时间会记录下一切。

image

最新2020整理收集的一些高频面试题(都整理成文档),有很多干货,包含mysql,netty,spring,线程,spring cloud、jvm、源码、算法等详细讲解,也有详细的学习规划图,面试题整理等,需要获取这些内容的朋友请加Q君样:756584822

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值