「Usaco2012 Dec」第一(字典树+拓扑排序)

 

 

(我恨字符串)

惯例化简题目:给定n个字符串,可以改变字符的相对大小(在字典序中的大小),问:字符串i是否能成为最小的字符串(字典序)

解题过程:

  • 首先你可以预处理出来26的全排列然后暴力然后你只要用神威太湖之光开O2就能过了
  • 秒切字典树
  • 推出一堆没用的结论。

说一下思考过程:首先字典树能找前缀都知道吧

  1. 观察样例,得:对于不能变成第一的,那么一定是有一个跟它有相同前缀的字符串,但是不完全相等,要有比当前串小的。
  2. 然后和旁边大佬一起歪歪瞎搞:建立一棵字典树,然后对于每个字符串跑一遍字典树,如果能找到相同的前缀&&最终终点不在一个分叉上&&分叉比当前串终点小(这里可能要意会一下....算了我上图)

 

 

(打标记的是字串结束的地方,软件为了防止重复在相同的字母后加了数字去重,无用)

先看omm,按照上面的结论,很明显可以,其实不然

然后发现....样例打了从样例发现的结论的脸......

solution:

想想怎么判断无解....

一个条件先于另外一个条件....

如果先到自己头上,那就暴毙了。。。

这....拓扑排序??

貌似是这样。

于是,我们在字典树上跑一下,对于一个分支(相同前缀的几个字符)像这样:

连单向边之后,跑tpsort,如果有环的话(先到自己头上了,不明白自己去学tpsort),那就是一个无解情况

注意,连边不是在树上,而是在字母表上,字母表的每一个都会有初始边(a->b->c->d->e->f->g......->y->z)然后在上述过程中人为加边,然后跑tpsort,一开始我在这里卡住了

然后每个跑就行了,代码没有太大难度,字典树的板子背掉就行了(或者拿临接表硬模)。

代码:(注意,每跑一个要清零。)

#include<bits/stdc++.h>
#define  register
using namespace std;
const int maxn=3e5+5;
string s[maxn];
struct edge
{
    int to,next;
}e[100];
 
int head[100],cnt;
 
inline void addedge(int from,int to)
{
    e[++cnt].next=head[from];
    e[cnt].to=to;
    head[from]=cnt;
}
 
int t[maxn][30];
int vis[maxn];
int used[30][30];
int f;
int tot;
int ru[100];
int n;
int ans[maxn];
int anscnt;
inline void update(string x)
{
    int u=0,tt;
    for( int i=0;i<x.size();i++)
    {
        tt=x[i]-'a';
        if(!t[u][tt])
        {
            t[u][tt]=++tot;
        }
        u=t[u][tt];
    }
    vis[u]=1;
}
inline void solve(string x)
{
    int u=0,tt;
    for( int i=0;i<x.size();i++)
    {
        tt=x[i]-'a';
        if(vis[u])
        {
            f=1;
            return;
        }
        for( int j=0;j<26;j++)
        {
            if(t[u][j]&&j!=tt&&!used[tt][j])
            {
                used[tt][j]=1;
                addedge(tt,j);
                ru[j]++;
            }
        }
        u=t[u][tt];
    }
}
 
 
 
inline int tpsort()
{
    deque < int > q;
    for( int i=0;i<26;i++)
    {
        if(ru[i]==0)
        q.push_back(i);
    }
    while(!q.empty())
    {
        int u=q.front();
        q.pop_front();
        for( int i=head[u];i;i=e[i].next)
        {
            int v=e[i].to;
            ru[v]--;
            if(ru[v]==0)
            {
                q.push_back(v);
            }
        }
    }
    for( int i=0;i<26;i++)
    {
        if(ru[i]!=0)
        return 0;
    }
    return 1;
}
 
int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for( int i=1;i<=n;i++)
    {
        cin>>s[i];
        update(s[i]);
    }
    for( int i=1;i<=n;i++)
    {
        cnt=0;
        f=0;
        memset(head,0,sizeof(head));
        memset(ru,0,sizeof(ru));
        memset(used,0,sizeof(used));
        solve(s[i]);
        if(f)    continue;
        if(tpsort())    ans[++anscnt]=i;
    }
    printf("%d\n",anscnt);
    for(int i=1;i<=anscnt;i++)
    {
        printf("%s\n",s[ans[i]].c_str());
    }
    return 0;
}

(完)

 

posted @ 2019-10-12 21:07  阿基米德的澡盆  阅读(171)  评论(0编辑  收藏  举报