文本文件单词的检索与计数(准备+结果)


一、问题的概述、分析

1.问题的概述

1)实验任务:

  • 建立一个文本文件,统计给定单词在文本文件中出现的总次数及位置。

2)实现要求:

  • ·文本文件中每个单词不包含空格且不跨行,单词由字符序列构成且区分大小写,统计给定单词在文本文件中出现的总次数,检索输出的某个单词出现在文本中的行号、在该行中出现的位置;
  • 设计数据量大的文本,进行子串的查询处理,分析算法运行的时间效率,对所有输出的匹配位置结果进行验证,以证明算法设计和实现的正确性;
  • 用朴素模式匹配算法或KMP算法实现字符串定位;
  • 可正确读取,保存文本。

2.问题的分析

  1. 建立一个文本,统计某个单词在该文件中出现的总次数,并进行计数;
  2. 检索某个特定的单词在文本中出现的行号和位置;
  3. 字符串定位,学习并运用朴素模式匹配算法或KMP算法。

二、开发工具及编程语言

1.开发工具

IntelliJ IDEA

2.编程语言

Java语言

三、算法分析

1.朴素模式匹配算法(BF算法)

  • 其基本思想是用p中的每一个字符去与t中的字符一一比较;
  • 如果匹配成功,则返回p在t中首次出现的起始位置; 如果匹配不成功则返回-1;
  • 最坏情况比较次数可达:(n-m+1)*m次;
  • 虽然这中算法比较简单,但是时间效率不高

2.KMP算法

  • 在匹配匹配过程中发生失配时,并不简单的从原始串下一个字符开始重新匹配,而是根据一些匹配过程中得到的信息跳过不必要的匹配,从而达到一个较高的匹配效率。

  • 例如,原始串S=abcabcabdabba,模式串为abcabd,当第一次匹配到T[5]!=S[5]时,KMP算法并不将T的下表回溯到0,而是回溯到2,S下标继续从S[5]开始匹配,直到匹配完成。

  • 串的前缀:包含第一个字符,且不包含最后一个字符的子串
    串的后缀:包含最后一个字符,且不包含第一个字符的子串

3.next数组

1)next数组的定义:

  • 设模式串T[0,m-1],(长度为m),那么next[i]表示既是是串T[0,i-1]的后缀又是串T[0,i-1]的前缀的串最长长度(不妨叫做前后缀),注意这里的前缀和后缀不包括串T[0,i-1]本身。
  • 如上面的例子,T=abcabd,那么next[5]表示既是abcab的前缀又是abcab的后缀的串的最长长度,显然应该是2,即串ab。注意到前面的例子中,当发生失配时T回溯到下表2,和next[5]数组是一致的,这当然不是个巧合,事实上,KMP算法就是通过next数组来计算发生失配时模式串应该回溯到的位置。

2)next数组的计算:

  1. 设模式串T[0,m-1],长度为m,由next数组的定义,可知next[0]=next[1]=0,(因为这里的串的后缀,前缀不包括该串本身)。
  2. 接下来,假设我们从左到右依次计算next数组,在某一时刻,已经得到了next[0]~next[i],现在要计算next[i+1],设j=next[i],由于知道了next[i],所以我们知道T[0,j-1]=T[i-j,i-1],现在比较T[j]和T[i],如果相等,由next数组的定义,可以直接得出next[i+1]=j+1。
  3. 如果不相等,那么我们知道next[i+1]<j+1,所以要将j减小到一个合适的位置po,使得po满足:
    1)T[0,po-1]=T[i-po,i-1]。
    2)T[po]=T[i]。
    3)po是满足条件(1),(2)的最大值。
    4)0<=po<j(显然成立)。

3.参考资料

参考资料1
参考资料2

四、源代码

读取文本

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Scanner;

public class Match {
    String textWord;
    String searchWord;
    Scanner scanner =new Scanner(System.in);

    /**
     *读文本文档
     */
    public void readText(){
        //防止异常
        try{
            StringBuffer buffer = new StringBuffer();
            BufferedReader bufferedReader= new BufferedReader(new FileReader("D:\\数据结构实践\\题目3\\文本文件.txt"));
            String readLine = null;
            while( (readLine = bufferedReader.readLine()) != null ){
                //设置数据文件的搜索路径
                buffer.append(readLine);
            }
            //转换为字符串型
            textWord = buffer.toString();
        }catch (IOException ex){
            System.out.println(ex.getMessage());
        }
    }

KMP算法

public int KMP(String textWord, String searchWord) {
        int num = 0;
        int i = 0;
        while( i< textWord.length()) {
            //字符型数组
            char[] charTextWord = textWord.toCharArray();
            char[] charSearchWord = searchWord.toCharArray();
            // 模式串的位置
            int j = 0;
            int[] next = next(searchWord);
            while (i < charTextWord.length && j < charSearchWord.length) {
                if (j == -1 || charTextWord[i] == charSearchWord[j]) {
                    // 当j为-1时,要移动i
                    i++;
                    j++;
                } else {
                    // j回溯到指定位置
                    j = next[j];
                }
            }
            if (j == charSearchWord.length) {
                site(i-j,searchWord);
                num++;
            }
        }
        return num;
    }

next数组

public static int[] next(String searchWord) {
        char[] charSearchWord = searchWord.toCharArray();
        int[] next = new int[charSearchWord.length];
        next[0] = -1;
        int j = 0;
        int k = -1;
        while (j < charSearchWord.length - 1) {
            if (k == -1 || charSearchWord[j] == charSearchWord[k]) {
                if (charSearchWord[++j] == charSearchWord[++k]) {
                    // 当两个字符相等时要跳过
                    next[j] = next[k];
                } else {
                    next[j] = k;
                }
            } else {
                k = next[k];
            }
        }
        return next;
    }

匹配过程

/**
     *单词匹配过程
     */
    public void start(){
        //寻找到的单词的计数器
        int count;
        //调用读文件
        readText();
        System.out.println("请输入要查询的单词");
        searchWord = scanner.next();
        //调用KMP方法
       count = KMP(textWord, searchWord);
        if(count==0){
            System.out.println("未找到单词");
        }
        else{
            System.out.println("一共找到"+count+"个");
        }
    }

获取位置

public void site(int firstLength, String word){
        int allLength = firstLength+word.length()-1;
        System.out.println("单词位置:"+firstLength+"~"+allLength);
    }

Main函数

public class Main {
    public static void main(String[]args){
        Match input=new Match();
        input.start();
    }
}

五、总结

  1. 用Java读取文件还是比较有挑战的,感觉这个点还需要花更多的时间进行巩固;
  2. 朴素模式匹配算法比较简单易懂易写,但是它的效率不是很高,相反KMP算法就比较高效,但是它有比较复杂,但综合时间复杂度等因素而言,选KMP算法来匹配会比较优选;
  3. 在KMP算法学习中花费的时间不太多,只是基本对这个算法的基本概念有所了解,用来编写题目时仍然存在一定的阻碍,所以对于KMP算法还需要进一步的系统学习
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值