(五)字符串算法
kmp算法
是在 的时间复杂度内求解模式串匹配问题的算法。
大致思路如下:定义 表示 子串使得前 个字符恰等于后 个字符的最大的 ,其中 。发现若在某一位失配过后,我们可以令模式串等于该位置的 数组值即可。同理 数组也可以在线性时间内求出。
void kmp()
{
int la = strlen(a + 1), lb = strlen(b + 1);
for (int i = 2, j = 0; i <= lb; i++) {
while (j > 0 && b[i] != b[j + 1]) j = nxt[j];
if (b[i] == b[j + 1]) j++;
nxt[i] = j;
}
for (int i = 1, j = 0; i <= la; i++) {
while (j > 0 && a[i] != b[j + 1]) j = nxt[j];
if (a[i] == b[j + 1]) j++;
if (j == lb) {
printf("%d\n", i - lb + 1);
j = nxt[j];
}
}
for (int i = 1; i <= lb; i++)
printf("%d ", nxt[i]);
}
扩展kmp(z 函数)
算法:
定义一个字符串的 函数数组为:它与它的每个后缀的 长度。
可以做到在线性时间内递推求得 函数数组。
(推导下标从 开始)
设当前要求的为 ,设 为使得 最大的 ,那么以下代码实现就不难得出了。
void get_z(std::string s)
{
int sz = s.size(), k = 0;
for (int i = 1; i < sz; i++)
{
if (k + z[k] > i) z[i] = std::min(z[i - k], k + z[k] - i);
while (i + z[i] < sz && s[i + z[i]] == s[z[i]]) z[i]++;
if (k + z[k] < i + z[i]) k = i;
}
z[0] = sz;
}
字符串哈希
将任意长度的字符串映射为一个非负整数,冲突概率几乎为零。
过程:首先将字符串化为一个非负整数(常见的方法有用 码代替所有字符)。将这个非负整数看做一个 进制数,并对 取模。这里的 与 都是我们自己选取的一个合适的数值,一般可以取 。用 存储可以避免低效的取模运算。
若我们已知字符串 的哈希值为 ,字符串 哈希值为 ,那么字符串 的哈希值就是 。
于是我们可以 预处理所有前缀的哈希值, 查询任意子串的哈希值。
AC 自动机
输出包含 mm 行,依次为删除每个元素之前,逆序对的个数。
自动机通过结合 和 匹配算法来进行多模式匹配。
一个节点 定义为从根节点到其的字符串,也称其为状态。
失配指针的构建: 自动机的 指针定义为当前状态 的存在于 树中的最长后缀。
欲求当前状态 的 值,设 的父节点为 。若 存在,那么令 ,相当于在其父状态上添一个字符。
否则就令 指向 。
最后再套一个 就好了。
询问时,考虑若一个子串能匹配成功,那么它的 一定也能匹配成功,于是我们不断的跳 就可以统计不同字符串的出现次数了。
求最大出现次数,一种暴力的方法就是类似于上题直接跳 数组,但是由于不打标记,时间复杂度最坏情况下并不能通过(好像数据很水,这样写也能过)。
考虑如下结论,所有 指针构成了一棵树。
因为 根节点没有 指针,剩下 个指针恰构成 条边。 个节点, 条边且图连通就是一棵树了。
于是每次就只用在当前节点打上标记,最后再用树形 跑一遍就好了。
和上题差不多,加个 unordered_map
判重就可以了。
字符串模板全家桶
然鹅,并没有保证正确性,只是单纯地打了一遍。
#include <cstring>
#include <cstdio>
#include <queue>
#include <iostream>
namespace string_algorithm {
struct kmp {
static const int lim = 1e6;
int nxt[lim + 5];
void __kmp(std::string a, std::string b) {
int az = a.size(), bz = b.size(), cnt = 0;
nxt[0] = -1;
for (int i = 0, j = -1; j < bz; j++) {
while (~j && b[j + 1] != b[i]) j = nxt[j];
if (b[j + 1] == b[i]) j++;
nxt[i] = j;
}
for (int i = 0, j = -1; j < bz; j++) {
while (~j && b[j + 1] != a[i]) j = nxt[j];
if (b[j + 1] == a[i]) j++;
if (j == bz) cnt++, j = nxt[j];
}
}
};
struct exkmp {
static const int Maxn = 1e7 + 5;
int z[Maxn];
void exkmp_getz(std::string s) {
int sz = s.size(), k = 0;
for (int i = 1; i < sz; i++) {
if (k + z[k] > i) z[i] = std::min(k + z[k] - i, z[i - k]);
while (i + z[i] < sz && s[z[i]] == s[i + z[i]]) z[i]++;
if (i + z[i] > k + z[k]) k = i;
}
z[0] = sz;
}
};
struct hash {
static const int Maxn = 1e6 + 5;
unsigned long long hash[Maxn], pw[Maxn];
const int P = 13331;
void Init_hash(std::string s) {
hash[0] = int(s[0]), pw[0] = 1;
for (int i = 1, sz = s.size(); i < sz; i++) {
hash[i] = hash[i - 1] * P + int(s[i]);
pw[i] = pw[i - 1] * P;
}
}
unsigned long long get_hash(int l, int r) {
if (!l) return hash[r];
else return hash[r] - hash[l - 1] * pw[r - l + 1];
}
};
struct trie {
static const int Maxn = 1e4 + 5;
int tr[Maxn * 50][26], exist[Maxn * 50], idx;
void insert(std::string s) {
int p = 1, len = s.size();
for (int i = 0; i < s.size(); i++) {
int ch = s[i] - 'a';
if (!tr[p][ch]) tr[p][ch] = ++idx;
p = tr[p][ch];
}
if (!exist[p]) exist[p] = 1;
}
bool query(std::string s) {
int p = 1, len = s.size();
for (int i = 0; i < s.size(); i++) {
int ch = s[i] - 'a';
if (!tr[p][ch]) return false;
p = tr[p][ch];
}
return exist[p];
}
};
struct AC_automation {
static const int lim = 1e6;
int tr[lim + 5][26], exists[lim + 5], fail[lim + 5], n, idx;
void insert(std::string &s) {
int l = int(s.length()), p = 0;
for (int i = 0; i < l; i++) {
int ch = s[i] - 'a';
if (!tr[p][ch]) tr[p][ch] = ++idx;
p = tr[p][ch];
}
exists[p]++;
}
void get_fail() {
std::queue<int> q;
for (int i = 0; i < 26; i++)
if (tr[0][i]) q.push(tr[0][i]);
while (!q.empty()) {
int p = q.front();
q.pop();
for (int i = 0; i < 26; i++) {
if (tr[p][i]) {
q.push(tr[p][i]);
fail[tr[p][i]] = tr[fail[p]][i];
}
else tr[p][i] = tr[fail[p]][i];
}
}
}
int query(std::string &t) {
int l = int(t.length()), p = 0, res = 0;
for (int i = 0; i < l; i++) {
p = tr[p][t[i] - 'a'];
for (int j = p; j && ~exists[j]; j = fail[j]) {
res += exists[j];
exists[j] = -1;
}
}
return res;
}
};
};
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】