KMP与Trie

发布时间 2023-08-03 17:25:05作者: 待到春来蕴

KMP算法

KMP算法用于解决字串与母串的匹配问题,可看作哈希的简单写法,时间复杂度O(m+n)

KMP算法的核心优势在于相对于暴力枚举,它可以省去重复的步骤,从而将匹配过程由O(mn)优化为近似O(2m),该算法的核心在于寻找子串前缀与后缀重合的最大长度,也就是next数组,那么怎么求呢?就是将子串自匹配

代码如下:

cin >> (a+1);
l = strlen(a+1);
for(int i = 2,j = 0;i <= l;i++){
while(j && a[i] != a[j+1]) j = nxt[j];
if(a[i] == a[j+1]) j++;
nxt[i] = j;
}

那么KMP算法的具体用法有哪些呢?大致分为三种

一、       求最小循环节

例如:我们在abababab中,找到其前后缀重复的最大长度,也就是next[8],此时其值为6,不难发现s1-6 = s3-8,s1-2 = s7-8,由此可推出其最小循环节长度为二,也就是L-next[L];

二、       求一个字符串是由多少个相同字符串重复组成的

分为两种情况:可重复的:直接用总长度除以最小循环节

int k = l-nxt[l];
if(l%k) printf("1\n");
else printf("%d\n",l/k);

不可重复的:子母串正常匹配,然后匹配到答案++,j归零

for(int i = 1,j = 0;i <= lb;i++){
while(j && b[i] != a[j+1]) j = nxt[j];
if(b[i] == a[j+1]) j++;
if(j == la) ans++,j = 0;
}
printf("%d\n",ans);

三、       题面如下

串是有限个小写字符的序列,特别的,一个空序列也可以是一个串。一个串 P 是串 A 的前缀,当且仅当存在串 B,使得 A=PB。如果P≠A并且 P 不是一个空串,那么我们说 P 是 A 的一个 proper 前缀。

定义 Q 是 A 的周期,当且仅当 Q 是 A 的一个 proper 前缀并且 A 是 QQ 的前缀(不一定要是 proper 前缀)。比如串 abab 和 ababab 都是串 abababa 的周期。串 A 的最大周期就是它最长的一个周期或者是一个空串(当 A 没有周期的时候),比如说,ababab 的最大周期是 abab。串 abc 的最大周期是空串。

给出一个串,求出它所有前缀的最大周期长度之和。

思路:求出最小next数组,用总长度减去即是答案

原因:next数组定义为前后缀重合最大长度,那么以这个长度切开再扩倍一定是可以前后相接的,代码:

#include<bits/stdc++.h>
using namespace std;
char a[1000005];
int n,la,lb,nxt[1000005];
long long ans;
int main(){
scanf("%d",&n);
cin >> (a+1);
for(int i = 2,j = 0;i <= n;i++){
while(j && a[i] != a[j+1]) j = nxt[j];
if(a[i] == a[j+1]) j++;
nxt[i] = j;
}
int q;
for(int i = 1;i <= n;i++){
q = i;
while(nxt[q]) q = nxt[q];
if(nxt[i]) nxt[i] = q;
ans+=i-q;
}
printf("%lld",ans);
return 0;
}

Trie字典树

解决多串前缀匹配问题

核心:有点更新指针,继续走,无点建点,一个串走完需标记

模板

#include<bits/stdc++.h>
using namespace std;
int t,n,tree[100005][15],idx,mk[100005],q,l;
string a;
bool insert(string s){
int c,p = 0,flag = 0;
for(int i = 0;i < l;i++){
c = s[i] - '0';
if(!tree[p][c]){
tree[p][c] = ++idx;
p = tree[p][c];
}else{
p = tree[p][c];
if(mk[p]||i == l-1) flag = 1;
}
}
mk[idx]++;
return flag;
}
int main(){
scanf("%d",&t);
for(int i = 1;i <= t;i++){
scanf("%d",&n);
q = 0;
idx = 0;
memset(tree,0,sizeof(tree));
memset(mk,0,sizeof(mk));
for(int j = 1;j <= n;j++){
cin >> a;
l = a.size();
if(insert(a)){
q = 1;
}
}
if(!q) printf("YES\n");
else printf("NO\n");
}
return 0;
}

注意p和idx初始值要相同,不然会出问题