回文串

Posted betablewaloot

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了回文串相关的知识,希望对你有一定的参考价值。

1694:回文串

时间限制: 1000 ms         内存限制: 262144 KB
【题目描述】

令F(A,B)表示选择一个串的非空前缀A和串B的非空后缀 使得将串S和串T拼接起来之后是回文串的方案数。

现在给定两个串A和B,令Ai表示串A的第i长的后缀,Bi为串B的第i长的前缀。

有Q组询问,第i 组询问给定xi 和yi ,对每组询问求F(Axi,Byi)的值。
【输入】

第一行一个字符串str,表示数据类型。

接下来的两行分别表示字符串A和B。

接下来一行一个正整数Q,表示询问的个数。

接下来Q行,每行两个正整数xi 和 yi。
【输出】

输出Q行,每行一个整数,表示这一组询问的答案。
【输入样例】

B
newionyzz
wyxioiwen
1
1 1

【输出样例】

16

【提示】

【样例解释】

对于样例 1,共有以下 16种方案:

{S=n,T=n};{S=n,T=en};{S=ne,T=n};{S=ne,T=en};

{S=ne,T=wen};{S=new,T=en};{S=new,T=wen};{S=new,T=iwen};

{S=new,T=ioiwen};{S=newi,T=wen};{S=newi,T=iwen};{S=newi,T=oiwen};

{S=newio,T=iwen};{S=newio,T=oiwen};{S=newio,T=ioiwen};{S=newion,T=oiwen}

【数据规模】

对于100%的数据,字符串中只出现小写字母。
子任务编号    子任务分值    max(|A|,|B|)<=8e5, Q<=1e5.

【题解】

首先将B串翻转,方便之后匹配。

对于前缀后缀拼接而成的回文串,出去A串和B串的最长公共前缀后,剩下的必须是一个回文串。

例如

A aba

B abacdqdc

拼接为abacdqdcaba。

此时除去最长公共前缀后是cdqdc

所以我们可以求出A,B中以每一位开头后头有几个回文串,设为f[i],g[i]。

统计答案时枚举A,B公共前缀,则答案为∑f[x+i]+g[y+i]+1。i为两串的公共前缀长度。

求出f,g数组前缀和即可用哈希二分最长公共前缀求解。

所以现在问题剩下求出f,g

可以跑一遍马拉车,对于以i为中心的回文串长度为cj[i]。则对f[i-cj[i]+1]到f[i]都有1的贡献可以差分求解,注意关于‘#’的处理。

时间复杂度

设ma=max(lena,lenb);

O(lena+lenb+Q*log2ma).

代码如下:

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
using namespace std;
const int N=8e5+5;
const int cc=131;
const int mo=998244353;
int cj[N<<1],n,m,f[N],g[N],q,zc,hu[N<<1],haa2[N],hab2[N],ch2[N];
char modeyong[10],s[N],t[N],c[N<<1];
ull haa[N],hab[N],ch[N];
inline void malache(int pu,int len)
{
    c[0]=$;c[1]=#;
    for(int i=1;i<=len;i++)
    {
        if(pu==0) c[i*2]=s[i];
        else c[i*2]=t[i];
        c[i*2+1]=#; 
    }
    int r=0,zai=0;
    len=len*2+1;c[len+1]=);
    memset(cj,0,sizeof(cj));
    for(int i=1;i<=len;i++)
    {
        if(i<r) cj[i]=min(r-i,cj[zai*2-i]);
        else cj[i]=1;
        while(c[cj[i]+i]==c[i-cj[i]]) cj[i]++;
        if(cj[i]+i>r) r=i+cj[i],zai=i;
    }
    memset(hu,0,sizeof(hu));
    for(int i=1;i<=len;i++)
    {
        int huu=i-cj[i]+1;
        if(huu%2==1) huu++;
        hu[huu]++;
        huu=i+1;
        if(huu%2==1) huu++;
        hu[huu]--; 
    }
}
inline int read()
{
    char c=getchar();
    int x=0,f=1;
    while(!isdigit(c)) {if(c==-) f=-1;c=getchar();}
    while(isdigit(c)) {x=(x<<3)+(x<<1)+c-0;c=getchar();}
    return x*f;
}
inline void get_hash()
{
    ch[0]=1;ch2[0]=1;
    for(int i=1;i<=N-3;i++)
        ch[i]=ch[i-1]*cc,ch2[i]=(ch2[i-1]*cc)%mo;
    for(int i=1;i<=n;i++) haa[i]=haa[i-1]*cc+s[i],haa2[i]=(haa2[i-1]*cc+s[i])%mo;
    for(int i=1;i<=m;i++) hab[i]=hab[i-1]*cc+t[i],hab2[i]=(hab2[i-1]*cc+t[i])%mo;
}
inline bool check(int x,int k1,int k2)
{
    ull hu1=haa[k1+x-1]-ch[x]*haa[k1-1];
    ull hu2=hab[k2+x-1]-hab[k2-1]*ch[x];
    int hu3=(haa2[k1+x-1]-haa2[k1-1]*ch2[x]%mo+mo)%mo;
    int hu4=(hab2[k2+x-1]-hab2[k2-1]*ch2[x]%mo+mo)%mo; 
    return (hu1==hu2)&&(hu3==hu4);
}
signed main()
{
    scanf("%s%s%s",modeyong,s+1,t+1);
    n=strlen(s+1);m=strlen(t+1);
    reverse(t+1,t+m+1);
    malache(0,n);
    for(int i=2;i<=2*n;i+=2)
        f[i/2]=hu[i];
    malache(1,m);zc=max(n,m);
    for(int i=2;i<=2*m;i+=2)
        g[i/2]=hu[i];
    for(int i=1;i<=n;i++) f[i]+=f[i-1];
    for(int i=1;i<=n;i++) f[i]+=f[i-1];
    for(int i=1;i<=m;i++) g[i]+=g[i-1];
    for(int i=1;i<=m;i++) g[i]+=g[i-1];
    get_hash();
    q=read();
    for(int i=1,x,y;i<=q;i++)
    {
        x=read();y=read();
        if(x>n||y>m)
        {
            cout<<"0
";
            continue;
        }
        int l=1,r=min(n-x+1,m-y+1),daan=0;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(check(mid,x,y)==1)
                l=mid+1,daan=mid;
            else
                r=mid-1;
        }
        cout<<f[min(n,x+daan)]+g[min(m,y+daan)]-f[x]-g[y]+daan<<"
";
    }
    return 0;
}

以上是关于回文串的主要内容,如果未能解决你的问题,请参考以下文章

最长回文字串暴力

CSDN|每日一练最长回文串

CSDN|每日一练最长回文串

添加字符判断是否为回文串

《算法竞赛入门经典》3.3最长回文子串

[JSOI2016]扭动的回文串