题意:
给出 N 个串,问这几个串的最长公共字串是什么?若有多个输出字典序最小的。
思路:
由于 N 小于 4000 且长度小于 200。我们完全可以用暴力的方法。
不妨将第一个串当成母串,枚举出他的所有子串。并将其所有子串作为模式串,与其他的串进行匹配。
这里匹配的方法直接利用了 AC自动机 。
由于要输出字典序最小的,利用 set 维护就行。
代码:
(借用了 Kuangbin 大大的 AC自动机 模板)
#include <iostream>
#include <set>
#include <cstring>
#include <queue>
#include <cstdio>
using namespace std;
string buf,str;
struct Trie
{
set <string> ansset;
int next[500010][27],fail[500010],end[500010],st[500010],length[500010],dans[205][205],ans[205][205];
int root,L,anslen;
//在这里要说明一下,此模板中 trie树 的每个节点
//都有自己的 id 。故 st 数组和 length 数组分别
//记录了每个节点对所在的子串的开头和从子串开始
//到这个点的长度。
int newnode()
{
for(int i = 0; i < 27; i++)
next[L][i] = -1;
end[L++] = 0;
return L-1;
}
void init()
{
L = 0;
root = newnode();
ansset.clear();
anslen = -1;
memset(ans,0,sizeof(ans));
}
void insert(int k,int len)
{
int now = root;
for(int i = k; i < len; i++)
{
if(next[now][buf[i]-'a'] == -1){
next[now][buf[i]-'a'] = newnode();
st[next[now][buf[i]-'a']] = k;
length[next[now][buf[i]-'a']] = i - k;
}
now = next[now][buf[i]-'a'];
}
end[now]++;
}
void build()
{
queue<int>Q;
fail[root] = root;
for(int i = 0; i < 27; i++)
if(next[root][i] == -1)
next[root][i] = root;
else
{
fail[next[root][i]] = root;
Q.push(next[root][i]);
}
while( !Q.empty() )
{
int now = Q.front();
Q.pop();
for(int i = 0; i < 27; i++)
if(next[now][i] == -1)
next[now][i] = next[fail[now]][i];
else
{
fail[next[now][i]]=next[fail[now]][i];
Q.push(next[now][i]);
}
}
}
int query()//由于匹配时会出现同一个串对一个结点重复匹配的情况,
//故利用 dans 记录一个串匹配的情况。匹配结束后,再计入总的匹配统计 ans 中。
{
int len = str.size();
int now = root;
int res = 0;
memset(dans,0,sizeof(dans));
for(int i = 0; i < len; i++)
{
now = next[now][str[i]-'a'];
int temp = now;
while( temp != root )
{
res += end[temp];
end[temp] = 0;
dans[st[temp]][length[temp]]=1;
temp = fail[temp];
}
}
for(int i=0;i<=200;i++)
for(int j=0;j<=200;j++){
ans[i][j]+=dans[i][j];
}
return res;
}
void solve(int tot){
for(int i=0;i<=200;i++){
for(int j=0;j<=200;j++){
if(j<anslen) continue;
if(ans[i][j]==tot){ //............当一个节点匹配 N - 1 次时,说明这个结点在每一个串中都出现过,即公共子串。
if(j<anslen) continue;//.......判断长度是否不足
if(j>anslen)//.................若长度更长了,要更新答案集。
ansset.clear();
ansset.insert(buf.substr(i,j+1));
anslen=j;
}
}
}
if(ansset.empty()) cout<<"IDENTITY LOST"<<endl;
else cout<<*ansset.begin()<<endl;
}
void debug()
{
for(int i = 0; i < L; i++)
{
printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]);
for(int j = 0; j < 128; j++)
printf("%2d",next[i][j]);
printf("]\n");
}
}
};
Trie ac;
int main()
{
int n;
while(cin>>n&&n){
cin>>buf;
int len=buf.size();
ac.init();
for(int i=0;i<len;i++)//..............................添加每个子串
ac.insert(i,len);
ac.build();
n--;
int t = n;
while(t--){
cin>>str;
ac.query();
}
ac.solve(n);
}
}