4.6~4.13Trie树

版权声明: https://blog.csdn.net/zjh_2017/article/details/79923449

我不知道为什么在进阶指南上Trie树是一个基本数据结构。。

模板

定义

a[x].link[ch]表示节点x的第ch个字母的节点编号
temp表示节点编号

long long temp=1;
struct atree
{
    long long link[26];
    bool endflag;
}
a[600010];

插入

依次扫描字符串s中的每个字符ch
若ch为空,则新建一个节点temp

void build(long long i,long long x)
//x表示当前节点编号,i枚举字符串的每一位
{
    if (i==s.size())
    {
        a[x].endflag=true;//标记当前节点为尾节点
        return;
    }
    long long ch=s[i]-'a';//ch表示当前字符(转换为数字)
    if (a[x].link[ch]==0) a[x].link[ch]=++temp;
    build(i+1,a[x].link[ch]);
}

检索

同样地,依次扫描字符串s中的每个字符ch
若ch为空,则说明字符串s没有被插入过Trie树,结束检索

bool find(long long i,long long x)
{
    if (i==s.size())
    {
        if (a[x].endflag) return true;
        else return false;
    }//返回x是否为尾节点
    long long ch=s[i]-'a';//ch为当前节点的第i位字母的节点编号
    if (a[x].link[ch]==0) return false;
    find(i+1,a[x].link[ch]);
}

T1trie树模板

它要求单词列表对应的单词查找树的节点数
这题只要建一棵trie树就好了,输出temp

#include<bits/stdc++.h>
using namespace std;
string s;
long long temp=1;
struct atree
{
    long long link[26];
}
a[600010];
void build(long long i,long long x)
{
    if (i==s.size()) return;
    long long ch=s[i]-'A';
    if (a[x].link[ch]==0) a[x].link[ch]=++temp;
    build(i+1,a[x].link[ch]);
}
int main()
{
    while(getline(cin,s))
    build(0,1);
    printf("%lld\n",temp);
    return 0;
}

T2 L语言

它要求这段文章在字典D可以被理解的最长前缀的位置
同样地,先建一棵trie树,只不过检索的时候要新开一个b数组,用来存储从字符串的当前字符开始扫描能够达到的最长前缀的长度,比如b[14]=6则表示从s的第6位开始扫,最长的前缀长度为14
它的标签是DP,我不是很能理解。。

#include<bits/stdc++.h>
using namespace std;
long long n,m,temp=1,b[1000010],ans;
string s;
struct atree
{
    long long link[26];
    bool endflag;
}
a[600010];
inline int read()
{
    int num=0,flag=1;
    char c=getchar();
    for (;c<'0'||c>'9';c=getchar())
    if (c=='-') flag=-1;
    for (;c>='0'&&c<='9';c=getchar())
    num=(num<<3)+(num<<1)+c-48;
    return num*flag;
}
void build(long long i,long long x)
{
    if (i==s.size())
    {
        a[x].endflag=true;
        return;
    }
    long long ch=s[i]-'a';
    if (a[x].link[ch]==0) a[x].link[ch]=++temp;
    build(i+1,a[x].link[ch]);
}
void find(long long i,long long x)
{
    if (a[x].endflag)
    {
        b[i]=b[0];
        ans=max(ans,i);
    }
    if (i==s.size()) return;
    long long ch=s[i]-'a';
    if (a[x].link[ch]==0) return;
    find(i+1,a[x].link[ch]);
}
int main()
{
    n=read();
    m=read();
    for (int i=1;i<=n;++i)
    {
        getline(cin,s);
        build(0,1);
    }
    for (int i=1;i<=m;++i)
    {
        getline(cin,s);
        ans=0;
        b[0]=i;
        for (int j=0;j<=s.size();++j)
        if (b[j]==i) find(j,1);
        printf("%lld\n",ans);
    }
    return 0;
}

T3 First

它要求哪些字符串可以以某种顺序排在所有字符串中首位
这类题目现在都要转换一下了啊
所以我们现在把题目意思理解为——对于每一个字符串,要满足两个条件:
①没有字符串前缀;②在同一个父亲下,该儿子要比其他兄弟的优先度高。
这里就要两步——①检索:如果在检索过程中遇见了其他字符串的尾节点,就说明它有前缀,不可以;②拓扑排序:如果出现了环或者冲突什么的,将会导致拓扑完了之后有些点的入度不为0,不可以。

#include<bits/stdc++.h>
using namespace std;
long long k,temp=1,id[50],ans[30010],tot;
string s[30010];
bool flag,b[50][50];
struct atree
{
    long long link[26];
    bool endflag;
}
a[600010];
inline int read()
{
    int num=0,flag=1;
    char c=getchar();
    for (;c<'0'||c>'9';c=getchar())
    if (c=='-') flag=-1;
    for (;c>='0'&&c<='9';c=getchar())
    num=(num<<3)+(num<<1)+c-48;
    return num*flag;
}
void build(long long i,long long x,int t)
{
    if (i==s[t].size())
    {
        a[x].endflag=true;
        return;
    }
    long long ch=s[t][i]-'a';
    if (a[x].link[ch]==0) a[x].link[ch]=++temp;
    build(i+1,a[x].link[ch],t);
}
void find(int i,int x,int t)
{
    if (i==s[t].size()) return;
    if (a[x].endflag)
    {
        flag=false;
        return;
    }
    long long ch=s[t][i]-'a';
    for (int j=0;j<26;++j)
    if (a[x].link[j]&&ch!=j&&b[ch][j]==false)
    {
        b[ch][j]=true;
        id[j]++;
    }
    find(i+1,a[x].link[ch],t);
}
void topsort()
{
    long long head=0,tail=0,q[50]={};
    for (int i=0;i<26;++i)
    if (id[i]==0) q[++tail]=i;
    while (head<tail)
    {
        long long k=q[++head];
        for (int i=0;i<26;++i)
        if (b[k][i])
        {
            id[i]--;
            if (id[i]==0) q[++tail]=i;
        }
    }
    for (int i=0;i<26;++i)
    if (id[i])
    {
        flag=false;
        return;
    }
}
int main()
{
    k=read();
    for (int i=1;i<=k;++i)
    {
        getline(cin,s[i]);
        build(0,1,i);
    }
    for (int i=1;i<=k;++i)
    {
        flag=true;
        temp=0;
        memset(b,0,sizeof(b));
        memset(id,0,sizeof(id));
        find(0,1,i);
        topsort();
        if (flag) ans[++tot]=i;
    }
    printf("%lld\n",tot);
    for (int i=1;i<=tot;++i)
    cout<<s[ans[i]]<<endl;
    return 0;
}

T4秘密消息

它要求有多少信息和这条密码有着相同的前缀
这里的a[x].s1表示x点被几个字符串覆盖,a[x].s2表示x点为几个字符串的尾节点,所以如果当前点不是尾节点,ans加上s2,如果是,加上s1
自己理解一下啦

#include<bits/stdc++.h>
using namespace std;
long long m,n,k,b[10010],temp=1,ans;
struct atree
{
    long long link[5],s1,s2; 
    bool endflag;
}
a[600010];
inline int read()
{
    int num=0,flag=1;
    char c=getchar();
    for (;c<'0'||c>'9';c=getchar())
    if (c=='-') flag=-1;
    for (;c>='0'&&c<='9';c=getchar())
    num=(num<<3)+(num<<1)+c-48;
    return num*flag;
}
void build(long long i,long long x)
{
    if (i>k)
    {
        a[x].endflag=true;
        a[x].s1++;
        a[x].s2++;
        return;
    }
    long long ch=b[i];
    if (a[x].link[ch]==0) a[x].link[ch]=++temp;
    a[x].s1++;
    build(i+1,a[x].link[ch]);
}
void find(long long i,long long x)
{
    if (i>k)
    {
        ans+=a[x].s1;
        return;
    }
    long long ch=b[i];
    ans+=a[x].s2;
    if (a[x].link[ch]==0) return;
    find(i+1,a[x].link[ch]);
}
int main()
{
    m=read();
    n=read();
    for (int i=1;i<=m;++i)
    {
        k=read();
        for (int j=1;j<=k;++j)
        b[j]=read();
        build(1,1);
    }
    for (int i=1;i<=n;++i)
    {
        k=read();
        for (int j=1;j<=k;++j)
        b[j]=read();
        ans=0;
        find(1,1);
        printf("%lld\n",ans);
    }
    return 0;
}

T5背单词

坑。

#include<bits/stdc++.h>
using namespace std;
long long n,l,temp,linkk[1020010],tot,k,sum,tail,dfs[1020010],ans;
bool f[1020010];
string s;
struct atree
{
    long long link[26],endflag;
}
a[1020010];
struct etree
{
    long long y,v,next,father;
}
e[1020010];
struct btree
{
    long long id,val;
}
b[600010];
bool mycmp(btree aa,btree bb)
{
    return aa.val<bb.val;
}
inline int read()
{
    int num=0,flag=1;
    char c=getchar();
    for (;c<'0'||c>'9';c=getchar())
    if (c=='-') flag=-1;
    for (;c>='0'&&c<='9';c=getchar())
    num=(num<<3)+(num<<1)+c-48;
    return num*flag;
}
void insertt(long long ss,long long ee)
{
    e[++tot].y=ee;
    e[tot].next=linkk[ss];
    linkk[ss]=tot;
}
void build(long long i,long long x,long long t)
{
    if (i<0)
    {
        a[x].endflag=t;
        return;
    }
    long long ch=s[i]-'a';
    if (a[x].link[ch]==0) a[x].link[ch]=++temp;
    build(i-1,a[x].link[ch],t);
}
void find(long long x,long long t)
{
    if (a[x].endflag)
    {
        insertt(t,a[x].endflag);
        insertt(a[x].endflag,t);
        t=a[x].endflag;
    }
    for (int i=0;i<26;++i)
    if (a[x].link[i]) find(a[x].link[i],t);
}
void dfs1(long long t)
{
    e[t].v=1;
    for (int i=linkk[t];i;i=e[i].next)
    {
        long long k=e[i].y;
        if (k==e[t].father) continue;
        e[k].father=t;
        dfs1(k);
        e[t].v+=e[k].v;
    }
}
void dfs2(long long t)
{
    f[t]=true;
    long long head=tail;
    sum++;
    dfs[t]=sum;
    for(int i=linkk[t];i;i=e[i].next)
    {
        k=e[i].y;
        if (k==e[t].father) continue;
        b[++tail].id=k;
        b[tail].val=e[k].v;
    }
    if (head==tail) return;
    sort(b+head+1,b+tail+1,mycmp);
    for (int i=head+1;i<=tail;++i)
    {
        if (f[b[i].id]) continue;
        dfs2(b[i].id);
        ans+=dfs[b[i].id]-dfs[t];
    }
}
int main()
{
    n=read();
    for (int i=1;i<=n;++i)
    {
        getline(cin,s);
        l=s.size();
        build(l-1,0,i);
    }
    find(0,0);
    dfs1(0);
    dfs2(0);
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zjh_2017/article/details/79923449