[BZOJ]3160 万径人踪灭 Manacher + FFT

3160: 万径人踪灭

Time Limit: 10 Sec   Memory Limit: 256 MB
Submit: 1753   Solved: 977
[ Submit][ Status][ Discuss]

Description

Input

Output

Sample Input

Sample Output

HINT

Source

[ Submit][ Status][ Discuss]


HOME Back

  FFT板子后的第一次实践~ 这道题要求求不连续的回文子序列(位置对称), 看起来就很不好搞对不对啊~ 所以说我们要考虑补集转化. 这就等于所有回文子序列 - 连续的回文子序列. 后面那个直接可以Manacher来搞. 我们考虑前面那个怎么做.

  我们令f[i]表示为在manacher补充#之后的数组s里关于位置i对称并相等的对数. 那么如果x, y要满足这个条件成为一对, 我们会发现这就要求原串里(x / 2) + (y / 2) = i, 因为x / 2和y / 2实际上就是原串位置 + 1(x, y是在s中的). 所以说原串中ss[x]如果等于ss[y], 那么就关于s中x + 1 + y + 1位置对称.  然后就有:


fi=1+i2j=0[sj==si2j]2   ⌋


  发现是个卷积于是就可以愉快的FFT辣.一开始Manacher写错T了一发身败名裂

#include<bits/stdc++.h>
using namespace std;
typedef long long lnt;
typedef complex<double> C;
const int mod = 1e9 + 7;
const int maxn = 4e5 + 5;
const double pi = acos(-1.0);
lnt ans;
char ss[maxn], s[maxn];
int n, L, rev[maxn], pal[maxn], pw[maxn], f[maxn];
C a[maxn], b[maxn];
inline void FFT(C *a, int f) {
	C x, y;
	for (int i = 0; i < n; ++ i)
		if (rev[i] > i) swap(a[i], a[rev[i]]);
	for (int i = 1; i < n; i <<= 1) {
		C wn(cos(pi / i), f * sin(pi / i));
		for (int j = 0; j < n; j += i << 1) {
			C w = 1;
			for (int k = 0; k < i; ++ k, w *= wn) {
				x = a[j + k], y = w * a[j + k + i];
				a[j + k] = x + y, a[j + k + i] = x - y;
			}
		}
	}
}
inline int manacher(char *r) {
	int m = 0, mx = 0, id = 0, sum = 0;
	for (int i = 0; r[i]; ++ i) s[++ m] = '#', s[++ m] = r[i];
	s[0] = '+', s[++ m] = '#';
	for (int i = 1; i < m; ++ i) {
		if (mx > i) pal[i] = min(mx - i, pal[2 * id - i]);
		while (s[i - pal[i]] == s[i + pal[i]]) pal[i] ++;
		if (i + pal[i] > mx) mx = pal[i] + i, id = i;
		sum = sum + pal[i] / 2;
		if (sum > mod) sum -= mod;
	}
	return sum;
}
int main() {
	scanf("%s", ss);
	int len = strlen(ss);
	register int i;
	for (pw[0] = 1, i = 1; i < maxn; ++ i) pw[i] = (pw[i - 1] << 1) % mod;
	for (n = 1; n <= len << 1; n <<= 1) L ++;
	for (i = 0; i < n; ++ i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (L - 1));
	for (i = 0; i < n; ++ i) a[i] = (ss[i] == 'a');
	FFT(a, 1);
	for (i = 0; i < n; ++ i) b[i] += a[i] * a[i];
	for (i = 0; i < n; ++ i) a[i] = (ss[i] == 'b');
	FFT(a, 1);
	for (i = 0; i < n; ++ i) b[i] += a[i] * a[i];
	FFT(b, -1);
	for (i = 2; i <= len << 1; ++ i) f[i] += (lnt)(b[i - 2].real() + 0.5) / n;
	for (i = 2; i <= len << 1; ++ i) ans = (ans + pw[(f[i] + 1) >> 1] - 1) % mod;
	printf("%lld\n", (ans - manacher(ss) + mod) % mod);
	return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值