CF17E Palisection
题意
给出字符串
求出字符串的子串中相交的对数
思路
-
问题转化为字符串总对数减去不相交的回文串对数
-
总对数为回文串总数: n ∗ ( n − 1 ) / 2 n*(n-1)/2 n∗(n−1)/2
-
不相交对数即为正反跑两边回文串
对于以
i
结尾的回文串,与他不相交的回文串为反向的以x
为结尾回文串总数 i ≤ x ≤ n i\leq x\leq n i≤x≤n
-
-
处理二百万的字符串的
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);
}