1. kmp
相当于往前求出一段字符信息,使得 这段字符信息和前缀相等。
void getnext(){ int k = -1, j = 0; nx[0] = -1; while(j < m){ if(k == -1 || b[j] == b[k]) nx[++j] = ++k; else k = nx[k]; } }
例:
#include<iostream> using namespace std; int nx[10000+5], b[10000+5], a[1000000+5]; int n, m; void getnext(){ int k = -1, j = 0; nx[0] = -1; while(j < m){ if(k == -1 || b[j] == b[k]) nx[++j] = ++k; else k = nx[k]; } } int KMP() { int flag = -1; for(int i = 0, j = 0; i < n; i++){ while(j > 0 && b[j] != a[i]) j = nx[j]; if(a[i] == b[j]) j++; if(j == m) { return i - j + 2; } } return -1; } int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int t; cin >> t; while(t--){ cin >> n >> m; for(int i = 0; i < n; i++) cin >> a[i]; for(int i = 0; i < m; i++) cin >> b[i]; getnext(); cout << KMP() << endl; } return 0; }
2. ex-kmp
相当于往后求出一段信息,使得这段信息和前缀相等。
int n, z[N]; char s[N]; void init(){ z[0] = n; int j = 1, k; for(int i = 1; i < n; i = k){ if(j < i) j = i; while(j < n && S[j] == S[j-i]) j++; z[i] = j-i; k = i+1; while(k + z[k-i] < j) z[k]=z[k-i],k++; } }
例:
T:
给出模板串A和子串B,长度分别为lenA和lenB,要求在线性时间内,对于每个A[i](1<=i<=lenA),求出A[i..lenA]与B的最长公共前缀长度。
#include<bits/stdc++.h> using namespace std; const int N = 2e6+100; char s[N], ss[N]; int z[N]; void init(int n){ z[0] = n; int j = 1, k; for(int i = 1; i < n; i = k){ if(j < i) j = i; while(j < n && s[j] == s[j-i]) j++; z[i] = j-i; k = i+1; while(k + z[k-i] < j) z[k]=z[k-i],k++; } } int Ac(){ scanf("%s", ss); scanf("%s", s); int n = strlen(s), m = strlen(ss); s[n] = '@'; strcpy(s+1+n, ss); init(n+m+1); for(int i = n+1; i <= n+m; ++i){ printf("%d%c", z[i], " \n"[i==n+m]); } return 0; } int main(){ Ac(); return 0; }