HDU_5157 Harry and magic string(回文树+前缀和)

Harry and magic string

Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 32768/32768 K (Java/Others)
Problem Description

Harry got a string T, he wanted to know the number of T’s disjoint palindrome substring pairs. A string is considered to be palindrome if and only if it reads the same backward or forward. For two substrings of T T T: x = T [ a 1 … b 1 ] x=T[a_1…b_1] x=T[a1b1], y = T [ a 2 … b 2 ] y=T[a_2…b_2] y=T[a2b2](where a 1 a_1 a1 is the beginning index of x, b 1 b_1 b1 is the ending index of x. a 2 a_2 a2, b 2 b_2 b2 as the same of y), if both x and y are palindromes and b 1 < a 2 b_1<a_2 b1<a2 or b 2 < a 1 b_2<a_1 b2<a1 then we consider ( x , y ) (x, y) (x,y) to be a disjoint palindrome substring pair of T.

Input

There are several cases.
For each test case, there is a string T in the first line, which is composed by lowercase characters. The length of T is in the range of [1,100000].

Output

For each test case, output one number in a line, indecates the answer.

Sample Input

aca
aaaa

Sample Output

3
15

题意

求字符串中不相交的回文子串的点对数。

题解:

对于每个位置 i i i,求出以它为结束位置和以它为起始位置的回文子串的数量,分别即为 a i a_i ai, b i b_i bi
对于每个点对,已知以 i i i为起始位置的回文子串有 b i b_i bi个,则回文点对有 ( a 1 + a 2 + . . + a i − 1 ) ∗ b i (a_1+a_2+..+a_{i-1})*b_i (a1+a2+..+ai1)bi个。
求出所有i的和即可。
b i b_i bi可以通过反向建回文树求得。
a 1 + a 2 + . . + a i − 1 a_1+a_2+..+a_{i-1} a1+a2+..+ai1可以通过前缀和计算。

#include<stdio.h>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<iterator>
#define dbg(x) cout<<#x<<" = "<<x<<endl;
#define INF 0x3f3f3f3f
#define eps 1e-6
 
using namespace std;
typedef long long LL;   
typedef pair<int, int> P;
const int maxn = 200010;
const int mod = 1000000007;
struct node{
	int len, fail, cnt, num, nex[26];
}st[maxn];
int cnt, last, len, c[maxn], b[maxn];
LL sum[maxn];
char str[maxn], s[maxn];
void init();
int creat(int x);
int getfail(int x);
int Insert(char ch);

int main()
{
	int n, i, j, k;
	while(~scanf("%s", str+1))
	{
		LL ans = 0;
		n = strlen(str+1);
		init();
		sum[0] = 0;
		for(i=1;i<=n;i++){
			b[i] = Insert(str[i]);
			sum[i] = sum[i-1] + b[i];
		}
		init();
		for(i=n;i>=1;i--){
			c[i] = Insert(str[i]);
			ans += c[i]*sum[i-1];
		}
		printf("%lld\n", ans);
	}
	return 0;
}

void init()
{
	cnt = -1;
	last = len = 0;
	str[0] = '*';
	creat(0);
	creat(-1);
	st[0].fail = 1;
}

int creat(int x)
{
	cnt++;
	st[cnt].len = x;
	memset(st[cnt].nex, 0, sizeof(st[cnt].nex));
	st[cnt].cnt = st[cnt].num = 0;
	st[cnt].fail = 0;
	return cnt;
}

int getfail(int x)
{
	while(s[len-st[x].len-1] != s[len]) x = st[x].fail;
	return x;
}

int Insert(char ch)
{
	s[++len] = ch;
	int cur = getfail(last);
	if(!st[cur].nex[ch-'a']){
		int now = creat(st[cur].len + 2);
		st[now].fail = st[getfail(st[cur].fail)].nex[ch-'a'];
		st[cur].nex[ch-'a'] = now;
		st[now].num = st[st[now].fail].num + 1;
	}
	last = st[cur].nex[ch-'a'];
	st[last].cnt++;
	return st[last].num;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值