问题描述
文学研究人员需要统计某篇英文小说中某些形容词的出现次数和位置。试写一个实现这一目标的文字统计系统,称为"文学研究助手"。
基本要求
英文小说存于文本文件中。待统计的词汇集合要一次输入完毕,即统计工作必需在程序的一次运行后就全部完成。程序的输出结果是每个词出现次数和出现位置所在行的行号,格式自行设计。
测试数据
以你的C源程序模拟英文小说,C语言的保留字集作为待统计的词汇集。
程序设计
原kmp算法中只能返回主串中匹配上的第一个模式串的位置,若主串中含有多个模式串则需要修改算法,对kmp函数新增两个变量,一个是count,用于记录一行中有多少个模式串,另一个是indices[]数组,用于记录模式串在主串中的位置,改进后的算法如下
int KMPIndex(SqString s, SqString t, int indices[], int* count){//count用于存储每一行出现了多少次,indices存模式串在主串中出现的第一个位置
int next[1000], i = 0, j = 0,k=0;//k用于记录是否匹配成功
if (t.length == 0) {
return -1;
}
GetNext(t, next);
while (i < s.length) {
if (j == -1 || s.data[i] == t.data[j]) {
i++; j++;
if(j == t.length) {//匹配成功
indices[*count] = i - j+1;//保存位置
(*count)++;
j = -1;//继续查找
k = 1;
}
}
else j = next[j];
}
if (count == 0) {
k = -1;
}
return k ;
}
随后即是对search函数功能的实现,有了改进后的kmp算法该功能实现就变得容易一些,首先利用fgets读取文本中每一行的内容,然后将每一行最后的换行符换为结束符,然后利用kmp对主串和模式串进行匹配,用int型变量u记录kmp的值
void search(SqString s) {
FILE *fp;
int indices[1000] ;
SqString w;
int j = 0, u=0,num = 0, i = 0 ;//num用于记录出现次数
fopen_s(&fp,file_path, "r");
if (fp == NULL) {
printf(" 无法打开文件!\n");
return;
}
while (fgets(w.data, sizeof(w.data),fp) != NULL) {
w.length = strlen(w.data);
int count = 0;
if (w.data[w.length - 1] == '\n') {
w.data[w.length - 1] = '\0'; // 将换行符替换为字符串结束符
w.length--;
}
i++;
u = KMPIndex(w,s,indices, &count);
if (u == 1 ) {
num = num + count;
printf(" 出现位置在第%d行,次数为%d\n",i,count);
}
}
printf(" 【%s】共计出现%d次\n", s.data, num);
}
运行截图
完整代码
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
char file_path[50]; //源文本路径
//C:\\Users\\86132\\Desktop\\novel.txt 方便输入文件路径
typedef struct SqString {
char data[100];
int length;
};
void GetNext(SqString s,int next[]){
int j, k;
j = 0, k = -1;//第一个字符前无字符串,给值-1
next[0] = -1;
while (j < s.length-1) {
if (k ==-1 || s.data[j] == s.data[k]) {
j++; k++;
next[j] = k;//对应字符匹配情况下,s与t指向同步后移
}
else {
k = next[k];
}
}
}
int KMPIndex(SqString s, SqString t, int indices[], int* count){//count用于存储每一行出现了多少次,indices存模式串在主串中出现的第一个位置
int next[1000], i = 0, j = 0,k=0;//k用于记录是否匹配成功
if (t.length == 0) {
return -1;
}
GetNext(t, next);
while (i < s.length) {
if (j == -1 || s.data[i] == t.data[j]) {
i++; j++;
if(j == t.length) {//匹配成功
indices[*count] = i - j+1;//保存位置
(*count)++;
j = -1;//继续查找
k = 1;
}
}
else j = next[j];
}
if (count == 0) {
k = -1;
}
return k ;
}
void FileRead(char ch[]){//ch为文件名
FILE* fp;
char str[1000];
fopen_s(&fp, ch, "rt");
if (fp == NULL) {
printf(" 打开文件失败\n");
exit(0);
}
/*拷贝文件中的字符串到数组str,并打印str*/
while (fgets(str, 1000, fp) != NULL) {
printf("%s\n", str);
}
fclose(fp);
}
void search(SqString s) {
FILE *fp;
int indices[1000] ;
SqString w{};
int j = 0, u=0,num = 0, i = 0 ;//num用于记录出现次数
fopen_s(&fp,file_path, "r");
if (fp == NULL) {
printf(" 无法打开文件!\n");
return;
}
while (fgets(w.data, sizeof(w.data),fp) != NULL) {
w.length = strlen(w.data);
int count = 0;
if (w.data[w.length - 1] == '\n') {
w.data[w.length - 1] = '\0'; // 将换行符替换为字符串结束符
w.length--;
}
i++;
u = KMPIndex(w,s,indices, &count);
if (u == 1 ) {
num = num + count;
printf(" 出现位置在第%d行,次数为%d\n",i,count);
}
}
printf(" 【%s】共计出现%d次\n", s.data, num);
}
void menu() {
printf(" |------------------------------------------------------|\n");
printf(" |------------------------------------------------------|\n");
printf(" | |\n");
printf(" | 文学研究助手 |\n");
printf(" | |\n");
printf(" | 1.显示文章 |\n");
printf(" | 2.查询单词 |\n");
printf(" | 3.退出系统 |\n");
printf(" | |\n");
printf(" | |\n");
printf(" |------------------------------------------------------|\n");
printf(" |------------------------------------------------------|\n");
}
int main() {
SqString t{};
int i=0;
char x[100] = {0};
menu();
printf(" 输入你的选择(1,2,3)\n ");
scanf_s("%d", &i);
while (1) {
switch (i) {
case 1:
printf(" 请输入文件路径\n ");
scanf_s(" %s", file_path,sizeof(file_path));
FileRead(file_path);
printf(" 输入你的选择(1,2,3)\n ");
scanf_s("%d", &i);
break;
case 2:
printf(" 输入查询的单词\n ");
scanf_s("%s",x,sizeof(x));
strcpy_s(t.data,x);
t.length = strlen(t.data);
search(t);
printf(" 输入你的选择(1,2,3)\n ");
scanf_s("%d", &i);
break;
case 3:
return 0;
default:
printf(" 输入正确的数字!(1,2,3)\n ");
scanf_s("%d", &i);
}
}
}