字符串笔记

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;
}
posted @ 2026-07-23 15:53  zzzdc  阅读(1)  评论(0)    收藏  举报