P3065 [USACO12DEC] First! G 题解
考场上一眼看出判环,但是没想到在trie树上判环,其实主要是因为只有13min了。
考虑暴力思路,那么就是 \(\mathcal O(n)\) 枚举当前考虑到哪个字符串,然后再里层 \(\mathcal O(n)\) 枚举 \(\mathrm{check}\) 一下,对于每一对向后枚举直到不相等,则从向 当前要成为最小的那个串的当前字母 向 \(\mathrm{check}\) 枚举到的那个串的当前字母连边,如果被包含要判掉,判掉时注意如果是较短的不要不判环。然后统一判字符有没有环就行。时间复杂度 \(\mathcal O(|\Sigma|^2n^2 )\)。
\(\mathrm{Code}\)
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
int n,m,m1,m2,k,cnt,fl;
string ch[N];bool tag[N],vis[N],add[30][30];
struct E{
int to,nxt;
}e[900];int hd[30],tot;
inline void Add(int u,int v){
e[++tot].to=v;e[tot].nxt=hd[u],hd[u]=tot;
}
inline void dfs(int nw){
if(!fl)return;
if(vis[nw]){fl=0;return;}
vis[nw]=1;
for(int i=hd[nw];i;i=e[i].nxt){
dfs(e[i].to);
}vis[nw]=0;
}
int main(){
ios::sync_with_stdio(false);
cin>>n;for(int i=1;i<=n;++i)cin>>ch[i];
for(int i=1;i<=n;++i){
tag[i]=1;m1=ch[i].size();
for(int j=1;j<=n;++j){
if(i==j) continue;
m2=ch[j].size();m=min(m1,m2);
for(k=0;k<m;++k){
if(ch[i][k]!=ch[j][k]){
if(!add[ch[i][k]-'a'+1][ch[j][k]-'a'+1]){
Add(ch[i][k]-'a'+1,ch[j][k]-'a'+1);
add[ch[i][k]-'a'+1][ch[j][k]-'a'+1]=1;
}break;
}
}if(k==m){
if(m1>m){tag[i]=0;break;}
else tag[i]=1;
}
}if(tag[i]){
fl=1;
for(int j=1;j<=26;++j){
dfs(j);if(!fl)break;
}tag[i]=fl;
}for(int j=1;j<=26;++j)hd[j]=0,vis[j]=0;
for(int j=1;j<=tot;++j)e[j].to=e[j].nxt=0;tot=0;
for(int j=1;j<=26;++j){
for(int k=1;k<=26;++k)add[j][k]=0;
}
}for(int i=1;i<=n;++i){
if(tag[i])++cnt;
}cout<<cnt<<"\n";
for(int i=1;i<=n;++i){
if(tag[i])cout<<ch[i]<<"\n";
}
return 0;
}
正解其实就是把所有串放到Trie上判环。
那么我们不难想到对于每个串的第 \(i\) 个字母,是在第 \(i\) 层的,那么其实就是我们要让这个字母在每一层中最小。
那么为什么这样复杂度就对了呢,因为我们比较的是前缀信息,时间复杂度瓶颈实际在于每次匹配都丢弃了原来的信息。我们每次不需要重建Trie树,每次用到Trie树的深度是很小的,边的条数就比较小,避免了重复,实际上严格复杂度应该是 \(\mathcal O(|\Sigma|^2 n)\) 的,因为边数最多是 \(\mathcal O(|\Sigma|^2)\) 的。
\(\mathrm{Code}\)
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
int n,fl,deg[30];
int tr[N][30],idx=1,ans;
bool ed[N],book[N],e[30][30];
vector<int> g[30];
inline int cal(char x){return x-'a'+1;}
inline int read(){
char ch;int x=0,f=1;
while(!isdigit(ch=getchar())){if(ch=='-')f=-1;}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
inline void insert(string s){
int nw=1,m=s.size(),k;
for(int j=0;j<m;++j){
k=cal(s[j]);
if(!tr[nw][k])tr[nw][k]=++idx;
nw=tr[nw][k];
}ed[nw]=1;
return;
}
inline void topo(){
queue<int> q;int h;
for(int i=1;i<=26;++i) if(!deg[i]) q.push(i);
while(!q.empty()){
h=q.front(),q.pop();
for(int i:g[h]){
if(--deg[i]==0)q.push(i);
}
}return;
}
inline bool chk(string x){
int u=1,v,m=x.size();
for(int i=1;i<=26;++i)g[i].clear();
memset(deg,0,sizeof(deg));
memset(e,0,sizeof(e));
for(int i=0;i<m;++i){
if(ed[u])return false;//是前缀
v=cal(x[i]);
for(int j=1;j<=26;++j){
if(v!=j&&tr[u][j]&&!e[v][j]){//同层连边
e[v][j]=1,++deg[j];
g[v].push_back(j);
// cout<<id<<" "<<v<<" "<<j<<"\n";
}
}u=tr[u][v];//向下一层走
}topo();
for(int i=1;i<=26;++i){
if(deg[i])return false;
}return true;
}
int main(){
ios::sync_with_stdio(false);
cin>>n;string s[N];
for(int i=1;i<=n;++i){
cin>>s[i];insert(s[i]);
}for(int i=1;i<=n;++i){
ans+=book[i]=chk(s[i]);
}cout<<ans<<"\n";
for(int i=1;i<=n;++i){
if(book[i])cout<<s[i]<<"\n";
}return 0;
}
不会有人像我一样关了流同步还getchar吧,被硬控一个晚自习

浙公网安备 33010602011771号