数据结构和算法 第三小题 文本文件单词的检索与计数

    题目如下:

实验任务:建立一个文本文件,统计给定单词在文本文件中出现的总次数及位置;

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

工具:IDEA    jdk 1.8

总结:要求没有完全实现,主要是KMP算法的实现。

讲解:KMP算法很有意思,但变化不多,二分搜索才是老折磨王了。用了两种方法实现,都基于KMP的原始思想。

原始思想如下:利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的.

首先讲第一种思路:参考链接:www.bilibili.com/video/BV1ci4y1F7Es   c语言技术网

第一:在匹配的过程中,目标串的指针不需要回溯,只回溯模式串的指针

第二:如果模式串和目标串前n个字符匹配成功,遇到匹配失败的字符时,模式串的指针回溯的位置由模式串的内容决定(回溯到匹配失败位置前的模式串内容

的最长公共前后缀的长度+1),然后继续比较。

这里面的next数组存放了匹配失败位置前的模式串内容的最长公共前后缀的长度+1。

nextval数组存放了next数组优化后的回溯位置。(优化逻辑:如果模式串当前位置字符与next数组相同位置的索引指向的字符相同,则用前面字符索引的nextval

值替换当前索引的nextval值,否则将当前索引的next值赋给当前索引的nextval值)

import java.util.ArrayList;
import java.util.List;

public class KMP2 {//原始思想+优化
private String pat;//模式串
private int[] next;//跳转位置数组
private int[] nextval;//优化跳转位置数组
 static List<Integer> list;//记录初始位置及个数的列表
public KMP2(String pat){
    this.pat = pat;
    int n=pat.length();
    next=new int[n];
    nextval=new int[n];
    list=new ArrayList<>();
    getnext();
    getnextval();
}
public void getnext(){
    int n=pat.length();
    if (n==0) return;
    if (n==1) {
        next[0]=-1;
        return;
    }
    if (n==2) {
        next[0]=-1;
        next[1]=0;
        return;
    }
    next[0]=-1;
    next[1]=0;
    for(int i=2;i<n;i++){
        int maxlen=0;
        for (int j = 1; j <i ; j++) {//比较前后缀是否相同
            String s1;
            String s2;
            s1=pat.substring(0,j);
            s2=pat.substring(n-j,n);
            if (s1.equals(s2))  maxlen=j;
        }
        next[i]=maxlen;
    }
}
public void getnextval(){
    int n=pat.length();
    nextval[0]=-1;
    for (int i = 1; i <n ; i++) {
        if (pat.charAt(i)==pat.charAt(next[i]))
            nextval[i]=nextval[nextval[i]];
        else
            nextval[i]=next[i];
    }
}
public void search(String txt) {
    int n = txt.length();
    int m = pat.length();
    int i = 0, j = 0;
    while (i < n) {
        if (j == -1 || txt.charAt(i) == pat.charAt(j)) {
            i++;
            j++;
        } else {
            //j=next[j];
            j = next[j];
        }
        if (j==m){
           list.add(i-m);
           j=0;
        }
    }
}
public void print(){
        int n=list.size();
        System.out.println("出现的总次数:"+n);
        for (int i = 0; i <n ; i++) {
            System.out.print("出现的起始位置:"+list.get(i)+"\t");
        }
    }
public static void main(String[] args) {
     KMP2 kmp2=new KMP2("ab");
    String txt="abhdfhdsihfabduidfghiodfabdjioegoerigabodfger09geoirgab";
    kmp2.search(txt);
    kmp2.print();
    }
}

第二种思路:    参考链接:mp.weixin.qq.com/s/r9pbkMyFyMAvmkf4QnL-1g  labuladong的算法小抄

动态规划是穷举加上去掉重叠子问题。难点是状态转移方程。具体自行了解。这里用到了动态规划的思想,结合了原始思想。

首先是状态转移二维数组,行长为模式串的长度,列长为256.(可以自行调整,有多少调多少,ASCII里通常最多为128个字符,写成256是因为二进制有8位)

二维保存当前状态,一维保存字符,值为当前状态遇到字符后转移到的下一个状态。

一共有n(模式串的长度)种状态,从0到n-1,如果状态等于n了就表示匹配成功一个,将其加入列表并将模式串指针置0。

import java.util.ArrayList;
import java.util.List;

public class KMP {//动态规划思想结合原始思想(状态转移)
    private  int [][]dp;//状态转移数组
    private  String pat;//模式串(搜索的字符串)
    static List<Integer>list;//记录初始位置及个数的列表

    public KMP(String pat) {
        this.pat = pat;
        int m=pat.length();
        list=new ArrayList<>();
        //dp[状态][字符] = 下个状态
        dp=new int[m][256];
        //base case
        dp[0][pat.charAt(0)]=1;
        //前方相同前缀字符位置索引初始化为0
        int X=0;
        //构建状态转移图
        for (int i = 1; i < m; i++) {
            for (int c = 0; c <256 ; c++) {
                dp[i][c]=dp[X][c];
                dp[i][pat.charAt(i)]=i+1;
                //更新前方相同前缀字符位置索引
                X=dp[X][pat.charAt(i)];
            }
        }
    }
    public void search(String txt){
          int m=pat.length();
          int n=txt.length();
          //pat的初始状态为0
          int j=0;
        for (int i = 0; i <n ; i++) {
            //计算pat的下一个状态
            j=dp[j][txt.charAt(i)];
            //到达终止状态,将起始位置加入列表,继续搜索
            if (j==m) {
                list.add(i-m+1);
                j=0;
            }
        }
    }
    public void print(){
        int n=list.size();
        System.out.println("出现的总次数:"+n);
        for (int i = 0; i <n ; i++) {
            System.out.print("出现的起始位置:"+list.get(i)+"\t");
        }
    }

    public static void main(String[] args) {
        String txt="abhdfhdsihfabduidfghiodfabdjioegoerigabodfger09geoirgab";
        KMP kmp=new KMP("ab");
        kmp.search(txt);
        kmp.print();
    }
}

 

 

 

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值