zz:https://blog.csdn.net/diaopang1934/article/details/102276403?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
题意: 给定n个总长不超过m的互不相同的字符串,现在你可以任意指定字符之间的大小关系。问有多少个串可能成为字典序最小的串,并输出这些串。n <= 30,000 , m <= 300,000 分析: 首先不考虑大小关系,如果一个串是另一个串的前缀,那么另一个串一定不能成为字典序最小的串,我们可以用trie树很好的解决。 考虑前缀相同的情况,这个串在前缀后的字符应该和含有相同前缀的串在前缀后的字符有明确的大小关系,根据这个大小关系连边,我们用拓扑排序判断是否矛盾。 以上都满足则可以成为字典序最小的串。
#include <stdio.h> #include <string.h> #include <algorithm> #include <queue> using namespace std; #define N 30050 struct A { int son[30],end; }t[N*10]; int n,tot,head[30],to[N],nxt[N],c[30],cnt=1,ans[30010]; char s[30010][310]; void add(int u,int v) { to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; c[v]++; //v的入度加1 } void insert(int x) { int p=1; int len=strlen(s[x]+1); for(int i=1;i<=len;i++) { int id=s[x][i]-'a'+1; //字符转成数字 if(!t[p].son[id]) t[p].son[id]=++cnt; p=t[p].son[id]; } t[p].end=1; } bool search(int x) { int p=1; int len=strlen(s[x]+1); for(int i=1;i<=len;i++) { if(t[p].end) return 0; int id=s[x][i]-'a'+1; //字符转成数字 for(int j=1;j<=26;j++) { if(j!=id&&t[p].son[j]) { add(id,j);//建一条从id到j的边,id是父亲点,j是其下面的子结点 } } p=t[p].son[id]; } return 1; } bool topsort() { queue <int> q; for(int i=1;i<=26;i++) if(c[i]==0) //加进入度为0的点 q.push(i); while(!q.empty()) { int x=q.front(); q.pop(); for(int i=head[x];i;i=nxt[i]) { c[to[i]]--; if(c[to[i]]==0) q.push(to[i]); } } for(int i=1;i<=26;i++) if(c[i]) //如果还存在入度不为0的点,则无法topsort return 0; return 1; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%s",s[i]+1); insert(i); } for(int i=1;i<=n;i++) { memset(head,0,sizeof(head)); memset(c,0,sizeof(c)); cnt=0; if(!search(i)) continue; if(!topsort()) continue; ans[++tot]=i; } printf("%d ",tot); for(int i=1;i<=tot;i++) { printf("%s ",s[ans[i]]+1); } }