将某一个字符串映射到某一个值,使得其方便操作与查找,同时又要尽可能降低冲突率
采用多项式Hash法: f ( s ) = ∑ s [ i ] × b i ( m o d M ) \displaystyle f(s)=\sum s[i]\times b^i\ (mod\ M) f(s)=∑s[i]×bi (mod M),使 b b b与 M M M互质,可以使错误率降低到 1 M \displaystyle \frac{1}{M} M1
typedef long long ll; // 可以改为自然溢出
const int M = 998244353;
const int B = 131; // 可以改为双模数
const int N = 100010;
int n;
ll f[N], b[N]; // 可以改用map来存储(时间复杂度多一个log)
char s[N];
inline void init()
{
b[0] = 1;
for (register int i = 1; i <= n; ++i) {
f[i] = (f[i - 1] * B + s[i]) % M;
b[i] = (b[i - 1] * B) % M;
}
}
inline ll get_hash(int l, int r)
{
return (f[r] - f[l - 1] * b[r - l + 1] % M + M) % M;
}
单字符串匹配。
对于第 i i i个位置,定义前缀函数 π [ i ] \pi[i] π[i],满足 π [ i ] = max k = 1 , 2 , 3 , . . . , i { k ∣ s [ 1 , 2 , . . . , k ] = s [ i − k + 1 , i − k + 2 , . . . , i ] } \displaystyle \pi[i]=\max\limits_{k=1,2,3,...,i}\{k|s[1,2,...,k]=s[i-k+1,i-k+2,...,i]\} π[i]=k=1,2,3,...,imax{k∣s[1,2,...,k]=s[i−k+1,i−k+2,...,i]}
较为显然的是,相邻两个位置的前缀函数值最多变化1
const int N = 100010;
int n, p[N];
char s[N];
inline void kmp()
{
p[1] = 0;
for (register int i = 2; i <= n; ++i) {
register int j = p[i - 1];
while (j > 0 && s[i] != s[j + 1]) j = p[j];
if (s[i] == s[j + 1]) ++j;
p[i] = j;
}
}
设单词串为 s s s,文本串为 t t t,构造新串 w = s + # + t w=s+\#+t w=s+#+t,其中 # \# #为不在 s s s或 t t t中出现的字符。
对串 w w w使用KMP算法,若某位置满足 π [ i ] = s . l e n g t h \pi[i]=s.length π[i]=s.length,表示字符串 w [ i − s . l e n g t h + 1 , i − s . l e n g t h + 2 , . . . , i ] w[i-s.length+1,i-s.length+2,...,i] w[i−s.length+1,i−s.length+2,...,i]与字符串 s s s相同,即在文本中找到该单词。
时间复杂度: O ( n ) O(n) O(n)
AC自动机以Trie树为结构基础,以kmp为思想建立的。
状态 u u u的失配指针只想 v v v,且 v ∈ Q v \in Q v∈Q, v v v是 u u u的最长后缀。
#include
using namespace std;
const int N = 1000010;
const int S = 55;
char s[S], a[N];
int t[N][26], tot;
int e[N], fail[N];
inline void insert(char *s)
{
register int now = 0;
for (register int i = 1; s[i]; ++i) {
if (!t[now][s[i] - 'a']) t[now][s[i] - 'a'] = ++tot;
now = t[now][s[i] - 'a'];
}
++e[now];
}
queue<int> q;
inline void build()
{
for (register int i = 0; i < 26; ++i) {
if (t[0][i]) q.push(t[0][i]);
}
while (q.size()) {
register int now = q.front(); q.pop();
for (register int i = 0; i < 26; ++i) {
if (t[now][i]) {
fail[t[now][i]] = t[fail[now]][i], q.push(t[now][i]);
} else {
t[now][i] = t[fail[now]][i];
}
}
}
}
inline int query(char *s)
{
register int now = 0, ret = 0;
for (register int i = 1; s[i]; ++i) {
now = t[now][s[i] - 'a'];
for (register int j = now; j && e[j]; j = fail[j]) {
ret += e[j], e[j] = 0;
}
}
return ret;
}
inline void del()
{
fail[0] = e[0] = 0;
for (register int i = 0; i < 26; ++i) {
if (t[0][i]) q.push(t[0][i]);
t[0][i] = 0;
}
while (q.size()) {
register int now = q.front(); q.pop();
fail[now] = e[now] = 0;
for (register int i = 0; i < 26; ++i) {
if (t[now][i]) q.push(t[now][i]);
t[now][i] = 0;
}
}
}
int main()
{
register int T;
scanf("%d", &T);
while (T--) {
register int n;
scanf("%d", &n);
for (register int i = 1; i <= n; ++i) {
scanf("%s", s + 1);
insert(s);
}
build();
scanf("%s", a + 1);
printf("%d\n", query(a));
del();
}
return 0;
}
单回文串匹配。
register int len = strlen(s + 1), n = 0, ans = 0;
t[++n] = '#';
for (register int i = 1; i <= len; ++i) {
t[++n] = s[i];
t[++n] = '#';
}
t[++n] = '\0';
for (register int i = 1, l = 1, r = 0; i <= n; ++i) {
register int k = (i > r) ? 1 : min(d[l + r - i], r - i);
while (1 <= i - k && i + k <= n && t[i - k] == t[i + k]) ++k;
d[i] = k--;
if (i + k > r) l = i - k, r = i + k;
ans = max(ans, d[i] - 1);
}
如果存在字符串 S [ i , i + 1 , i + 2 , . . . , n ] + S [ 1 , 2 , 3 , . . . , i − 1 ] = T S[i, i+1,i+2,...,n]+S[1,2,3,...,i-1]=T S[i,i+1,i+2,...,n]+S[1,2,3,...,i−1]=T,则称字符串 S S S与字符串 T T T同构。
字符串 S S S的最小表示为所有字符串 S S S的循环同构中字典序最小的字符串。
字符串 S S S的最大表示为所有字符串 S S S的循环同构中字典序最大的字符串。
const int N = 100010;
char s[N];
inline int small()
{
register int i = 1, j = 2, k = 0;
while (i <= n && j <= n && k <= n) {
if (s[(i + k) % n + 1] == s[(j + k) % n + 1]) ++k;
else if (s[(i + k) % n + 1] > s[(j + k) % n + 1]) i = i + k + 1, k = 0;
else j = j + k + 1, k = 0;
if (i == j) ++i;
}
return min(i, j);
}
s.substr(i, len)
提取 s [ i , i + 1 , i + 2 , . . . , i + l e n − 1 ] s[i,i+1,i+2,...,i+len-1] s[i,i+1,i+2,...,i+len−1],即从第 i i i个位置获得长度为 l e n len len的子串。
s.find(t)
查询字符串 s s s中是否含有字符串 t t t,若不含有返回string::npos
,否则返回其第一次出现的首字母的索引。
strcmp(s + 1, t + 1)
若前者字典序小于后者字典序,返回负值;若两者字典序相等,返回零;若前者字典序大于后者字典序,返回正值。
strcpy(s + 1, t + 1)
将后者字符数组复制给前者。
Oi-wiki