kmp:
1.前缀数组:kmp[i]:指在s[1]-s[i]所组成的字符串的前缀和后缀相等的长度
令j=kmp[i-1],已知1~j与i-1-j~i-1相等,若s[i]==s[j+1],则易得kmp[i]==kmp[i-1]+1;
否则令j=kmp[kmp[i-1]], 重复以上操作,若j==0,则说明字符串s[1]~s[i]中前缀和后缀没有相等的
在O(n)的时间内可以求出:
int kmp[500010];
char s[500010];
kmp[0]=kmp[1]=0;
int j=0;
for(int i=2;i<=strlen(s);i++){
while(j&&s[j+1]!=s[i]) j=kmp[j];
if(s[j+1]==s[i]) j++;
kmp[i]=j;
}
2.前缀数组的应用:
kmp算法是前缀数组的最经典应用
现在要匹配字符串s1与s2,找到s1中s2子串的个数
eg:s1=babdbabac,s2=baba
方法:先将s2的前缀数组求出(kmp[1]=0;kmp[2]=0;kmp[3]=1;kmp[4]=2),然后遍历s1
假设s1第i位与s2第j位失配,说明s1与s2之前位置都成功匹配
则令j=kmp[j],继续将s1[i]与s2[j+1]匹配,直到成功匹配令j++;
时间复杂度O(strlen(s1)+strlen(s2));
int la=strlen(s1+1);
int lb=strlen(s2+1);
int j=0;
for(int i=1;i<=la;i++){//1串与2串匹配
while(j&&s2[j+1]!=s1[i]) j=qz[j];//匹配不上,j到j的前缀数组末尾位置
if(s2[j+1]==s1[i]) j++;//匹配上了,匹配长度+1
if(j==lb) printf("%d\n",i-lb+1);
}
求s2的前缀数组的过程就是将s2与自己匹配的过程
eg:洛谷P4391
此题应用了“求s2的前缀数组的过程就是将s2与自己匹配的过程”这个性质,答案就是a-kmp[a](重复叠加s[1]-s[a-kmp[a]],s一定是最后形成的字符串的子串
trie
类似于字典,树型数据结构,每一个节点代表一个单词,每一条边是一个字母
操作:insert:
int ch[500010][26],num,gs[500010];
bool ok[500010];
void insert(char *s,int l){
int p=0;
for(int i=0;i<l;i++){
int ty=s[i]-'a';
if(!ch[p][ty]) ch[p][ty]=++num;//若没有这个节点,将这个节点的id求出来
p=ch[p][ty];//p是这个节点代表单词的id,ch[p][ty]是这个节点经过边ty所到的节点的id
}
ok[p]=true;
}
find:
int find(char *s,int l){
int p=0;
for(int i=0;i<l;i++){
int ty=s[i]-'a';
if(!ch[p][ty]) return ***;
p=ch[p][ty];
}
//循环结束后p的值就是字符串s的id
return ***;//返回什么需要联系题目
}
erase:
void erase(char *s,int l){
int p=0;
for(int i=0;i<l;i++){
int ty=s[i]-'a';
p=ch[p][ty];
}
ok[p]=0;
}