Mediocre String Problem (manacher+扩展kmp+差分)

题目大意:

给出,s1,s2, 挑一些 s1的字串+s2的前缀字串,看他们是不是回文串, len(subs1)>len(subs2)>1=1;

统计有多少个

  • 首先 subs1是要和s2成回文,他的内部也要是回文,于是就分为2部分
  • 求第一部分: 把s1翻转,然后来求一个扩展kmp,对于目标窜s2,求的是个数,那么求出的最大长度就有len个那个ans
  • 然后看后面部分是不是回文窜,如何看有多少个? 就是看有一个回文串是以后面那个字母位开始(因为翻转了,就是前面一个字母,最后在翻回去)
  • 利用manacher来求出这个东东
  • 利用差分来来搞,因为求出的是最大的回文串,字串也要计入贡献
  • 最后统计一下就ok了
  • 详细情况看代码
#include <bits/stdc++.h>
using namespace std;
#define ri register int
#define M 4000015

int n,m;
string s1,s2;
int nxt[M];
int et[M];
void getnxt()
{
    int len=s2.length();
    nxt[0]=len;
    int i=0;
    while(s2[i]==s2[i+1]&&i+1<len) i++;nxt[1]=i;
    int po=1;
    for(ri i=2;i<len;i++)
    {
        if(nxt[i-po]+i<po+nxt[po])
        {
            nxt[i]=nxt[i-po];
        }
        else
        {
            int j=po+nxt[po]-i;
            if(j<0) j=0;
            while(j<len&&s2[j]==s2[i+j]) j++;nxt[i]=j;
            po=i;
        }
    }    
    
}
long long num[M];
long long nn[M];
string s;
void getet()
{
    for(ri i=s1.length()-1,j=0;i>=0;i--,j++)
    {
       s[j]=s1[i];
    }
    int i=0;
    int l1=s1.length(),l2=s2.length();
    while(s[i]==s2[i]&&i<l2&&i<l1) i++;
    et[0]=i;
    int po=0;
    for(ri i=1;i<l1;i++)
    {
        if(nxt[i-po]+i<po+et[po])
        {
            et[i]=nxt[i-po];
        }
        else
        {
            int j=po+et[po]-i;
            if(j<0) j=0;
            while(j<l2&&i+j<l1&&s[i+j]==s2[j]) j++;et[i]=j;
            po=i;
        }
        num[i-1]=et[i];
    //    cout<<num[i-1]<<" ";
    }
    for(ri i=s1.length()-1,j=0;i>=0;i--,j++)
    {
       nn[j]=num[i];
    }
    
}
long long ans=0;
int p[M];
void manacher()
{
    s="$#";
    for(ri i=0;i<s1.length();i++)
    {
        s+=s1[i];
        s+="#";
    }
    int mid=0,r=0;
    for(ri i=0;i<s1.length();i++) num[i]=0;
    for(ri i=0;i<s.length();i++)
    {
        if(i<r)
        {
            p[i]=min(r-i+1,p[2*mid-i]);
        }
            while(s[i-p[i]]==s[i+p[i]]) p[i]++;
           if(p[i]+i>r) r=p[i]+i-1,mid=i;
           int b=(i-p[i])/2;
           if((p[i]-1)&1)
           {
                 int a=(i-p[i])/2;
                 
                num[a]+=1;
                num[a+((p[i]-1)/2)+1]+=-1;
           }
           else
           {
                 int a=(i-p[i])/2;
                num[a]+=1;
                num[(p[i]-1)/2+a]+=-1;
           }
        
    }
    
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    
    cin>>s1>>s2;
    getnxt();
    getet();
    manacher();
    long long arr=0;
    for(ri i=0;i<s1.length();i++)
    {
       arr+=num[i];
       ans+=arr*nn[i];
    }
    cout<<ans;
    
}
View Code

后记:

  • 为了方便,内存开4倍
  • 注意在处理回文的时候,找他的真实位置(初始位置来确定,而不是 i
  • p[i]-1 是0,可以直接跳过
  • 思路就是,想思路,然后遇到新的问题,在此基础上想解决方案,而不是重新开始
posted @ 2022-07-13 16:47  VxiaohuanV  阅读(24)  评论(0编辑  收藏  举报