一开始确实没有想到怎么做。。
定义状态dp[i]表示自动机上的节点i还能够附加多长的字符,因此每一个串的结尾字符在自动机上对应的dp为0,这就是初始条件
状态转移方程就是dp[i]=max{dp[j]}+1,其中j表示i点可达的节点,最后的答案就是dp[root]
输出No的情况有两个,一个是在记忆化搜索的过程中,走到了一个已经访问过的节点了,此时有环,长度为无穷。还有一个就是答案为空串的情况
标记访问过了用一个vis数组就好,用一个flag标记记录是否有环
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int maxm=5E4+5;
int times,n,sz,root,s,ch[maxm][26],fail[maxm],dp[maxm],rec[maxm],newnode();
bool vis[maxm],val[maxm],flag;
char str[55];
void init();
void ins(int k);
void getfail();
void dfs(int node);
int main(){
scanf("%d",×);
while(times--){
init();
scanf("%d%d",&n,&s);
for(int i=0;i<s;++i){
scanf("%s",str);
ins(i);
}
getfail();
dfs(root);
if(flag||!dp[ch[root][rec[root]]])
printf("No\n");
else{
int u=root;
while(dp[ch[u][rec[u]]]){
printf("%c",rec[u]+'A');
u=ch[u][rec[u]];
}
printf("\n");
}
}
return 0;
}
void dfs(int node){
if(vis[node]&&dp[node]==-1){
flag=true;
}
else if(!vis[node]){
vis[node]=true;
int te=0;
for(int i=0;i<n;++i){
dfs(ch[node][i]);
if(flag)
return;
if(te<=dp[ch[node][i]])
te=dp[ch[node][i]],rec[node]=i;
}
dp[node]=te+1;
}
}
void init(){
sz=flag=0;
root=newnode();
}
int newnode(){
dp[sz]=-1;
vis[sz]=rec[sz]=val[sz]=0;
memset(ch[sz],0,sizeof ch[sz]);
return sz++;
}
void ins(int k){
int u=root;
for(int i=0;str[i];++i){
str[i]-='A';
if(ch[u][str[i]]==root)
ch[u][str[i]]=newnode();
u=ch[u][str[i]];
}
val[u]=vis[u]=1;
dp[u]=0;
}
void getfail(){
queue<int> q;
fail[root]=root;
for(int i=0;i<n;++i)
if(ch[root][i]!=root){
fail[ch[root][i]]=root;
q.push(ch[root][i]);
}
int u;
while(!q.empty()){
u=q.front();q.pop();
for(int i=0;i<n;++i){
if(ch[u][i]==root)
ch[u][i]=ch[fail[u]][i];
else{
fail[ch[u][i]]=ch[fail[u]][i];
q.push(ch[u][i]);
}
}
}
}