洛谷 P3065 [USACO12DEC]First! G(字典树,环的判断)

传送门


解题思路

先用字典树存储所有的字符串,然后对每个字符串进行判断是否能成为符合要求的字符串:

对于这个字符串的每一位,如果这一位上有别的字符串与之不同,那么一定就把别的字符串这一位上对应的字符j这个字符串这一位上对应的字符t连一条边,表示我们规定的字母表中j大于t。

然后判断有没有环出现即可。

注意dfs要每查一次要清空(代码中),否则会挂,比如A->B,A->B->C。

当然拓扑也行……

AC代码

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<iomanip>
 5 #include<cmath>
 6 using namespace std;
 7 const int maxn=30005;
 8 string s[maxn];
 9 int ch[maxn*10][30],vis[maxn*10],cnt=1,n,en[maxn*10],num,ans[maxn];
10 bool dayu[30][30],vis3[30];
11 void insert(int id){
12     int len=s[id].length(),now=1;
13     for(int i=0;i<len;i++){
14         if(!ch[now][s[id][i]-'a']) ch[now][s[id][i]-'a']=++cnt;
15         now=ch[now][s[id][i]-'a'];
16         vis[now]++;
17     }
18     en[now]++;
19 }
20 bool dfs(int u){
21     if(vis3[u]) return 0;
22     vis3[u]=1;
23     for(int i=0;i<=25;i++){
24         if(u==i) continue;
25         if(!dayu[u][i]) continue;
26         if(!dfs(i)) return 0;
27     }
28     vis3[u]=0;//注意 
29     return 1;
30 }
31 bool query(int id){
32     memset(dayu,0,sizeof(dayu));
33     memset(vis3,0,sizeof(vis3));
34     int len=s[id].length(),now=1;
35     for(int i=0;i<len;i++){
36         if(en[now]) return 0;
37         int t=s[id][i]-'a';
38         for(int j=0;j<=25;j++){
39             if((j!=t)&&ch[now][j]){
40                 if(dayu[t][j]) return 0;
41                 else dayu[j][t]=1;
42             }
43         }
44         now=ch[now][s[id][i]-'a'];
45         if(vis[now]==1) break;
46     }
47     for(int i=0;i<=25;i++) if(!vis3[i]&&!dfs(i)) return 0;
48     return 1;
49 }
50 int main(){
51     cin>>n;
52     for(int i=1;i<=n;i++){
53         cin>>s[i];
54         insert(i);
55     }
56     for(int i=1;i<=n;i++){
57         if(query(i)) ans[++num]=i;
58     }
59     cout<<num<<endl;
60     for(int i=1;i<=num;i++) cout<<s[ans[i]]<<endl;
61     return 0;
62 }

 

posted @ 2021-01-10 23:39  尹昱钦  阅读(125)  评论(0编辑  收藏  举报