M - Mediocre String Problem(Manacher算法+hash二分求lcp)

思路:由题意可知,回文的中心一定在s所取的部分中,然后可以想到 s [ i . . . k ] s[i...k] s[i...k]必定和 t [ 1... k ] t[1...k] t[1...k]是lcp(s中是倒着匹配),并且 s [ k + 1... j ] s[k+1...j] s[k+1...j]也需要是回文。所以可以枚举s中每个点为断点,用hash和二分求出lcp,并在Manacher算法中预处理出每个断点右边相邻的回文串的个数。

char s[N<<1], t[N];
int lens, lent;
ll p1[N], p2[N], h1[N], h2[N], h3[N], h4[N];
void initbase()
{
	p1[0] = p2[0] = 1;
	f(i, 1, N-1)
	{
		p1[i] = p1[i - 1] * 377 % mod;
		p2[i] = p2[i - 1] * 377 % mod2;
	}
	reverse(s + 1, s + lens + 1);
	for (int i = 1;i <= lens;i++)
		h1[i] = (h1[i - 1] * 377 + s[i]) % mod,
		h2[i] = (h2[i - 1] * 377 + s[i]) % mod2;
	for (int i = 1;i <= lent;i++)
		h3[i] = (h3[i - 1] * 377 + t[i]) % mod,
		h4[i] = (h4[i - 1] * 377 + t[i]) % mod2;
}
ll pre_of_suf(int x, int y)
{
	ll t1 = (h1[lens - x + 1] - (ll)h1[lens - y] * p1[y - x + 1] % mod + mod) % mod;
	ll t2 = (h2[lens - x + 1] - (ll)h2[lens - y] * p2[y - x + 1] %mod2 + mod2) % mod2;
	return t1 << 31 | t2;
}
ll suf_of_pre(int x, int y)
{
	ll t1 = (h3[y] - (ll)h3[x-1] * p1[y - x + 1] % mod + mod) % mod;
	ll t2 = (h4[y] - (ll)h4[x-1] * p2[y - x + 1] % mod2 + mod2) % mod2;
	return t1 << 31 | t2;
}
int tree[N],fin[N];
void add(int k, int num) 
{
	for (int i = k;i <= N; i += i & -i)
		tree[i] += num;
 
}
int read(int k)
{
	int sum = 0;
	for (int i = k;i > 0; i -= i & -i)
		sum += tree[i];
	return sum;
}
char Ma[N << 1], ss[N];
int r[N << 1];
void Manacher(char s[], int len)
{
	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++)
	{
		r[i] = mx > i ? min(r[2 * id - i], mx - i) : 1;
		while (Ma[i + r[i]] == Ma[i - r[i]])r[i]++;
		if (i + r[i] > mx)
		{
			mx = i + r[i];
			id = i;
		}
	}
	int idx = 0;
	f(i, 2, l - 1)
	{
		if (Ma[i] != '#')idx++;
		if (r[i] == 1)continue;
		int L = idx - r[i]/2+1, R = idx;
		add(L, 1);//树状数组区间算贡献
		add(R+1, -1);
	}
	f(i, 1, lens)fin[i] = read(i);
}
int main()
{
	//freopen("in.txt", "r", stdin);
	scanf("%s%s", s + 1, t + 1);
	strcpy(ss + 1, s + 1);
	lens = strlen(s + 1);lent = strlen(t + 1);
	Manacher(s + 1, lens);
	initbase();
	ll ans = 0;
	//f(i, 1, lens)debug(fin[i + 1]);
	f(i, 1, lens-1)
	{
		if (ss[i] != t[1])continue;
		int l = 1, r = min(lent, i);
		ll res = 1;
		while (l <= r)
		{
			int mid = l + r >> 1;
			if (suf_of_pre(1, mid) == pre_of_suf(i - mid + 1, i))
			{
				res = mid;
				l = mid + 1;
			}
			else r = mid - 1;
		}
		ans += fin[i + 1] * res;
	}
	cout << ans << endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值