【模板】KMP字符串匹配

前言

学自OI-wiki 前缀函数与kmp

第二个优化

从第二个优化开始我看的时间比较久,如图部分

其中公式

看了挺久,主要是第二个部分到第三个部分,关于这个转换,在例图展示中如

因为 s[0...3] == s[(i-3)...i] && s[0...1] == s[(i-1)...i],所以 s[0...1] == s[2...3] 的,所以指针 j 就可以直接转到 pi[j-1] 的地方,开始比较 s[j] 与 s[i+1];

真的很佩服这三个人(跪下)

j指针转移样例

例题

洛谷P3375 【模板】KMP字符串匹配

题链

字符以 0 为开头写

#include <bits/stdc++.h>
#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//#pragma GCC optimize("O2")
using namespace std;
#define LL long long
#define ll long long
#define ULL unsigned long long
#define ls rt<<1
#define rs rt<<1|1
#define one first
#define two second
#define MS 1000009
#define INF 1e18
#define mod 99999997
#define Pi acos(-1.0)
#define Pair pair<LL,LL>
#define eps 1e-9

LL n,m,k;
char s[MS]; // 文本串 
char e[MS]; // 模式串 
LL hs,he;  	// 串长 
LL kp[MS]; 	// 模式串 e 的前缀数组 
LL kps[MS];	// 文本串 s 的前缀数组 
LL ac[MS],tac;	// 记录答案 

void kmp_e(){ // 求模式串 e 的前缀数组 
	kp[0] = 0;
	for(int i=1;i<he;i++){
		int j = kp[i-1];
		while(j>0 && e[j] != e[i]) j = kp[j-1];
		if(e[j] == e[i]) j++;
		kp[i] = j;
	}
}

void kmp_s(){ // 求文本串 s 的前缀数组  
	int j = 0;
	for(int i=0;i<hs;i++){
		while(j>0 && s[i] != e[j]) j = kp[j-1];
		if(s[i] == e[j]) j++;
		kps[i] = j; // 记录 
	}
}

int main(){
	ios::sync_with_stdio(false);
	cin >> s;
	cin >> e;
	hs = strlen(s);
	he = strlen(e);
	kmp_e();
	kmp_s();
	for(int i=0;i<hs;i++){ // 若 s 前缀数组的值 与 模式串 e 相等 则记录位置 
		if(kps[i] == he){
			ac[++tac] = i-he+1+1;
		}
	}
	for(int i=1;i<=tac;i++){
		cout << ac[i] << endl;
	}
	for(int i=0;i<he;i++){
		cout << kp[i] << " ";
	}
	cout << endl;
	

	return 0;
}

时隔一个多月还是写一写以 1 为开头的kmp;

感觉 1 开头的舒服些;

领悟了下 j 指针总是在模式串上跑的;

#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define ll long long
#define ULL unsigned long long
#define Pair pair<LL,LL>
#define ls rt<<1
#define rs rt<<1|1
#define Pi acos(-1.0)
#define eps 1e-6
#define DBINF 1e100
#define mod 998244353
#define MAXN 1e18
#define MS 1000009 

int n,m;
char s1[MS];
char s2[MS];
int kmps1_s2[MS];
int kmps2[MS];
int ac[MS];

int main() {
	ios::sync_with_stdio(false);
	cin >> s1+1;
	cin >> s2+1;
	int h1 = strlen(s1+1);
	int h2 = strlen(s2+1);
	for(int i=2,j=0;i<=h2;i++){
		while(j && s2[i] != s2[j+1]) j = kmps2[j];
		if(s2[i] == s2[j+1]) j++;
		kmps2[i] = j;
	}
	for(int i=1,j=0;i<=h1;i++){
		while(j && s1[i] != s2[j+1]) j = kmps2[j]; // j 指针总是在模式串上的 kmp 跑 
		if(s1[i] == s2[j+1]) j++;
		kmps1_s2[i] = j;
	}
	int tot = 0;
	for(int i=1;i<=h1;i++){
		if(kmps1_s2[i] == h2){
			ac[++tot] = i-h2+1;
		}
	}
	for(int i=1;i<=tot;i++){
		cout << ac[i] << "\n";
	}
	for(int i=1;i<=h2;i++){
		cout << kmps2[i] << " ";
	}
	cout << "\n";
	
	
	return 0;
}

又时隔4个多月,用fft板子试试字符串匹配,当然求border还是kmp;

 #include <bits/stdc++.h>
using namespace std;
#define eps 1e-6
#define MS 1000009
#define LL long long

int n,m;
char s1[MS], s2[MS];
int kmps2[MS];
int h1, h2;
double A[MS], B[MS], S[MS];
double sumB, qzA[MS], jjSA[MS<<1];

// 洛谷 P3803 【模板】多项式乘法(FFT)
// MS = 1e6 
// time: max( 800ms ) 
// ========================================================================================
typedef complex<double> comp;
const double PI = acos(-1);
const int N = (1<<21)+10; // 长度为原长度向上的2^n, 再乘 2  
int lim, r[N];
comp a[N], b[N];

void fft(comp * a, int type) {
	for(int i = 0; i < lim; i ++)
		if(i < r[i]) swap(a[i], a[r[i]]);
	for(int i = 1; i < lim; i <<= 1) {
		comp x(cos(PI / i), type * sin(PI / i));
		for(int j = 0; j < lim; j += (i << 1)) {
			comp y(1, 0);
			for(int k = 0; k < i; k ++, y *= x) {
				comp p = a[j + k], q = y * a[j + k + i];
				a[j + k] = p + q; a[j + k + i] = p - q;
			}
		}
	}
}

// [0, n], [0, m]
void get(double *c1, int n, double *c2, int m){ 
	for(int i = 0; i <= n; i ++) a[i] = c1[i];
	for(int i = 0; i <= m; i ++) b[i] = c2[i];
	int l = 0;
	for(lim = 1; lim <= n + m; lim <<= 1) ++ l;
	for(int i = 0; i < lim; i ++)
		r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
		
	fft(a, 1), fft(b, 1);
	for(int i = 0; i <= lim; i ++) a[i] *= b[i];
	fft(a, -1);
	// 得出系数 
	for(int i = 0; i <= n + m; i ++) jjSA[i] = (LL)(0.5 + a[i].real() / lim);
}
// ========================================================================================

void kmp(char *s, int len){
	for(int i=1,j=0;i<len;i++){
		while(j && s[i] != s[j]) j = kmps2[j-1];
		if(s[i] == s[j]) j++;
		kmps2[i] = j;
	}
}

int main() {
	scanf("%s",s1); getchar();
	scanf("%s",s2); getchar();
	int h1 = strlen(s1), h2 = strlen(s2);
	for(int i=0;i<h1;i++) A[i] = s1[i]-'a'+1; // 文本串 
	for(int i=0;i<h2;i++) B[i] = s2[i]-'a'+1; // 模式串 
	for(int i=0;i<h2;i++) S[i] = B[h2-i-1];	  // 模式串翻转 
	
	// ∑B(i)^2 => sumB
	for(int i=0;i<h2;i++) sumB += B[i]*B[i];
	
	// ∑A(i)^2 的前缀 => qzA[]
	qzA[0] = A[0]*A[0];
	for(int i=1;i<h1;i++) qzA[i] = qzA[i-1] + A[i]*A[i];
	
	// ∑S(i)A(j) => jjSA[]
	// i+j=x
	get(S, h2-1, A, h1-1);
	
	// P(x) = sumB + qzA[x] - qzA[x-h2] - 2*jjSA[x]
	for(int x=h2-1;x<h1;x++){
		double P;
		if(x == h2-1) P = sumB + qzA[x] - jjSA[x]*2;
		else P = sumB + qzA[x] - qzA[x-h2] - jjSA[x]*2;
		if(abs(P) < eps){
			printf("%d\n",x-h2+2);
		}
	}
	
	kmp(s2, h2);
	for(int i=0;i<h2;i++){
		printf("%d ",kmps2[i]);
	}
	
	
	
	return 0;
}
posted @ 2021-03-17 16:38  棉被sunlie  阅读(80)  评论(0)    收藏  举报