热门标签 | HotTags
当前位置:  开发笔记 > 人工智能 > 正文

Java实现的双向匹配分词算法示例

这篇文章主要介绍了Java实现的双向匹配分词算法,结合完整实例形式详细分析了双向匹配分词算法的原理与java实现技巧,需要的朋友可以参考下

本文实例讲述了Java实现的双向匹配分词算法。分享给大家供大家参考,具体如下:

目前比较流行的几大分词算法有:基于字符串匹配的分词方法、基于理解的分词方法和基于统计的分词方法。本文采用的是基于字符串匹配法。

正向最大匹配分词:

该算法是基于分词词典实现,从字符串左侧进行分割匹配,如果词典存在则返回分割出来的词语并将该词从之前的字符串中切除,循环进行切割直到字符串大小为0。

例如:str = "我们都是西北农林科技大学信息工程学院的学生。",(假设我们定义最大切割长度max = 8,也就是八个汉字)

1.定义分词长度len = max,从左边取出len个字符word = "我们都是西北农林",并在字典中匹配word;
2.字典中没有该词,那么去掉最后一个字符赋值给word,len值减1;
3.重复步骤2,直到在字典中找到该词或是len = 1,退出循环;
4.从str中切除掉已分割的词语,重复进行1、2、3步骤,直到str被分解完。

逆向最大匹配分词:

和正向分词算法一样,只是从字符串的右边开始切分词语,直到字符串长度为0,。这里不再赘述。

双向匹配分词:

该方法是在正向分词和逆向分词的基础上,对于分割有歧义的语句进行歧义分词。提出“贪吃蛇算法”实现。要进行分词的字符串,就是食物。有2个贪吃蛇,一个从左向右吃;另一个从右向左吃。只要左右分词结果有歧义,2条蛇就咬下一口。贪吃蛇吃下去的字符串,就变成分词。 如果左右分词一直有歧义,两条蛇就一直吃。两条蛇吃到字符串中间交汇时,就肯定不会有歧义了。这时候,左边贪吃蛇肚子里的分词,中间没有歧义的分词,右边贪吃蛇肚子里的分词,3个一拼,就是最终的分词结果。

本文是对整段话进行分词,首先将这段话根据标点符号断句,然后将每句话再进行分词输出。

package cn.nwsuaf.spilt;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
/**
 * 基于正逆双向最大匹配分词算法实现
 * @author 刘永浪
 *
 */
public class WordSpilt {
  /**
   * 存储分词词典
   */
  private Map map = new HashMap();
  /**
   * 最大切词长度,即为五个汉字
   */
  private int MAX_LENGTH = 5;
  /**
   * 构造方法中读取分词词典,并存储到map中
   *
   * @throws IOException
   */
  public WordSpilt() throws IOException {
    BufferedReader br = new BufferedReader(new FileReader("src/dict.txt"));
    String line = null;
    while ((line = br.readLine()) != null) {
      line = line.trim();
      map.put(line, 0);
    }
    br.close();
  }
  /**
   * 设置最大切词长度
   *
   * @param max
   *      最大切词长度
   */
  public void setMaxLength(int max) {
    this.MAX_LENGTH = max;
  }
  /**
   * 获取当前最大切词长度,默认为5(5个汉字)
   *
   * @return 当前最大切词长度
   */
  public int getMaxLength() {
    return this.MAX_LENGTH;
  }
  /**
   * 最大匹配分词算法
   *
   * @param spiltStr
   *      待切分的字符串
   * @param leftToRight
   *      切分方向,true为从左向右,false为从右向左
   * @return 切分的字符串
   */
  public List spilt(String spiltStr, boolean leftToRight) {
    // 如果带切分字符串为空则返回空
    if (spiltStr.isEmpty())
      return null;
    // 储存正向匹配分割字符串
    List leftWords = new ArrayList();
    // 储存负向匹配分割字符串
    List rightWords = new ArrayList();
    // 用于取切分的字串
    String word = null;
    // 取词的长度,初始化设置为最大值
    int wordLength = MAX_LENGTH;
    // 分词操作中处于字串当前位置
    int position = 0;
    // 已经处理字符串的长度
    int length = 0;
    // 去掉字符串中多余的空格
    spiltStr = spiltStr.trim().replaceAll("\\s+", "");
    // 当待切分字符串没有被切分完时循环切分
    while (length = '0' && spiltStr
                    .charAt(i) <= '9')
                    || (spiltStr.charAt(i) >= 'A' && spiltStr
                        .charAt(i) <= 'Z')
                    || (spiltStr.charAt(i) >= 'a' && spiltStr
                        .charAt(i) <= 'z')) {
                  word += spiltStr.charAt(i);
                } else
                  break;
              }
            } else {
              // 如果是逆向匹配,从当前位置之前的连续数字、字母字符加起来并翻转
              for (int i = spiltStr.indexOf(word, position - 1) - 1; i >= 0; i--) {
                if ((spiltStr.charAt(i) >= '0' && spiltStr
                    .charAt(i) <= '9')
                    || (spiltStr.charAt(i) >= 'A' && spiltStr
                        .charAt(i) <= 'Z')
                    || (spiltStr.charAt(i) >= 'a' && spiltStr
                        .charAt(i) <= 'z')) {
                  word += spiltStr.charAt(i);
                  if (i == 0) {
                    StringBuffer sb = new StringBuffer(word);
                    word = sb.reverse().toString();
                  }
                } else {
                  // 翻转操作
                  StringBuffer sb = new StringBuffer(word);
                  word = sb.reverse().toString();
                  break;
                }
              }
            }
          }
          break;
        }
        // 如果是正向最大匹配,舍去最后一个字符
        if (leftToRight)
          word = word.substring(0, word.length() - 1);
        // 否则舍去第一个字符
        else
          word = word.substring(1);
      }
      // 将切分出来的字符串存到指定的表中
      if (leftToRight)
        leftWords.add(word);
      else
        rightWords.add(word);
      // 已处理字符串增加
      length += word.length();
    }
    // 如果是逆向最大匹配,要把表中的字符串调整为正向
    if (!leftToRight) {
      for (int i = rightWords.size() - 1; i >= 0; i--) {
        leftWords.add(rightWords.get(i));
      }
    }
    // 返回切分结果
    return leftWords;
  }
  /**
   * 判断两个集合是否相等
   *
   * @param list1
   *      集合1
   * @param list2
   *      集合2
   * @return 如果相等则返回true,否则为false
   */
  public boolean isEqual(List list1, List list2) {
    if (list1.isEmpty() && list2.isEmpty())
      return false;
    if (list1.size() != list2.size())
      return false;
    for (int i = 0; i  resultWord(String inputStr) {
    // 分词结果
    List result = new ArrayList();
    // “左贪吃蛇”分词结果
    List resultLeft = new ArrayList();
    // “中贪吃蛇”(分歧部分)分词结果
    List resultMiddle = new ArrayList();
    // “右贪吃蛇”分词结果
    List resultRight = new ArrayList();
    // 正向最大匹配分词结果
    List left = new ArrayList();
    // 逆向最大匹配分词结果
    List right = new ArrayList();
    left = spilt(inputStr, true);
    /*System.out.println("正向分词结果:");
    for (String string : left) {
      System.out.print(string + "/");
    }
    System.out.println("\n逆向分词结果:");*/
    right = spilt(inputStr, false);
    /*for (String string : right) {
      System.out.print(string + "/");
    }
    System.out.println("\n双向分词结果:");*/
    // 判断两头的分词拼接,是否已经在输入字符串的中间交汇,只要没有交汇,就不停循环
    while (left.get(0).length() + right.get(right.size() - 1).length()  输入字符串总长度,就直接取正向的
    if (left.get(0).length() + right.get(right.size() - 1).length() > inputStr
        .length())
      resultMiddle = left;
    // 如果中间交汇,刚好吃完,没有重叠:
    // 正向第一个分词 + 反向最后一个分词的长度 = 输入字符串总长度,那么正反向一拼即可
    if (left.get(0).length() + right.get(right.size() - 1).length() == inputStr
        .length()) {
      resultMiddle.add(left.get(0));
      resultMiddle.add(right.get(right.size() - 1));
    }
    // 将没有歧义的分词结果添加到最终结果result中
    for (String string : resultLeft) {
      result.add(string);
    }
    for (String string : resultMiddle) {
      result.add(string);
    }
    // “右贪吃蛇”存储的分词要调整为正向
    for (int i = resultRight.size() - 1; i >= 0; i--) {
      result.add(resultRight.get(i));
    }
    return result;
  }
  /**
   * 将一段话分割成若干句话,分别进行分词
   *
   * @param inputStr
   *      待分割的一段话
   * @return 这段话的分词结果
   */
  public List resultSpilt(String inputStr) {
    // 用于存储最终分词结果
    List result = new ArrayList();
    // 如果遇到标点就分割成若干句话
    String regex = "[,。;!?]";
    String[] st = inputStr.split(regex);
    // 将每一句话的分词结果添加到最终分词结果中
    for (String stri : st) {
      List list = resultWord(stri);
      result.addAll(list);
    }
    return result;
  }
  public static void main(String[] args) {
    // example:过来看房价贵不贵?乒乓球拍卖完了
    Scanner input = new Scanner(System.in);
    String str = input.nextLine();
    WordSpilt wordSpilt = null;
    try {
      wordSpilt = new WordSpilt();
    } catch (IOException e) {
      e.printStackTrace();
    }
    List list = wordSpilt.resultWord(str);
    for (String string : list) {
      System.out.print(string + "/");
    }
  }
}

其中的src/dict.txt为字典文件,这里设置如下:

欢迎

脚本下载
脚本
教程
素材
下载

运行结果如下:

更多关于java算法相关内容感兴趣的读者可查看本站专题:《Java数据结构与算法教程》、《Java操作DOM节点技巧总结》、《Java文件与目录操作技巧汇总》和《Java缓存操作技巧汇总》

希望本文所述对大家java程序设计有所帮助。


推荐阅读
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 深入解析 Apache Shiro 安全框架架构
    本文详细介绍了 Apache Shiro,一个强大且灵活的开源安全框架。Shiro 专注于简化身份验证、授权、会话管理和加密等复杂的安全操作,使开发者能够更轻松地保护应用程序。其核心目标是提供易于使用和理解的API,同时确保高度的安全性和灵活性。 ... [详细]
  • 使用Numpy实现无外部库依赖的双线性插值图像缩放
    本文介绍如何仅使用Numpy库,通过双线性插值方法实现图像的高效缩放,避免了对OpenCV等图像处理库的依赖。文中详细解释了算法原理,并提供了完整的代码示例。 ... [详细]
  • 本文详细介绍了 MySQL 的查询处理流程,包括从客户端连接到服务器、查询缓存检查、语句解析、查询优化及执行等步骤。同时,深入探讨了 MySQL 中的乐观锁机制及其在并发控制中的应用。 ... [详细]
  • 网络运维工程师负责确保企业IT基础设施的稳定运行,保障业务连续性和数据安全。他们需要具备多种技能,包括搭建和维护网络环境、监控系统性能、处理突发事件等。本文将探讨网络运维工程师的职业前景及其平均薪酬水平。 ... [详细]
  • PHP 5.5.0rc1 发布:深入解析 Zend OPcache
    2013年5月9日,PHP官方发布了PHP 5.5.0rc1和PHP 5.4.15正式版,这两个版本均支持64位环境。本文将详细介绍Zend OPcache的功能及其在Windows环境下的配置与测试。 ... [详细]
  • 百度服务再次遭遇技术问题,疑似DNS解析故障
    近日晚间,百度多项在线服务出现加载异常,包括移动端搜索在内的多个功能受到影响。初步迹象表明,问题可能与DNS服务器解析有关。 ... [详细]
  • 深入解析:阿里实战 SpringCloud 微服务架构与应用
    本文将详细介绍 SpringCloud 在微服务架构中的应用,涵盖入门、实战和案例分析。通过丰富的代码示例和实际项目经验,帮助读者全面掌握 SpringCloud 的核心技术和最佳实践。 ... [详细]
  • ThinkPHP框架中处理JS和CSS缓存问题的解决方案
    本文探讨了在ThinkPHP框架中,当启用调试模式(APP_DEBUG)时,删除public文件夹中的CSS和JS文件后页面仍然显示旧样式的问题,并提供了一种有效的解决方法。 ... [详细]
  • 配置Windows操作系统以确保DAW(数字音频工作站)硬件和软件的高效运行可能是一个复杂且令人沮丧的过程。本文提供了一系列专业建议,帮助你优化Windows系统,确保录音和音频处理的流畅性。 ... [详细]
  • GIMP 2.99.2 发布:UI 采用 GTK3 实现、原生支持高分屏和 Wayland
    开源项目评选最后一周,手里的5票再不用就没用了https:www.oschina.netprojecttop_cn_2020GIMP2.99.2已发布,同时这也标志着GIMP3.0的到来,其中最显著的变化是从GTK2过渡到GTK3工具包。基于 ... [详细]
  • 本文深入探讨了 Redis 的两种持久化方式——RDB 快照和 AOF 日志。详细介绍了它们的工作原理、配置方法以及各自的优缺点,帮助读者根据具体需求选择合适的持久化方案。 ... [详细]
  • 本文探讨了如何在日常工作中通过优化效率和深入研究核心技术,将技术和知识转化为实际收益。文章结合个人经验,分享了提高工作效率、掌握高价值技能以及选择合适工作环境的方法,帮助读者更好地实现技术变现。 ... [详细]
  • 本文详细介绍了在企业级项目中如何优化 Webpack 配置,特别是在 React 移动端项目中的最佳实践。涵盖资源压缩、代码分割、构建范围缩小、缓存机制以及性能优化等多个方面。 ... [详细]
author-avatar
手机用户2502928203
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有