CF17E Palisection(迴文自動機+鏈式前向星優化空間)

CF17E Palisection


題意

給出字符串

求出字符串的子串中相交的對數


思路

  • 問題轉化爲字符串總對數減去不相交的迴文串對數

    • 總對數爲迴文串總數:n(n1)/2n*(n-1)/2

    • 不相交對數即爲正反跑兩邊迴文串

      對於以i結尾的迴文串,與他不相交的迴文串爲反向的以x爲結尾迴文串總數ixni\leq x\leq 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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章