http://acm.hdu.edu.cn/showproblem.php?pid=6138
题目大意:给出n个字符串后,会有q个询问,每个询问会输入两个数,记为x,y。然后要求的是,x,y的 最长公共子串,而且这个最长公共子串要是n个字符串中某个字符串的前缀。输出这个串的长度。
解题思路:这题的题意很明确,首先我们要求出x,y串的公共子串,然后要在n个字符串中出现过。这样就可以用的AC自动机。AC自动机原来是用来多模式匹配的,原来每个节点维护的值有一个标记是否是一个完整的字符串,然而由于该题是要前缀长度,我们就要队节点维护的值有所改进,每个节点维护其所代表的前缀的长度即可。这样的话,我们可以用这n个字符串构建字典树,然后通过BFS找到每个节点对应的fail指针,接下来,由于我们要找的是第x个字符串和第y个字符串的最长公共字串长度,且该字串是n个字符串中某个字符串的前缀。因此我们需要用第x个字符串和第y个字符串在字典树上进行一个匹配。当第x个字符串去匹配的时候,我们要做的就是把所有在匹配时经过的节点都标记一下。代表这些节点到字典树的根所代的字符串既是某个字符串的前缀,还在第x个字符串中。同理我们让第y个字符串在按相同的方法跑一遍AC自动机,有所不同的是,第一个串跑完之后已经留下标记,我们可以在第2个串跑的时候,直接通过检测标记来确定某个节点所代表的字符串是不是x,y的共同子串。然后边检测边求解答案就可以了。
#include <bits/stdc++.h>
using namespace std;
struct Trie
{
int len,flag;
Trie *fail;
Trie *child[26];
Trie()
{
flag = 0;
len = 0;
fail = NULL;
for(int i=0; i<26; i++)
child[i] = NULL;
}
};
Trie *root,*current,*temp;
string str[100005];
int ans;
void _insert(string s)
{
current = root;
for(int i=0; i<s.length(); i++)
{
int index = s[i] - 'a';
if(current->child[index] == NULL)
{
temp = new Trie;
temp->len = current->len+1;
current->child[index] = temp;
}
current = current->child[index];
// (current->len)++;
}
}
void Find_fail()
{
queue<Trie*> Q;
Q.push(root);
while(!Q.empty())
{
current = Q.front();
Q.pop();
for(int i=0; i<26; i++)
{
if(current->child[i]!=NULL)
{
if(current==root)
current->child[i]->fail = root;
else
{
Trie *p = current->fail;
while(p)
{
if(p->child[i])
{
current->child[i]->fail = p->child[i];
break;
}
p=p->fail;
}
if(p==NULL)
current->child[i]->fail = root;
}
Q.push(current->child[i]);
}
}
}
}
void _search(string s,int f)
{
current = root;
for(int i=0; i<s.length(); i++)
{
int index = s[i]-'a';
while(!current->child[index]&¤t!=root)
current = current->fail;
current = current->child[index];
if(!current) current = root;
temp = current;
while(temp!=root)
{
temp->flag = f;
temp = temp->fail;
}
}
}
void AC_authomation(string s,int f)
{
current = root;
for(int i=0; i<s.length(); i++)
{
int index = s[i]-'a';
while(!current->child[index]&¤t!=root)
current = current->fail;
current = current->child[index];
if(!current) current = root;
temp = current;
while(temp!=root)
{
if(temp->flag == f)
{
ans = max(temp->len,ans);
}
temp = temp->fail;
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,q,x,y;
root = new Trie;
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
cin>>str[i];
_insert(str[i]);
}
Find_fail();
scanf("%d",&q);
for(int i=1; i<=q; i++)
{
ans = 0;
scanf("%d %d",&x,&y);
_search(str[x],i);
AC_authomation(str[y],i);
printf("%d\n",ans);
}
}
return 0;
}