KMP

KMP

首先有暴力算法:

int findPos(char S[], char T[]) {
    int lens = strlen(S), lent = strlen(T), i = 0, j = 0;
    while(i<lens && j<lent) {
        if (S[i] == T[j])
            i++,j++;
        else
            i = i -j + 1,j = 0;    // i, j均移动
  }
  return (j >= lent ? i - lent : -1);
}

然后对暴力算法进行优化:

只移动j,而不移动i。

对于j的移动,j应该如何移动?

当前的s[i]t[j],如果对于t串,其t[0~j-1]有着相同的前后缀,那么,当s[i]t[j]时,j最佳的移动方法就是去与后缀相同的前缀的下一个字符处。

由此,我们想知道,对于每一次s[i]t[j],其前缀的下一处是什么。即nxt[j]。

对于nxt数组,求nxt[j]也就是对t[0~j-1]的最长相同前后缀的长度,那么问题就是,如何快速地得到最长相同前后缀的长度。

下面求nxt数组:

考虑我们已经得到了nxt[0~i],要求nxt[i+1]。

根据定义,nxt[i+1]就是t[0~i]的最长相同前后缀的长度。有下图:

求nxt代码:

void CalcNext(char T[],int next[]) {
  next[0] = -1, next[1] = 0;
  for (int i = 1;i < (int)(strlen(T) - 1);i++) {
        int k = next[i];
        while (k != -1 && T[i] != T[k])
            k = next[k];
        next[i + 1] = k + 1;
  }
}

由此,我们已经得到了KMP:

int findPos_kmp(char S[], char T[], int next[]) {
    int lens = strlen(S), lent = strlen(T), i = 0, j = 0;
    while(i<lens && j<lent) {
        if(j == -1 || S[i] == T[j])
            i++,j++;
        else
            j = next[j];   // 只移动j,而不移动i.
  }
  return (j >= lent ? i - lent : -1);
}

可选优化:

我们知道,对于最大相同前后缀的后缀的后一个字符,如果匹配失败,显然会找到前缀的后一个字符,再进行判断,如果前缀的后一个字符和后缀的后一个字符相等的话,那从后缀的后一个字符到前缀的后一个字符的操作显然无意义,肯定不相等。于是,我们可以判断后缀的后一个字符和前缀的后一个字符是否相等,如果相等,就让后缀的后一个字符的nxt等于前缀的后一个字符的nxt。

void CalcNextVal(char T[],int next[],int nextVal[]) {
    int len = strlen(T);
    for (int i = 0;i < len;i++)
        nextVal[i] = next[i];
    for (int i = 1;i < len;i++) {
        int k = nextVal[i]; // 前缀的后一个字符。
        if (T[i] == T[k])
            nextVal[i] = nextVal[k];  // 后缀的后一个字符的nxt等于前缀的后一个字符的nxt
    }
}
posted @   Uzhia  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
点击右上角即可分享
微信分享提示