HDU 5785 Interesting 2016 Multi-University Training Contest 5(Manacher)

题目链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1005&cid=708

题意:对于长度1e6的字符串,某个字串区间[L, R]若可以分割成两个回文字串[L, mid]和[mid + 1, R],那么这个区间的权值为L * R, 不能分割则权值为0, 求所有区间的权值和。

思路:用manacher算法O(n)求出所有的回文半径。有了回文半径后,就可以求出cntL[i]表示以i结尾的回文串的起始位置的和cntR[i]表示以i起始的回文串的结尾位置的和,然后就可以求出答案了,这里要注意奇偶长度回文串的不同处理。


代码:

 #include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <iostream>
#include <string>
#include <cmath>
#include <vector>
#include <set>
#include <map>
using namespace std;

#define REP(i, a, b) for (int i = (a), i##_end_ = (b); i < i##_end_; ++i)
#define MP make_pair
#define PB push_back
#define SZ(x) (int((x).size()))
#define ALL(x) (x).begin(), (x).end()
#define X first
#define Y second
template<typename T> inline bool chkmin(T &a, const T &b) { return a > b ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; }

template <class T>  
inline bool RD(T &ret) {  
        char c; int sgn;  
        if (c = getchar(), c == EOF) return 0;  
        while (c != '-' && (c<'0' || c>'9')) c = getchar();  
        sgn = (c == '-') ? -1 : 1 , ret = (c == '-') ? 0 : (c - '0');  
        while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');  
        ret *= sgn;  
        return 1;  
}  
template <class T>  
inline void PT(T x) {  
        if (x < 0) putchar('-') ,x = -x;  
        if (x > 9) PT(x / 10);  
        putchar(x % 10 + '0');  
}

const int INF = 0x3f3f3f3f;
typedef long long LL;
typedef pair<int, int> pii;
typedef long double LD;


const LL MOD = 1e9 + 7;

const int N = 1e6 + 100;
int Mp[N<<1];  
char Ma[N<<1];  

LL Lcnt[N], Rcnt[N], OLcnt[N], ORcnt[N];
LL Lval[N], Rval[N], OLval[N], ORval[N];
LL Lsum[N], Rsum[N];

void add(LL &a, LL val) {
	a += val;
	if(a >= MOD) a -= MOD;
}
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 = 1;i < l ;i ++){  
                Mp[i]= mx>i? min(Mp[2*id-i],mx-i) :1;  
                while(Ma[Mp[i]+i ] == Ma[i-Mp[i] ]) Mp[i]++;  
                if(Mp[i]+i > mx){  
                        mx = Mp[i]+i;  
                        id = i;  
                }  
                if(Mp[i] >= 2){  
                        int L = (i-Mp[i])>>1;   //逆推到原串中的区间左端点  
                        int R = (i+Mp[i]-4)>>1; //逆推到原串中的区间右端点 
						L ++, R ++;
						Lcnt[L] ++;
						Rcnt[R] ++;
						add(Lval[L], (L + R) / 2 * 2 + (R - L) % 2);
						add(Rval[R], (L + R) / 2 * 2 + (R - L) % 2);
						int one = 1;
						if((L + R) % 2 == 0) {
							add(OLcnt[(L + R) / 2], one);
			  			 	add(ORcnt[(L + R) / 2], one);	
							add(OLval[(L + R) / 2], (L + R) / 2 * 2 + (R - L) % 2);
						   	add(ORval[(L + R) / 2], (L + R) / 2 * 2 + (R - L) % 2);	
						} else {
							add(OLcnt[(L + R) / 2], one);
			  			 	add(ORcnt[(L + R) / 2 + 1], one);	
							add(OLval[(L + R) / 2], (L + R) / 2 * 2 + (R - L) % 2);
							add(ORval[(L + R) / 2 + 1], (L + R) / 2 * 2 + (R - L) % 2);
						}
				}  
        }  
}

char s[N];
int main() {
	while(~ scanf("%s", s)) {
		int len = strlen(s);
		for(int i = 0; i <= len; i ++) {
			Lcnt[i] = Rcnt[i] = OLcnt[i] = ORcnt[i] = Lval[i] = Rval[i] = Lsum[i] = Rsum[i] = OLval[i] = ORval[i] = 0;
		}
		Manacher(s, len);
		LL cur = 0, c = 0;
		for(int i = 1; i <= len; i ++) {
			add(cur, Lval[i]);
			add(c, Lcnt[i]);
			Rsum[i] = (cur - (i * c) % MOD + MOD) % MOD;
			cur = (cur - OLval[i] + MOD) % MOD;
			c = (c - OLcnt[i] + MOD) % MOD;
		}	
		cur = c = 0;
		for(int i = len; i >= 1; i --) {
			add(cur, Rval[i]);
			add(c, Rcnt[i]);
			Lsum[i] = (cur - (i * c) % MOD + MOD) % MOD;
			cur = (cur - ORval[i] + MOD) % MOD;
		   	c = (c - ORcnt[i] + MOD) % MOD;	
		}
		LL res = 0;
		for(int i = 1; i < len; i ++) res = (res + Lsum[i] * Rsum[i + 1]) % MOD;
		printf("%lld\n", res);
	
	}

}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值