bzoj3160

题意:给定一个字符串,求出所有不连续的回文子序列,并且该子序列在原串的位置关于某位置对称。


先忽略掉不连续这个条件,先求出所有的然后减去连续的。

连续的就是回文子串 用Manacher算法可以O(n)求解,(注意这题回文中心可能是在2个字符之间)

所有的情况,要分2种情况,第一个是回文中心是某个字符,另外一个就是在2个字符之间。

考虑到如果s[i-k]=s[i+k],那么这就会对第i个字符产生1的贡献。

如果s[j]=s[k],那么会对(j+k)/2这个位置有1的贡献。

第一种情况,(i-k)+(i+k)=2i,第二种情况j+k。

这好像是个卷积?

所以对ab两种字符分开做,第一种就是s[i]='a',这个位置就是1,否则就是0。第二种情况s[i]='b',这个位置是1否则是0。

然后对这2个多项式进行平方操作(设平方后一个是a一个是b),2个多项式长度*2-1,这时候就相当于在每2个字符中插入一个字符,对于一个项的系数,它的贡献就是2^((a[i]+1)/2+(b[i]+1)/2)  -1,这里的除以2都是下取整,对于一个多项式的平方,一个项的系数A[i] = sigma(a[j] * a[i - j]),若是j不等于i-j,那么a[i]和a[i-j]就会被贡献2次,若是i=j,则贡献一次,那么假设i这个位置是1,那么平方后i*2这个位置肯定是个奇数因为i这个位置必然贡献了1。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 262149;
const double pi = 3.141592653589793238462643383;
const int MOD = 1000000007;
double si[MAXN], co[MAXN];
int n, i, j, k, po[MAXN], m, f[MAXN], bit[MAXN], ans;
char s[MAXN], c[MAXN];
struct sb{
    double x, y;
    inline friend sb operator * (const sb &a, const sb &b)
    {
        return (sb){a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x};
    }
    inline friend sb operator + (const sb &a, const sb &b)
    {
        return (sb){a.x + b.x, a.y + b.y};
    }
    inline friend sb operator - (const sb &a, const sb &b)
    {
        return (sb){a.x - b.x, a.y - b.y};
    }
};
sb a[MAXN], b[MAXN];
inline int fft_init(int m)
{
    int n = 1, nn = 0;
    si[1] = sin(pi * 2.0);
    co[1] = cos(pi * 2.0);
    while (n < m) n <<= 1, nn ++, si[n] = sin(pi * 2.0 / n), co[n] = cos(pi * 2.0 / n);
    for(int i = 0; i < n; i ++)
        bit[i] = (bit[i >> 1] >> 1) | ((i & 1) << nn - 1);
    return nn;
}
inline void fft(sb *a, int nn, int ty)
{
    int n = 1 << nn;
    for(int i = 0; i < n; i ++)
        if (i < bit[i]) swap(a[i], a[bit[i]]);
    for(int k = 1; k <= nn; k ++)
    {
        int len = 1 << k;
        sb wn = (ty == 1) ? (sb){co[len], si[len]} : (sb){co[len], -si[len]};
        for(int j = 0; j < n; j += len)
        {
            int m = len >> 1;
            sb w = (sb){1.0, 0.0};
            for(int i = j; i < j + m; i ++)
            {
                sb l = a[i], t = a[i + m] * w;
                a[i] = l + t;
                a[i + m] = l - t;
                w = w * wn;
            }
        }
    }
}
int main()
{
    scanf("%s", s);
    n = strlen(s);
    po[0] = 1;
    for(i = 1; i <= 250000; i ++)
        po[i] = po[i - 1] * 2 % MOD;
    c[0] = '.';
    for(i = 0, j = 1; i < n; j ++)
        if (j & 1) c[j] = s[i ++];
        else c[j] = '.';
    int p = 0, mx = 0, N = (n << 1) + 1; f[0] = 0; c[N - 1] = '.';
    for(i = 1; i < N; i ++)
    {
        if (p + mx > i) f[i] = min(f[i - ((i - p) << 1)], p + mx - i);
        else f[i] = 0;
        while (i - f[i] - 1 >= 0 && f[i] + i + 1 < N && c[i - f[i] - 1] == c[i + f[i] + 1]) f[i] ++;
        if (i + f[i] > p + mx) p = i, mx = f[i];
    }
    for(i = 0; i < N; i ++)
    {
        ans -= (f[i] + 1 >> 1);
        if (ans < 0) ans += MOD;
    }
    for(i = 0; i < n; i ++)
        if (s[i] == 'a') a[i].x = 1;
        else b[i].x = 1;
    int nn = fft_init(n + n + 1);
    N = 1 << nn;
    fft(a, nn, 1);
    fft(b, nn, 1);
    for(i = 0; i < N; i ++)
        a[i] = a[i] * a[i], b[i] = b[i] * b[i];
    fft(a, nn, -1);
    fft(b, nn, -1);
    for(i = 0; i < N; i ++)
    {
        int x = (int)(a[i].x / N + 1.5) >> 1, y = (int)(b[i].x / N + 1.5) >> 1;
        ans += po[x + y] - 1;
        if (ans < 0) ans += MOD;
        if (ans >= MOD) ans -= MOD;
    }
    cout << ans << endl;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值