【算法】KMP 与 Z 函数
1. KMP#
1.1 算法简介#
可以做到线性匹配的快速匹配字符串的算法,并可以维护字符串最长公共前后缀,扩展出计算字符串周期。
在 OI 界 KMP 算法是字符串板块中很经典的算法,可以扩展出很多巧妙的解题技巧。
1.2 算法流程#
1.2.1 字符串匹配#
考虑
然后就是考虑如何优化,无非就是利用已经计算过的信息。
这里补充一个 最长公共前后缀 的概念:对于字符串
一个匹配下分
假设已经计算出了
s: caabaaabacb
t: aaba
此时
;此时两串匹配的为空串, ;此时两串可匹配成功,匹配了aaba
, ;此时两串匹配了a
, ;此时两串匹配的为空串, ;此时两串匹配了aa
, ;此时两串可匹配成功,匹配了aaba
,
如此匹配,我们便找到了所有的合法匹配位置,分别为
考虑分析时间复杂度,可以看成
1.2.2 计算 kmp 数组#
和匹配很像,相当于自己和自己匹配。所到之处的
不过更好的理解是用两个指针
这个过程一定也是
1.3 算法实现#
计算
for (int i = 2, j = 0; i <= n; i++) {
while(j && t[j + 1] != t[i]) j = nx[j];
if(t[j + 1] == t[i]) j++;
nx[i] = j;
}
匹配:
for (int i = 1, j = 0; i <= n; i++) {
while(j && t[j + 1] != s[i]) j = nx[j];
if(t[j + 1] == s[i]) j++;
if(j == m) {
cout << i - m + 1 << '\n';
}
}
然后是 P3375 【模板】KMP,就把她俩整合一下即可。
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
using namespace std;
const int N = 1e6 + 10;
int n, m, nx[N];
char s[N], t[N];
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> (s + 1) >> (t + 1);
n = strlen(s + 1);
m = strlen(t + 1);
for (int i = 2, j = 0; i <= n; i++) {
while(j && t[j + 1] != t[i]) j = nx[j];
if(t[j + 1] == t[i]) j++;
nx[i] = j;
}
for (int i = 1, j = 0; i <= n; i++) {
while(j && t[j + 1] != s[i]) j = nx[j];
if(t[j + 1] == s[i]) j++;
if(j == m) {
cout << i - m + 1 << '\n';
}
}
For(i,1,m) cout << nx[i] << ' ';
return 0;
}
1.4 扩展#
1.4.1 字符串周期#
给定一个字符串
可以发现一些性质:如果对于
证明很简单。
按照这样的方式对于每一个小块染上不同的颜色。
这里红色段和黄色段相等。
这样每一个小段可以传递相等。这样就可以证明出其为周期字符串。
代码挂着
#include<bits/stdc++.h>
#define int long long
#define reg register
#define For(i,l,r) for(reg int i=l;i<=r;++i)
#define FOR(i,r,l) for(reg int i=r;i>=l;--i)
using namespace std;
const int N = 1e6 + 10;
int n, kmp[N], id;
char s[N];
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
while(cin >> n && n != 0) {
For(i,1,n) kmp[i] = 0;
cin >> (s + 1);
for (int i = 2, j = 0; i <= n; ++i) {
while(j && s[i] != s[j + 1]) j = kmp[j];
if(s[i] == s[j + 1]) j++;
kmp[i] = j;
}
++id;
cout << "Test case #" << id << '\n';
For(i,1,n) {
if(i % (i - kmp[i]) == 0 && kmp[i] != 0) {
cout << i << ' ' << (i / (i - kmp[i])) << '\n';
}
}
cout << '\n';
}
return 0;
}
2. Z 函数#
2.1 算法流程#
有一个这样的问题:
给定两个字符串
的 函数数组 ,即 与 的每一个后缀的 LCP 长度。 与 的每一个后缀的 LCP 长度数组 。
对于第一个 subtask 所求的数组
2.1.1 暴力求解#
很好想,就拿一对指针
时间复杂度
2.1.2 Z-box 引入#
维护一个区间
对于
;此时 ;此时 ,然后暴力匹配。
其他情况暴力匹配,然后更新
时间复杂度
可以发现
2.2 算法实现。#
计算
z[1] = m;
for (reg int i = 2, l, r = 0; i <= m; ++i) {
if(i <= r) z[i] = min(z[i - l + 1], r - i + 1);
while(i + z[i] <= m && t[1 + z[i]] == t[i + z[i]]) z[i]++;
if(i + z[i] - 1 > r) l = i, r = i + z[i] - 1;
}
计算
把匹配
for (reg int i = 1, l, r = 0; i <= n; ++i) {
if(i <= r) p[i] = min(z[i - l + 1], r - i + 1);
while(1 + p[i] <= m && i + p[i] <= n && s[i + p[i]] == t[1 + p[i]]) p[i]++;
if(i + p[i] - 1 > r) l = i, r = i + p[i] - 1;
}
#include<bits/stdc++.h>
#define int long long
#define reg register
#define For(i,l,r) for(reg int i=l;i<=r;++i)
#define FOR(i,r,l) for(reg int i=r;i>=l;--i)
using namespace std;
const int N = 2e7 + 10;
int n, m, z[N], p[N], ans1, ans2;
char s[N], t[N];
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> (s + 1) >> (t + 1);
n = strlen(s + 1), m = strlen(t + 1);
z[1] = m;
for (reg int i = 2, l, r = 0; i <= m; ++i) {
if(i <= r) z[i] = min(z[i - l + 1], r - i + 1);
while(i + z[i] <= m && t[1 + z[i]] == t[i + z[i]]) z[i]++;
if(i + z[i] - 1 > r) l = i, r = i + z[i] - 1;
}
for (reg int i = 1, l, r = 0; i <= n; ++i) {
if(i <= r) p[i] = min(z[i - l + 1], r - i + 1);
while(1 + p[i] <= m && i + p[i] <= n && s[i + p[i]] == t[1 + p[i]]) p[i]++;
if(i + p[i] - 1 > r) l = i, r = i + p[i] - 1;
}
For(i,1,m) ans1 = (ans1 ^ (i * (z[i] + 1)));
For(i,1,n) ans2 = (ans2 ^ (i * (p[i] + 1)));
cout << ans1 << '\n' << ans2 << '\n';
return 0;
}
作者:Daniel-yao
出处:https://www.cnblogs.com/Daniel-yao/p/18554369
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效