KMP知识点总结

本文详细介绍了KMP算法中的next数组概念,以及如何构建next数组。KMP算法用于字符串匹配,通过next数组实现不回溯的高效匹配。此外,还讨论了KMP算法的变形——最小重复子串问题,并提供了相关例题解析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

next数组的含义

在这里插入图片描述
在这里插入图片描述

next[i]代表的是S[0,1,…,i-1]的前缀后缀最大值
next[0]=-1

next数组的初始化

我们使用t1来遍历所有的next的下标。if语句实现的是为next[t1+1] (长度为t1或者说下标从0到t1-1的字符串的最大前缀后缀)赋值。

赋值过程是一个递推的过程。首先需要明晰的是,当我们为长度为t1的子串配置next值时,t2的值为长度为t1-1的子串的next值,即t21所指向的位置。黄色部分为长度为t1-1的最大前缀和后缀,根据定义可知,两个黄色区域是一致的。我们比较t21处的值和t1处的值是否相等,若相等,则黄色区域可以向后拓展一个字符,以形成长度为t1的最大前缀和后缀;若不相等,则使得t22指向next[t21],此时可以观察到,图中四个绿色区域是完全一致的,继续讲t22处的值和t1处的值进行比较,重复上述过程。

在这里插入图片描述

void get_next(int *next,char *s2,int lens){
   //用于构建s2的next数组,kmp的前奏
    int t1=0,t2;
    next[0]=t2=-1;
    while(t1<lens){
        if(t2==-1||s2[t1]==s2[t2]){
            next[t1+1]=t2+1;
            t1++;
            t2++;
        }
        else t2=next[t2];
    }
}

KMP求匹配串的问题

假设W为一个字符串,T为待匹配的字符串,使用t2指向W,t1指向T。
假设t1和t2所指向的位置是相同的,则t1和t2一同增加;否则,根据next数组的含义,不移动t1,仅将t2移动到nxt[t2]的位置。当t2的值出现-1的时候,代表W的第一个字符便和t1指向的字符不同,因此需要将t1和t2都向后一个,以选取新的T的起点来进行下一步的匹配。

        tl = strlen(T), wl = strlen(W);
		int t1 = 0, t2 = 0;
		while (t1 < tl && t2 < wl) {
			if (t2==-1 || T[t1] == W[t2]) {
				t1++, t2++;
			}
			else t2 = nxt[t2]; //注意这里仅和W串有关系
			if (t2 == wl) {
				times++;
				t2 = nxt[t2];//注意在能够进行一个完全的匹配后应该对t2进行更新
			}
		}

例题:poj 406

#include<iostream>
#include<string>
using namespace std;
#define WMAX  10010
#define TMAX  1000010
char W[WMAX];
char T[TMAX];
int nxt[TMAX];
int tl, wl;
int N;
int num;
void get_Lat(int* nxt, char* s, int lens) {
	nxt[0] = -1;
	int t1 = 0, t2 = -1;
	while (t1 < lens) {
		if (t2 == -1 || s[t1] == s[t2]) {
			nxt[t1 + 1] = t2 + 1;
			t1++;
			t2++;
		}
		else t2 = nxt[t2];
	}
}
int main() {
	cin >> num;
	while (num--) {
		//scanf_s("\n%s\n%s", W, T);
		int times = 0;
		cin >> W >> T;
		tl = strlen(T), wl = strlen(W);
		get_Lat(nxt, W, wl);
		int t1 = 0, t2 = 0;
		while (t1 < tl && t2 < wl) {
			if (t2==-1 || T[t1] == W[t2]) {
				t1++, t2++;
			}
			else t2 = nxt[t2]; //注意这里仅和W串有关系
			if (t2 == wl) {
				times++;
				t2 = nxt[t2];//注意在能够进行一个完全的匹配后应该对t2进行更新
			}
		}
		cout << times << endl;
	}
}

KMP变形 - 最小重复子串问题

最小重复子串是指对于字符串S,其若为n个s子串串接而成,则称s为s的重复子串,称长度最小的重复子串为最小重复子串。特别的,若S不能看成由子串串接而成,则其重复子串为自身,重复次数为。
例如:
absh其最小重复子串为absh,重复次数为1,ababab的最小重复子串为ab,重复次数为3

对于最小子串问题的求解思路:
在这里插入图片描述

以下证明该求解思路的正确性:
假设按照len-suffix[len]来对字符串进行分块(b1,b2,b3…bl),则根据最大前缀后缀的定义可以知道前缀的第i块一定等一后缀的第i块,前缀的第i块为bi,后缀的第i块为bi+1,于是便有bi=bi+1,于是可以得到所有的块都是相等的。
在这里插入图片描述
例题:poj 1961

#include<iostream>
#include<string>
using namespace std;
#define NMAX  1000100
char s[NMAX];
int Lat[NMAX];
int N;
void get_Lat(int* Lat, char* s2, int lens) {
    //用于构建s2的Lat数组,kmp的前奏
    int t1 = 0, t2;
    Lat[0] = t2 = -1;
    while (t1 < lens) { //逐步向前寻找t2
        if (t2 == -1 || s2[t1] == s2[t2]) {
            Lat[t1 + 1] = t2 + 1;
            t1++;
            t2++; //t2首先指向的是Lat[t1-1]即前一个的位置
        }
        else t2 = Lat[t2]; //保证了0 与t1-1等对的一致,若出现了不等,则会直接
    }
}
int main() {
    int num = 1;
    while(1) {
        cin >> N;
        if (!N) break;
        cin >> s;
        get_Lat(Lat, s, N);
       // for (int i = 1; i < strlen(s); i++) cout << Lat[i] << " ";
        cout << "Test case #"<<num++<<endl;
        for (int i = 2; i <= N; i++) {
            //cout << "length"<<strlen(s) << endl;
            int start = i;
            int standard = start - Lat[start];
            if (i % standard == 0 && i / standard>1) cout << i << " " << i / standard << endl;
        }
        cout << endl;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值