【数据结构题集(c语言版)】文学研究助手 题解(字符串+文件读取+KMP算法)

文学研究助手

题目描述

文学研究人员需要统计某篇英文小说中某些形容词的出现次数和位置。试写一个实现这一目标的文字统计系统,称为“文学研究助手”。

基本要求

  • 英文小说存于一个文本文件中。
  • 待统计的词汇集合要一次输入完毕,即统计工作必须在程序的一次运行之后就全部完成。
  • 程序的输出结果是每个词的出现次数和出现位置所在行的行号,格式自行设计。

测试数据

以你的C源程序模拟英文小说,C语言的保留字集作为待统计的词汇集。

实现提示

约定小说中的词汇一律不跨行。这样,每读入一行,就统计每个词在这行中的出现次数。出现位置所在行的行号可以用链表存储。若某行中出现了不止一次,不必存多个相同的行号。
如果读者希望达到选做部分(1)和(2)所提出的要求,则首先应把KMP算法改写成如下的等价形式,再将它推广到多个模式的情形。

i = 1;
j = 1;
while (i != s.curlen + 1 && j != t.curlen + 1) {
    while (j != 0 && s.ch[i] != t.ch[j]) j = next[j];
    // j == 0 或 s.ch[i] == t.ch[j]
    i++;
}

选作内容

  1. 模式匹配要基于KMP算法。
  2. 整个统计过程中只对小说文字扫描一遍以提高效率。
  3. 假设小说中的每个单词或者从行首开始,或者前置一个空格符。利用单词匹配特点另写一个高效的统计程序,与KMP算法统计程序进行效率比较。
  4. 推广到更一般的模式集匹配问题,并设待查模式串可以跨行(提示:定义操作GetAChar)。

思路

首先定义一个结构体Info,其中cnt用于存储单词出现的次数,ln是一个set,用于存储单词出现的行号。

kmp函数实现了KMP算法,用于在字符串s1中查找字符串s2的出现次数。

在main函数中,首先打开一个名为text.txt的文件,然后逐行读取文件内容。对于每一行,先将标点符号替换为空格,然后使用stringstream将行分割为单词。对于每个单词,如果它还没有被统计过,就将它添加到vis集合中,并使用kmp函数统计它在当前行中的出现次数,然后将行号添加到stats[word].ln中。最后,遍历stats,打印每个单词的出现次数和位置。

算法分析

时间复杂度主要取决于KMP算法和map的操作,KMP算法的时间复杂度为 O ( n + m ) O(n+m) O(n+m),map的插入和查找操作的时间复杂度为 O ( log ⁡ n ) O(\log n) O(logn),所以总的时间复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn)。空间复杂度主要取决于存储单词信息的map,为 O ( n ) O(n) O(n)


AC代码

#include <algorithm>
#include <fstream>
#include <iostream>
#include <map>
#include <set>
#include <sstream>
#include <string>
#define AUTHOR "HEX9CF"
using namespace std;
using Status = int;
using ElemType = int;

const int N = 1e3 + 7;
const int TRUE = 1;
const int FALSE = 0;
const int OK = 1;
const int ERROR = 0;
const int INFEASIBLE = -1;
// const int OVERFLOW = -2;

struct Info {
	int cnt;
	set<int> ln;
};

int kmp(string s1, string s2) {
	int j = 0;
	int pmt[N];
	int cnt = 0;
	int l1 = s1.length();
	int l2 = s2.length();

	s1 = " " + s1;
	s2 = " " + s2;

	// 生成部分匹配表
	j = 0;
	for (int i = 2; i <= l2; i++) {
		while (j && s2[i] != s2[j + 1]) {
			j = pmt[j];
		}
		if (s2[i] == s2[j + 1]) {
			j++;
		}
		pmt[i] = j;
	}

	// 查找字符串
	j = 0;
	for (int i = 1; i <= l1; i++) {
		while (j && s1[i] != s2[j + 1]) {
			j = pmt[j];
		}
		if (s1[i] == s2[j + 1]) {
			j++;
		}
		if (j == l2) {
			cnt++;
			j = pmt[j];
		}
	}
	return cnt;
}

int main() {
	string line;
	int lineNumber = 0;
	map<string, Info> stats;
	ifstream ifs;

	ifs.open("text.txt");
	if (!ifs) {
		cout << "无法打开文件" << endl;
		return 1;
	}

	while (getline(ifs, line)) {
		string word;
		stringstream ss;
		set<string> vis;

		lineNumber++;
		// cout << lineNumber << ": ";
		// 移除标点符号
		replace(line.begin(), line.end(), '.', ' ');
		replace(line.begin(), line.end(), ',', ' ');
		ss.str(line);
		while (ss >> word) {
			// cout << word << " ";
			if (!vis.count(word)) {
				vis.insert(word);
				stats[word].cnt += kmp(" " + line + " ", " " + word + " ");
				stats[word].ln.insert(lineNumber);
			}
		}
		cout << endl;
	}

	for (const auto i : stats) {
		cout << i.first << ": " << endl;
		cout << "出现次数:" << i.second.cnt << endl;
		cout << "出现位置:";
		for (const auto j : i.second.ln) {
			cout << j << " ";
		}
		cout << endl;
	}
	ifs.close();
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值