A1393. Palisection
时间限制:
2.0s 内存限制:
256.0MB
试题来源
CODEFORCES 17E
问题描述
给你一个长度 n (1 ≤ n ≤ 2·10
6) 的只由小写字母组成的字符串s。
我们考虑s的所有连续且回文的子串集合P。位置不同但内容相同的两个串算作不同。
问从P中选出两个串且他们在s中有公共位置的方法数有几个?
我们考虑s的所有连续且回文的子串集合P。位置不同但内容相同的两个串算作不同。
问从P中选出两个串且他们在s中有公共位置的方法数有几个?
输入格式
第一行一个整数n
第二行字符串s
第二行字符串s
输出格式
一行答案,输出结果对51123987取余。
样例输入
4
babb
babb
样例输出
6
数据规模和约定
10%的数据 n<=5
15%的数据 n<=100
25%的数据 n<=1000
50%的数据 n<= 2·10 6
15%的数据 n<=100
25%的数据 n<=1000
50%的数据 n<= 2·10 6
反着插一次,计算后缀和,再正着插一次,计算没有重复位置的字符串对,然后用总的减掉就好了
/*======================================================
# Author: whai
#
# Last modified: 2015-10-05 17:03
#
# Filename: tsinsen_A1393.cpp
======================================================*/
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <set>
#include <map>
using namespace std;
#define LL long long
#define PB push_back
#define P pair<int, int>
#define X first
#define Y second
const int N = 2 * 1e6 + 5;
const int M = 26;
const int MOD = 51123987;
struct PalTree {
int nxt[N][M]; //表示编号为i的节点表示的回文串在两边添加字符c以后变成的回文串的编号
int fail[N]; //表示节点i失配以后跳转不等于自身的节点i表示的回文串的最长后缀回文串
int cnt[N]; //表示节点i表示的本质不同的串的个数(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的)
int num[N]; //表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数
int len[N]; //表示编号为i的节点表示的回文串的长度(一个节点表示一个回文串)
int S[N]; //表示第i次添加的字符(一开始设S[0] = -1(可以是任意一个在串S中不会出现的字符))
int last; //指向新添加一个字母后所形成的最长回文串表示的节点
int n; //表示添加的字符个数
int p; //表示添加的节点个数
int new_node(int x) {
memset(nxt[p], 0, sizeof(nxt[p]));
cnt[p] = 0;
num[p] = 0;
len[p] = x;
return p++;
}
void init() {
p = 0;
new_node(0);
new_node(-1);
last = 0;
n = 0;
S[0] = -1;
fail[0] = 1;
}
int get_fail(int x) {
while(S[n - len[x] - 1] != S[n]) x = fail[x];
return x;
}
int add(int x) {
x -= 'a';
S[++n] = x;
int cur = get_fail(last);
if(!nxt[cur][x]) {
int now = new_node(len[cur] + 2);
fail[now] = nxt[get_fail(fail[cur])][x];
nxt[cur][x] = now;
num[now] = num[fail[now]] + 1;
}
last = nxt[cur][x];
++cnt[last];
return num[last];
}
void count() {
for(int i = p - 1; i >= 0; --i)
cnt[fail[i]] += cnt[i];
}
LL pal_str_num() {
LL ret = 0;
for(int i = p - 1; i > 0; --i) {
cnt[fail[i]] = (cnt[fail[i]] + cnt[i]) % MOD;
ret = (ret + cnt[i]) % MOD;
}
return ret;
}
};
PalTree pt;
char str[N];
int sum[N];
void gao(int n) {
LL ans = 0;
pt.init();
sum[n] = 0;
for(int i = n - 1; i >= 0; --i) {
sum[i] = (sum[i + 1] + pt.add(str[i])) % MOD;
}
pt.init();
for(int i = 0; i < n; ++i) {
ans = (ans + (LL)pt.add(str[i]) * sum[i + 1]) % MOD;
}
LL all = pt.pal_str_num();
ans = (((LL)all * (all - 1) / 2 % MOD - ans) % MOD + MOD) % MOD;
cout<<ans<<endl;
}
int main() {
int n;
while(scanf("%d", &n) != EOF) {
scanf("%s", str);
gao(n);
}
return 0;
}