模式串 從 0 開始的KMP演算法


/**
 *  看了 b站影片 BV1jb411V78H 對KMP有了一點理解,然後我寫了這個程式碼
 *  這個程式碼和影片裡面的有一點不同,字元串是從 0 開始的,而不是從1 開始的
 *  希望能夠幫到學習KMP的人
 */


#include <stdbool.h>
#include <malloc.h>
#include "KMP.h"

// KMP.h 內容
/*
#define MAXSIZE (255)

typedef struct {
    char ch[MAXSIZE + 1];
    int length;
}SString;

void Next(SString const *t);
extern int *next;
int IndexOf_KMP(SString const *s, SString const *t);
*/

int *next;      // 計算 next數組

/**
 * 計算 0-based 模式串t的next數組
 * @param t 模式串
 */
void Next(SString const *t) {
    //  模式串 0 開始
    int length = t->length;

    // 分配 next數組 所需要的空間
    next = (int *)malloc(sizeof(int) * length);
    for (int *p = next; p < next + length; p++)
    {
        *p = 0;
    }

    for (int i = 0; i < length; i++)
    {
        if (i <= 1)     // 前兩個next[i]置為-1
        {
            next[i] = -1;
            continue;
        }

        // 執行下面的語句的時候, 一定有 i >= 2



        int maxlen = 0;         // 存儲最長公共前後綴的長度

        /**
         *  // len 表示前綴或後綴的最大長度, 可取的值是 [1..i-1]      // i 為(模式串或next數組)的訪問下標
         *  這裡主要是 對 模式串在i位置 求 它的最大公共前後綴的長度
         *  從 1 開始 到 i-1 一個一個去試 
         *  
         */
        for (int len = 1; len < i; len++)
        {
            int val = 0;
            bool flag = false;

            for (int j = 0, k = i - len; j < len; j++, k++)
            {
                if (t->ch[j] == t->ch[k])
                {
                }
                else
                {
                    flag = true;        // len 長度的公共前後綴匹配失敗
                    break;
                }
            }

            if (flag == false)          //  公共前後綴長度為len
                val = len;
            if (val > maxlen)           // 這個比較不是必須的,因為找公共前後綴的長度的時候, len 從 1 到 i-1
                maxlen = val;           // maxlen 就是 next[i]的值了
        }
        next[i] = maxlen;
    }
}

// 調用這個函數之前,一定要調用 Next函數,為模式串構建 next數組
int IndexOf_KMP(SString const *s, SString const *t)
{
    // 開始匹配

    int i = 0, j = 0;       // i 表示主串的下標, j 表示模式串的下標
    while (i < s->length)
    {
        if (j == -1 || s->ch[i] == t->ch[j])
        {
            i++;
            j++;
        }
        else
        {
            j = next[j];
        }

        // 匹配成功
        if (j == t->length)
        {
            return i - t->length;
        }
    }

    // 匹配失敗
    return -1;
}