文学研究助手
题目描述
文学研究人员需要统计某篇英文小说中某些形容词的出现次数和位置。试写一个实现这一目标的文字统计系统,称为“文学研究助手”。
基本要求
- 英文小说存于一个文本文件中。
- 待统计的词汇集合要一次输入完毕,即统计工作必须在程序的一次运行之后就全部完成。
- 程序的输出结果是每个词的出现次数和出现位置所在行的行号,格式自行设计。
测试数据
以你的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++;
}
选作内容
- 模式匹配要基于KMP算法。
- 整个统计过程中只对小说文字扫描一遍以提高效率。
- 假设小说中的每个单词或者从行首开始,或者前置一个空格符。利用单词匹配特点另写一个高效的统计程序,与KMP算法统计程序进行效率比较。
- 推广到更一般的模式集匹配问题,并设待查模式串可以跨行(提示:定义操作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;
}