HDU 6138 2017多校第八场1006 Fleet of the Eternal Throne :AC自动机

本文介绍了一道关于字符串匹配的问题,通过构建AC自动机解决批量字符串的最长公共前缀查询。详细展示了AC自动机的构造过程及如何进行高效查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给出n(<=1e5)个串(总长度<=1e5),以及q(<=300,数据有点弱,q改成1e5才好)次询问,每次询问给出x和y,表示输入的第x个和第y个串,要求找出一个最长的字串p,满足:p是x的字串,p是y的字串,且p是这n个串中某一个(可以是多个)的前缀,输出这个最大的长度。

题解:对n个串造出 自动AC机 ,这里的AC机不需要维护单词的结束点  需要维护一个每个节点到根的距离,也就是前缀的长度。然后用x跑一遍AC机,在所有匹配成功的结束节点上记上一个标记(直接记成询问次数就行了:第一次询问flag记成1,第二次记成2,这样保证每次的flag都不一样就不用清空标记了),然后这些标记点的意思就是:这个前缀是n个串中某个的前缀,且这个前缀是串x的字串。然后再用y跑一次AC机,在y匹配成功的节点,如果他刚刚被打了标记,那就统计最长的length就可以。


十分钟手切的自动AC机,在剩下最后五分钟的时候,过了样例就直接交了,然后队友一直在刷新Ranklist,突然发现A了。。。妈耶。。这太惊悚了有木有


Code:

#include<bits/stdc++.h>
using namespace std;
const int MAX = 100005;
struct AC{
	AC* nxt[26];
	AC* fail;
	int length;
	int flag;
};
AC* root;
int n,q,ans;
int index[MAX];
char allchar[MAX*10];
void clear(AC* node){
	for (int i=0;i<=25;i++){
		if (node->nxt[i]!=NULL){
			clear(node->nxt[i]);
		}
	}
	free(node);
}
AC* create(){
	AC* node = (AC*)(malloc(sizeof(AC)));
	memset(node->nxt,0,sizeof(node->nxt));
	node->fail = NULL;
	node->flag = -1;
	node->length =0;
	return node;
}
AC* insert(AC* root,char* word){
	AC* node = root;
	char* p = word;
	while (*p){
//		cout<<*p<<endl;
		int id = *p-'a';
		if (node->nxt[id]==NULL){
			node->nxt[id]= create();
		}
		node->nxt[id]->length = node->length+1;
		node = node->nxt[id];
		p ++;
	}
}
void init(){
	if (root!=NULL){
		clear(root);
	}
	root = create();
}
void input(){
	scanf("%d",&n);
	int delta = 0;
	for (int i=1;i<=n;i++){
		index[i] = delta;
		scanf("%s",allchar+delta);
		insert(root,allchar+delta);
		delta+=strlen(allchar+delta)+1;
	}
}
AC* que[MAX*10];
void build(){
	int l=0;
	int r=1;
	que[1]= root;
	root->fail = root;
	while (l<r){
		l++;
		AC* q = que[l];
		for (int i=0;i<=25;i++){
			if (q->nxt[i]!=NULL){
				if (q==root){
					q->nxt[i]->fail = root;
				}else{
					q->nxt[i]->fail = q->fail;
					while (q->nxt[i]->fail->nxt[i]==NULL&&q->nxt[i]->fail!=root){
						q->nxt[i]->fail = q->nxt[i]->fail->fail;
					}
					if (q->nxt[i]->fail->nxt[i]!=NULL){
						q->nxt[i]->fail = q->nxt[i]->fail->nxt[i];
					}
				}
				r++;
				que[r] = q->nxt[i];
			}
		}
	}
}
void search(char * word,int f){
	AC* node = root;
	char* p = word;
	while (*p){
//		cout<<*p<<endl;
		int id = *p-'a';
		while (node!=root&&node->nxt[id]==NULL){
			node = node->fail;
		}
		if (node->nxt[id]!=NULL){
			node = node->nxt[id];
		}
		AC* temp = node;
		while (temp!=root){
			temp->flag = f;
			temp = temp->fail;
		}
		p++;
	}
}
void query(char * word,int f){
	AC* node = root;
	char* p = word;
	while (*p){
//		cout<<*p<<endl;
		int id = *p-'a';
		while (node!=root&&node->nxt[id]==NULL){
			node = node->fail;
		}
		if (node->nxt[id]!=NULL){
			node = node->nxt[id];
		}
		AC* temp = node;
		while (temp!=root){
			if (temp->flag ==f){
				ans = max(ans,temp->length);
			}
			temp = temp->fail;
		}
		p++;
	}
}
void solve(){
	scanf("%d",&q);
	for (int i=1;i<=q;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		search(allchar+index[x],i);
		ans =0;
		query(allchar+index[y],i);
		printf("%d\n",ans);
	}
}
int main(){
	int t;
	scanf("%d",&t);
	while (t--){
		init();
		input();
		build();
		solve();
	}
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值