题目3:文本文件单词的检索与计数(实验准备)

题目3:文本文件单词的检索与计数(实验准备)

实验任务

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

实验要求

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

编程语言的选择

(1)编程语言:Java
(2)编译环境:JDK12
(3)开发工具:IntelliJ IDEA

项目思路

项目解析

任务一

(1)建立一个只包含英文单词的文本文档
(2)读取文本文档的内容
(3)使用者输入一个要查询的单词
(4)检索该单词出现的次数以及位置

任务二

(1)同问题一中的(3),输入数据量大的文本
(2)在读取到的文本文档的内容中检索(1)中输入文本的字串
(3)在控制台打印算法运行时间,分析算法的运行效率
(4)输出检索成功的文本次数以及位置
(5)检验算法的正确性

算法选择

朴素模式匹配算法

模式匹配的最原始想法,从目标串T的的第一个字符开始与模式串P的第一个字符开始进行比较,如果相等,则继续对后续的字符进行比较,否则目标串T从第二个字符开始与模式串P的第一个字符重新比较,直至模式串P中的每个字符依次和目标串T中的一个子串相等为止,此时称为匹配成功,否则匹配失败。 如果模式串P的长度是m,目标串T的长度是n,假设最坏的情况是每遍比较都在最后出现不等,即每遍最多比较m次,最多比较n-m+1遍,总的比较次数最多为m(n-m+1),因此朴素的模式匹配算法的时间复杂度为O(mn)。由于朴素的模式匹配算法的效率不高,在实际应用中很少使用,但易于理解。

public static int puSu(String str1, String match1) {
		int length1 = str1.length();
		int length2 = match1.length();
		//i,j,k分别表示的是主串下标、匹配串下标、记录下次循环主串开始的位置
		int i = 0;
		int j = 0;
		int k = 0;
		char [] str = str1.toCharArray();
		char [] match = match1.toCharArray();
		while(i < length1 && j < length2) {
			if(str[i] == match[j]) {
				i++;
				j++;
			}else {
				k++;
				j = 0;
				i = k;
			}
		}
		if(j == length2) {
			return k;
		}else {
			return -1;
		}	
	}
	public static void main(String[] args) {
		String str = "asdfghg";
		String m1 = "sd";
		String m2 = "ad";
		System.out.println(puSu(str, m1));
		System.out.println(puSu(str, m2));
	}

KMP算法

KMP,一般用来表示克努斯-莫里斯-普拉特算法(Knuth-Morris-Pratt),是在一个“主文本字符串” Str内查找一个“词” Match 的出现,通过观察发现,在不匹配发生的时候这个词自身包含足够的信息来确定下一个匹配将在哪里开始, 以此避免对以前匹配过的字符重新检查。KMP模式匹配算法主要解决的是传统朴素模式匹配算法,当主串从i开始进行匹配,当匹配到j位置时,发现不匹配,主串跳回i+1位置,匹配串跳回0位置,这就导致匹配的效率很低,时间复杂度很高。KMP则当到j位置不匹配时,主串不动,匹配串先通过计算从当前位置的前缀子串和后缀子串的最大匹配串的长度, KMP算法的精髓就是在于求nextArray的过程,这个数组的作用就是把待匹配字符串从头开始向右滑动和主串进行匹配(每次滑动的大小就是nextArray[i]存储的值的大小)从而省去了match字符串返回到终点和主串返回到i+1位置 上的过程。

public static int getIndexOf(String s, String m) {
		if(s == null || m == null || m.length() < 1 || s.length() < m.length()) {
			return -1;
		}
		char [] ss = s.toCharArray();
		char [] ms = m.toCharArray();
		int si = 0;
		int mi = 0;
		int [] nextArray  = getNextArray(ms);
		while(si < s.length() && mi < m.length()) {
			if(ss[si] == ms[mi]) {
				si++;
				mi++;
			}else if(nextArray[mi] == -1) {
				si++;
			}else {
				mi = nextArray[mi];
			}
		}
		
		return mi == m.length() ? si - mi : -1;
		
	}
	
	//求nextArray的过程
	public static int[] getNextArray(char[] ms) {
		//当待匹配串不匹配时已匹配串的的长度+1等于1时,由定义可知nextArray==-1
		if(ms.length == 1) {
			return new int [] {-1};
		}
		int [] nextArray = new int [ms.length];
		//由定义可知,nextArray的第一个值和第二个值为-1和0
		nextArray[0] = -1;
		nextArray[1] = 0;
		//匹配失败的位置
		int pos = 2;
		//因为是从头向后进行匹配,所以在算nextArray[j-1]的值得时候nextArray[j-i-1]的值就已经知道了
		//cn代表的值就是最大匹配长度,他的含义是已匹配字符串的前缀字符串的下一个字符串,
		//match[cn]和match[j-i-1]进行比较,若相等,则nextArray[j-i] = nextArray[j-i-1]+1
		//若不相等,则看match[cn]这个字符的最长前缀和后缀匹配情况,也就是求nextArray[cn],
		//求这个数组的长度和上一步一样,看nextArray[cn-1]的值,和两个前缀字符串假设两个区域是n和m,n的下一个
		//字符串的值为x,则判断x的值和match[cn-1]的值是否相等,若相等,则nextArray[j-i] = nextArray[cn]+1
		//不相等则继续进行判断,直至match[cn] <= 0时证明该段字符串的最长匹配值为0,即nextArray[j-i] = 0
		int cn = 0;
		while(pos < ms.length) {
			if(ms[pos] == ms[cn]) {
				nextArray[pos++] = ++cn;
			}else if(cn > 0) {
				cn = nextArray[cn];
			}else {
				nextArray[pos++] = 0;
			}
		}
	
		return nextArray;
	}
	public static void main(String[] args) {
		String str = "asdfghg";
		String m1 = "df";
		String m2 = "ad";
		System.out.println(getIndexOf(str,m1));
		System.out.println(getIndexOf(str, m2));
	}

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页