第四章学习小结
Posted lccgl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第四章学习小结相关的知识,希望对你有一定的参考价值。
一、本章内容小结
本章主要讲解的知识点有:串、数组、和广义表。其中包括串的定义、案例引入、存储结构、模式匹配算法;数组的类型定义以及存储方式;广义表的定义和存储结构。
串,即字符串。它是一种特殊的线性表,它的特殊性表现在其数据元素是一个字符,这也意味着它是一种内容受限的线性表。但是在日常应用中,字符串是一种使用率非常高的线性表,因此我觉得学好字符串相关的知识是非常重要的。现在我们的浏览器都有 “搜索” 的功能,只要我们输入我们想查找的信息,浏览器就能够帮我们找到我们想要的内容。如果我们使用过百度、搜狗等软件,我们就可以在查询结果中发现,我们输入的字符信息会被标注为红色,如下图所示:
这其中就运用了本章所学的知识:字符串匹配。而它的实现有两种算法:BF算法和KMP算法。这里以BF算法为例,它的实现思路就是通过主串与字串之间的字符一个一个比对,当比对失败时,子串就相对主串原本的位置整体后移一个字符,重新开始依次比对,以此类推。
接下来是数组,数组对于我们而言接触得非常多,尤其是我以前做过ACM中的基础题中,运用数组得次数非常多,比如行列式的计算、进行连续的加减乘除工作等。而这一章的数组知识主要讲述的是创建数组时压缩存储的方法,以此来节省内存空间。我们知道,数组常用顺序存储结构,并且数组一般不进行插入或删除工作,因此,查找元素会非常便捷。如果我们已知元素个数,那么我们用数组是非常节约内存的。
关于广义表,它其实就是线性表的推广,也被称为列表。我们可以发现,在描述广义表时,又会用到广义表的概念。因此,广义表的定义是一个递归的定义。广义表的数据元素可以有不同的结构,因此通常采用链式存储结构。
二、完成作业实践时的心得体会
(1)关于第四章作业
第四章作业的编程题的要求是:给定一个主串S(长度<=10^6)和一个模式T(长度<=10^5),要求在主串S中找出与模式T相匹配的子串,返回相匹配的子串中的第一个字符在主串S中出现的位置。
为了追求内存占用少,在这道题中,我定义了两个结构体
#define SLENMAX 1000000
#define TLENMAX 100000
typedef struct
{
char ch[SLENMAX + 1];
int len;
}SString;
typedef struct
{
char ch[TLENMAX + 1];
int len;
}TString;
我觉得这样操作可以有效节约内存空间,同时将主串S和模式T更好地区别开来,使程序拥有更好的可读性。
解决这道题的算法是KMP算法,相对于BF算法而言,KMP算法拥有更高的执行效率,其包含的函数如下:
void get_next(TString T, int next[])
{
int j,k;
j=0;
k=-1;
next[0]=-1;
while(j<T.len)
{
if(k==-1||T.ch[j]==T.ch[k])
{
j++;
k++;
if(T.ch[j]!=T.ch[k])
next[j]=k;
else
next[j]=next[k];
}
else
k=next[k];
}
}
int Index_KMP(SString S, TString T, int p)
{
int i, j;
int next[TLENMAX + 1];
get_next(T, next);
i = p-1;
j = 0;
while (i < S.len&&j < T.len)
{
if (j==-1||S.ch[i] == T.ch[j])
{
++i;
++j;
}
else
j = next[j];
}
if (j >= T.len)
return i - T.len + 1;
else
return -1;
}
调用这些函数可以直接得出字符串是否匹配以及它们出现的位置,int main()函数中的调用如下:
int main()
{
SString S; TString T;
cin.getline(S.ch, SLENMAX+1);
S.len = strlen(S.ch);
if (S.len<1 || S.len>SLENMAX)
return 0;
cin.getline(T.ch, TLENMAX+1);
T.len = strlen(T.ch);
if (T.len<1 || T.len>TLENMAX)
return 0;
if(S.len<T.len)
return 0;
if(S.len == 1)
{
if(S.ch[0]==T.ch[0])
{
cout << "1";
return 0;
}
cout << "0";
return 0;
}
int m = Index_KMP(S, T, 1);
if(m != -1)
{
cout << m;
}
else
cout << "0";
return 0;
}
在这道题目的实际操作中,如果主串包含的字符只有一个,我发现KMP算法试用效果不佳,因此我单独将主串中只有一个元素的情况独立出来操作。因为Index_KMP(S, T, 1)能够直接得出字符串是否匹配以及它们出现的位置,所以我们只需在主函数中创建一个int型变量,并将函数值赋给该变量即可得出答案。
(2)关于第四章实践
(a)稀疏矩阵
这道题我采用的方法是创建三个一元数组,分别存储稀疏矩阵的非零元素所在的行、列号和非零元素的值。
int m, n, N;
cin >> m >> n >> N;
if (N<1 || N>500)
return 0;
int* a = new int[N];
int* pos1 = new int[N];
int* pos2 = new int[N];
先输入了非零元素的个数N,我们就可以定义三个包含N个元素的一维数组,在这道题目中,我令a为非零元素的值,pos1为行号值,pos2为列号值
关于判断,我的方法是:
int i, j;
for (i = 0; i < N; i++)
{
cin >> pos1[i] >> pos2[i] >> a[i];
if (pos1[i]<1 || pos1[i]>m || pos2[i]<1 || pos2[i]>n || a[i] == 0)
{
delete a;
delete pos1;
delete pos2;
return 0;
}
}
for (i = 1; i < N; i++)
{
for (j = 0; j < i; j++)
{
if (pos1[i] == pos1[j] && pos2[i] == pos2[j])
{
delete a;
delete pos1;
delete pos2;
return 0;
}
}
}
第一个for循环用于判断行列值是否出界和是否输入的元素为0,第二个for循环用于判断是否存在元素位置重合的情况。剩下的事情我们只需输入我们要找的元素即可,即若在a数组中有元素a[x]与输入值匹配,则同时输出与该元素数组下标相同的行数组、列数组元素pos1[x]、pos2[x]。
int i, j;
for (i = 0; i < N; i++)
{
cin >> pos1[i] >> pos2[i] >> a[i];
if (pos1[i]<1 || pos1[i]>m || pos2[i]<1 || pos2[i]>n || a[i] == 0)
{
delete a;
delete pos1;
delete pos2;
return 0;
}
}
for (i = 1; i < N; i++)
{
for (j = 0; j < i; j++)
{
if (pos1[i] == pos1[j] && pos2[i] == pos2[j])
{
delete a;
delete pos1;
delete pos2;
return 0;
}
}
}
(b)Al核心代码
这题要求你实现一个简易版的 AI 英文问答程序,主要规则包括:
- 消除原文中多余空格:把相邻单词间的多个空格换成 1 个空格,把行首尾的空格全部删掉,把标点符号前面的空格删掉;
- 把原文中所有大写英文字母变成小写,除了 I;
- 把原文中所有独立的 I 和 me 换成 you;
- 把原文中所有的问号 ? 换成惊叹号 !;
- 把原文中所有独立的 can you 换成 I can —— 这里“独立”是指被空格或标点符号分隔开的单词;
- 在一行中输出替换后的句子作为 AI 的回答。
做这道题时,我和老师的思路不一样,我多创建了一个int型数组,用于判断该字符是否输出,代码如下:
int N;
cin >> N;
if (N<1 || N>10)
return 0;
char c[10][1001];
int i, j, len[10], k, x;
for (i = 0; i < N; i++)
{
if (!i)
getchar();
cin.getline(c[i], 1001);
len[i] = strlen(c[i]);
if (len[i]<1 || len[i]>1000)
return 0;
}
for (i = 0; i < N; i++)
{
for (j = 0; j < len[i]; j++)
{
if (c[i][j]<0 || c[i][j]>127)
return 0;
cout << c[i][j];
}
cout << endl;
int* a = new int[len[i]];
for (j = 0; j < len[i]; j++)
a[j] = 1;
我默认输入的所有字符都输出,即a数组中的各个元素值均为1。我们要想改变输出结果,只需改变c[i][j]对应的是否输出的int型数组元素a[j]即可,不改变c[i]字符串中的值(c[i]表示第i+1句对话)。实现结果的代码如下:
j = 0;
while (c[i][j] == ‘ ‘)
{
a[j] = 0;
j++;
}
while (j < len[i])
{
if (c[i][j] >= ‘A‘&&c[i][j] <= ‘Z‘&&c[i][j] != ‘I‘)
c[i][j] = c[i][j] + 32;
j++;
}
j = len[i] - 1;
while (c[i][j] == ‘ ‘)
{
a[j] = 0;
j--;
}
for (j = 0; j < len[i]; j++)
{
if (c[i][j] == ‘?‘)
c[i][j] = ‘!‘;
}
for (j = 0; j < len[i]; j++)
{
if (a[j] == 1)
{
if (c[i][j] == ‘ ‘)
{
k = j + 1;
while (c[i][k] == ‘ ‘)
{
a[k] = 0;
k++;
}
}
}
}
for (j = 0; j < len[i]; j++)
{
if (a[j] == 1)
{
if ((c[i][j] >= 33 && c[i][j] <= 47) || (c[i][j] >= 91 && c[i][j] <= 96) || (c[i][j] >= 123 && c[i][j] <= 126))
{
k = j - 1;
while (c[i][k] == ‘ ‘)
{
a[k] = 0;
k--;
}
}
}
}
for (j = 0; j < len[i]; j++)
{
if (a[j] == 1)
{
if ((j == 0 || (c[i][j - 1] >= 32 && c[i][j - 1] <= 47) || (c[i][j - 1] >= 91 && c[i][j - 1] <= 96) || (c[i][j - 1] >= 123 && c[i][j - 1] <= 126)) && (j == len[i] - 1 || (c[i][j + 1] >= 32 && c[i][j + 1] <= 47) || (c[i][j + 1] >= 91 && c[i][j + 1] <= 96) || (c[i][j + 1] >= 123 && c[i][j + 1] <= 126)) && (c[i][j] == ‘I‘))
a[j] = 2;
if ((j == 0 || (c[i][j - 1] >= 32 && c[i][j - 1] <= 47) || (c[i][j - 1] >= 91 && c[i][j - 1] <= 96) || (c[i][j - 1] >= 123 && c[i][j - 1] <= 126)) && (j + 1 == len[i] - 1 || (c[i][j + 2] >= 32 && c[i][j + 2] <= 47) || (c[i][j + 2] >= 91 && c[i][j + 2] <= 96) || (c[i][j + 2] >= 123 && c[i][j + 2] <= 126)) && (c[i][j] == ‘m‘) && (c[i][j + 1] == ‘e‘))
{
a[j] = 2;
a[j + 1] = 0;
}
}
}
for (j = 0; j < len[i]; j++)
{
if (a[j] == 1)
{
if ((c[i][j] == ‘c‘) && (c[i][j + 1] == ‘a‘) && (c[i][j + 2] == ‘n‘))
{
if (j == 0 || (c[i][j - 1] >= 32 && c[i][j - 1] <= 47) || (c[i][j - 1] >= 91 && c[i][j - 1] <= 96) || (c[i][j - 1] >= 123 && c[i][j - 1] <= 126))
{
k = j + 3;
while (c[i][k] == ‘ ‘)
k++;
if ((c[i][k] == ‘y‘) && (c[i][k + 1] == ‘o‘) && (c[i][k + 2] == ‘u‘) && ((c[i][k + 3] >= 32 && c[i][k + 3] <= 47) || (c[i][k + 3] >= 91 && c[i][k + 3] <= 96) || (c[i][k + 3] >= 123 && c[i][k + 3] <= 126)))
{
a[j] = 3;
for (x = j + 1; x < k + 3; x++)
a[x] = 0;
}
}
}
}
}
实现去空格的思路就是将这些空格的输出数组a[j]赋值为0;大写改小写、?变!直接操作字符;I变you、can you变I can的操作我将它们的首字母的a数组值分别赋值为2和3,而其余字符的值赋值为0,那么定义2和3这样的值好处就是直接通过if语句输出you、I can等单词。
for (j = 0; j < len[i]; j++)
{
if (a[j] == 1)
cout << c[i][j];
if (a[j] == 2)
cout << "you";
if (a[j] == 3)
cout << "I can";
}
以上就是我的主要解题思路。
三、参考的资料
本章的学习主要参考书本上的知识,主要是关于KMP算法,而Al核心代码一题我使用的是自己的解题思路。
四、待改进的问题
关于KMP算法了解的还是有疑惑的地方,希望能再抽出一点时间了解。
五、接下来的目标
多打题,改进思维方式,养成课前预习课后复习的好习惯。自己的想法未必不好,也许它是一种更优的思路,多讨论解题思路,就可以从中举一反三,解决更多的问题。
以上是关于第四章学习小结的主要内容,如果未能解决你的问题,请参考以下文章
数据结构:第四章学习小结
第四章学习小结
第四章学习小结
第四章学习小结
第四章学习小结
第四章学习小结