写在前面
本来刚开始看的链表,看着看着感觉以前都学过了,那就看下一章栈和队,这两个看着看着感觉以前也学过了,那就开下一章串吧。
但先把两个栈模拟队一说再说串。
主要内容
- 用两个栈模拟队。
2.KMP算法(初步)
起飞
两个栈模拟队
队的主要操作无非入队和出队,这里入队的操作和入栈倒是没有什么区别,就是出队的话是先出第一个而出栈则是先出最后一个。用两个栈表示出队,的话就是先把第一个栈里面的所有元素全部出栈,然后出去的元素都入栈到第二个栈,再把第二个栈出栈一个元素,再把剩下的元素入栈到第一个栈就行了;
下面附上代码
#include <stdio.h>
typedef struct {
int date[20];
int top;
}Sqstack;
int empty(Sqstack *s){
if(s->top<0){
return 1;
}
return 0;
}
int push(Sqstack *s,int n){
if(s->top>=10){
return 0;
}
s->date[++s->top]=n;
return 1;
}
int pops(Sqstack *s){
if(empty(s)){
return 0;
}
return s->date[s->top--];
}
int popq(Sqstack *s1,Sqstack *s2){
int n;
while(!empty(s1)){
s2->date[++s2->top]=pops(s1);
}
pops(s2);
while(!empty(s2)){
s1->date[++s1->top]=pops(s2);
}
return 1;
}
void print(Sqstack s)
{
int i;
for(i=0;i<=s.top;i++)
{
printf("%d ",s.date[i]);
}
printf("\n");
return;
}
int main(){
Sqstack s1,s2;
s1.top=-1;
s2.top=-1;
int choice;
while(1)
{
scanf("%d",&choice);
if(choice==1){
int n;
scanf("%d",&n);
push(&s1,n);
}else if(choice==2){
popq(&s1,&s2);
}else if(choice==3){
print(s1);
}
}
}
KMP算法
什么是KMP算法,说的通俗易懂就是在一个主串中查找子串的算法,在介绍KMP算法之前,我们先来说一下与其功能相同但是时间复杂度较高的朴素。。。叫啥来着 ,队朴素模式匹配算法,朴素模式匹配算法应该是正常人一般可以一次想到的,主串从第一位开始与字串的第一位开始比较如若相同则继续比较后面的,如若不同则主串第二位与字串第一位开始比较后面的以此类推。
int findstep(char s[],char t[]){
int lens=strlen(s);
int lent=strlen(t);
for(int i=0;i<lens;i++)
{
for(int j=0,t=i;j<lent;j++)
{
if(s[t]==t[j]){
j++;
t++;
}
else{
break;
}
}
if(j==lent)
{
break;
}
}
if(lens-i>lent){
return 1;
}
else{
return 0;
}
}
这样很好理解,但是有一些不好的情况,假设现在的子串为”aaaaaaaaaaaaac"字串为”aaac“如此以往每次都要把前三个比较一下,大大浪费了时间。
这时候就有了伟大的前人,设计了著名的KMP算法,这个算法大大减少了重复比较的次数下面就让我们看看KMP是如何达到如此神奇的功效的。
介绍KMP之前要明白一个概念字符串中最长相等的前缀和后缀
举个例子
“abcdeabcd”
前缀就是从a开始算比如"a", “ab”, “abc”, “abcd”
后缀就是从后面开始算比如"d", “cd”, “bcd”, “abcd”
那么很显然这个串里面的最长相等前后缀就是"abcd"
了解完这个我们进入到第二步,用一个数组来存储一个字符串中的最长相等前后缀
就拿字符串"ababcabc"来举例,数组名定义为next,长度应该和这个字符串一样大
next | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
next[i] | -1 | 0 | 0 | 1 | 2 | 0 | 1 | 2 |
那么如何用代码制作这个next数组呢
这真是个抽象的问题,我也是不太懂,我真想给这里插一个视频;
其中最难以理解的就是它的回溯
先放代码 非原创
void MakeNext(str s,int next[]){
int j,k;
j=0;
k=-1;
next[0]=-1;
while(j<s.len-1)
{
if(k==-1||s.date[j]==s.date[k]){
k++;
j++;
next[j]=k;
}
else{
k=next[k];
}
}
int i;
for(i=0;i<s.len;i++)
{
printf("%d ",next[i]);
}
}
估计前面的问题都不大最令人纳闷的就是k=next[k]
这一句
这里让我引用一下前人的精彩绝伦的讲述
原作者出处
构造完next数组后我们就可以进行KMP了
int MYKMP(str f,str s){
int next[20],i,j;
MakeNext(s,next);
i=0,j=0;
while(i<f.len&&j<s.len)
{
if(j==-1||f.date[i]==s.date[j]){
i++;
j++;
}
else{
j=next[j];
}
}
if(j>=s.len){
return i-s.len;
}
else{
return -1;
}
}
KMP里面的回溯和构造next数组里面的有异曲同工之妙
写在最后、
、、、、、、、
那么这周的周报也就到此结束了,KMP真的太抽象了,以后用的时候可以再写一点博客