给出很多字符串,要求构造一个字符串,使得所有给出的字符串在这个串当中都是出现次数最多子串。输出长度最短的答案。如果有多个答案,输出字典序最小的。
首先,答案中每个字母最多出现一次。因为如果一个字母出现多次,那么这单个的字母就是出现次数最多的子串了。
接着,我们发现答案中的字母有一定的顺序,顺序必须和所给的若干个单词中的顺序一致。要求有序,我们想到了构造有向图进行拓扑排序,把每个字母当做不同的点,最多26个点,把每个单词当中的前一个字母c1向后一个字母c2连边,表示答案当中c1必须在c2前面。
实际上,因为顺序不能矛盾,所以构造的图去掉重边之后,只能是很多条链组成的森林,不能有环和自环,否则答案就是NO。
接着就遍历所有链,把这些链构成的若干个词连起来就是答案。
需要注意的是一个串长度为1的情况,这时若这单个的字母没有连边,可能会被答案忽略。特判把它加上就好了。
#include <cstdio>
#include <iostream>
#include <string.h>
#include <string>
#include <map>
#include <queue>
#include <deque>
#include <vector>
#include <set>
#include <algorithm>
#include <math.h>
#include <cmath>
#include <stack>
#include <iomanip>
#define mem0(a) memset(a,0,sizeof(a))
#define meminf(a) memset(a,0x3f,sizeof(a))
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
const int maxn=100005,inf=0x3f3f3f3f;
const ll llinf=0x3f3f3f3f3f3f3f3f;
const ld pi=acos(-1.0L);
int in[35],out[35];
bool d[35][35];
string a[maxn],ans;
bool visit[35],p[35];
bool dfs(int now) { //dfs找链
visit[now]=1;
ans=ans+(char)(now+'a');
for (int j=0;j<26;j++) {
if (d[now][j]) {
if (visit[j]) return false;
return dfs(j);
}
}
return true;
}
int main() {
int n,i,j,len;
scanf("%d",&n);
mem0(d);mem0(p);
int flag=0;
for (i=1;i<=n;i++) {
cin >> a[i];
int len=a[i].length();
for (j=0;j<len-1;j++) { //邻接矩阵建图
d[a[i][j]-'a'][a[i][j+1]-'a']=1;
// cout << a[i][j]-'a' <<' ' << a[i][j+1]-'a' << endl;
}
if (len==1) p[a[i][0]-'a']=1;
}
for (i=0;i<26;i++)
if (d[i][i]) {
printf("NO\n");return 0;
}
for (i=0;i<26;i++) {
for (j=0;j<26;j++) {
if (d[i][j]) in[j]++,out[i]++;
}
}
for (i=0;i<26;i++) {
if (in[i]>1||out[i]>1) {
printf("NO\n");return 0;
}
}
mem0(visit);
ans="";
for (i=0;i<26;i++) {
if (out[i]!=0&&in[i]==0) {
if (!dfs(i)) {
printf("NO\n");return 0;
}
}
if (p[i]&&in[i]==0&&out[i]==0) ans=ans+(char)(i+'a'); //特判单个字母
}
for (i=0;i<;26;i++) { //判环
if (out[i]!=0||in[i]!=0) {
if (!visit[i]) {
printf("NO\n");return 0;
}
}
}
cout << ans << endl;
return 0;
}