DFA-KMP字符匹配

public class DfaAndKmp {

    /**
     * DFA查询
     * @param document 待查询文档
     * @param KeyWords 关键词集合
     * @param matchType 匹配规则:0最小规则,1最大规则
     * @return map(key为匹配关键词,value匹配次数)
     */
    public static Map<String, Integer> dfaSearch(String document, Set<String> KeyWords, int matchType) {
        // 匹配结果, key为匹配关键词,value匹配次数
        Map<String, Integer> matchRes = new HashMap<>();
        // 构造关键字Map
        Map<String, Object> wordMap = createWordMap(KeyWords);
        // 当前处理关键词
        String curWord = "";
        // 当前使用关键词map
        Map<String, Object> curWordMap = wordMap;
        // 当前正在匹配的关键词首字符序号
        int n = 0;
        for (int i = 0; i < document.length(); i++) {
            String c = String.valueOf(document.charAt(i));
            Object curObj = curWordMap.get(c);
            // 关键词集合中不存在当前字符,当前字符匹配失败,重新开始匹配文档中下一个字符
            if (null == curObj) {
                // 重新使用初始关键词map进行匹配
                curWordMap = wordMap;
                // 当前处理关键词重置为空
                curWord = "";
                i = n++;
                continue;
            }
            // 当前字符匹配成功,添加到匹配关键词中
            curWord += document.charAt(i);
            // 当前字符对应关键词map
            Map<String, Object> curMap = (Map<String, Object>) curObj;
            if (curMap.size() == 0) { // map为空代表当前字符为最后一个,匹配成功
                // 保存匹配记录
                Integer value = matchRes.putIfAbsent(curWord, 1);
                if (null != value) {
                    matchRes.put(curWord, ++value);
                }
                // 最小匹配则直接返回结果
                if (matchType == 0) {
                    return matchRes;
                }

                // 重新使用初始关键词map进行匹配
                curWordMap = wordMap;
                // 当前处理关键词重置为空
                curWord = "";
                // 开始下一个字符的匹配
                i = n++;
            } else { // map非空,则使用当前字符对应关键词map继续进行匹配
                curWordMap = curMap;
            }
        }
        return matchRes;
    }

    /**
     * 利用kmp算法查找字符匹配次数
     * @param document 查询文档
     * @param keyWord 关键词(匹配字符串)
     * @param matchType 匹配规则:0最小规则,1最大规则
     * @return
     */
    public static int kmpSearch(String document, String keyWord, int matchType) {
        // 成功匹配此时
        int matchCount = 0;
        // 被匹配字符串字符序号
        int curNext = 0;
        // 匹配字符串字符序号
        // 关键词部分匹配值
        int keyWorkKmpValue = getKmpValue(keyWord);
        int j = 0;
        for (int i = 0; i < document.length();) {
            char textChar = document.charAt(i);
            char keyWordChar = keyWord.charAt(j);
            if (textChar == keyWordChar) { //当前字符匹配成功
                //关键词完全匹配
                if (j == keyWord.length() - 1) {
                    // 匹配次数加1
                    matchCount ++;
                    if (matchType == 0) { // 最小匹配则直接返回
                        return matchCount;
                    } else { //最大匹配则使用关键词的kmp部分匹配值进行移动,然后继续匹配
                        curNext += (keyWord.length() - keyWorkKmpValue);
                        i = curNext + keyWorkKmpValue;
                        j = keyWorkKmpValue;
                        continue;
                    }
                }
                // 继续匹配下一个字符
                j++;
                i++;
                continue;
            } else { //未匹配成功
                // 首字符不匹配,则重新开始下一字符的匹配
                if (j == 0) {
                    curNext++;
                    i = curNext;
                    j = 0;
                    continue;
                }
                // 使用kmp部分匹配值进行移动,然后重新开始匹配
                int kmpValue = getKmpValue(keyWord.substring(0, j));
                curNext += (j - kmpValue);
                i = curNext + kmpValue;
                j = kmpValue;
            }
        }
        return matchCount;
    }

    /**
     * 构造关键字Map
     */
    private static Map<String, Object> createWordMap(Set<String> KeyWords) {
        Map<String, Object> keyWordsMap = new HashMap<>(KeyWords.size());
        // 遍历关键词
        KeyWords.stream().forEach(keyWord -> {
            // 当前操作map
            Map<String, Object> curMap = keyWordsMap;
            // 遍历关键词每个字符
            for (int i = 0; i < keyWord.length(); i++) {
                String keyChar = String.valueOf(keyWord.charAt(i));
                if (curMap.get(keyChar) == null) { // 字符所属map为空则初始化
                    Map<String, Object> chaMap = new HashMap<>();
                    curMap.put(keyChar, chaMap);
                    curMap = chaMap;
                } else { //非空则操作当前字符所属map
                    curMap = (Map<String, Object>) curMap.get(keyChar);
                }
                // 最后一个字符,设置为空map
                if (i == keyWord.length() - 1) {
                    curMap = new HashMap<>(0);
                }
            }
        });
        return keyWordsMap;
    }

    /**
     * 获取kmp部分匹配值
     * @return
     */
    private static int getKmpValue(String partMatch) {
        Objects.requireNonNull(partMatch);
        if (partMatch.length() <= 1) {
            return 0;
        }
        // 前缀集合(除了最后一个字符以外,一个字符串的全部头部组合)
        Set<String> prefix = new HashSet<>();
        // 后缀集合(指除了第一个字符以外,一个字符串的全部尾部组合)
        Set<String> suffix = new HashSet<>();
        // 获取前缀集合、后缀集合
        int length = partMatch.length();
        for (int i = 0; i < length; i++) {
            for (int j = 1; j < length; j++) {
                prefix.add(partMatch.substring(0, j));
                suffix.add(partMatch.substring(j, length));
            }
        }
        // 获取部分匹配值("前缀"和"后缀"的最长共有元素的长度)
        prefix.retainAll(suffix);
        if (prefix.size() <= 0) {
            return 0;
        }
        int value = prefix.stream()
                .max(Comparator.comparingInt(String::length))
                .get()
                .length();
        return value;
    }

    public static void main(String[] args) {
        String[] keyWorks = {"电影","影视节", "电吉他开始做"};
        String document = "此次电吉他开始,电吉他开始做电影视节日计费电影视节";
        Map<String, Integer> stringIntegerMap = dfaSearch(document, new HashSet<>(Arrays.asList(keyWorks)), 1);
        System.out.println("DFA匹配结果:" + stringIntegerMap);

        String kmpTest = "abcdabfabcdabcdabdeabcdabdabc";
        int count = kmpSearch(kmpTest, "abcdabdeee", 1);
        System.out.println("kmp匹配次数:" + count);
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值