💡 KMP算法、关键词索引

This commit is contained in:
康建伟
2019-10-29 22:03:30 +08:00
parent e526cd8f5b
commit 2fd00636f7
54 changed files with 3760 additions and 26 deletions

View File

@@ -0,0 +1,7 @@
# 包含公共库
include_directories(${CMAKE_SOURCE_DIR}/Status)
# 生成可执行文件
add_executable(KMP SString.h SString.c KMP.h KMP.c KMP-main.c)
# 链接公共库
target_link_libraries(KMP Scanf_lib)

View File

@@ -0,0 +1,64 @@
#include "KMP.h" //**▲04 串**//
// 测试函数,打印字符串
void PrintElem(SString S);
int main(int argc, char** argv) {
char* s = "abaaabcaabaabcacabaabcaabaabcac";
char* t = "abaabcac";
SString S, T;
int* next; // 模式串的next函数值
int* nextval; // 模式串的nextval函数值
int pos; // 匹配起点
int i, j;
StrAssign(S, s); // 初始化主串
printf("S = ");
PrintElem(S);
StrAssign(T, t); // 初始化模式串
printf("T = ");
PrintElem(T);
// 注next数组和nextval数组的0号单元是弃用的从1号单元开始存储有效数据
next = (int*) malloc((T[0] + 1) * sizeof(int));
nextval = (int*) malloc((T[0] + 1) * sizeof(int));
get_next(T, next); // 算法4.7
get_nextval(T, nextval); // 算法4.8即算法4.7的改进版
printf("next : ");
for(i = 1; i <= T[0]; i++) {
printf("%d", next[i]);
}
printf("\n");
printf("nextval : ");
for(i = 1; i <= T[0]; i++) {
printf("%d", nextval[i]);
}
printf("\n");
pos = 1;
i = Index_KMP(S, T, pos, next);
j = Index_KMP(S, T, pos, nextval);
printf("从%d个字符起T 在 S 中第一次匹配成功的位置为 %d\n", pos, i);
printf("从%d个字符起T 在 S 中第一次匹配成功的位置为 %d\n", pos, j);
return 0;
}
// 测试函数,打印字符串
void PrintElem(SString S) {
int i;
for(i = 1; i <= S[0]; i++) {
printf("%c", S[i]);
}
printf("\n");
}

View File

@@ -0,0 +1,106 @@
/*=======================
* KMP算法
*
* 包含算法: 4.6、4.7、4.8
========================*/
#include "KMP.h" //**▲04 串**//
/*
* ████████ 算法4.6 ████████
*
* 查找
*
* 从pos处开始搜索模式串T在主串S中首次出现的位置如果不存在则返回0。
* 如果查找成功,返回匹配的位置。
*
*【注】
* 1.该实现用到了KMP算法是一种比较高效的字符串匹配方式
* 2.教材中没有next参数
*/
int Index_KMP(SString S, SString T, int pos, int next[]) {
int i = pos;
int j = 1;
if(pos < 1) {
return 0;
}
// 比较字符串
while(i <= S[0] && j <= T[0]) {
/*
* 两种情形:
* 1.在模式串的第一个字符处就失配
* 2.主串和模式串处的字符相等
*/
if(j == 0 || S[i] == T[j]) {
i++;
j++;
} else {
// 失配时回到前一个适当的位置
j = next[j];
}
}
if(j > T[0]) {
// 匹配成功,返回匹配位置
return i - T[0];
} else {
// 匹配失败
return 0;
}
}
/*
* ████████ 算法4.7 ████████
*
* 计算模式串的“失配数组”用于KMP算法。
*/
void get_next(SString T, int next[]) {
int i = 1;
int j = 0;
// 模式串第一个字符处失配时,模式串需要从头比较,主串需要前进到下一个位置比较
next[1] = 0;
// 遍历模式串上的字符
while(i < T[0]) {
if(j == 0 || T[i] == T[j]) {
i++;
j++;
next[i] = j;
} else {
j = next[j];
}
}
}
/*
* ████████ 算法4.8 ████████
*
* 计算模式串的“失配数组”用于KMP算法。
* 这是一个优化后的版本效率较算法4.7有所提高。
*/
void get_nextval(SString T, int nextval[]) {
int i = 1;
int j = 0;
// 模式串第一个字符处失配时,模式串需要从头比较,主串需要前进到下一个位置比较
nextval[1] = 0;
// 遍历模式串上的字符
while(i < T[0]) {
if(j==0 || T[i] == T[j]) {
i++;
j++;
if(T[i] != T[j]) {
nextval[i] = j;
} else {
nextval[i] = nextval[j];
}
} else {
j = nextval[j];
}
}
}

View File

@@ -0,0 +1,43 @@
/*=======================
* KMP算法
*
* 包含算法: 4.6、4.7、4.8
========================*/
#ifndef KMP_H
#define KMP_H
#include <stdio.h>
#include <stdlib.h>
#include "SString.h" //**▲04 串**//
/*
* ████████ 算法4.6 ████████
*
* 查找
*
* 从pos处开始搜索模式串T在主串S中首次出现的位置如果不存在则返回0。
* 如果查找成功,返回匹配的位置。
*
*【注】
* 1.该实现用到了KMP算法是一种比较高效的字符串匹配方式
* 2.教材中没有next参数
*/
int Index_KMP(SString S, SString T, int pos, int next[]);
/*
* ████████ 算法4.7 ████████
*
* 计算模式串的“失配数组”用于KMP算法。
*/
void get_next(SString T, int next[]);
/*
* ████████ 算法4.8 ████████
*
* 计算模式串的“失配数组”用于KMP算法。
* 这是一个优化后的版本效率较算法4.7有所提高。
*/
void get_nextval(SString T, int nextval[]);
#endif

View File

@@ -0,0 +1,33 @@
/*=============================
* 串的定长顺序存储表示(顺序串)
*
* 包含算法: 4.1、4.2、4.3、4.5
==============================*/
#include "SString.h" //**▲04 串**//
/*
* 初始化
*
* 构造一个值为chars的串T。
*
*【注】
* 该操作属于最小操作子集
*/
Status StrAssign(SString T, const char* chars) {
int i, len;
len = (int) strlen(chars);
// chars过长
if(len > MAXSTRLEN) {
return ERROR;
}
T[0] = len;
for(i = 1; i <= len; i++) {
T[i] = chars[i - 1];
}
return OK;
}

View File

@@ -0,0 +1,36 @@
/*=============================
* 串的定长顺序存储表示(顺序串)
*
* 包含算法: 4.1、4.2、4.3、4.5
==============================*/
#ifndef SSTRING_H
#define SSTRING_H
#include <stdio.h>
#include <string.h> // 提供strlen原型
#include "Status.h" //**▲01 绪论**//
/* 宏定义 */
#define MAXSTRLEN 255 // 顺序串的最大串长
/*
* 串的顺序存储类型定义
*
* 注有效元素从SString的1号单元开始存储
* SString的0号单元用来存储其长度
*/
typedef unsigned char SString[MAXSTRLEN + 1]; // 0号单元存放串的长度
/*
* 初始化
*
* 构造一个值为chars的串T。
*
*【注】
* 该操作属于最小操作子集
*/
Status StrAssign(SString T, const char* chars);
#endif

View File

@@ -0,0 +1,12 @@
# 包含公共库
include_directories(${CMAKE_SOURCE_DIR}/Status)
# 生成可执行文件
add_executable(WordList ELinkList.h ELinkList.c HString.h HString.c WordList.h WordList.c WordList-main.c)
# 链接公共库
target_link_libraries(WordList Scanf_lib)
# 记录要拷贝到*.exe目录下的资源文件
file(GLOB TestData TestData*.txt)
# 将资源文件拷贝到*.exe目录下不然无法加载
file(COPY ${TestData} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

View File

@@ -0,0 +1,95 @@
/*=======================
* 扩展的单链表(线性链表)
*
* 包含算法: 2.20
========================*/
#include "ELinkList.h" //**▲02 线性表**//
/*━━━━━━━━━━━━━━━━━━━━━━ 内存操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 内存分配
*
* 为线性链表申请一个结点并存入指定的数据e。
*
*【备注】
* static修饰的含义是该函数仅限当前文件内使用
*/
Status MakeNode(Link* p, ElemType e) {
if(p == NULL) {
return ERROR;
}
// 申请空间
*p = (Link) malloc(sizeof(LNode));
if(*p == NULL) {
// 这里没有退出程序,而是返回错误提示
return ERROR;
}
(*p)->data = e;
(*p)->next = NULL;
return OK;
}
/*━━━━━━━━━━━━━━━━━━━━━━ 链表常规操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 初始化
*
* 初始化成功则返回OK否则返回ERROR。
*/
Status InitList(ELinkList* L) {
Link p;
if(L == NULL) {
return ERROR;
}
// 创建头结点
p = (Link) malloc(sizeof(LNode));
if(p == NULL) {
exit(OVERFLOW);
}
p->next = NULL;
// 只有头结点时,首位游标指向自身
(*L).head = (*L).tail = p;
(*L).len = 0;
return OK;
}
/*━━━━━━━━━━━━━━━━━━━━━━ 链表扩展操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 向尾部添加
*
* 将s所指的一串结点链接在链表L后面
*/
Status Append(ELinkList* L, Link s) {
int count;
if(L == NULL || (*L).head == NULL || s == NULL) {
return ERROR;
}
count = 0;
(*L).tail->next = s;
// 确定新的尾结点位置
while(s != NULL) {
(*L).tail = s;
s = s->next;
count++;
}
(*L).len += count;
return OK;
}

View File

@@ -0,0 +1,75 @@
/*=======================
* 扩展的单链表(线性链表)
*
* 包含算法: 2.20
========================*/
#ifndef ELINKLIST_H
#define ELINKLIST_H
#include <stdio.h>
#include <stdlib.h> // 提供malloc、realloc、free、exit原型
#include <limits.h> // 提供一些极限常量
#include "Status.h" //**▲01 绪论**//
/*
* ████ 注意 ████
*
* 教材中的线性链表命名为LinkList
* 这里为了与单链表区分故将其命名为ELinkList。
* 线性链表可以理解成对普通链表的一种扩展。
*/
/* 线性链表元素类型定义 */
typedef int ElemType;
/*
* 线性链表结构
*
* 注:这里的线性链表存在头结点
*/
typedef struct LNode {
ElemType data;
struct LNode* next;
} LNode, * Link, * Position;
/* 维护线性链表头尾指针及长度信息 */
typedef struct {
Link head, tail; // 分别指向线性链表中的头结点和尾结点
int len; // 指示线性链表中数据元素的个数
} ELinkList;
/*━━━━━━━━━━━━━━━━━━━━━━ 内存操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 内存分配
*
* 为线性链表申请一个结点并存入指定的数据e。
*
*【备注】
* static修饰的含义是该函数仅限当前文件内使用
*/
Status MakeNode(Link* p, ElemType e);
/*━━━━━━━━━━━━━━━━━━━━━━ 链表常规操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 初始化
*
* 初始化成功则返回OK否则返回ERROR。
*/
Status InitList(ELinkList* L);
/*━━━━━━━━━━━━━━━━━━━━━━ 链表扩展操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 向尾部添加
*
* 将s所指的一串结点链接在链表L后面
*/
Status Append(ELinkList* L, Link s);
#endif

View File

@@ -0,0 +1,95 @@
/*=========================
* 串的堆分配存储表示(堆串)
*
* 包含算法: 4.4
==========================*/
#include "HString.h" //**▲04 串**//
/*
* 初始化
*
* 构造一个值为chars的串T。
*
*【注】
* 该操作属于最小操作子集
*/
Status StrAssign(HString* T, const char* chars) {
int i, j;
// 求chars的长度
i = (int) strlen(chars);
// 没有有效元素
if(i == 0) {
(*T).ch = NULL;
(*T).length = 0;
return OK;
}
// 存在有效元素时,需要分配存储空间
(*T).ch = (char*) malloc(i * sizeof(char));
if(!((*T).ch)) {
exit(OVERFLOW);
}
for(j = 0; j < i; j++) {
(*T).ch[j] = chars[j];
}
(*T).length = i;
return OK;
}
/*
* 比较
*
* 比较串S和串T返回比较结果。
*
*【注】
* 该操作属于最小操作子集
*/
Status StrCompare(HString S, HString T) {
int i;
for(i = 0; i < S.length && i < T.length; i++) {
// 遇到不同的字符时,比较其大小
if(S.ch[i] != T.ch[i]) {
return S.ch[i] - T.ch[i];
}
}
return S.length - T.length;
}
/*
* 复制
*
* 将串S复制到串T。
*/
Status StrCopy(HString* T, HString S) {
int i;
if(S.length == 0) {
(*T).ch = NULL;
(*T).length = 0;
} else {
// 分配空间
(*T).ch = (char*) malloc(S.length * sizeof(char));
if(!(*T).ch) {
exit(OVERFLOW);
}
// 复制元素
for(i = 0; i < S.length; i++) {
(*T).ch[i] = S.ch[i];
}
// 复制长度信息
(*T).length = S.length;
}
return OK;
}

View File

@@ -0,0 +1,53 @@
/*=========================
* 串的堆分配存储表示(堆串)
*
* 包含算法: 4.4
==========================*/
#ifndef HSTRING
#define HSTRING
#include <stdio.h>
#include <stdlib.h> // 提供malloc、realloc、free、exit原型
#include <string.h> // 提供strlen原型
#include "Status.h" //**▲01 绪论**//
/*
* 串的堆存储表示
*
* 注有效元素从ch的0号单元开始存储
*/
typedef struct {
char* ch; // 若是非空串则按串长分配存储区否则ch为NULL
int length;
} HString;
/*
* 初始化
*
* 构造一个值为chars的串T。
*
*【注】
* 该操作属于最小操作子集
*/
Status StrAssign(HString* T, const char* chars);
/*
* 比较
*
* 比较串S和串T返回比较结果。
*
*【注】
* 该操作属于最小操作子集
*/
Status StrCompare(HString S, HString T);
/*
* 复制
*
* 将串S复制到串T。
*/
Status StrCopy(HString* T, HString S);
#endif

View File

@@ -0,0 +1,7 @@
书 号 书 名
005 Computer Data Structures
010 Introduction to Data Structures
023 Fundamentals of Data Structures
034 The Design and Analysis of Computer Algorithms
050 Introduction to Numerical Analysis
067 Numerical Analysis

View File

@@ -0,0 +1,27 @@
#include <stdio.h> // 提供system原型
#include "WordList.h" //**▲04 串**//
int main(int argc, char** argv) {
FILE* fp;
char line[MaxLineLen];
char* bookinfo = "TestData.txt"; // 书目文件名
char* bookidx = "BookIdx.txt"; // 关键词索引文件名
// 创建索引表
Main(bookinfo, bookidx);
// 显示索引表到屏幕
if((fp = fopen(bookidx, "r"))!=NULL) {
printf("---------索引表生成功!---------\n\n");
while(feof(fp)==FALSE) {
fgets(line, MaxLineLen, fp);
printf("%s", line);
}
} else {
printf("---------未发现索引表!---------\n");
}
return 0;
}

View File

@@ -0,0 +1,308 @@
/*===========================================
* 索引表
*
* 包含算法: 4.9、4.10、4.11、4.12、4.13、4.14
============================================*/
#include "WordList.h" //**▲04 串**//
/*
* ████████ 算法4.9 ████████
*
* 从文件bookinfo中读取书目信息并依此创建相应的关键词索引表然后将索引表写入文件bookidx。
*/
void Main(char* bookinfo, char* bookidx) {
FILE* f, * g;
char head[MaxLineLen]; // 书目的表头信息(未使用)
IdxListType idxlist; // 关键词索引表
ElemType bno; // 书号
int count;
// 以“只读”模式打开书目文件
if((f = fopen(bookinfo, "r")) != NULL) {
// 以“只写”模式打开索引文件
if((g = fopen(bookidx, "w")) != NULL) {
// 初始化索引表
InitIdxList(&idxlist);
// 跳过文件第一行
fgets(head, MaxLineLen, f);
count = 0;
// 如果未到文件结尾,则一直读文件
while(feof(f) == FALSE && count < MaxBookNum) {
// 从文件f读入一个书目信息到书目缓冲区gBuf
GetLine(f);
// 从gBuf提取关键词到词表gWdList书号存入bno
ExtractKeyWord(&bno);
// 将书号及对应的关键词插入索引表idxlist
InsIdxList(&idxlist, bno);
count++;
}
// 向文件g中写入索引表数据
PutText(g, idxlist);
fclose(g);
}
fclose(f);
}
}
/*
* 初始化索引表
*
*【注】
* 教材中将索引表表头置为空串,但这里设定了一个有意义的表头
*/
void InitIdxList(IdxListType* idxlist) {
// 索引表的表头信息
char* chars = "关键词 书号索引";
IdxTermType e;
// 初始化表头信息
StrAssign(&(e.key), chars);
// 初始化书号索引链表
InitList(&(e.bnolist));
(*idxlist).item[0] = e;
// 表头为第0条信息
(*idxlist).last = 0;
}
/*
* 从文件f中读取一条书目信息存入书目缓冲区gBuf。
*/
void GetLine(FILE* f) {
// 读取一行数据存入缓冲区gBuf
fgets(gBuf, MaxLineLen, f);
}
/*
* 从缓冲区gBuf中提取书名关键词到词表gWdList书号存入bno。
*/
void ExtractKeyWord(ElemType* bno) {
char delim[] = {'-', ' ', '\r', '\n', '\t'}; // 分隔符
char* title; // 书名
char* token; // 从书名中分解出的关键词
// 分解书目字符串将书号存入bno(十进制)并用title指向剩下的字符串(书名)
*bno = (int) strtol(gBuf, &title, 10);
// 将书名的由大写变小写
strlwr(title);
// 清空关键词词表
gWdList.last = 0;
// 分解关键词
for(token = strtok(title, delim); token != NULL; token = strtok(NULL, delim)) {
// 如果该关键词是常用词,则忽略它
if(isCommonWords(token) == TRUE) {
continue;
}
// 记下从书名中提取的关键词
gWdList.item[gWdList.last++] = token;
}
}
/*
* ████████ 算法4.10 ████████
*
* 将书号bno对应的书名中的关键词按词典顺序插入到索引表idxlist。
*/
Status InsIdxList(IdxListType* idxlist, int bno) {
int i, j;
Boolean boo;
HString wd;
if(gWdList.last <= 0) {
return ERROR;
}
// 遍历书号bno对应的书名中的所有关键词
for(i = 0; i < gWdList.last; i++) {
// 获取待插入的关键词
GetWord(i, &wd);
// 判断该关键词是否已经位于索引表中
j = Locate(*idxlist, wd, &boo);
// 如果该关键词不在索引表中,则需要插入关键词
if(boo == FALSE) {
// 将关键词wd插入到索引表
InsertNewKey(idxlist, j, wd);
}
// 在关键词已存在的情形下,插入书号
if(!InsertBook(idxlist, j, bno)) {
return ERROR;
}
}
return OK;
}
/*
* ████████ 算法4.11 ████████
*
* 用wd返回词表gWdList中第i个关键词。
*/
void GetWord(int i, HString* wd) {
if(i < 0 || i > gWdList.last - 1) {
StrAssign(wd, "");
} else {
StrAssign(wd, gWdList.item[i]);
}
}
/*
* ████████ 算法4.12 ████████
*
* 查询在索引表idxlist中是否存在与wd相等的关键词。
* 若存在则返回wd在词表中的位置并置b为TRUE。
* 若不存在则返回wd应插入的位置并置b为FALSE。
*/
int Locate(IdxListType idxlist, HString wd, Boolean* b) {
int i, m = -1;
/*
* 在索引表idxlist中查找关键词wd是否存在
* 注0号单元存储了表头信息
*/
for(i = idxlist.last; i > 0 && (m = StrCompare(idxlist.item[i].key, wd)) > 0; i--) {
}
// 如果找到了关键词wd
if(m == 0) {
*b = TRUE;
return i;
} else {
*b = FALSE;
return i + 1;
}
}
/*
* ████████ 算法4.13 ████████
*
* 在索引表的索引i(>=0)处插入关键词wd并初始化书号索引的链表为空表。
*/
void InsertNewKey(IdxListType* idxlist, int i, HString wd) {
int j;
/*
* 索引项后移
* 注0号单元存储了表头信息
*/
for(j = (*idxlist).last; j >= i; j--) {
(*idxlist).item[j + 1] = (*idxlist).item[j];
}
// 插入索引项
StrCopy(&((*idxlist).item[i].key), wd);
// 初始化书号索引链表
InitList(&((*idxlist).item[i].bnolist));
// 索引数目增一
(*idxlist).last++;
}
/*
* ████████ 算法4.14 ████████
*
* 为索引表在索引i(>0)处的关键词插入书号。
*/
Status InsertBook(IdxListType* idxlist, int i, ElemType bno) {
Link p;
// 内存分配失败
if(MakeNode(&p, bno) == FALSE) {
return ERROR;
}
// 插入新的书号索引
Append(&((*idxlist).item[i].bnolist), p);
return OK;
}
/*
* 将生成的索引表idxlist输出到文件g。
*/
void PutText(FILE* g, IdxListType idxlist) {
int i, j, m, n;
Link p;
HString S;
ELinkList L;
if(idxlist.last <= 0) {
return;
}
// 先输出表头信息
S = idxlist.item[0].key;
for(m = 0; m < S.length; m++) {
fprintf(g, "%c", S.ch[m]);
}
fprintf(g, "\n");
// 输出索引信息
for(i = 1; i <= idxlist.last; i++) {
// 1.输出关键词
S = idxlist.item[i].key;
for(m = 0; m < S.length; m++) {
fprintf(g, "%c", S.ch[m]);
}
// 2.输出空格
for(j = 1; j <= 18 - idxlist.item[i].key.length; j++) {
fprintf(g, " ");
}
// 3.输出书号索引
L = idxlist.item[i].bnolist;
for(n = 1, p = L.head->next; n <= L.len; n++) {
fprintf(g, "%03d", p->data);
p = p->next;
if(p) {
fprintf(g, "");
}
}
// 4.输出换行
fprintf(g, "\n");
}
}
// 判断str是否为常用词
static Status isCommonWords(char* str) {
int i;
// 常用词词表,这些词汇会被排除在关键词之外
WordListType words = {{"a", "an", "the", "of", "and", "is", "to", "as", "in", "for"}, 10};
// 查询常用词词表
for(i = 0; i < words.last; i++) {
// 对两字符串进行忽略大小写的比较
if(strcasecmp(str, words.item[i]) == 0) {
// 如果该字符串是常用词则返回TRUE
return TRUE;
}
}
return FALSE;
}

View File

@@ -0,0 +1,130 @@
/*===========================================
* 索引表
*
* 包含算法: 4.9、4.10、4.11、4.12、4.13、4.14
============================================*/
#ifndef WORDLIST_H
#define WORDLIST_H
#include <stdio.h> // 提供fopen、fclose、feof、fgets原型
#include <stdlib.h> // 提供exit、strtol原型
#include <string.h> // 提供strlen、strcmpi、strlwr原型
#include "Status.h" //**▲01 绪论**//
#include "ELinkList.h" //**▲02 线性表**//
#include "HString.h" //**▲04 串**//
/* 宏定义 */
#define MaxBookNum 1000 // 允许的最大书目数假设最多只对1000本书建索引表
#define MaxKeyNum 2500 // 索引表最大容量(索引项数量的最大值)
#define MaxLineLen 500 // 书目串(行)的最大长度
#define MaxWordNum 100 // 词表的最大容量
/* 类型定义 */
// 布尔类型
typedef Status Boolean;
// 词表类型(顺序表)
typedef struct {
char* item[MaxWordNum]; // 词表集合
int last; // 词表的长度
} WordListType;
// 索引项类型(索引表的行)
typedef struct {
HString key; // 关键词
ELinkList bnolist; // 存放书号索引的链表
} IdxTermType;
/*
* 索引表类型
*
*【注】
* 0号单元存储表头信息这一点与教材有所不同
*/
typedef struct {
IdxTermType item[MaxKeyNum + 1]; // 索引项的集合
int last; // 索引表中包含的索引项数目
} IdxListType;
/* 全局变量变量名称前面都加了g标记 */
// 书目缓存区
char gBuf[MaxLineLen];
// 关键词词表(普通词表),已经排除了常用词
WordListType gWdList;
/*
* ████████ 算法4.9 ████████
*
* 从文件bookinfo中读取书目信息并依此创建相应的关键词索引表然后将索引表写入文件bookidx。
*/
void Main(char* bookinfo, char* bookidx);
/*
* 初始化索引表
*
*【注】
* 教材中将索引表表头置为空串,但这里设定了一个有意义的表头
*/
void InitIdxList(IdxListType* idxlist);
/*
* 从文件f中读取一条书目信息存入书目缓冲区gBuf。
*/
void GetLine(FILE* f);
/*
* 从缓冲区gBuf中提取书名关键词到词表gWdList书号存入bno。
*/
void ExtractKeyWord(ElemType* bno);
/*
* ████████ 算法4.10 ████████
*
* 将书号bno对应的书名关键词按词典顺序插入到索引表idxlist。
*/
Status InsIdxList(IdxListType* idxlist, int bno);
/*
* ████████ 算法4.11 ████████
*
* 用wd返回词表gWdList中第i个关键词。
*/
void GetWord(int i, HString* wd);
/*
* ████████ 算法4.12 ████████
*
* 查询在索引表idxlist中是否存在与wd相等的关键词。
* 若存在则返回wd在词表中的位置并置b为TRUE。
* 若不存在则返回wd应插入的位置并置b为FALSE。
*/
int Locate(IdxListType idxlist, HString wd, Boolean* b);
/*
* ████████ 算法4.13 ████████
*
* 在索引表的索引i(>=0)处插入关键词wd并初始化书号索引的链表为空表。
*/
void InsertNewKey(IdxListType* idxlist, int i, HString wd);
/*
* ████████ 算法4.14 ████████
*
* 为索引表在索引i(>0)处的关键词插入书号。
*/
Status InsertBook(IdxListType* idxlist, int i, int bno);
/*
* 将生成的索引表idxlist输出到文件g。
*/
void PutText(FILE* g, IdxListType idxlist);
// 判断str是否为常用词
static Status isCommonWords(char* str);
#endif

View File

@@ -23,3 +23,5 @@ add_subdirectory(0309_BankQueuing)
add_subdirectory(0401_SString)
add_subdirectory(0402_HString)
add_subdirectory(0403_LString)
add_subdirectory(0404_KMP)
add_subdirectory(0405_WordList)

View File

@@ -0,0 +1,64 @@
#include "KMP.h" //**▲04 串**//
// 测试函数,打印字符串
void PrintElem(SString S);
int main(int argc, char** argv) {
char* s = "abaaabcaabaabcacabaabcaabaabcac";
char* t = "abaabcac";
SString S, T;
int* next; // 模式串的next函数值
int* nextval; // 模式串的nextval函数值
int pos; // 匹配起点
int i, j;
StrAssign(S, s); // 初始化主串
printf("S = ");
PrintElem(S);
StrAssign(T, t); // 初始化模式串
printf("T = ");
PrintElem(T);
// 注next数组和nextval数组的0号单元是弃用的从1号单元开始存储有效数据
next = (int*) malloc((T[0] + 1) * sizeof(int));
nextval = (int*) malloc((T[0] + 1) * sizeof(int));
get_next(T, next); // 算法4.7
get_nextval(T, nextval); // 算法4.8即算法4.7的改进版
printf("next : ");
for(i = 1; i <= T[0]; i++) {
printf("%d", next[i]);
}
printf("\n");
printf("nextval : ");
for(i = 1; i <= T[0]; i++) {
printf("%d", nextval[i]);
}
printf("\n");
pos = 1;
i = Index_KMP(S, T, pos, next);
j = Index_KMP(S, T, pos, nextval);
printf("从%d个字符起T 在 S 中第一次匹配成功的位置为 %d\n", pos, i);
printf("从%d个字符起T 在 S 中第一次匹配成功的位置为 %d\n", pos, j);
return 0;
}
// 测试函数,打印字符串
void PrintElem(SString S) {
int i;
for(i = 1; i <= S[0]; i++) {
printf("%c", S[i]);
}
printf("\n");
}

View File

@@ -0,0 +1,106 @@
/*=======================
* KMP算法
*
* 包含算法: 4.6、4.7、4.8
========================*/
#include "KMP.h" //**▲04 串**//
/*
* ████████ 算法4.6 ████████
*
* 查找
*
* 从pos处开始搜索模式串T在主串S中首次出现的位置如果不存在则返回0。
* 如果查找成功,返回匹配的位置。
*
*【注】
* 1.该实现用到了KMP算法是一种比较高效的字符串匹配方式
* 2.教材中没有next参数
*/
int Index_KMP(SString S, SString T, int pos, int next[]) {
int i = pos;
int j = 1;
if(pos < 1) {
return 0;
}
// 比较字符串
while(i <= S[0] && j <= T[0]) {
/*
* 两种情形:
* 1.在模式串的第一个字符处就失配
* 2.主串和模式串处的字符相等
*/
if(j == 0 || S[i] == T[j]) {
i++;
j++;
} else {
// 失配时回到前一个适当的位置
j = next[j];
}
}
if(j > T[0]) {
// 匹配成功,返回匹配位置
return i - T[0];
} else {
// 匹配失败
return 0;
}
}
/*
* ████████ 算法4.7 ████████
*
* 计算模式串的“失配数组”用于KMP算法。
*/
void get_next(SString T, int next[]) {
int i = 1;
int j = 0;
// 模式串第一个字符处失配时,模式串需要从头比较,主串需要前进到下一个位置比较
next[1] = 0;
// 遍历模式串上的字符
while(i < T[0]) {
if(j == 0 || T[i] == T[j]) {
i++;
j++;
next[i] = j;
} else {
j = next[j];
}
}
}
/*
* ████████ 算法4.8 ████████
*
* 计算模式串的“失配数组”用于KMP算法。
* 这是一个优化后的版本效率较算法4.7有所提高。
*/
void get_nextval(SString T, int nextval[]) {
int i = 1;
int j = 0;
// 模式串第一个字符处失配时,模式串需要从头比较,主串需要前进到下一个位置比较
nextval[1] = 0;
// 遍历模式串上的字符
while(i < T[0]) {
if(j==0 || T[i] == T[j]) {
i++;
j++;
if(T[i] != T[j]) {
nextval[i] = j;
} else {
nextval[i] = nextval[j];
}
} else {
j = nextval[j];
}
}
}

View File

@@ -0,0 +1,102 @@
[Project]
FileName=KMP.dev
Name=KMP
Type=1
Ver=2
ObjFiles=
Includes=
Libs=
PrivateResource=
ResourceIncludes=
MakeIncludes=
Compiler=
CppCompiler=
Linker=
IsCpp=0
Icon=
ExeOutput=
ObjectOutput=
LogOutput=
LogOutputEnabled=0
OverrideOutput=0
OverrideOutputName=
HostApplication=
UseCustomMakefile=0
CustomMakefile=
CommandLine=
Folders=
IncludeVersionInfo=0
SupportXPThemes=0
CompilerSet=1
CompilerSettings=0000000000000000001000000
UnitCount=5
[VersionInfo]
Major=1
Minor=0
Release=0
Build=0
LanguageID=1033
CharsetID=1252
CompanyName=
FileVersion=
FileDescription=Developed using the Dev-C++ IDE
InternalName=
LegalCopyright=
LegalTrademarks=
OriginalFilename=
ProductName=
ProductVersion=
AutoIncBuildNr=0
SyncProduct=1
[Unit3]
FileName=KMP-main.cpp
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit1]
FileName=KMP.cpp
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit4]
FileName=SString.cpp
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit2]
FileName=KMP.h
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit5]
FileName=SString.h
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=

View File

@@ -0,0 +1,43 @@
/*=======================
* KMP算法
*
* 包含算法: 4.6、4.7、4.8
========================*/
#ifndef KMP_H
#define KMP_H
#include <stdio.h>
#include <stdlib.h>
#include "SString.h" //**▲04 串**//
/*
* ████████ 算法4.6 ████████
*
* 查找
*
* 从pos处开始搜索模式串T在主串S中首次出现的位置如果不存在则返回0。
* 如果查找成功,返回匹配的位置。
*
*【注】
* 1.该实现用到了KMP算法是一种比较高效的字符串匹配方式
* 2.教材中没有next参数
*/
int Index_KMP(SString S, SString T, int pos, int next[]);
/*
* ████████ 算法4.7 ████████
*
* 计算模式串的“失配数组”用于KMP算法。
*/
void get_next(SString T, int next[]);
/*
* ████████ 算法4.8 ████████
*
* 计算模式串的“失配数组”用于KMP算法。
* 这是一个优化后的版本效率较算法4.7有所提高。
*/
void get_nextval(SString T, int nextval[]);
#endif

View File

@@ -0,0 +1,33 @@
/*=============================
* 串的定长顺序存储表示(顺序串)
*
* 包含算法: 4.1、4.2、4.3、4.5
==============================*/
#include "SString.h" //**▲04 串**//
/*
* 初始化
*
* 构造一个值为chars的串T。
*
*【注】
* 该操作属于最小操作子集
*/
Status StrAssign(SString T, const char* chars) {
int i, len;
len = (int) strlen(chars);
// chars过长
if(len > MAXSTRLEN) {
return ERROR;
}
T[0] = len;
for(i = 1; i <= len; i++) {
T[i] = chars[i - 1];
}
return OK;
}

View File

@@ -0,0 +1,36 @@
/*=============================
* 串的定长顺序存储表示(顺序串)
*
* 包含算法: 4.1、4.2、4.3、4.5
==============================*/
#ifndef SSTRING_H
#define SSTRING_H
#include <stdio.h>
#include <string.h> // 提供strlen原型
#include "Status.h" //**▲01 绪论**//
/* 宏定义 */
#define MAXSTRLEN 255 // 顺序串的最大串长
/*
* 串的顺序存储类型定义
*
* 注有效元素从SString的1号单元开始存储
* SString的0号单元用来存储其长度
*/
typedef unsigned char SString[MAXSTRLEN + 1]; // 0号单元存放串的长度
/*
* 初始化
*
* 构造一个值为chars的串T。
*
*【注】
* 该操作属于最小操作子集
*/
Status StrAssign(SString T, const char* chars);
#endif

View File

@@ -0,0 +1,95 @@
/*=======================
* 扩展的单链表(线性链表)
*
* 包含算法: 2.20
========================*/
#include "ELinkList.h" //**▲02 线性表**//
/*━━━━━━━━━━━━━━━━━━━━━━ 内存操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 内存分配
*
* 为线性链表申请一个结点并存入指定的数据e。
*
*【备注】
* static修饰的含义是该函数仅限当前文件内使用
*/
Status MakeNode(Link* p, ElemType e) {
if(p == NULL) {
return ERROR;
}
// 申请空间
*p = (Link) malloc(sizeof(LNode));
if(*p == NULL) {
// 这里没有退出程序,而是返回错误提示
return ERROR;
}
(*p)->data = e;
(*p)->next = NULL;
return OK;
}
/*━━━━━━━━━━━━━━━━━━━━━━ 链表常规操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 初始化
*
* 初始化成功则返回OK否则返回ERROR。
*/
Status InitList(ELinkList* L) {
Link p;
if(L == NULL) {
return ERROR;
}
// 创建头结点
p = (Link) malloc(sizeof(LNode));
if(p == NULL) {
exit(OVERFLOW);
}
p->next = NULL;
// 只有头结点时,首位游标指向自身
(*L).head = (*L).tail = p;
(*L).len = 0;
return OK;
}
/*━━━━━━━━━━━━━━━━━━━━━━ 链表扩展操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 向尾部添加
*
* 将s所指的一串结点链接在链表L后面
*/
Status Append(ELinkList* L, Link s) {
int count;
if(L == NULL || (*L).head == NULL || s == NULL) {
return ERROR;
}
count = 0;
(*L).tail->next = s;
// 确定新的尾结点位置
while(s != NULL) {
(*L).tail = s;
s = s->next;
count++;
}
(*L).len += count;
return OK;
}

View File

@@ -0,0 +1,75 @@
/*=======================
* 扩展的单链表(线性链表)
*
* 包含算法: 2.20
========================*/
#ifndef ELINKLIST_H
#define ELINKLIST_H
#include <stdio.h>
#include <stdlib.h> // 提供malloc、realloc、free、exit原型
#include <limits.h> // 提供一些极限常量
#include "Status.h" //**▲01 绪论**//
/*
* ████ 注意 ████
*
* 教材中的线性链表命名为LinkList
* 这里为了与单链表区分故将其命名为ELinkList。
* 线性链表可以理解成对普通链表的一种扩展。
*/
/* 线性链表元素类型定义 */
typedef int ElemType;
/*
* 线性链表结构
*
* 注:这里的线性链表存在头结点
*/
typedef struct LNode {
ElemType data;
struct LNode* next;
} LNode, * Link, * Position;
/* 维护线性链表头尾指针及长度信息 */
typedef struct {
Link head, tail; // 分别指向线性链表中的头结点和尾结点
int len; // 指示线性链表中数据元素的个数
} ELinkList;
/*━━━━━━━━━━━━━━━━━━━━━━ 内存操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 内存分配
*
* 为线性链表申请一个结点并存入指定的数据e。
*
*【备注】
* static修饰的含义是该函数仅限当前文件内使用
*/
Status MakeNode(Link* p, ElemType e);
/*━━━━━━━━━━━━━━━━━━━━━━ 链表常规操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 初始化
*
* 初始化成功则返回OK否则返回ERROR。
*/
Status InitList(ELinkList* L);
/*━━━━━━━━━━━━━━━━━━━━━━ 链表扩展操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 向尾部添加
*
* 将s所指的一串结点链接在链表L后面
*/
Status Append(ELinkList* L, Link s);
#endif

View File

@@ -0,0 +1,95 @@
/*=========================
* 串的堆分配存储表示(堆串)
*
* 包含算法: 4.4
==========================*/
#include "HString.h" //**▲04 串**//
/*
* 初始化
*
* 构造一个值为chars的串T。
*
*【注】
* 该操作属于最小操作子集
*/
Status StrAssign(HString* T, const char* chars) {
int i, j;
// 求chars的长度
i = (int) strlen(chars);
// 没有有效元素
if(i == 0) {
(*T).ch = NULL;
(*T).length = 0;
return OK;
}
// 存在有效元素时,需要分配存储空间
(*T).ch = (char*) malloc(i * sizeof(char));
if(!((*T).ch)) {
exit(OVERFLOW);
}
for(j = 0; j < i; j++) {
(*T).ch[j] = chars[j];
}
(*T).length = i;
return OK;
}
/*
* 比较
*
* 比较串S和串T返回比较结果。
*
*【注】
* 该操作属于最小操作子集
*/
Status StrCompare(HString S, HString T) {
int i;
for(i = 0; i < S.length && i < T.length; i++) {
// 遇到不同的字符时,比较其大小
if(S.ch[i] != T.ch[i]) {
return S.ch[i] - T.ch[i];
}
}
return S.length - T.length;
}
/*
* 复制
*
* 将串S复制到串T。
*/
Status StrCopy(HString* T, HString S) {
int i;
if(S.length == 0) {
(*T).ch = NULL;
(*T).length = 0;
} else {
// 分配空间
(*T).ch = (char*) malloc(S.length * sizeof(char));
if(!(*T).ch) {
exit(OVERFLOW);
}
// 复制元素
for(i = 0; i < S.length; i++) {
(*T).ch[i] = S.ch[i];
}
// 复制长度信息
(*T).length = S.length;
}
return OK;
}

View File

@@ -0,0 +1,53 @@
/*=========================
* 串的堆分配存储表示(堆串)
*
* 包含算法: 4.4
==========================*/
#ifndef HSTRING
#define HSTRING
#include <stdio.h>
#include <stdlib.h> // 提供malloc、realloc、free、exit原型
#include <string.h> // 提供strlen原型
#include "Status.h" //**▲01 绪论**//
/*
* 串的堆存储表示
*
* 注有效元素从ch的0号单元开始存储
*/
typedef struct {
char* ch; // 若是非空串则按串长分配存储区否则ch为NULL
int length;
} HString;
/*
* 初始化
*
* 构造一个值为chars的串T。
*
*【注】
* 该操作属于最小操作子集
*/
Status StrAssign(HString* T, const char* chars);
/*
* 比较
*
* 比较串S和串T返回比较结果。
*
*【注】
* 该操作属于最小操作子集
*/
Status StrCompare(HString S, HString T);
/*
* 复制
*
* 将串S复制到串T。
*/
Status StrCopy(HString* T, HString S);
#endif

View File

@@ -0,0 +1,7 @@
Êé ºÅ Êé Ãû
005 Computer Data Structures
010 Introduction to Data Structures
023 Fundamentals of Data Structures
034 The Design and Analysis of Computer Algorithms
050 Introduction to Numerical Analysis
067 Numerical Analysis

View File

@@ -0,0 +1,27 @@
#include <stdio.h> // 提供system原型
#include "WordList.h" //**▲04 串**//
int main(int argc, char** argv) {
FILE* fp;
char line[MaxLineLen];
char* bookinfo = "TestData.txt"; // 书目文件名
char* bookidx = "BookIdx.txt"; // 关键词索引文件名
// 创建索引表
Main(bookinfo, bookidx);
// 显示索引表到屏幕
if((fp = fopen(bookidx, "r"))!=NULL) {
printf("---------索引表生成功!---------\n\n");
while(feof(fp)==FALSE) {
fgets(line, MaxLineLen, fp);
printf("%s", line);
}
} else {
printf("---------未发现索引表!---------\n");
}
return 0;
}

View File

@@ -0,0 +1,308 @@
/*===========================================
* 索引表
*
* 包含算法: 4.9、4.10、4.11、4.12、4.13、4.14
============================================*/
#include "WordList.h" //**▲04 串**//
/*
* ████████ 算法4.9 ████████
*
* 从文件bookinfo中读取书目信息并依此创建相应的关键词索引表然后将索引表写入文件bookidx。
*/
void Main(char* bookinfo, char* bookidx) {
FILE* f, * g;
char head[MaxLineLen]; // 书目的表头信息(未使用)
IdxListType idxlist; // 关键词索引表
ElemType bno; // 书号
int count;
// 以“只读”模式打开书目文件
if((f = fopen(bookinfo, "r")) != NULL) {
// 以“只写”模式打开索引文件
if((g = fopen(bookidx, "w")) != NULL) {
// 初始化索引表
InitIdxList(&idxlist);
// 跳过文件第一行
fgets(head, MaxLineLen, f);
count = 0;
// 如果未到文件结尾,则一直读文件
while(feof(f) == FALSE && count < MaxBookNum) {
// 从文件f读入一个书目信息到书目缓冲区gBuf
GetLine(f);
// 从gBuf提取关键词到词表gWdList书号存入bno
ExtractKeyWord(&bno);
// 将书号及对应的关键词插入索引表idxlist
InsIdxList(&idxlist, bno);
count++;
}
// 向文件g中写入索引表数据
PutText(g, idxlist);
fclose(g);
}
fclose(f);
}
}
/*
* 初始化索引表
*
*【注】
* 教材中将索引表表头置为空串,但这里设定了一个有意义的表头
*/
void InitIdxList(IdxListType* idxlist) {
// 索引表的表头信息
char* chars = "关键词 书号索引";
IdxTermType e;
// 初始化表头信息
StrAssign(&(e.key), chars);
// 初始化书号索引链表
InitList(&(e.bnolist));
(*idxlist).item[0] = e;
// 表头为第0条信息
(*idxlist).last = 0;
}
/*
* 从文件f中读取一条书目信息存入书目缓冲区gBuf。
*/
void GetLine(FILE* f) {
// 读取一行数据存入缓冲区gBuf
fgets(gBuf, MaxLineLen, f);
}
/*
* 从缓冲区gBuf中提取书名关键词到词表gWdList书号存入bno。
*/
void ExtractKeyWord(ElemType* bno) {
char delim[] = {'-', ' ', '\r', '\n', '\t'}; // 分隔符
char* title; // 书名
char* token; // 从书名中分解出的关键词
// 分解书目字符串将书号存入bno(十进制)并用title指向剩下的字符串(书名)
*bno = (int) strtol(gBuf, &title, 10);
// 将书名的由大写变小写
strlwr(title);
// 清空关键词词表
gWdList.last = 0;
// 分解关键词
for(token = strtok(title, delim); token != NULL; token = strtok(NULL, delim)) {
// 如果该关键词是常用词,则忽略它
if(isCommonWords(token) == TRUE) {
continue;
}
// 记下从书名中提取的关键词
gWdList.item[gWdList.last++] = token;
}
}
/*
* ████████ 算法4.10 ████████
*
* 将书号bno对应的书名中的关键词按词典顺序插入到索引表idxlist。
*/
Status InsIdxList(IdxListType* idxlist, int bno) {
int i, j;
Boolean boo;
HString wd;
if(gWdList.last <= 0) {
return ERROR;
}
// 遍历书号bno对应的书名中的所有关键词
for(i = 0; i < gWdList.last; i++) {
// 获取待插入的关键词
GetWord(i, &wd);
// 判断该关键词是否已经位于索引表中
j = Locate(*idxlist, wd, &boo);
// 如果该关键词不在索引表中,则需要插入关键词
if(boo == FALSE) {
// 将关键词wd插入到索引表
InsertNewKey(idxlist, j, wd);
}
// 在关键词已存在的情形下,插入书号
if(!InsertBook(idxlist, j, bno)) {
return ERROR;
}
}
return OK;
}
/*
* ████████ 算法4.11 ████████
*
* 用wd返回词表gWdList中第i个关键词。
*/
void GetWord(int i, HString* wd) {
if(i < 0 || i > gWdList.last - 1) {
StrAssign(wd, "");
} else {
StrAssign(wd, gWdList.item[i]);
}
}
/*
* ████████ 算法4.12 ████████
*
* 查询在索引表idxlist中是否存在与wd相等的关键词。
* 若存在则返回wd在词表中的位置并置b为TRUE。
* 若不存在则返回wd应插入的位置并置b为FALSE。
*/
int Locate(IdxListType idxlist, HString wd, Boolean* b) {
int i, m = -1;
/*
* 在索引表idxlist中查找关键词wd是否存在
* 注0号单元存储了表头信息
*/
for(i = idxlist.last; i > 0 && (m = StrCompare(idxlist.item[i].key, wd)) > 0; i--) {
}
// 如果找到了关键词wd
if(m == 0) {
*b = TRUE;
return i;
} else {
*b = FALSE;
return i + 1;
}
}
/*
* ████████ 算法4.13 ████████
*
* 在索引表的索引i(>=0)处插入关键词wd并初始化书号索引的链表为空表。
*/
void InsertNewKey(IdxListType* idxlist, int i, HString wd) {
int j;
/*
* 索引项后移
* 注0号单元存储了表头信息
*/
for(j = (*idxlist).last; j >= i; j--) {
(*idxlist).item[j + 1] = (*idxlist).item[j];
}
// 插入索引项
StrCopy(&((*idxlist).item[i].key), wd);
// 初始化书号索引链表
InitList(&((*idxlist).item[i].bnolist));
// 索引数目增一
(*idxlist).last++;
}
/*
* ████████ 算法4.14 ████████
*
* 为索引表在索引i(>0)处的关键词插入书号。
*/
Status InsertBook(IdxListType* idxlist, int i, ElemType bno) {
Link p;
// 内存分配失败
if(MakeNode(&p, bno) == FALSE) {
return ERROR;
}
// 插入新的书号索引
Append(&((*idxlist).item[i].bnolist), p);
return OK;
}
/*
* 将生成的索引表idxlist输出到文件g。
*/
void PutText(FILE* g, IdxListType idxlist) {
int i, j, m, n;
Link p;
HString S;
ELinkList L;
if(idxlist.last <= 0) {
return;
}
// 先输出表头信息
S = idxlist.item[0].key;
for(m = 0; m < S.length; m++) {
fprintf(g, "%c", S.ch[m]);
}
fprintf(g, "\n");
// 输出索引信息
for(i = 1; i <= idxlist.last; i++) {
// 1.输出关键词
S = idxlist.item[i].key;
for(m = 0; m < S.length; m++) {
fprintf(g, "%c", S.ch[m]);
}
// 2.输出空格
for(j = 1; j <= 18 - idxlist.item[i].key.length; j++) {
fprintf(g, " ");
}
// 3.输出书号索引
L = idxlist.item[i].bnolist;
for(n = 1, p = L.head->next; n <= L.len; n++) {
fprintf(g, "%03d", p->data);
p = p->next;
if(p) {
fprintf(g, "");
}
}
// 4.输出换行
fprintf(g, "\n");
}
}
// 判断str是否为常用词
static Status isCommonWords(char* str) {
int i;
// 常用词词表,这些词汇会被排除在关键词之外
WordListType words = {{"a", "an", "the", "of", "and", "is", "to", "as", "in", "for"}, 10};
// 查询常用词词表
for(i = 0; i < words.last; i++) {
// 对两字符串进行忽略大小写的比较
if(strcmpi(str, words.item[i]) == 0) {
// 如果该字符串是常用词则返回TRUE
return TRUE;
}
}
return FALSE;
}

View File

@@ -0,0 +1,131 @@
[Project]
FileName=WordList.dev
Name=WordList
Type=1
Ver=2
ObjFiles=
Includes=
Libs=
PrivateResource=
ResourceIncludes=
MakeIncludes=
Compiler=
CppCompiler=
Linker=
IsCpp=0
Icon=
ExeOutput=
ObjectOutput=
LogOutput=
LogOutputEnabled=0
OverrideOutput=0
OverrideOutputName=
HostApplication=
UseCustomMakefile=0
CustomMakefile=
CommandLine=
Folders=
IncludeVersionInfo=0
SupportXPThemes=0
CompilerSet=1
CompilerSettings=0000000000000000001000000
UnitCount=8
[VersionInfo]
Major=1
Minor=0
Release=0
Build=0
LanguageID=1033
CharsetID=1252
CompanyName=
FileVersion=
FileDescription=Developed using the Dev-C++ IDE
InternalName=
LegalCopyright=
LegalTrademarks=
OriginalFilename=
ProductName=
ProductVersion=
AutoIncBuildNr=0
SyncProduct=1
[Unit7]
FileName=WordList-main.cpp
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit1]
FileName=ELinkList.cpp
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit3]
FileName=HString.cpp
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit5]
FileName=WordList.cpp
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit2]
FileName=ELinkList.h
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit4]
FileName=HString.h
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit6]
FileName=WordList.h
CompileCpp=0
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit8]
FileName=TestData.txt
Folder=
Compile=0
Link=0
Priority=1000
OverrideBuildCmd=0
BuildCmd=

View File

@@ -0,0 +1,130 @@
/*===========================================
* 索引表
*
* 包含算法: 4.9、4.10、4.11、4.12、4.13、4.14
============================================*/
#ifndef WORDLIST_H
#define WORDLIST_H
#include <stdio.h> // 提供fopen、fclose、feof、fgets原型
#include <stdlib.h> // 提供exit、strtol原型
#include <string.h> // 提供strlen、strcmpi、strlwr原型
#include "Status.h" //**▲01 绪论**//
#include "ELinkList.h" //**▲02 线性表**//
#include "HString.h" //**▲04 串**//
/* 宏定义 */
#define MaxBookNum 1000 // 允许的最大书目数假设最多只对1000本书建索引表
#define MaxKeyNum 2500 // 索引表最大容量(索引项数量的最大值)
#define MaxLineLen 500 // 书目串(行)的最大长度
#define MaxWordNum 100 // 词表的最大容量
/* 类型定义 */
// 布尔类型
typedef Status Boolean;
// 词表类型(顺序表)
typedef struct {
char* item[MaxWordNum]; // 词表集合
int last; // 词表的长度
} WordListType;
// 索引项类型(索引表的行)
typedef struct {
HString key; // 关键词
ELinkList bnolist; // 存放书号索引的链表
} IdxTermType;
/*
* 索引表类型
*
*【注】
* 0号单元存储表头信息这一点与教材有所不同
*/
typedef struct {
IdxTermType item[MaxKeyNum + 1]; // 索引项的集合
int last; // 索引表中包含的索引项数目
} IdxListType;
/* 全局变量变量名称前面都加了g标记 */
// 书目缓存区
static char gBuf[MaxLineLen];
// 关键词词表(普通词表),已经排除了常用词
static WordListType gWdList;
/*
* ████████ 算法4.9 ████████
*
* 从文件bookinfo中读取书目信息并依此创建相应的关键词索引表然后将索引表写入文件bookidx。
*/
void Main(char* bookinfo, char* bookidx);
/*
* 初始化索引表
*
*【注】
* 教材中将索引表表头置为空串,但这里设定了一个有意义的表头
*/
void InitIdxList(IdxListType* idxlist);
/*
* 从文件f中读取一条书目信息存入书目缓冲区gBuf。
*/
void GetLine(FILE* f);
/*
* 从缓冲区gBuf中提取书名关键词到词表gWdList书号存入bno。
*/
void ExtractKeyWord(ElemType* bno);
/*
* ████████ 算法4.10 ████████
*
* 将书号bno对应的书名关键词按词典顺序插入到索引表idxlist。
*/
Status InsIdxList(IdxListType* idxlist, int bno);
/*
* ████████ 算法4.11 ████████
*
* 用wd返回词表gWdList中第i个关键词。
*/
void GetWord(int i, HString* wd);
/*
* ████████ 算法4.12 ████████
*
* 查询在索引表idxlist中是否存在与wd相等的关键词。
* 若存在则返回wd在词表中的位置并置b为TRUE。
* 若不存在则返回wd应插入的位置并置b为FALSE。
*/
int Locate(IdxListType idxlist, HString wd, Boolean* b);
/*
* ████████ 算法4.13 ████████
*
* 在索引表的索引i(>=0)处插入关键词wd并初始化书号索引的链表为空表。
*/
void InsertNewKey(IdxListType* idxlist, int i, HString wd);
/*
* ████████ 算法4.14 ████████
*
* 为索引表在索引i(>0)处的关键词插入书号。
*/
Status InsertBook(IdxListType* idxlist, int i, int bno);
/*
* 将生成的索引表idxlist输出到文件g。
*/
void PutText(FILE* g, IdxListType idxlist);
// 判断str是否为常用词
static Status isCommonWords(char* str);
#endif

View File

@@ -91,29 +91,31 @@ Commit信息中的`emoji`参考来源:
## 附:教材源码目录
| 章 | 节 | 内容 | 包含算法 | 备注 |
| :--------- | :---------- | :----------- | :-------------------- | :------------------- |
| 01 绪论 | Status | | | 定义一些共享常量和函数 |
| 02 线性表 | SqList | 顺序表 | 2.3、2.4、2.5、2.6 | 线性表的顺序存储结构 |
| | Union | A=AB | 2.1 | |
| | MergeSqList | C=A+B | 2.2、2.7 | 归并顺序表 |
| | LinkList | 链表 | 2.8、2.9、2.10、2.11 | 线性表的链式存储结构 |
| | MergeList | C=A+B | 2.12 | 归并链表 |
| | SLinkList | 静态链表 | 2.13、2.14、2.15、2.16 | |
| | Difference | (A-B)(B-A) | 2.17 | |
| | DuLinkList | 双向循环链表 | 2.18、2.19 | |
| | ELinkList | 扩展的线性链表 | 2.20 | |
| | MergeEList | C=A+B | 2.21 | 归并扩展的线性链表 |
| | Polynomial | 一元多项式 | 2.22、2.23 | |
| 03 栈和队列 | SqStack | 栈 | | 顺序存储结构 |
| | Conversion | 进制转换 | 3.1 | 栈的应用 |
| | LineEdit | 行编辑程序 | 3.2 | 栈的应用 |
| | Maze | 迷宫寻路 | 3.3 | 栈的应用 |
| | Expression | 表达式求值 | 3.4 | 栈的应用 |
| | Hanoi | 汉诺塔 | 3.5 | 递归 |
| | LinkQueue | 链列 | | 链式存储结构 |
| | SqQueue | 顺序队列 | | 循环队列,顺序存储结构 |
| | BankQueuing | 模拟银行排队 | 3.6、3.7 | 队列的应用 |
| 04 串 | SString | 顺序串 | 4.1、4.2、4.3、4.5 | 顺序存储 |
| | HString | 堆串 | 4.4 | 顺序存储,动态分配内存 |
| | LString | 块链串 | | 顺序存储+链式存储 |
| 章 | 节 | 内容 | 包含算法 | 备注 |
| :--------- | :---------- | :----------- | :------------------------------- | :------------------- |
| 01 绪论 | Status | | | 定义一些共享常量和函数 |
| 02 线性表 | SqList | 顺序表 | 2.3、2.4、2.5、2.6 | 线性表的顺序存储结构 |
| | Union | A=AB | 2.1 | |
| | MergeSqList | C=A+B | 2.2、2.7 | 归并顺序表 |
| | LinkList | 链表 | 2.8、2.9、2.10、2.11 | 线性表的链式存储结构 |
| | MergeList | C=A+B | 2.12 | 归并链表 |
| | SLinkList | 静态链表 | 2.13、2.14、2.15、2.16 | |
| | Difference | (A-B)(B-A) | 2.17 | |
| | DuLinkList | 双向循环链表 | 2.18、2.19 | |
| | ELinkList | 扩展的线性链表 | 2.20 | |
| | MergeEList | C=A+B | 2.21 | 归并扩展的线性链表 |
| | Polynomial | 一元多项式 | 2.22、2.23 | |
| 03 栈和队列 | SqStack | 栈 | | 顺序存储结构 |
| | Conversion | 进制转换 | 3.1 | 栈的应用 |
| | LineEdit | 行编辑程序 | 3.2 | 栈的应用 |
| | Maze | 迷宫寻路 | 3.3 | 栈的应用 |
| | Expression | 表达式求值 | 3.4 | 栈的应用 |
| | Hanoi | 汉诺塔 | 3.5 | 递归 |
| | LinkQueue | 链列 | | 链式存储结构 |
| | SqQueue | 顺序队列 | | 循环队列,顺序存储结构 |
| | BankQueuing | 模拟银行排队 | 3.6、3.7 | 队列的应用 |
| 04 串 | SString | 顺序串 | 4.1、4.2、4.3、4.5 | 顺序存储 |
| | HString | 堆串 | 4.4 | 顺序存储,动态分配内存 |
| | LString | 块链串 | | 顺序存储+链式存储 |
| | KMP | KMP算法 | 4.6、4.7、4.8 | 字符串匹配算法 |
| | WordList | 关键词索引 | 4.9、4.10、4.11、4.12、4.13、4.14 | 堆串和线性表的应用 |

View File

@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{5D2A1F0B-F177-43A9-873A-56C7E08E6987}</ProjectGuid>
<RootNamespace>My0404_KMP</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<IncludePath>$(SolutionDir)\..\Status;$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<AdditionalDependencies>$(SolutionDir)\..\Status\Status.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="KMP.h" />
<ClInclude Include="SString.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="KMP-main.c" />
<ClCompile Include="KMP.c" />
<ClCompile Include="SString.c" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="源文件">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="头文件">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="资源文件">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="KMP.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="SString.h">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="KMP.c">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="KMP-main.c">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="SString.c">
<Filter>源文件</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
</Project>

View File

@@ -0,0 +1,64 @@
#include "KMP.h" //**▲04 串**//
// 测试函数,打印字符串
void PrintElem(SString S);
int main(int argc, char** argv) {
char* s = "abaaabcaabaabcacabaabcaabaabcac";
char* t = "abaabcac";
SString S, T;
int* next; // 模式串的next函数值
int* nextval; // 模式串的nextval函数值
int pos; // 匹配起点
int i, j;
StrAssign(S, s); // 初始化主串
printf("S = ");
PrintElem(S);
StrAssign(T, t); // 初始化模式串
printf("T = ");
PrintElem(T);
// 注next数组和nextval数组的0号单元是弃用的从1号单元开始存储有效数据
next = (int*) malloc((T[0] + 1) * sizeof(int));
nextval = (int*) malloc((T[0] + 1) * sizeof(int));
get_next(T, next); // 算法4.7
get_nextval(T, nextval); // 算法4.8即算法4.7的改进版
printf("next : ");
for(i = 1; i <= T[0]; i++) {
printf("%d", next[i]);
}
printf("\n");
printf("nextval : ");
for(i = 1; i <= T[0]; i++) {
printf("%d", nextval[i]);
}
printf("\n");
pos = 1;
i = Index_KMP(S, T, pos, next);
j = Index_KMP(S, T, pos, nextval);
printf("从%d个字符起T 在 S 中第一次匹配成功的位置为 %d\n", pos, i);
printf("从%d个字符起T 在 S 中第一次匹配成功的位置为 %d\n", pos, j);
return 0;
}
// 测试函数,打印字符串
void PrintElem(SString S) {
int i;
for(i = 1; i <= S[0]; i++) {
printf("%c", S[i]);
}
printf("\n");
}

View File

@@ -0,0 +1,106 @@
/*=======================
* KMP算法
*
* 包含算法: 4.6、4.7、4.8
========================*/
#include "KMP.h" //**▲04 串**//
/*
* ████████ 算法4.6 ████████
*
* 查找
*
* 从pos处开始搜索模式串T在主串S中首次出现的位置如果不存在则返回0。
* 如果查找成功,返回匹配的位置。
*
*【注】
* 1.该实现用到了KMP算法是一种比较高效的字符串匹配方式
* 2.教材中没有next参数
*/
int Index_KMP(SString S, SString T, int pos, int next[]) {
int i = pos;
int j = 1;
if(pos < 1) {
return 0;
}
// 比较字符串
while(i <= S[0] && j <= T[0]) {
/*
* 两种情形:
* 1.在模式串的第一个字符处就失配
* 2.主串和模式串处的字符相等
*/
if(j == 0 || S[i] == T[j]) {
i++;
j++;
} else {
// 失配时回到前一个适当的位置
j = next[j];
}
}
if(j > T[0]) {
// 匹配成功,返回匹配位置
return i - T[0];
} else {
// 匹配失败
return 0;
}
}
/*
* ████████ 算法4.7 ████████
*
* 计算模式串的“失配数组”用于KMP算法。
*/
void get_next(SString T, int next[]) {
int i = 1;
int j = 0;
// 模式串第一个字符处失配时,模式串需要从头比较,主串需要前进到下一个位置比较
next[1] = 0;
// 遍历模式串上的字符
while(i < T[0]) {
if(j == 0 || T[i] == T[j]) {
i++;
j++;
next[i] = j;
} else {
j = next[j];
}
}
}
/*
* ████████ 算法4.8 ████████
*
* 计算模式串的“失配数组”用于KMP算法。
* 这是一个优化后的版本效率较算法4.7有所提高。
*/
void get_nextval(SString T, int nextval[]) {
int i = 1;
int j = 0;
// 模式串第一个字符处失配时,模式串需要从头比较,主串需要前进到下一个位置比较
nextval[1] = 0;
// 遍历模式串上的字符
while(i < T[0]) {
if(j==0 || T[i] == T[j]) {
i++;
j++;
if(T[i] != T[j]) {
nextval[i] = j;
} else {
nextval[i] = nextval[j];
}
} else {
j = nextval[j];
}
}
}

View File

@@ -0,0 +1,43 @@
/*=======================
* KMP算法
*
* 包含算法: 4.6、4.7、4.8
========================*/
#ifndef KMP_H
#define KMP_H
#include <stdio.h>
#include <stdlib.h>
#include "SString.h" //**▲04 串**//
/*
* ████████ 算法4.6 ████████
*
* 查找
*
* 从pos处开始搜索模式串T在主串S中首次出现的位置如果不存在则返回0。
* 如果查找成功,返回匹配的位置。
*
*【注】
* 1.该实现用到了KMP算法是一种比较高效的字符串匹配方式
* 2.教材中没有next参数
*/
int Index_KMP(SString S, SString T, int pos, int next[]);
/*
* ████████ 算法4.7 ████████
*
* 计算模式串的“失配数组”用于KMP算法。
*/
void get_next(SString T, int next[]);
/*
* ████████ 算法4.8 ████████
*
* 计算模式串的“失配数组”用于KMP算法。
* 这是一个优化后的版本效率较算法4.7有所提高。
*/
void get_nextval(SString T, int nextval[]);
#endif

View File

@@ -0,0 +1,33 @@
/*=============================
* 串的定长顺序存储表示(顺序串)
*
* 包含算法: 4.1、4.2、4.3、4.5
==============================*/
#include "SString.h" //**▲04 串**//
/*
* 初始化
*
* 构造一个值为chars的串T。
*
*【注】
* 该操作属于最小操作子集
*/
Status StrAssign(SString T, const char* chars) {
int i, len;
len = (int) strlen(chars);
// chars过长
if(len > MAXSTRLEN) {
return ERROR;
}
T[0] = len;
for(i = 1; i <= len; i++) {
T[i] = chars[i - 1];
}
return OK;
}

View File

@@ -0,0 +1,36 @@
/*=============================
* 串的定长顺序存储表示(顺序串)
*
* 包含算法: 4.1、4.2、4.3、4.5
==============================*/
#ifndef SSTRING_H
#define SSTRING_H
#include <stdio.h>
#include <string.h> // 提供strlen原型
#include "Status.h" //**▲01 绪论**//
/* 宏定义 */
#define MAXSTRLEN 255 // 顺序串的最大串长
/*
* 串的顺序存储类型定义
*
* 注有效元素从SString的1号单元开始存储
* SString的0号单元用来存储其长度
*/
typedef unsigned char SString[MAXSTRLEN + 1]; // 0号单元存放串的长度
/*
* 初始化
*
* 构造一个值为chars的串T。
*
*【注】
* 该操作属于最小操作子集
*/
Status StrAssign(SString T, const char* chars);
#endif

View File

@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{FA2740D7-2D96-4F57-AE79-FCDA5C13CA86}</ProjectGuid>
<RootNamespace>My0405_WordList</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<IncludePath>$(SolutionDir)\..\Status;$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<AdditionalDependencies>$(SolutionDir)\..\Status\Status.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="ELinkList.c" />
<ClCompile Include="HString.c" />
<ClCompile Include="WordList-main.c" />
<ClCompile Include="WordList.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="ELinkList.h" />
<ClInclude Include="HString.h" />
<ClInclude Include="WordList.h" />
</ItemGroup>
<ItemGroup>
<None Include="TestData.txt" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="源文件">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="头文件">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="资源文件">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="ELinkList.c">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="HString.c">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="WordList.c">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="WordList-main.c">
<Filter>源文件</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ELinkList.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="HString.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="WordList.h">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="TestData.txt">
<Filter>资源文件</Filter>
</None>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
</Project>

View File

@@ -0,0 +1,95 @@
/*=======================
* 扩展的单链表(线性链表)
*
* 包含算法: 2.20
========================*/
#include "ELinkList.h" //**▲02 线性表**//
/*━━━━━━━━━━━━━━━━━━━━━━ 内存操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 内存分配
*
* 为线性链表申请一个结点并存入指定的数据e。
*
*【备注】
* static修饰的含义是该函数仅限当前文件内使用
*/
Status MakeNode(Link* p, ElemType e) {
if(p == NULL) {
return ERROR;
}
// 申请空间
*p = (Link) malloc(sizeof(LNode));
if(*p == NULL) {
// 这里没有退出程序,而是返回错误提示
return ERROR;
}
(*p)->data = e;
(*p)->next = NULL;
return OK;
}
/*━━━━━━━━━━━━━━━━━━━━━━ 链表常规操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 初始化
*
* 初始化成功则返回OK否则返回ERROR。
*/
Status InitList(ELinkList* L) {
Link p;
if(L == NULL) {
return ERROR;
}
// 创建头结点
p = (Link) malloc(sizeof(LNode));
if(p == NULL) {
exit(OVERFLOW);
}
p->next = NULL;
// 只有头结点时,首位游标指向自身
(*L).head = (*L).tail = p;
(*L).len = 0;
return OK;
}
/*━━━━━━━━━━━━━━━━━━━━━━ 链表扩展操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 向尾部添加
*
* 将s所指的一串结点链接在链表L后面
*/
Status Append(ELinkList* L, Link s) {
int count;
if(L == NULL || (*L).head == NULL || s == NULL) {
return ERROR;
}
count = 0;
(*L).tail->next = s;
// 确定新的尾结点位置
while(s != NULL) {
(*L).tail = s;
s = s->next;
count++;
}
(*L).len += count;
return OK;
}

View File

@@ -0,0 +1,75 @@
/*=======================
* 扩展的单链表(线性链表)
*
* 包含算法: 2.20
========================*/
#ifndef ELINKLIST_H
#define ELINKLIST_H
#include <stdio.h>
#include <stdlib.h> // 提供malloc、realloc、free、exit原型
#include <limits.h> // 提供一些极限常量
#include "Status.h" //**▲01 绪论**//
/*
* ████ 注意 ████
*
* 教材中的线性链表命名为LinkList
* 这里为了与单链表区分故将其命名为ELinkList。
* 线性链表可以理解成对普通链表的一种扩展。
*/
/* 线性链表元素类型定义 */
typedef int ElemType;
/*
* 线性链表结构
*
* 注:这里的线性链表存在头结点
*/
typedef struct LNode {
ElemType data;
struct LNode* next;
} LNode, * Link, * Position;
/* 维护线性链表头尾指针及长度信息 */
typedef struct {
Link head, tail; // 分别指向线性链表中的头结点和尾结点
int len; // 指示线性链表中数据元素的个数
} ELinkList;
/*━━━━━━━━━━━━━━━━━━━━━━ 内存操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 内存分配
*
* 为线性链表申请一个结点并存入指定的数据e。
*
*【备注】
* static修饰的含义是该函数仅限当前文件内使用
*/
Status MakeNode(Link* p, ElemType e);
/*━━━━━━━━━━━━━━━━━━━━━━ 链表常规操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 初始化
*
* 初始化成功则返回OK否则返回ERROR。
*/
Status InitList(ELinkList* L);
/*━━━━━━━━━━━━━━━━━━━━━━ 链表扩展操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 向尾部添加
*
* 将s所指的一串结点链接在链表L后面
*/
Status Append(ELinkList* L, Link s);
#endif

View File

@@ -0,0 +1,95 @@
/*=========================
* 串的堆分配存储表示(堆串)
*
* 包含算法: 4.4
==========================*/
#include "HString.h" //**▲04 串**//
/*
* 初始化
*
* 构造一个值为chars的串T。
*
*【注】
* 该操作属于最小操作子集
*/
Status StrAssign(HString* T, const char* chars) {
int i, j;
// 求chars的长度
i = (int) strlen(chars);
// 没有有效元素
if(i == 0) {
(*T).ch = NULL;
(*T).length = 0;
return OK;
}
// 存在有效元素时,需要分配存储空间
(*T).ch = (char*) malloc(i * sizeof(char));
if(!((*T).ch)) {
exit(OVERFLOW);
}
for(j = 0; j < i; j++) {
(*T).ch[j] = chars[j];
}
(*T).length = i;
return OK;
}
/*
* 比较
*
* 比较串S和串T返回比较结果。
*
*【注】
* 该操作属于最小操作子集
*/
Status StrCompare(HString S, HString T) {
int i;
for(i = 0; i < S.length && i < T.length; i++) {
// 遇到不同的字符时,比较其大小
if(S.ch[i] != T.ch[i]) {
return S.ch[i] - T.ch[i];
}
}
return S.length - T.length;
}
/*
* 复制
*
* 将串S复制到串T。
*/
Status StrCopy(HString* T, HString S) {
int i;
if(S.length == 0) {
(*T).ch = NULL;
(*T).length = 0;
} else {
// 分配空间
(*T).ch = (char*) malloc(S.length * sizeof(char));
if(!(*T).ch) {
exit(OVERFLOW);
}
// 复制元素
for(i = 0; i < S.length; i++) {
(*T).ch[i] = S.ch[i];
}
// 复制长度信息
(*T).length = S.length;
}
return OK;
}

View File

@@ -0,0 +1,53 @@
/*=========================
* 串的堆分配存储表示(堆串)
*
* 包含算法: 4.4
==========================*/
#ifndef HSTRING
#define HSTRING
#include <stdio.h>
#include <stdlib.h> // 提供malloc、realloc、free、exit原型
#include <string.h> // 提供strlen原型
#include "Status.h" //**▲01 绪论**//
/*
* 串的堆存储表示
*
* 注有效元素从ch的0号单元开始存储
*/
typedef struct {
char* ch; // 若是非空串则按串长分配存储区否则ch为NULL
int length;
} HString;
/*
* 初始化
*
* 构造一个值为chars的串T。
*
*【注】
* 该操作属于最小操作子集
*/
Status StrAssign(HString* T, const char* chars);
/*
* 比较
*
* 比较串S和串T返回比较结果。
*
*【注】
* 该操作属于最小操作子集
*/
Status StrCompare(HString S, HString T);
/*
* 复制
*
* 将串S复制到串T。
*/
Status StrCopy(HString* T, HString S);
#endif

View File

@@ -0,0 +1,7 @@
Êé ºÅ Êé Ãû
005 Computer Data Structures
010 Introduction to Data Structures
023 Fundamentals of Data Structures
034 The Design and Analysis of Computer Algorithms
050 Introduction to Numerical Analysis
067 Numerical Analysis

View File

@@ -0,0 +1,27 @@
#include <stdio.h> // 提供system原型
#include "WordList.h" //**▲04 串**//
int main(int argc, char** argv) {
FILE* fp;
char line[MaxLineLen];
char* bookinfo = "TestData.txt"; // 书目文件名
char* bookidx = "BookIdx.txt"; // 关键词索引文件名
// 创建索引表
Main(bookinfo, bookidx);
// 显示索引表到屏幕
if((fp = fopen(bookidx, "r"))!=NULL) {
printf("---------索引表生成功!---------\n\n");
while(feof(fp)==FALSE) {
fgets(line, MaxLineLen, fp);
printf("%s", line);
}
} else {
printf("---------未发现索引表!---------\n");
}
return 0;
}

View File

@@ -0,0 +1,308 @@
/*===========================================
* 索引表
*
* 包含算法: 4.9、4.10、4.11、4.12、4.13、4.14
============================================*/
#include "WordList.h" //**▲04 串**//
/*
* ████████ 算法4.9 ████████
*
* 从文件bookinfo中读取书目信息并依此创建相应的关键词索引表然后将索引表写入文件bookidx。
*/
void Main(char* bookinfo, char* bookidx) {
FILE* f, * g;
char head[MaxLineLen]; // 书目的表头信息(未使用)
IdxListType idxlist; // 关键词索引表
ElemType bno; // 书号
int count;
// 以“只读”模式打开书目文件
if((f = fopen(bookinfo, "r")) != NULL) {
// 以“只写”模式打开索引文件
if((g = fopen(bookidx, "w")) != NULL) {
// 初始化索引表
InitIdxList(&idxlist);
// 跳过文件第一行
fgets(head, MaxLineLen, f);
count = 0;
// 如果未到文件结尾,则一直读文件
while(feof(f) == FALSE && count < MaxBookNum) {
// 从文件f读入一个书目信息到书目缓冲区gBuf
GetLine(f);
// 从gBuf提取关键词到词表gWdList书号存入bno
ExtractKeyWord(&bno);
// 将书号及对应的关键词插入索引表idxlist
InsIdxList(&idxlist, bno);
count++;
}
// 向文件g中写入索引表数据
PutText(g, idxlist);
fclose(g);
}
fclose(f);
}
}
/*
* 初始化索引表
*
*【注】
* 教材中将索引表表头置为空串,但这里设定了一个有意义的表头
*/
void InitIdxList(IdxListType* idxlist) {
// 索引表的表头信息
char* chars = "关键词 书号索引";
IdxTermType e;
// 初始化表头信息
StrAssign(&(e.key), chars);
// 初始化书号索引链表
InitList(&(e.bnolist));
(*idxlist).item[0] = e;
// 表头为第0条信息
(*idxlist).last = 0;
}
/*
* 从文件f中读取一条书目信息存入书目缓冲区gBuf。
*/
void GetLine(FILE* f) {
// 读取一行数据存入缓冲区gBuf
fgets(gBuf, MaxLineLen, f);
}
/*
* 从缓冲区gBuf中提取书名关键词到词表gWdList书号存入bno。
*/
void ExtractKeyWord(ElemType* bno) {
char delim[] = {'-', ' ', '\r', '\n', '\t'}; // 分隔符
char* title; // 书名
char* token; // 从书名中分解出的关键词
// 分解书目字符串将书号存入bno(十进制)并用title指向剩下的字符串(书名)
*bno = (int) strtol(gBuf, &title, 10);
// 将书名的由大写变小写
strlwr(title);
// 清空关键词词表
gWdList.last = 0;
// 分解关键词
for(token = strtok(title, delim); token != NULL; token = strtok(NULL, delim)) {
// 如果该关键词是常用词,则忽略它
if(isCommonWords(token) == TRUE) {
continue;
}
// 记下从书名中提取的关键词
gWdList.item[gWdList.last++] = token;
}
}
/*
* ████████ 算法4.10 ████████
*
* 将书号bno对应的书名中的关键词按词典顺序插入到索引表idxlist。
*/
Status InsIdxList(IdxListType* idxlist, int bno) {
int i, j;
Boolean boo;
HString wd;
if(gWdList.last <= 0) {
return ERROR;
}
// 遍历书号bno对应的书名中的所有关键词
for(i = 0; i < gWdList.last; i++) {
// 获取待插入的关键词
GetWord(i, &wd);
// 判断该关键词是否已经位于索引表中
j = Locate(*idxlist, wd, &boo);
// 如果该关键词不在索引表中,则需要插入关键词
if(boo == FALSE) {
// 将关键词wd插入到索引表
InsertNewKey(idxlist, j, wd);
}
// 在关键词已存在的情形下,插入书号
if(!InsertBook(idxlist, j, bno)) {
return ERROR;
}
}
return OK;
}
/*
* ████████ 算法4.11 ████████
*
* 用wd返回词表gWdList中第i个关键词。
*/
void GetWord(int i, HString* wd) {
if(i < 0 || i > gWdList.last - 1) {
StrAssign(wd, "");
} else {
StrAssign(wd, gWdList.item[i]);
}
}
/*
* ████████ 算法4.12 ████████
*
* 查询在索引表idxlist中是否存在与wd相等的关键词。
* 若存在则返回wd在词表中的位置并置b为TRUE。
* 若不存在则返回wd应插入的位置并置b为FALSE。
*/
int Locate(IdxListType idxlist, HString wd, Boolean* b) {
int i, m = -1;
/*
* 在索引表idxlist中查找关键词wd是否存在
* 注0号单元存储了表头信息
*/
for(i = idxlist.last; i > 0 && (m = StrCompare(idxlist.item[i].key, wd)) > 0; i--) {
}
// 如果找到了关键词wd
if(m == 0) {
*b = TRUE;
return i;
} else {
*b = FALSE;
return i + 1;
}
}
/*
* ████████ 算法4.13 ████████
*
* 在索引表的索引i(>=0)处插入关键词wd并初始化书号索引的链表为空表。
*/
void InsertNewKey(IdxListType* idxlist, int i, HString wd) {
int j;
/*
* 索引项后移
* 注0号单元存储了表头信息
*/
for(j = (*idxlist).last; j >= i; j--) {
(*idxlist).item[j + 1] = (*idxlist).item[j];
}
// 插入索引项
StrCopy(&((*idxlist).item[i].key), wd);
// 初始化书号索引链表
InitList(&((*idxlist).item[i].bnolist));
// 索引数目增一
(*idxlist).last++;
}
/*
* ████████ 算法4.14 ████████
*
* 为索引表在索引i(>0)处的关键词插入书号。
*/
Status InsertBook(IdxListType* idxlist, int i, ElemType bno) {
Link p;
// 内存分配失败
if(MakeNode(&p, bno) == FALSE) {
return ERROR;
}
// 插入新的书号索引
Append(&((*idxlist).item[i].bnolist), p);
return OK;
}
/*
* 将生成的索引表idxlist输出到文件g。
*/
void PutText(FILE* g, IdxListType idxlist) {
int i, j, m, n;
Link p;
HString S;
ELinkList L;
if(idxlist.last <= 0) {
return;
}
// 先输出表头信息
S = idxlist.item[0].key;
for(m = 0; m < S.length; m++) {
fprintf(g, "%c", S.ch[m]);
}
fprintf(g, "\n");
// 输出索引信息
for(i = 1; i <= idxlist.last; i++) {
// 1.输出关键词
S = idxlist.item[i].key;
for(m = 0; m < S.length; m++) {
fprintf(g, "%c", S.ch[m]);
}
// 2.输出空格
for(j = 1; j <= 18 - idxlist.item[i].key.length; j++) {
fprintf(g, " ");
}
// 3.输出书号索引
L = idxlist.item[i].bnolist;
for(n = 1, p = L.head->next; n <= L.len; n++) {
fprintf(g, "%03d", p->data);
p = p->next;
if(p) {
fprintf(g, "");
}
}
// 4.输出换行
fprintf(g, "\n");
}
}
// 判断str是否为常用词
static Status isCommonWords(char* str) {
int i;
// 常用词词表,这些词汇会被排除在关键词之外
WordListType words = {{"a", "an", "the", "of", "and", "is", "to", "as", "in", "for"}, 10};
// 查询常用词词表
for(i = 0; i < words.last; i++) {
// 对两字符串进行忽略大小写的比较
if(strcmpi(str, words.item[i]) == 0) {
// 如果该字符串是常用词则返回TRUE
return TRUE;
}
}
return FALSE;
}

Some files were not shown because too many files have changed in this diff Show More