HDU 6138——Fleet of the Eternal Throne(AC自动机)

题意:给出n个字符串,m次查询,每次查询给两个字符串的编号,输出满足是其它字符串的前缀(一个就行)的这两个字符串的最长公共子串的长度。题意是比较简单易懂了。

题解:首先多个字符串又牵涉到匹配找公共子串的问题,就应该考虑AC自动机了,但这道题的问题不只是找最长公共子串,还要求这个子串是其它字符串的前缀。这里可以利用ac自动机fail指针的特点,回忆一下,fail指针是怎么来的,在trie树上查找单词时,如果当前字符的下一位失配,那么就从和它有同样前缀的位置开始从新找(和KMP一样的道理),一个字符的失败指针指向的也就是这个和它有同样前缀的位置,那么两个字符串的公共子串要满足是别的字符串的前缀,肯定这个公共子串的最后一位是有非根节点的失败指针的(理解一下这里),这个公共子串的长度也就是它的失败指针到根节点的距离。所以题解就显而易见了,将所有的字符串建ac机,对每一组询问的两个字符串x,y,我们将x串在ac机上跑一遍,它所有的点的fail指针都标记一下(定义一个mark变量就可以了),再用y串跑一遍,对于它能到的点的fail指针,如果被标记过的话,那就一定是x,y串的公共子串,只需要不断地维护长度最大就行了。至于记录长度,这里我们在建trie树的时候就可以记录每个点到根节点的距离,而不用再像普通的ac自动机一样在每个字符串的结尾标记。

附上代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;

typedef struct node
{
	node *next[26];
	node *fail;
	int sum;
	int mark;//标记每个节点是否已经被访问过
	int len;//每个节点到根节点的长度
}node;

node *root;
node* que[100010];
char str[maxn*10];
int index[maxn];
int ans;

void init(node *a,int ll)//初始化
{
	for(int i=0;i<26;i++)
		a->next[i]=NULL;
	a->fail=NULL;
	a->sum=0;
	a->len=ll+1;
	a->mark=0;
}
void Insert(char word[])//将新的字符串插入字典树
{
	node *p=root;
	for(int i=0;word[i];i++)
	{
		int x=word[i]-'a';
		if(!p->next[x])
		{
			p->next[x]=(node *)malloc(sizeof(node));
			init(p->next[x],p->len);
		}
		p=p->next[x];
	}
	p->sum++;
}


void build_fail()
{
	int head,tail;
	head=tail=0;
	que[tail++]=root;

	while(head<tail)
	{
		node* tmp=que[head];
		head++;

		for(int i=0;i<26;i++)
		{
			if(!tmp->next[i]) continue;
			if(tmp==root) tmp->next[i]->fail=root;
			else
			{
				node *p=tmp->fail;
				while(p)
				{
					if(p->next[i])
					{
						tmp->next[i]->fail=p->next[i];
						break;
					}
					p=p->fail;
				}
				if(p==NULL) tmp->next[i]->fail=root;
			}
			que[tail++]=tmp->next[i];
		}
	}
}


void solve(char des[],char ff[])
{
	int ans=0;
	node *p=root;
	for(int i=0;des[i]!='\0';i++)//遍历第一个数组
	{
		int x=des[i]-'a';
		p=p->next[x];
		node *temp;
		temp=p;
		while(temp!=root)//
		{
			temp->mark=1;
			temp=temp->fail;//有非根节点的失败指针的就一定是别的字符串的前缀
		}
	}
	p=root;
	for(int i=0;ff[i]!='\0';i++)
	{
		int x=ff[i]-'a';
		p=p->next[x];
		node *temp;
		temp=p;
		while(temp!=root)
		{
			if(temp->mark==1&&temp->len>ans)
			{
				ans=temp->len;
			}
			temp=temp->fail;
		}
	}
	p=root;
	for(int i=0;des[i]!='\0';i++)//消除标记
	{
		int x=des[i]-'a';
		p=p->next[x];
		node *temp;
		temp=p;
		while(temp!=root)
		{
			temp->mark=0;
			temp=temp->fail;
		}
	}
	printf("%d\n",ans);

}

int main()
{
	int t,n,m,a,b;
	scanf("%d",&t);
	while(t--)
	{
		root=(node*)malloc(sizeof(node));
		init(root,-1);
		scanf("%d",&n);
		getchar();
		int loca=0;
		for(int i=0;i<n;i++)
		{
			index[i]=loca;
			scanf("%s",str+loca);
			Insert(str+loca);
			loca+=strlen(str+loca)+1;

		}
		build_fail();
		scanf("%d",&m);
		for(int i=0;i<m;i++)
		{
			scanf("%d%d",&a,&b);
			solve(str+index[a-1],str+index[b-1]);
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/wookaikaiko/article/details/81155762