定义 1
构造过程
构造过程就是初始化
假设存在
就是说有
说明最大的自匹配为
最理想的情况下,
否则需要
(因为
永远单调增大, 每次最多单调增加 1
极端情况就是减少到
匹配过程
其思路是
以
整体流程和构造过程相似,但需要多处理一个越界的情况
完成版
#include <bits/stdc++.h>
struct Matcher {
std::string pattern;
std::vector<int> next;
Matcher(std::string p):
pattern(std::move(p)), next(pattern.length())
{ initNext(); }
void initNext() {
next[0] = -1;
for(int i = 1, j = -1; i < pattern.length(); ++i) {
for(; j > -1 && pattern[i] != pattern[j+1]; j = next[j]);
if(pattern[i] == pattern[j+1]) ++j;
next[i] = j;
}
}
int execute(const std::string &text) {
int count = 0;
for(int i = 0, j = -1; i < text.length(); ++i) {
// 需要处理**越界**或失配
for(; j > -1 && (j+1 >= text.length() || text[i] != pattern[j+1]); j = next[j]);
if(text[i] == pattern[j+1]) ++j;
// matchAt[i] = j;
if(j+1 == pattern.length()) ++count;
}
return count;
}
};
int main() {
for(std::string p, t; std::cin >> p >> t;) {
std::cout << Matcher(p).execute(t) << std::endl;
}
return 0;
}
感觉 EXKMP
用处并不大,教程不写了
至于 AC自动机
,其实就是定义
其余还有构造时的路径压缩,以及查询时可能用到的拓扑排序优化
然后没了
#include <bits/stdc++.h>
namespace utils {
template <typename Container, typename ...Args>
inline void append(Container &c, Args &&...args) {
c.emplace_back(std::forward<Args>(args)...);
}
inline void gkd() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
}
template <unsigned int init = 19260817>
inline int random() {
static auto seed = init;
seed = seed * 998244353 + 12345;
return static_cast<int>(seed / 1024);
}
void debug() { std::cout << std::endl; }
template <typename Arg, typename ...Args>
void debug(Arg arg, Args ...args) {
std::cout << arg << ' ';
debug(args...);
}
}
struct Trie {
std::vector<std::array<int, 26>> dict;
std::vector<bool> flag;
std::vector<int> end;
int root;
Trie(): root(node()) { }
int node() {
auto cur = dict.size();
utils::append(dict);
utils::append(flag, false);
utils::append(end, 0);
return cur;
}
void add(const std::string &str) {
auto cur = root;
for(auto ch : str) {
auto c = ch - 'a';
if(!dict[cur][c]) {
auto t = node();
dict[cur][c] = t;
}
cur = dict[cur][c];
}
flag[cur] = true;
++end[cur];
}
std::array<int, 26>& operator[](size_t pos) {
return dict[pos];
}
};
struct AcAutomaton {
Trie trie;
std::vector<int> fail;
int root;
AcAutomaton(const std::vector<std::string> &strs): root(0) {
for(auto &str : strs) trie.add(str);
fail.resize(trie.dict.size());
std::queue<int> que;
fail[root] = root;
for(int i = 0, j = 0; i < 26; ++i) {
if(j = trie[root][i]) {
fail[j] = root;
que.push(j);
}
}
while(!que.empty()) {
auto u = que.front();
que.pop();
for(int i = 0; i < 26; ++i) {
auto v = trie[u][i];
auto f = fail[u];
if(v) {
fail[v] = trie[f][i];
que.push(v);
} else {
trie[u][i] = trie[f][i];
}
}
}
}
long long query(const std::string &str) {
auto cur = root;
long long res = 0;
for(auto ch : str) {
auto c = ch - 'a';
cur = trie[cur][c];
for(auto u = cur; u && ~trie.end[u]; u = fail[u]) {
res += trie.end[u];
trie.end[u] = -1;
}
}
return res;
}
};
int main() {
utils::gkd();
int testcase;
std::cin >> testcase;
while(testcase--) {
int n;
std::cin >> n;
std::vector<std::string> vec(n);
for(auto &i : vec) std::cin >> i;
AcAutomaton ac(vec);
std::string s;
std::cin >> s;
std::cout << ac.query(s) << std::endl;
}
return 0;
}