#C231107B. 残片(garbage)
标签(Label)
- Trie树
- 判环
- 图论转化
- 拓扑排序
前言(Front talk)
$\qquad$这道题也是绝笔,我打了半天,结果发现自己的实现非常的复杂,反正今天的题总是因为想出来但是实现的太慢导致没有做对。(→_→)
$\qquad$做题前先想好思路,然后想好用什么打,写好提纲!!!
$\qquad$结果下来 $20min$ 就打出了正解,哎,对题目的理解还是不够清晰。
同时,看到和字符串有关的问题,就只有三种处理方式:
- $KMP$ 字符串匹配
- $Trie$ 树
- $AC$ 自动机
哈希和 $Z$ 函数( $KMP$ 扩展 )用的还是比较的少,而且哈希不会太直接的考(要考思维)。
网址(Website)
题解(Solution)
$\qquad$这道题很明显就是建个 $Trie$ 树处理,对于每一个字符串,如果要选择其为最小的,那么就一定使该层其他的字符都要大于当前字符,于是乎,我们可以很容易地得到很多类似 $a<b$ 的等量关系,类比图论转化思想,如果最后得到的关系式不成立,当且仅当在建立一条 $a\to b$ 的边之后,该图出现环,则在最后用 $tarjan$ 或 拓扑排序 或 $SPFA$ 判环即可。
$\qquad$建立单向边可以不按上述要求建,因为方向的反并不会影响判环。
$\qquad$注意: 如果当前字符串包括了其他的字符串,那么这个字符串一定不是最小的,要特判。(见代码第 $39$ 行)
$\qquad tarjan$ 太长了 ,关于$SPFA$ ,它死了,还是拓扑好用(按个人喜好,不喜勿喷)。
备注:
此题还可以用 传递闭包 来做,详见 cx 大佬的代码。
cx大佬 的博客网址: Huasushis ‘s Blog
附有此题的 $tarjan$ 做法 ,感谢 dzr 大佬的贡献。
dzr 大佬的博客网址: DZhearMins
代码(Code)
我的代码
#include<bits/stdc++.h>
#include<vector>
#include<queue>
#define For(i,l,r) for(int i=l;i<=r;i++)
#define Rof(i,l,r) for(int i=l;i>=r;i--)
using namespace std;
inline int input(){int x;return cin>>x,x;}
const int N = 30123;
const int L = 301234;
string a[N];
int n;
//Trie树
int t[L][26],idx;
int v[L];
void insert(string s,int id){
int p=0,c,ln=s.length();
For(i,0,ln-1){
c=s[i]-'a';
if(!t[p][c]) t[p][c]=++idx;
p=t[p][c];
}v[p]++;
}
vector<int> ft[26];
queue<int> q;
int inn[N],cnt;
inline bool check(string s){
//初始化
For(i,0,25) ft[i].clear(),inn[i]=0;
while(q.size()) q.pop();cnt=0;
//建边
int p=0,c,ln=s.length();
For(i,0,ln-1){
c=s[i]-'a';
For(j,0,25) if(j!=c && t[p][j])
ft[c].emplace_back(j),inn[j]++;
if(v[p]) return false;//注意这个点一开始没有加
p=t[p][c];
}
//拓扑排序判断环,有环则无解
For(i,0,25) if(inn[i]==0) q.push(i);
while(q.size()){
int x=q.front();q.pop();cnt++;
for(auto y:ft[x]){
if(--inn[y]==0) q.push(y);
}
}return cnt==26;
}
vector<int> Ans;
signed main(){
ios::sync_with_stdio(false);
string str("garbage");
freopen((str+".in").c_str(),"r",stdin);
freopen((str+".out").c_str(),"w",stdout);
cin>>n;For(i,1,n){
cin>>a[i];insert(a[i],i);
}For(i,1,n){
if(check(a[i])){
Ans.emplace_back(i);
}
}cout<<Ans.size()<<'\n';
for(auto k:Ans) cout<<a[k]<<'\n';
return 0;
}
cx的代码
$\qquad$cx大佬 的博客网址: Huasushis ‘s Blog
#include <bits/stdc++.h>
#define N 30010
#define M 300010
using namespace std;
char sbase[M << 1], *s[N];
int n;
int len[N];
void rd(int x) {
static int ptr = 0;
s[x] = sbase + ptr;
scanf("%s", s[x]);
len[x] = strlen(s[x]);
ptr += len[x] + 1;
}
int ch[M][26];
bool ans[N];
int bo[M];
int tot;
void add(char *s, int len, int id) {
static int cnt = 1;
int u = 1;
for (int i = 0; i < len; ++i) {
if (!ch[u][s[i] - 'a']) {
ch[u][s[i] - 'a'] = ++cnt;
}
u = ch[u][s[i] - 'a'];
}
bo[u] = id;
}
void dfs(int u, vector<int> xz) {
if (bo[u]) {
ans[bo[u]] = 1;
++tot;
return;
}
int js = 0, zong = 0;
for (int i = 0; i < 26; ++i) {
if (ch[u][i]) js |= xz[i], zong |= (1 << i);
}
for (int i = 0; i < 26; ++i) {
if (ch[u][i] && !((js >> i) & 1)) {
auto son = xz;
son[i] |= zong ^ (1 << i);
auto tmp = zong ^ (1 << i);
while (tmp) {
int v = tmp & -tmp;
son[i] |= son[__builtin_ctz(v)];
tmp ^= tmp & -tmp;
}
for (int j = 0; j < 26; ++j)
((son[j] >> i) & 1) && (son[j] |= son[i]);
dfs(ch[u][i], son);
}
}
}
int main() {
freopen("garbage.in", "r", stdin);
freopen("garbage.out", "w", stdout);
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
rd(i);
add(s[i], len[i], i);
}
dfs(1, vector<int>(26));
printf("%d\n", tot);
for (int i = 1; i <= n; ++i) {
if (ans[i]) {
printf("%s\n", s[i]);
}
}
return 0;
}
dzr的代码
$\qquad$dzr 大佬的博客网址: DZhearMins
#include<bits/stdc++.h>
using namespace std;
#define fi(l,r) for(int i=l;i<=r;++i)
#define ff(x,l,r) for(int x=l;x<=r;++x)
#define ll long long
#define lp ll
#define lson(x) ((x)<<1)
#define rson(x) (lson(x)|1)
#define Lson(x) tr[lson(x)]
#define Rson(x) tr[rson(x)]
#define mod %P
#define nC 28
#define N 30005
#define nS 150005
#define S 400005
int n;
char s_c[S],*si[N],*it;
int son[S][nC],cnt,acnt;
int able[N];
int tot,fir[nC],nxt[nS],to[nS],val[S],ed[N];
void add(int x,int y){
nxt[++tot]=fir[x];
fir[x]=tot;
to[tot]=y;
};
bool insert(char*s,int ii){
int x=0;
for(char *ch=s;*(ch);++ch){
int c=(*ch)-'a';
if(son[x][c]==0)son[x][c]=++cnt;
x=son[x][c];
}
++val[x];
ed[ii]=x;
return true;
}
int dfn[nC],dfc,low[nC],stk[nC],stl;
bool ins[nC];
bool tarjan(int x){
dfn[x]=low[x]=++dfc;
ins[x]=1;
stk[++stl]=x;
for(int e=fir[x];e;e=nxt[e]){
int u=to[e];
if(dfn[u]==0){
tarjan(u);
low[x]=min(low[x],low[u]);
}else if(ins[u])low[x]=min(low[x],dfn[u]);
}
if(dfn[x]==low[x]){
if(stk[stl]!=x)return false;
ins[x]=0;--stl;
}
return true;
}
bool query(char*s,int ii){
// printf("Qring %s\n",s);
int x=0;
fi(1,tot)nxt[i]=0;
tot=0;dfc=0;stl=0;
memset(ins,0,sizeof ins);
memset(fir,0,sizeof fir);
memset(dfn,0,sizeof dfn);
memset(low,0,sizeof low);
for(char*ch=s;*(ch);++ch){
int c=(*ch)-'a';
fi(0,26){
if(i!=c&&son[x][i]!=0){
add(i,c);
}
}
// printf("\tx=%d ed=%d val=%d\n",x,ed[ii],val[x]);
if(val[x]>=1)return false;
x=son[x][c];
}
if(val[x]>1)return false;
fi(0,26){
if(dfn[i]==0){
if(tarjan(i)==false)return false;
}
}
return true;
}
int main(){
freopen("garbage.in","r",stdin);
freopen("garbage.out","w",stdout);
scanf("%d",&n);
char ch=getchar();
it=si[0]=s_c;
fi(1,n){
++it;
si[i]=++it;//下标从 0 开始!
while(ch<'a'||ch>'z')ch=getchar();
while(ch>='a'&&ch<='z'){
*it=ch;
++it;
ch=getchar();
}
}
fi(1,n)if(insert(si[i],i)==false){able[i]=-1;};
fi(1,n)if(able[i]!=-1&&query(si[i],i)){
able[i]=1;++acnt;
}
printf("%d\n",acnt);
fi(1,n)if(able[i]==1)printf("%s\n",si[i]);
}