CF17E Palisection(回文自动机+链式前向星优化空间)

CF17E Palisection


题意

给出字符串

求出字符串的子串中相交的对数


思路

  • 问题转化为字符串总对数减去不相交的回文串对数

    • 总对数为回文串总数: n ∗ ( n − 1 ) / 2 n*(n-1)/2 n(n1)/2

    • 不相交对数即为正反跑两边回文串

      对于以i结尾的回文串,与他不相交的回文串为反向的以x为结尾回文串总数 i ≤ x ≤ n i\leq x\leq n ixn

  • 处理二百万的字符串的Trie树明显会MLE,使用链式前向星维护Trie

    • 即使用链式前向星创建字典树
    • 查询:遍历所有边,由于每个点在字典树访问次数有限,所以复杂不会超
    • 赋值:即为建边

代码

链式前向星优化回文自动机
struct Edge{
	int v;
	int w;	//w为字母的值
	int next;
}edge[maxn];
int head[maxn], tot;
inline void AddEdge(int u, int v, int w) {
	edge[++tot].v = v;
	edge[tot].w = w;
	edge[tot].next = head[u];
	head[u] = tot;
}
int find(int now, int w) {	//寻找函数
	for (int i = head[now]; i; i = edge[i].next)
		if (edge[i].w == w) return edge[i].v;
	return 0;
}
for (int i = 1; i <= n; i++) {
	while (in[i - len[last] - 1] != in[i]) last = fail[last];
	int treenode = find(last, in[i] - 'a');//寻找节点
	if (!treenode) {
		len[++num] = len[last] + 2;
		int j = fail[last];
		while (in[i - len[j] - 1] != in[i]) j = fail[j];
		fail[num] = find(j, in[i] - 'a');//寻找节点
		AddEdge(last, num, in[i] - 'a');//建边
		mark[num] = mark[fail[num]] + 1;
		treenode = num;	//更新节点
	}
	last = treenode;
	cnt[i] = mark[last];
}
寻找不相交对数
scanf("%s", in + 1);in[0] = '#';
insert(in, n);		//正向回文自动机
for (int i = 1; i <= n; i++) pre_cnt[i] = cnt[i];//记录cnt[]
reverse(in + 1, in + 1 + n);//翻转
memset(head, 0, sizeof(head));	tot = 0;//初始化前向星
insert(in, n);//反向回文自动机
for (int i = 2; i <= n; i++)
    cnt[i] = (cnt[i - 1] + cnt[i]) % mod;//后缀和
LL ans = 0;
for (int i = 1; i <= n; i++)//统计相交对数
	ans = (ans + (LL(pre_cnt[i]) * cnt[n - i]) % mod) % mod;
printf("%I64d\n", ((LL(cnt[n]) * (cnt[n] - 1) / 2 % mod - ans) % mod + mod) % mod);//不相交对数
AC
#pragma comment(linker, "/STACK:102400000,102400000")
#pragma warning (disable:4996)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn = 2000005;
const LL mod = 51123987;
char in[maxn];
int len[maxn], fail[maxn];
int last, num;
int mark[maxn], cnt[maxn], pre_cnt[maxn];
struct Edge{
	int v;
	int w;
	int next;
}edge[maxn];
int head[maxn], tot;
inline void AddEdge(int u, int v, int w) {
	edge[++tot].v = v;
	edge[tot].w = w;
	edge[tot].next = head[u];
	head[u] = tot;
}
int find(int now, int w) {
	for (int i = head[now]; i; i = edge[i].next)
		if (edge[i].w == w) return edge[i].v;
	return 0;
}
void insert(char* in, int n) {
	len[1] = -1;
	fail[0] = 1;
	num = 1;
	last = 0;
	for (int i = 1; i <= n; i++) {
		while (in[i - len[last] - 1] != in[i]) last = fail[last];
		int treenode = find(last, in[i] - 'a');
		if (!treenode) {
			len[++num] = len[last] + 2;
			int j = fail[last];
			while (in[i - len[j] - 1] != in[i]) j = fail[j];
			fail[num] = find(j, in[i] - 'a');
			AddEdge(last, num, in[i] - 'a');
			mark[num] = mark[fail[num]] + 1;
			treenode = num;
		}
		last = treenode;
		cnt[i] = mark[last];
	}
}
int main() {
	int n;
	scanf("%d", &n);
	scanf("%s", in + 1);
	in[0] = '#';
	insert(in, n);
	for (int i = 1; i <= n; i++) pre_cnt[i] = cnt[i];
	reverse(in + 1, in + 1 + n);
	memset(head, 0, sizeof(head)); tot = 0;
	insert(in, n);
	for (int i = 2; i <= n; i++) cnt[i] = (cnt[i - 1] + cnt[i]) % mod;
	LL ans = 0;
	for (int i = 1; i <= n; i++)
		ans = (ans + (LL(pre_cnt[i]) * cnt[n - i]) % mod) % mod;
	printf("%I64d\n", ((LL(cnt[n]) * (cnt[n] - 1) / 2 % mod - ans) % mod + mod) % mod);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值