字符串

前缀函数

给定一个字符串 \(s\),定义第 \(i\) 个位置上的前缀函数 \(fail[i]\) 表示其前缀和后缀相等的最大长度,对于每个 \(i\),求出 \(fail[i]\)

举例:对于文本串 abcab

\(fail[1]=0\),我们规定如此。

\(fail[2]=0\),因为 ab 没有相等的前缀和后缀。

\(fail[3]=0\),因为 abc 没有相等的前缀和后缀。

\(fail[4]=1\),因为 abca 有一个相等的前缀和后缀 a,长度为 \(1\)

\(fail[5]=2\),因为 abcab 有一个相等的前缀和后缀 ab,长度为 \(2\)

这里我们介绍 Wilson_Inversion 大佬教我的求 fail 数组的方法:

对于位置 \(i\)

  1. \(p=fail[i-1]\)
  2. 如果 \(s[i]\ne s[p+1]\),令 \(p=fail[p]\),否则 \(fail[i]=p+1\)
  3. 重复步骤 \(2\) 直到求出 \(fail[i]\)

具体地,我们仍然使用 abcab 来演示这一步骤:

对于位置 0:
\(fail[0]=-1\),为了方便,我们规定如此。

对于位置 1:
由于 \(fail[1-1]=-1\),所以 \(fail[1]\) 被直接赋值为 \(0\)

对于位置 2:
由于 \(fail[2-1]=0\)\(s[2]\ne s[1]\),所以 \(p\) 被赋值为 \(0\)
由于 \(fail[0]=-1\),所以 \(fail[2]\) 被直接赋值为 \(0\)

对于位置 3:
由于 \(fail[3-1]=0\)\(s[3]\ne s[1]\),所以 \(p\) 被赋值为 \(0\)
由于 \(fail[0]=-1\),所以 \(fail[3]\) 被直接赋值为 \(0\)

对于位置 4:
由于 \(fail[4-1]=0\)\(s[4]=s[1]\),所以 \(fail[4]=1\)

对于位置 5:
由于 \(fail[4-1]=1\)\(s[5]=s[2]\),所以 \(fail[5]=2\)

code:

int p = 0;
fail[0] = -1, fail[1] = 0;
for(int i = 2; i <= len1; i++) {
    while(p != -1 && s[i] != s[p + 1]) p = fail[p];
    fail[i] = ++p;
}

KMP

给定文本串 \(t\) 和模式串 \(s\),求模式串在文本串中所有出现位置。

和求 fail 数组的原理一样,只不过这次是在文本串中匹配模式串,我们按照如下方法进行:

  1. \(p=fail[i-1]\)
  2. 如果 \(t[i]\ne s[p+1]\),令 \(p=fail[p]\),否则 \(ans[i]=p+1\)
  3. 重复步骤 \(2\) 直到求出 \(ans[i]\)

这里 \(ans[i]\) 的含义是文本串的第 \(i\) 个位置的后缀与模式串的前缀的最大匹配长度,当这个长度与模式串长度相等时,代表模式串在文本串中出现了。

code:

int p = 0;
for(int i = 1; i <= len2; i++) {
    while(p != -1 && t[i] != s[p + 1]) p = fail[p];
    ans[i] = ++p;
}

Trie 树

图源:oi-wiki

posted @ 2023-01-21 14:11  lnlmz  阅读(40)  评论(1编辑  收藏  举报