CF17E Palisection
題意
給出字符串
求出字符串的子串中相交的對數
思路
-
問題轉化爲字符串總對數減去不相交的迴文串對數
-
總對數爲迴文串總數:
-
不相交對數即爲正反跑兩邊迴文串
對於以
i
結尾的迴文串,與他不相交的迴文串爲反向的以x
爲結尾迴文串總數
-
-
處理二百萬的字符串的
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);
}