1. 程式人生 > >【[USACO12DEC]第一!First!】

【[USACO12DEC]第一!First!】

一個串不能成為第一的情況有兩種

  1. 另外一個單詞是它的字首

  2. 在分配字母表大小關係的時候出現了矛盾的情況

第一種很好判斷,一旦我們在一個單詞沒有匹配完之前遇到一個結束標誌,那麼就說明另外一個單詞是它的字首

至於第二種,看到大小關係和是否矛盾我們很容易就聯想到了拓撲排序

於是我們匹配的時候,發現某一層除了當前正在匹配的串以外還有其他字母,那麼這些字母在我們構建的字母表中必須大於當前的這個字母,於是我們連一條有向邊表示一下大小關係就好了

最後跑一遍拓撲判斷一下是否有環就好了

#include<cstring>
#include<cstdio>
#include<iostream>
#include<string>
#define re register
#define maxn 300005
using namespace std;
struct node
{
    int v,nxt;
}e[1005];
int son[maxn][26],flag[maxn];
std::string S[30005];
int len[30005];
int n,num,cnt,tot;
int head[26];
int ans[maxn];
inline void add_edge(int x,int y)
{
    e[++num].v=y;
    e[num].nxt=head[x];
    head[x]=num;
}
inline void ins(int x)
{
    int now=0;
    for(re int i=0;i<len[x];i++)
    {
        if(!son[now][S[x][i]-'a']) son[now][S[x][i]-'a']=++cnt;
        now=son[now][S[x][i]-'a'];
    }
    flag[now]=1;
}
inline int check(int x)
{
    int now=0;
    memset(e,0,sizeof(e));
    int c[26];
    memset(c,0,sizeof(c));
    memset(head,0,sizeof(head));
    num=0;
    for(re int i=0;i<len[x];i++)
    {
        if(flag[now]) return 0;
        for(re int j=0;j<26;j++)
        if(son[now][j]&&S[x][i]-'a'!=j)
            add_edge(S[x][i]-'a',j),c[j]++;
        now=son[now][S[x][i]-'a'];
    }
    int cur=0;
    int q[27];
    memset(q,0,sizeof(q));
    for(re int i=0;i<26;i++)
    if(!c[i]) q[++cur]=i;
    for(re int i=1;i<=cur;i++)
    {
        for(re int j=head[q[i]];j;j=e[j].nxt)
        {
            c[e[j].v]--;
            if(!c[e[j].v]) q[++cur]=e[j].v;
        }
    }
    return cur==26;
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(re int i=1;i<=n;i++)
    {
        cin>>S[i];
        len[i]=S[i].size();
        ins(i);
    }
    tot=0;
    for(re int i=1;i<=n;i++)
        if(check(i)) ans[++tot]=i;
    cout<<tot<<endl;
    for(re int i=1;i<=tot;i++)
    cout<<S[ans[i]]<<endl;
    return 0;
}