KMP
KMP用于快速匹配字符串,查出一个串在另一个串中出现的位置及次数。
KMP的核心是失配数组的构建以及应用。
失配数组:习惯命名为next[],next[i]中存储信息含义是: 字符串前i位构成的字串中,若存在该串的某一前缀与后缀完全相同,则存储满足该条件的最长前缀/后缀长度(next[1]记为0)。如串abab,next[1]=0,next[2]=0,next[3]=1,next[4]=2;,在匹配中对模板串(即需要在另一串中查询其位置/次数的串)构建next数组
可以注意到,失配数组中存储的信息,也可用于表示上一个与此处相同的字串的末尾位置(如abab中,末尾ab与前端ab相同,next[4]中值为2,标记着前一个ab的末尾位置)
在匹配过程中,若出现失配(当前匹配到的位置上两串字符不同),利用失配数组可以快速前移模板串,使得模板串该部分的前缀,与之前已匹配过的,内容完全一样的后缀位置重合,并继续匹配,大幅度减短失配造成的调整时间
先看看如何运用next数组:
for(int i=0,j=0;t[i];i++) //w为模板串
{
while(j&&w[j]!=t[i])j=next[j];
if(t[i]==w[j])j++;
if(j==len)ans++;
}
而构建next数组的过程,思路上可看作自己与自己匹配
void getnext()
{
int l=strlen(w);
for(int i=1;i<l;i++)
{
int j=next[i];
while(j&&w[i]!=w[j])j=next[j];
next[i + 1] = w[i] == w[j] ? j + 1 : 0;
}
}
除用于匹配外,next数组还存在一些其他性质,如最小循环节的长度等
trie树
中文名字典树,是一种用于存储多个字符串的数据结构
trie树示意图:(图片来自百度百科)
trie树存储形式:
int ch[][26],其中存储的值为大于等于0的整数,0表示此处不是单词节点,正数表示此处是一个单词(已存储的字符串)
trie树的插入:
void insert(int x)
{
int n = strlen(tmp),p = 0;
for(int i = 0;i < n;i ++) {
int c = tmp[i] - 'a' + 1;
if(!ch[p][c]) ch[p][c] = ++ Tcnt;
p = ch[p][c];
}
val[p] = x;
}
manacher(马拉车)算法
马拉车算法用于计算字符串中每一个点,以其为中心,最长的回文串长度
马拉车算法分两步:
①两两字符插入特殊无关字符,将原串变为长度固定为奇数的串,便于处理
②暴力运算每个点扩展出的回文串长度,但当当前处理的点已存在于现前发现的回文串中时,可利用对称性质减少判定次数
(扩展意义似乎不大,直接上代码)
void prepare() {
n = strlen(s1 + 1);
s2[++len] = '@';
s2[++len] = '#';
for (int i = 1; i <= n; i++) {
s2[++len] = s1[i];
s2[++len] = '#';
}
s2[++len] = '\0';
}
void manacher() {
int right = 0, pos = -1;
for (int i = 1; i <= len; i++) {
int x;
if (right < i) x = 1;
else x = std::min(r[2 * pos - i], right - i);
while (s2[i - x] == s2[i + x]) x++;
if (x + i > right) {
right = x + i;
pos = i;
}
r[i] = x;
}
}
例题
POJ - 2752(kmp)
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
char s[400010];
int next[400010];
void getnext()
{
int l=strlen(s);
for(int i=1;i<l;i++)
{
int j=next[i];
while(j&&s[i]!=s[j])j=next[j];
next[i+1]=s[i]==s[j]?j+1:0;
}
}
int main()
{
int ans[400010],cnt;
while(scanf("%s",s)!=EOF)
{
cnt=0;
int len=strlen(s);
next[0]=next[1]=0;
getnext();
while(len)
{
ans[cnt++]=len;
len=next[len];
}
for(int i=cnt-1;i>=0;i--)
printf("%d ",ans[i]);
printf("\n");
}
return 0;
}
POJ 3974(马拉车)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
char s[1000010];
char t[2000050];
int r[2000050];
int len;
void prepare()
{
t[0]='@';
t[1]='#';
len=2;
for(int i=0;s[i];i++)
{
t[len++]=s[i];
t[len++]='#';
}
t[len++]=0;
}
int main()
{
int cnt=0,right,pos,ans;
scanf("%s",s);
while(!(s[0]=='E'&&s[1]=='N'&&s[2]=='D'))
{
ans=0;
right=0;pos=-1;
printf("Case %d: ",++cnt);
prepare();
for(int i=1;i<=len;i++)
{
int x;
if(right<i)x=1;
else x=min(r[2*pos-i],right-i+1);
while(t[i-x]==t[i+x])x++;
if(x+i-1>right)
{
right=x+i-1;
pos=i;
}
r[i]=x;
int res;
if(t[i]=='#')res=(x/2)*2;
else res=((x-1)/2)*2+1;
if(res>ans)ans=res;
}
printf("%d\n",ans);
/*for(int i=1;i<len;i++)printf("%c",t[i]);
cout<<endl;
for(int i=1;i<len;i++)printf("%d ",r[i]);*/
scanf("%s",s);
}
return 0;
}