M - Mediocre String Problem( 扩展KMP + Manacher + 差分 )

M - Mediocre String Problem( 扩展KMP + Manacher + 差分 )

题意:给出一个串S,和一个串T. 要求 从S串中取一个子串,后面接上T串的一个前缀 组成一个结果串,(要求S串的部分比T串的部分长),结果串是回文串的个数。

思路:

S串贡献的部分 可以分成两部分,S1+S2;

前面的S1 是T部分的反转;

S2 就只能是回文串,因为S串的部分必须比T的多,所以S2长度必须大于等于1

然后我们可以分成两部分,首先先把S中的所有回文串求出,可以用(回文树/马拉车/字符串哈希)

对于每一个回文串,它的左边半径部分都可以作为S1的右端点,除了中心,而且边缘也可以吃到一个

比如 CABABA 其中 回文串中心是第二个A,S1的右端点可以是CAB 注意C也可以的哦

然后找出这个端点剩下的就是求S1和T的lcp的长度了,根据题意每一个长度都一个贡献一个符合要求的答案

针对这个问题 我们可以把S 反转 和T用EXKMP 求lcp (也可以用后缀数组/字符串哈希/exkmp/后缀自动机)

所以 这题的解法大概有(3×3=9 或者3×4=12)种。

注意:使用差分优化,开long long

代码:

#include<bits/stdc++.h>
#define int long long

using namespace std;

const int maxn = 2000006;
char s[maxn],ss[maxn],t[maxn];
int nxt[maxn],extend[maxn];
char Ma[maxn*3];
int Mp[maxn*3];
int st[maxn*3];

void get_nxt()
{
    int len = strlen(t);
    nxt[0] = len;
    int j = 0;
    while ( j+1<len && t[j]==t[j+1] ) j++;
    nxt[1] = j;
    int k = 1;
    for ( int i = 2; i<len; i++ ) {
        int p = nxt[k]+k-1;
        int L = nxt[i-k];
        if( i+L < p+1 ) nxt[i] = L;
        else {
            j = max(1ll*0,p-i+1);
            while( i+j<len && t[i+j]==t[j] ) j++;
            nxt[i] = j;
            k = i;
        }
    }
}

int exkmp()
{
    int lens = strlen(s);
    int lent = strlen(t);
    get_nxt();
    int j = 0;
    while( j<lens && j<lent && t[j]==s[j] ) j++;
    extend[0] = j;
    int k = 0;
    for( int i = 1; i<lens; i++ ) {
        int p = extend[k]+k-1;
        int L = nxt[i-k];
        if( i+L<p+1 ) extend[i] = L;
        else {
            j = max(1ll*0,p-i+1);
            while( i+j<lens && j<lent && s[i+j]==t[j] ) j++;
            extend[i] = j;
            k = i;
        }
    }

}

int Manacher()
{
    int len = strlen(s);
    int l = 0;
    Ma[l++] = '$';
    Ma[l++] = '#';
    for ( int i=0; i<len; i++ ) {
        Ma[l++] = s[i];
        Ma[l++] = '#';
    }
    Ma[l] = 0;
    int mx=0,id=0;
    for ( int i=0; i<l; i++ ) {
        Mp[i] = mx>i?min(Mp[2*id-i],mx-i):1;
        while ( Ma[i+Mp[i]]==Ma[i-Mp[i]] ) Mp[i]++;
        if ( i+Mp[i]>mx ) {
            mx = i+Mp[i];
            id = i;
        }
    }
    int ans = 0;
    for ( int i=1; i<l; i++ ) { /// 差分统计
        st[i]++; st[i+Mp[i]]--;
    }
    int now = 0;
    for ( int i=1; i<l; i++ ) {
        now += st[i];
        if ( Ma[i]=='#' ) continue ;
        if ( now>0 ) ans+=extend[i/2]*now;
    }
    cout << ans << endl;
}

signed main()
{
    scanf("%s %s",ss,t);
    int len = strlen(ss);
    for ( int i=0; i<len; i++ ) s[i]=ss[len-i-1];
    exkmp();
    Manacher();

    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值