首页 > 其他分享 >KMP 和扩展 KMP

KMP 和扩展 KMP

时间:2023-02-19 23:45:25浏览次数:32  
标签:前缀 int 扩展 cdots KMP tie size

给定一个字符串 \(s[1 \cdots n]\)。

定义前缀函数 \(f_i\) 表示 \(s[1 \cdots i]\) 最长的相等的真前缀与真后缀的长度。

规定 \(f_1=0\)。

发现 \(f_i\) 至多为 \(f_{i-1}+1\)(匹配了 \(i\) 上的字符)。

考虑 \(i\) 上的字符不匹配的情况。

那么找到第二长的满足真前缀 \(=\) 真后缀的位置一定最优。

然后会发现这玩意儿其实就是前缀数组的定义。

直接不断跳前缀数组即可。

#include <bits/stdc++.h>
using namespace std;

const int N = 3e6 + 10;
string s, t, g; int f[N];

int main() {
    ios_base::sync_with_stdio(false); cin.tie(0), cout.tie(0);
    cin >> s >> t; g = "$" + t + "#" + s;
    int len = g.size() - 1, lent = t.size();
    for (int i = 2; i <= len; ++i) {
        int pos = i - 1;
        while (pos && g[f[pos] + 1] ^ g[i]) pos = f[pos];
        if (g[f[pos] + 1] == g[i]) f[i] = f[pos] + 1;
        if (f[i] == lent) cout << i - 2 * lent << endl;
    }
    for (int i = 1; i <= lent; ++i) cout << f[i] << ' ';
    return cout << endl, 0;
}

那么扩展 KMP 其实和这个类似。

定义 \(z_i\) 表示 \(s\) 和 \(s[i \cdots n]\) 的 LCP(最长公共前缀)长度。

实时维护一个区间 \([l,r]\) 表示当前能匹配的右端点最右的区间。

那么由定义有 \(s[1 \cdots r-l+1]=s[l \cdots r]\)。

  • 如果当前 \(i \le r\)。

那么易得 \(s[i \cdots r]=s[i-l+1 \cdots r-l+1]\)。

根据 \(z_{i-l+1}\),得到 \(s\) 与 \(i-l+1\) 开头的后缀的 LCP 长度。

那么,若 \(z_{i-l+1} < r-i+1\),那么表示能匹配的长度至多为 \(z_{i-l+1}\)。

否则直接暴力向后匹配,更新 \(l,r\)。

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 4e7 + 10;
string a, b, s; int l, r, resz, resp, z[N];

signed main() {
    ios_base::sync_with_stdio(false); cin.tie(0), cout.tie(0);
    cin >> a >> b; s = "#" + b + "$" + a;
    int len = s.size() - 1, lena = a.size(), lenb = b.size();
	z[1] = lenb, l = r = 1;
    for (int i = 2; i <= len; ++i) {
        if (i <= r && z[i - l + 1] < r - i + 1) z[i] = z[i - l + 1];
        else {
            z[i] = max(0ll, r - i + 1);
            while (i + z[i] <= len && s[z[i] + 1] == s[i + z[i]]) ++z[i];
        }
        if (i + z[i] - 1 > r) l = i, r = i + z[i] - 1;
    }
    for (int i = 1; i <= lenb; ++i) resz ^= (i * (z[i] + 1));
    for (int i = 1; i <= lena; ++i) resp ^= (i * (z[i + lenb + 1] + 1));
    return cout << resz << endl << resp << endl, 0;
}

标签:前缀,int,扩展,cdots,KMP,tie,size
From: https://www.cnblogs.com/MistZero/p/KMP.html

相关文章