Manacher算法总结

Manacher算法

Manacher算法是一种优秀的在O(n)时间复杂度内计算回文串的算法。利用该算法可以解决许多与回文串有关的问题。

求最长回文串这种模板就不说了。这里有用manacher解决字符串问题的两道进阶题。

BZOJ2565:最长双回文串。

定义双回文串T,满足存在T=ab,a,b都为回文串。给定字符串S,求maxlen(T),T∈S.

题解:
考虑对于每一个双回文串必然有一个分界线,而对于每一个分界线,左右必然存在最优解。所以应该维护一个分界点两边的最长回文串。

显然向左的最长回文串与向右的最长回文串求法一样(只需要将字符串翻转求即可)。那么考虑怎么求向左的最长回文串。

对于一个分界点,其向左的最长回文字串一定是manacher算法中mx第一次覆盖的时候。

证明:
设结尾为i的最长回文串中点为p,半径为R。
假设此时mx未覆盖i,那么p+R>mx ,与mx定义矛盾,故mx一定覆盖i。
假设mx已经覆盖i,覆盖i的时候中点为p1,半径为r1,若存在p2,使得i-p2>i-p1,那么p2一定在p1之前,而此时i未被mx覆盖,矛盾,故i的最长回文串一定在mx第一次覆盖i时被更新。

有了上面的证明这道题就很好做了,manacher时每更新mx,就更新之前未被更新的lmax,因为每个点只会被更新1次,故时间复杂度为O(n).

  • Code
#include
using namespace std;
const int Maxn=1e5+50;

char s[Maxn],t[Maxn*2];
int R[Maxn*2],tot,p,mx,lmax[Maxn*2],rmax[Maxn*2];

inline void manacher(int *a)
{
    p=1,mx=2,R[1]=1;
    int bg=2;
    for(int i=2;i<=tot;i++)
    {
        if(mx>i)R[i]=min(R[2*p-i],mx-i);
        else R[i]=1;
        while(t[i+R[i]]==t[i-R[i]])R[i]++;
        if(i+R[i]>mx)
        {
            mx=i+R[i];p=i;
            while(bgint main()
{
    scanf("%s",s+1);
    int len=strlen(s+1);
    t[++tot]='!';
    t[++tot]='#';
    for(int i=1;i<=len;i++)
    {
        t[++tot]=s[i];
        t[++tot]='#';
    }
    t[++tot]='?';
    manacher(lmax);
    reverse(t+1,t+tot+1);
    manacher(rmax);
    int ans=0;
    for(int i=2;i1],ans);
    }
    printf("%d\n",ans);
}

Codeforces 17E:Palisection

给字符串S,求S中所有相交的回文串的对数。

题解:
求相交回文串很难,只需求总对数减去不相交回文串对数。

St[i] 为以 i 点为起点的回文串个数, Ed[i] 为以 i 点为终点的回文串个数。设总的不相交回文串对数为ans。容易发现:

ans=i=1nSt[i]j=i+1nEd[i]

St[i],Ed[i] ,可以用差分,处理 St[i] ,对于每个点i,有R[i],在i-R[i]处加上1,i+1处减去1。那么前缀和就对应 St[i] .
Ed[i] 类似,统计完后处理 Ed[i] 的后缀和(或者前缀和)就很好做了。

  • Code
#include
using namespace std;
const int Maxn=4e6+50;
const int Mod=51123987;

char s[Maxn],t[Maxn];
int len,R[Maxn],tot,tag1[Maxn],tag2[Maxn],sum[Maxn];

inline void manacher()
{
    int p=0,mx=0;
    for(int i=1;i<=tot;i++)
    {
        if(mx>i)R[i]=min(R[2*p-i],mx-i);
        else R[i]=1;
        while(t[i+R[i]]==t[i-R[i]])R[i]++;
        if(i+R[i]>mx){mx=i+R[i];p=i;}
    }
}

int main()
{
    scanf("%d%s",&len,s+1);
    t[0]='?';
    t[++tot]='#';
    for(int i=1;i<=len;i++)
    {
        t[++tot]=s[i];
        t[++tot]='#';
    }
    t[++tot]='!';
    manacher();
    int all=0;
    for(int i=2;iif(i&1)
        {
            all=(1ll*all+1ll*(R[i]-1)/2)%Mod;
            tag1[(i-R[i]+2)/2]++;
            tag1[(i+1)/2]--;
            tag2[(i+R[i]-2)/2+1]--;
            tag2[(i-1)/2+1]++;
        }
        else
        {
            all=(1ll*all+1ll*(R[i]+1)/2)%Mod;
            tag1[(i-R[i]+2)/2]++;
            tag1[i/2+1]--;
            tag2[(i+R[i]-2)/2+1]--;
            tag2[i/2]++;
        }
    }
    all=((1ll*all*(all-1))/2)%Mod;
    for(int i=1;i<=len;i++)
    {
        tag1[i]=(tag1[i]+tag1[i-1])%Mod;
    }
    int ans=0;
    for(int i=1;i<=len;i++)
    {
        tag2[i]=(tag2[i]+tag2[i-1])%Mod;
        sum[i]=(sum[i-1]+tag2[i])%Mod;
        ans=(1ll*ans+1ll*tag1[i]*sum[i-1])%Mod;
    }
    printf("%d\n",(all+Mod-ans)%Mod);
}

你可能感兴趣的:(manacher)