迴文自動機常用操作和題目集

迴文自動機


ii爲尾節點的字符串維護

增加一個節點後

即爲在原有的所有真後綴迴文串(mark[fail[num]]mark[fail[num]]

增加一個迴文串更新(mark[num]

if (!tree[last][in[i] - 'a']) {
	/*operation*/
	mark[num] = mark[fail[num]] + 1;
    //統計以該節點爲結尾的迴文串個數
}
//更新last
cnt[i]=mark[last];

迴文自動機(PAM)

Interesting


本質不同的字符串總數

即爲節點個數,拋去1號節點

mark[i]=num-1

Palindromes and Super Abilities

CA Loves Palindromic


維護本質不同的字符串(信息來自於子樹)

對於一個本質不同的字符串

他的子樹都可以訪問他

標記所有字符串,統計他所有子樹信息即可

由於編號越大,層數約深,所以倒着遍歷即可

for (int i = 1; i <= n; i++) {
	while (in[i - len[last] - 1] != in[i]) last = fail[last];
	if (!tree[last][in[i] - 'a']) {
		/*operation*/
	}
	last = tree[last][in[i] - 'a'];
	mark[last]++;	//標記信息
}
for (int i = num; i; i--) {
	mark[fail[i]] += mark[i];
    //統計子樹信息
}

P3649 迴文串

拉拉隊排練


維護本質不同的迴文串(信息來自於父親、祖先)

建立failfail

利用dfsdfs,回溯維護迴文串祖先的信息

void build() {
	memset(head, 0, sizeof(int) * (num + 1)); tot = 0;
	for (int i = 2; i <= num; i++) AddEdge(fail[i], i);
    //建樹,用fail邊建樹
}
int vis[maxn],res[maxn];
void dfs(int now) {
	vis[len[now]]++;//維護祖先信息
    /*operation 統計答案*/
	for (int i = head[now]; i; i = edge[i].next)
		dfs(edge[i].v);	//dfs
	vis[len[now]]--;//回溯
	return;
}

HDU6599 I Love Palindrome String

Palindromeness


維護信息有前後兩端的迴文串

建立兩個迴文串

一個正着建,一個倒着建

對於i節點維護i節點正序迴文串信息和i+1節點的倒序迴文串信息合併即可

struct PAM {
	int tree[maxn][26];
	int len[maxn], fail[maxn];
	int last, num , mark[maxn];
	void insert(char* in, int n) {
		/*operation*/
	}
}pre, suf;
int main() {
	scanf("%s", in + 1); in[0] = '#';
	int n = strlen(in + 1);
	pre.insert(in, n);
    //正向建立
	reverse(in + 1, in + 1 + n);
	suf.insert(in, n);
    //反向建立
	int ans = 0;
	for (int i = 1; i < n; i++) {
		ans = max(ans, pre.mark[i] + suf.mark[n-i]);
        //兩個串信息合併
	}
}

P4287 雙倍迴文

Harry and magic string


前端添加節點

pre,last兩個標記維護前後添加

pre爲例,當添加節點後

新增的節點後最長迴文串不是本身時,新添加的節點不會影響last

len[last]fail[last]len[last],fail[last]爲正確的

新增的節點後,本身爲最長迴文串時,要更新last

更新方法爲將last=prelast=pre

由於只統計一邊,所以統計不同本質字符串個數和總共字符串個數是正確的

但維護其他信息的時候需要認真思考

以下爲向前插入:

向後插入類似

void pre_insert(char* in, int i) {	//向前添加方法
	while (in[i + len[pre] + 1] != in[i]) pre = fail[pre];
	if (!tree[pre][in[i] - 'a']) {
		len[++num] = len[pre] + 2;
		int j = fail[pre];
		while (in[i + len[j] + 1] != in[i]) j = fail[j];
		fail[num] = tree[j][in[i] - 'a'];
		tree[pre][in[i] - 'a'] = num;
		mark[num] = mark[fail[num]] + 1;	//統計個數
	}
	pre = tree[pre][in[i] - 'a'];
	if (len[pre] == stdr - stdl + 1) last = pre;//當本身變爲迴文串,更新last
	sum = sum + mark[pre];	//維護總個數
}

Victor and String


前向星優化空間

MLE時,我們可以使用鏈式前向星優化

即使用鏈式前向星創建字典樹

查詢:遍歷所有邊,由於每個點在字典樹訪問次數有限,所以複雜不會超

賦值:即爲建邊

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];
}

CF17E Palisection

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章