字符串笔记
KMP
定义前缀数组 \(p_i\) 为 \([...,i]\) 与 \([1,...]\) 相等的最大长度。
按照定义前后倒一倒就对了。
p[0]=0;
for(int i=1;i<n;i++)
{
int j=p[i-1];
while(j&&str[j]!=str[i]) j=p[j-1];//p是长度,str从0开始,减一
if(str[j]==str[i]) j++;
p[i]=j;
}
exKMP
定义函数 \(𝑧_𝑖\),\(z_i\) 表示以 \(𝑠tr_𝑖\) 开头的后缀的最长公共前缀(LCP)的长度。
记录一个当前匹配最远段 \(r\),在 \(i\) 到 \(r\) 的box中,\(str[i,r]\) 与 \(str[i-l,r-l]\) 相等,所以 \(z[i] \geq min(z[i-l],r-i+1)\) ,再暴力扩展一下就好了。
int l=0,r=0;
for(int i=1;i<n;i++)
{
if(i<=r) z[i]=min(z[i-l],r-i+1);
while(i+z[i]<n&&str[z[i]-1]==str[z[i]+i-1]) z[i]++;
if(i+z[i]-1>r) l=i,r=i+z[i]-1;
}
manacher
字符串关于中间对称,和扩展kmp类似的,维护一对 \(l,r\) 。
每次也暴力扩展一下。
//先将字符串处理成"a#b#c#a#..."的形式,将偶回文和奇回文一块处理
int l=0,r=-1;
for(int i=0;i<n;i++)
{
if(i<=r) d[i]=min(d[l+r-i],r-i);
while(i+d[i]<n&&i-d[i]>0&&str[i+d[i]]==str[i-d[i]]) d[i]++;
if(i+d[i]>r) l=i-d[i],r=i+d[i];
}
//答案是d[i]/2
trie树
void Insert()
{
int now=0;
for(auto ch:str)
{
if(!nxt[now][ch-'a']) nxt[now][ch]=++addr;
now=nxt[now][ch];
}
ed[now]++;
}
int Find()
{
int now=0;
for(auto ch:str)
{
if(!nxt[now][ch-'a']) return 0;
now=nxt[now][ch-'a'];
}
if(!ed[now]) return 0;
return 1;
}

浙公网安备 33010602011771号