BZOJ 3012 [Usaco2012 Dec]First! wzq脑洞hash树(正解trie树)

博客风格转化计划实验篇2

题意:
给n(n<=30000)个字符串,所有字符串长度加起来不超过300000(字符串只含有小写字母)。
求解哪些字符串可以称作第一字符串。
一个字符串能被称为第一字符串的条件为存在一种字典序使它排在第一名。
方法:
wzq脑洞hash树…..
然而并没有trie树优越。
并且我脑洞的这个树好处有啥:暂且不知道。
坏处有啥:很容易被卡,自带常数。
所以为什么要这么写?只是因为我跟wjc说这题我特么一定要用带hash的东西搞过去!
*目标达成√
解析:
对于以下内容,请不要吐槽这个树如何辣鸡,因为我也知道这个树很辣鸡,不过自己立下的flag流着泪也要写完。

先讲正常做法。
我们首先搞出来trie树,然后对于每一个串,我们遍历它,如果路径上有其他串的结尾点,则该串一定不会被选,否则对于每一层该串的点,我们向该层其他存在的点连一条有向边。
所有边连完之后,我们对于之前连的那些边跑一个拓扑,找是否有环出现,如果有环出现则一定不会选取该串,否则该串可选。
然后我们来讲wzq脑洞树

这个树是怎么实现的呢…假如说有一个字符串abbc。
这个串的插入过程是什么呢?
初始hash值是0,第一个字母是a
所以插入(0,a),hash值变为x
接着插入(x,b),hash值变为y
然后插入(y,b),hash值变为z
继续插入(z,c),hash值变为整个串的hash值
最后插入(hash,-1)。
然后做法跟trie树类似辣.

立个flag
也许有一天我就发现这个东西的优点了呢?

代码:
没写trie树,看脑洞树吧

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 300100
#define M 3000100
#define base 131
#define mod 10000007
using namespace std;
typedef unsigned long long ull;
int n;
char all[M+N];
int fir[N];
int hash[27];
int head[mod];
int head2[50];
int cnt2;
int boom[N];
int map[30][30];
struct node
{
    int from,to,next,lenth;
    ull val;
}edge[N],edge2[N];
int cnt;
void init()
{
    memset(head,-1,sizeof(head));
    cnt=1;
}
void init2()
{
    memset(map,0,sizeof(map)); 
    memset(head2,-1,sizeof(head2));
    cnt2=1; 
}
void edgeadd2(int from,int to)
{
    edge2[cnt2].from=from,edge2[cnt2].to=to;
    edge2[cnt2].next=head2[from];
    head2[from]=cnt2++;
}
void edgeadd(int from,int to,ull val)
{
    edge[cnt].from=from,edge[cnt].to=to,edge[cnt].val=val;
    edge[cnt].next=head[from];
    head[from]=cnt++;
}
int find(ull x,int no)
{
    int szy=x%mod;
    for(int i=head[szy];i!=-1;i=edge[i].next)
    {
        if(edge[i].val==x&&edge[i].to==no){return 1;}
    }
    return 0;
}
void ins(ull x,int no)
{
    int szy=x%mod;
    edgeadd(szy,no,x);
}
int in[30];
int find2(int alpha1,int alpha2)
{
    for(int i=head2[alpha1];i!=-1;i=edge2[i].next)
    {
        if(edge2[i].to==alpha2)return 1;
    }
    return 0;
}
void work(ull x,int alpha)
{
    int szy=x%mod;
    for(int i=head[szy];i!=-1;i=edge[i].next)
    {
        if(edge[i].val==x&&edge[i].to!=alpha&&!map[alpha][edge[i].to])
            edgeadd2(alpha,edge[i].to),in[edge[i].to]++;
    }
}
int vvvv[30];
int sta[100];
bool check()
{
    int l=1,r=0;
    for(int i=1;i<=26;i++)
        if(!in[i])sta[++r]=i;
    while(l<=r)
    {
        int u=sta[l++];
        for(int i=head2[u];i!=-1;i=edge2[i].next)
        {
            int to=edge2[i].to;
            in[to]--;
            if(!in[to])sta[++r]=to;
        }
    }
    return r==26;
}
int tail[N];
int main()
{
    init();
    scanf("%d",&n);
    int la=0;
    for(int i=1;i<=n;i++)
    {
        fir[i]=la+1;
        scanf("%s",all+la+1);
        int len=strlen(all+la+1);
        tail[i]=la+len;
        la+=len+1;
    }
    for(int i=1;i<=n;i++)
    {
        ull hasha=0;
        for(int j=fir[i];j<=tail[i];j++)
        {
            if(!find(hasha,all[j]-'a'+1))
                ins(hasha,all[j]-'a'+1);
            hasha=hasha*base+all[j];
        }
        ins(hasha,-1);
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        init2();
        memset(in,0,sizeof(in));
        ull hasha=0;
        for(int j=fir[i];j<=tail[i];j++)
        {
            if(find(hasha,-1)){boom[i]=1;break;}
            work(hasha,all[j]-'a'+1); 
            hasha=hasha*base+all[j];
        }
        if(!check())boom[i]=1;
        if(!boom[i])ans++;
    }
    printf("%d\n",ans);
    for(int i=1;i<=n;i++)
    {
        if(!boom[i])printf("%s\n",all+fir[i]);
    }
}
    原文作者:Trie树
    原文地址: https://blog.csdn.net/wzq_QwQ/article/details/48622345
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞