代码:

const int mod=998244353,MAXN=1e6+8;
int nex[MAXN];
void getnext(char p[],int len){
    memset(nex,0,sizeof(nex))
    int k=nex[0]=-1,i=0;
    while(i<len){
        while(k!=-1&&p[i]!=p[k])k=nex[k];
        nex[++i]=++k;
    }
}
void kmp(char s[],char p[]){
    int lens=strlen(s),lenp=strlen(p);
    getnext(p,lenp);
    for(int i=0,j=0;i<lens;){//如果看是否找到,i<lens&&j<lenp;
        while(j!=-1&&s[i]!=p[j])j=nex[j];
        ++i,++j;
        if(j==lenp){
            printf("%d\n",i-j+1);//输出所有出现的位置
            j=nex[j];
        }
    }
    //if(j==lenp)return i-j;
    //return -1
}

假设上面是字符串s,下面是p。

假设匹配到了字符a!= b。
当前next[j]的值就是串3的长度

因为串1234都是一样的,所以j跳回到串3的最后面。

串2和3是相等的,所以直接比较串2和串3后面的字符就行了
最麻烦的就是next数组,next[j]是除去字符j后p[0,j-1]这个子串里前缀和后缀相等的最长长度。
这里只考虑出现不同字符的情况,否则就直接+1
k最开始是指向字符a的,但是a!=j指向的字符,所以k=next[k]
即k指向前k个字符组成的串的next值,就是下图所示。

如图,又有4个子串是一样的,继续比较k指向的字符和j指向的字符就行了,显然就是一个递归的过程。