【数据结构与算法】文学语言助手(C\C++)

news2024/12/23 20:40:56

实践要求

1. 问题描述

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


2. 基本要求

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


3. 测试数据

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


4. 实现提示

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

do{
	do { 
		j=next[j]; 
	} while (j!=0 && s.ch[i]!=t.ch[j]); 
	j++; 
	i++; //每次进入循环体i只增加一次 
} while (i!=(s.curlen+1)&&j!=(t.curlen+1)); 


5. 选作内容

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

实践报告

1. 题目分析

说明程序设计的任务,强调的是程序要做什么,此外列出各成员分工

程序设计任务:
设计一个文字统计系统“文学研究助手”,统计文本文件中输入的词汇出现次数和位置


2. 数据结构设计

说明程序用到的数据结构的定义,主程序的流程及各模块之间的层次关系

该程序主要用到的数据结构是动态数组

主程序流程图

在这里插入图片描述


3. 程序设计

实现概要设计中的数据类型,对主程序、模块及主要操作写出伪代码,画出函数的调用关系

各函数层次关系图

在这里插入图片描述

各模块伪代码

获取单词数量

在这里插入图片描述

存储要查询的单词

在这里插入图片描述

读取文本文件

在这里插入图片描述

KMP算法

在这里插入图片描述

Main函数

在这里插入图片描述


4. 调试分析

问题1:内存管理,如何选择初始空间大小,扩容时机

解决:选取较小初始空间,当空间使用率过高时扩容,每次增加一倍空间

问题2:如何让KMP算法在匹配成功后继续匹配而不重复记录行号

解决:记录最后匹配行号,仅当匹配行号改变时更新并打印
时间复杂度O(文本长度 * 查询单词数量),空间复杂度O(文本长度 + 查询单词数量)


5. 测试结果

列出测试结果,包括输入和输出

输入还包括novel.txt,文本较长,可见附件
在这里插入图片描述


6. 用户使用说明

给出主界面及主要功能界面

首先输入你想查询的单词的数量,然后一次输入你想要查询的单词,然后程序会打印查询的单词所在的行号


7. 选作内容

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


8. 附件

源程序文件清单:
literary_research_assistance.c //主程序
novel.txt //文本文件

literary_research_assistance.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 获取需要查找的单词数
int get_word_count() {
    int word_count;
    scanf("%d", &word_count);
    return word_count;
}

// 为word_count个单词分配空间,并检查内存分配是否成功
char **allocate_words(int count) {
    char **words = (char **)malloc(sizeof(char *) * count);
    if (words == NULL) {
        printf("Memory allocation failed!\n");
        exit(1); // 内存分配失败则退出程序
    }
    return words;
}

// 读取word_count个单词到words中,并检查每个单词的内存分配是否成功
void get_words(char **words, int count) {
    for (int i = 0; i < count; ++i) {
        char *word = (char *)malloc(sizeof(char) * 50);
        if (word == NULL) {
            printf("Memory allocation failed!\n");
            exit(1); // 内存分配失败则退出程序
        }
        scanf("%s", word);
        words[i] = word;
    }
}

// 动态读取文本内容到*text中,记录每行开始位置到*line_numbers
// 使用扩容机制,每次内存不足时多分配一倍的空间,直到文件末尾
void read_text(FILE *fp, char **text, int **line_numbers, int *char_count, int *line_count) {
    int size = 100;
    *text = (char *)malloc(size * sizeof(char));
    *line_numbers = (int *)malloc(size * sizeof(int));
    if (*text == NULL || *line_numbers == NULL) {
        printf("Memory allocation failed!\n");
        exit(1);
    }

    // 读取文本内容
    while (!feof(fp)) {
        // 扩容
        if (*char_count + 1 >= size) {
            size *= 2;
            char *tmp = (char *)malloc(size * sizeof(char));
            int *line_numbers_tmp = (int *)malloc(size * sizeof(int));
            if (tmp == NULL || line_numbers_tmp == NULL) {
                printf("Memory allocation failed!\n");
                exit(1);
            }
            strcpy(tmp, *text);
            free(*text);
            *text = tmp;
            memcpy(line_numbers_tmp, *line_numbers, (*char_count) * sizeof(int));
            free(*line_numbers);
            *line_numbers = line_numbers_tmp;
        }

        char next_char = fgetc(fp);
        if (next_char == '\n')
            (*line_count)++; // 记录行数
        else {
            (*line_numbers)[*char_count] = *line_count; // 记录当前行号
            (*text)[*char_count] = next_char;           // 添加字符
            (*char_count)++;                            // 统计字符数
        }
    }
}

// KMP算法在text中查找pattern,打印所在行号
void kmp(char *text, char *pattern, int *line_numbers, int char_count, int line_count) {
    int text_len = strlen(text);
    int pat_len = strlen(pattern);
    if (text_len < pat_len) {
        return;
    }
    int last_line = -1;
    int *next = (int *)malloc(pat_len * sizeof(int));
    next[0] = 0;
    // 构建next数组
    for (int i = 1, j = 0; i < pat_len;) {
        if (pattern[i] == pattern[j]) { // 如果当前字符匹配前缀
            j++;
            next[i] = j;
            i++; // 继续匹配下一个字符
        } else if (j > 0) {
            j = next[j - 1];
        } else {
            next[i] = 0;
            i++; // 继续匹配下一个字符
        }
    }

    // KMP匹配算法
    for (int i = 0, j = 0; i < text_len;) {
        if (text[i] == pattern[j]) { // 如果当前字符匹配
            i++;                     // 文本下标右移
            j++;                     // 模式下标右移
        } else if (j != 0) {
            j = next[j - 1]; // 模式下标回退到next[j-1]
        } else {
            i++; // 文本下标右移
        }
        if (j == pat_len) { // 如果找到匹配
            if (line_numbers[i - j] != last_line) {
                last_line = line_numbers[i - j];
                printf("%d ", last_line); // 打印行号
            }
        }
    }
    printf("\n");
    free(next); // 释放next数组空间
}

int main() {
    // 获取需要查找的单词数
    printf("Please enter the number of words you want to query: ");
    int word_count = get_word_count();

    // 为word_count个单词分配空间,并检查内存分配是否成功
    char **words = allocate_words(word_count);

    // 读取word_count个单词到words中,并检查每个单词的内存分配是否成功
    printf("Please enter the words you want to query in order: \n");
    get_words(words, word_count);

    // 打开文本文件,检查文件打开是否成功
    FILE *fp = fopen("./novel.txt", "r");
    if (fp == NULL) {
        printf("File open failed!\n");
        exit(1); // 文件打开失败则退出程序
    }

    // 定义变量,记录文本内容、文本长度、行号数组、字符数、行数
    char *text;
    int *line_numbers;
    int char_count = 0;
    int line_count = 1;

    // 读取文本
    read_text(fp, &text, &line_numbers, &char_count, &line_count);

    // 查找每个单词并精准打印行号
    for (int i = 0; i < word_count; ++i) {
        printf("%s exist in lines: ", words[i]);
        kmp(text, words[i], line_numbers, char_count, line_count);
    }

    // 释放所有内存空间,避免内存泄露
    for (int i = 0; i < word_count; ++i)
        free(words[i]);
    free(words);
    free(text);
    free(line_numbers);
}

novel.txt

The Adventures of C Program
It was a bright morning. C Program woke up and checked if (time == 9AM) then he would go to work. C worked as a coder at a tech company. 
While C was walking to work, he thought about the tasks for today. His boss asked him to implement some new functions for their software. C needed to design the functions, define the arguments, return the values and make sure no errors occurred. 
When C arrived at the office, he turned on his computer and began to code. He wrote:
#include <stdio.h>
int main(){
char name[50]; 
printf("Please enter your name: ");
scanf("%s", name); 
print("Hello %s! Welcome.", name);
if (age > 0 && age < 120){
   print("Your age is %d.", age);
}else{
   print("Invalid age!");
}
float height;
printf("Please enter your height (cm): ");
scanf("%f", &height); 
switch(gender){
   case 'm':
   printf("Male");
   break;
   case 'f':
   printf("Female");
   break;
   default:
   printf("Invalid"); 
}
}
While C was coding, he sipped some coffee and took a break when he was tired. By the end of the day, his functions were finished. C felt content and looked forward to more work the next day.
The end.

结束语

  因为是算法小菜,所以提供的方法和思路可能不是很好,请多多包涵~如果有疑问欢迎大家留言讨论,你如果觉得这篇文章对你有帮助可以给我一个免费的赞吗?我们之间的交流是我最大的动力!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/714901.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux常用命令介绍 06 篇——Linux查看目录层级结构以及创建不同情况的层级目录

linux常用命令介绍 06 篇——Linux查看目录层级结构以及创建不同情况的层级目录 1. 前言1.1 Linux常用命令其他篇1.2 关于tree简介 2. 安装并使用 tree2.1 安装tree2.1.1 方式1&#xff1a;yum安装2.1.2 方式2&#xff1a;下载安装包安装2.1.2.1 下载安装包2.1.2.2 解压安装2.1…

transformer入坑指南

*免责声明: 1\此方法仅提供参考 2\搬了其他博主的操作方法,以贴上路径. 3* 场景一: Attention is all you need 场景二: VIT 场景三: Swin v1 场景四: Swin v2 场景五: SETR 场景六: TransUNet 场景七: SegFormer 场景八: PVT 场景九: Segmeter … 场景一:Attention…

Spring Boot 中的 Spring Cloud Ribbon:什么是它,原理及如何使用

Spring Boot 中的 Spring Cloud Ribbon&#xff1a;什么是它&#xff0c;原理及如何使用 在分布式系统中&#xff0c;服务之间的通信是非常重要的。在大型的分布式系统中&#xff0c;有许多服务需要相互通信&#xff0c;而这些服务可能会部署在多个服务器上。为了实现服务之间…

超详细Redis入门教程——Redis分布式系统

前言 本文小新为大家带来 Redis分布式系统 相关知识&#xff0c;具体内容包括数据分区算法&#xff08;包括&#xff1a;顺序分区&#xff0c;哈希分区&#xff09;&#xff0c;系统搭建与运行&#xff08;包括&#xff1a;系统搭建&#xff0c;系统启动与关闭&#xff09;&…

把 OpenGrok search 上的Android 开源代码扒下来

1、下载工具 wget &#xff08;window10版本&#xff09;以及配置环境变量 工具我会上传到本篇博客的“代码包”区域&#xff0c;可以自行下载&#xff01; 当然如果可以访问如下链接的话&#xff0c;也可以在这个地址自行下载一个比较新的版本即可&#xff01;GNU Wget 1.21.…

Web服务器群集:LVS+Keepalived高可用群集

目录 一、理论 1.Keepalived 2.VRRP协议&#xff08;虚拟路由冗余协议&#xff09; 3.部署LVSKeepalived 高可用群集 二、实验 1.LVSKeepalived 高可用群集 三、问题 1.备服务器网卡启动报错 四、总结 一、理论 1.Keepalived &#xff08;1&#xff09;简介 Keepal…

【动态规划算法】-第一题:1137.第N个斐波那契数

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树 &#x1f389;作者宣言&#xff1a;认真写好每一篇博客 &#x1f38a;作者gitee:gitee 如 果 你 喜 欢 作 者 的 文 章 &#xff0c;就 给 作 者 点 点 关 注 吧&#xff01; 文章目录 前言 前言 各位友友们&#xff0c…

element之el-table合并列功能

目标效果如下&#xff1a; 实现代码如下&#xff1a; html部分&#xff1a; <!--定义表格组件,用组件自带的span-method属性定义合并列的方法--> <el-table :data"tableData" :span-method"spanRow"><el-table-column prop"RegionNa…

在proteus中仿真arduino驱动点阵屏matrix-led

我们都知道&#xff0c;如果我们仅仅在某个时间段点亮一个数码管是没有任何困难的&#xff0c;但如果我们点亮多个数码管就会出现问题&#xff0c;因为多个数码管都使用着同样的端口来控制数码管的各个段的亮灭。所以&#xff0c;就会用上一个很重要的方法&#xff0c;对&#…

使用javaScript脚本生成openFoam网格

简介 OpenFoam的首选网格生成器是blockMesh。blockMesh可以根据blockMeshDict这个字典中的信息生成openFoam网格。但是有时候需要修改网格&#xff0c;而网格中的几何点之间又存在约束关系&#xff0c;如果手动修改blockMeshDict那么工作量将是巨大的&#xff0c;所以有必要使…

有没有免费提取音频的软件,分享几个给大家!

在日常生活中&#xff0c;我们经常遇到需要从视频中提取音频的情况&#xff0c;无论是为了制作音频片段、录制语音笔记还是进行后期编辑。本文将介绍三种免费提取音频的方法&#xff0c;分别是记灵在线工具、PR&#xff08;Adobe Premiere Pro&#xff09;和剪映。通过这些方法…

【Vue3】学习笔记-自定义hook函数

概念 什么是hook? 本质是一个函数&#xff0c;把setup函数中使用的Composition API进行了封装。 类似于vue2.x中的mixin。(但是mixins会组件的配置项覆盖。vue3使用了自定义hooks替代mixnins&#xff0c;hooks本质上是函数&#xff0c;引入调用。) 自定义hook的优势: 复用代…

PPU (power policy unit)

写在前边 最近在做低功耗验证&#xff0c;项目中涉及到PPU这一块儿&#xff0c;在家查了好久资料&#xff0c;发现能找到的有价值的文章真的好少&#xff0c;机缘巧合之下&#xff0c;让我找到下边总结&#xff0c;分享出来&#xff0c;希望对和我有相同境遇的小伙伴带来帮助&a…

每周学点数学 2:概率论基础1

泊松分布、正态分布、二项分布 文章目录 1.概率论学习中的重难点2.主要工具介绍1. Python2. MATLAB3. R4. Octave5. Microsoft Excel6. 统计软件 3.理论内容概览&#xff08;前两点&#xff09;1. 概率2. 概率分布 注&#xff1a;本文适用于在在数学建模的应用中&#xff0c;回…

牛客网基础语法101~110题

牛客网基础语法101~110题&#x1f618;&#x1f618;&#x1f618; &#x1f4ab;前言&#xff1a;今天是咱们第十期刷牛客网上的题目。 &#x1f4ab;目标&#xff1a;对打印图案做到有手就行。 &#x1f4ab;鸡汤&#xff1a;与其花时间应付以后不理想的生活&#xff0c;不如…

学习c++ Part02

学习c Part02 前言1.函数注意点&#xff1a;全局函数&#xff08;默认函数&#xff09;静态函数 2.预处理2.1 变量 3.头文件4.宏函数5.指针5.1 普通变量与指针变量建立关系&#xff1a;5.2 指针初始化5.3 指针变量的注意事项5.3.1 void 不能定义普通变量,void * 可以定义指针变…

SpringBoot源码解析

1.Spring Boot介绍,源码阅读环境搭建,插件安装 2.spring boot 源码解析2-SpringApplication初始化 3.spring boot 源码解析3-SpringApplication#run 4.spring boot 源码解析4-SpringApplication#run第4步 5.spring boot 源码解析5-SpringApplication#run第5步 6.spring boot 源…

springboot医院挂号小程序

医院挂号系统 springboot医院挂号系统小程序 java医院挂号小程序 技术&#xff1a; 基于springbootvue小程序医院挂号系统的设计与实现 运行环境&#xff1a; JAVA版本&#xff1a;JDK1.8 IDE类型&#xff1a;IDEA、Eclipse都可运行 数据库类型&#xff1a;MySql&#xff08;…

在线教育场景下客户端实践与优化——RTC服务在线教育

在线教育场景下对提供稳定、高质量的音视频服务提出了非常高的要求。而不断推陈出新的课堂形式以及新技术的应用&#xff0c;使得好未来自研音视频SDK面临更多的挑战。 LiveVideoStackCon 2022北京站邀请到好未来音视频开发高级专家郭晓明介绍好未来自研SDK在工程化上所做出的努…

【编译、链接、装载十五】系统调用与API——printf源码分析

【编译、链接、装载十五】系统调用与API——printf源码分析 一、系统调用介绍1、什么是系统调用2、Linux系统调用3、系统调用的弊端 二、系统调用原理1、中断 三、linux下系统调用实现1、 strace 查看可知&#xff0c;printf调用了系统函数write2、gdb调试查看——printf3、gdb…