51nod1869 那些年,我们一起讲的故事

首先把trie的广义后缀自动机建出来,那么任选起点在trie上走就是在SAM 的起点开始走。在SAM上求出sg函数,先手必胜当且仅当两个自动机上的节点的sg函数值不同。对第一个SAM的每个节点求出它在第二个SAM上对应的合法的路径条数【注意是路径条数,不是节点个数】,然后按照求字典序k小的方法dfs就可以了。
注意几个细节:
1.后继状态总数会爆long long,但是显然总数只需要存到 k
2.一个点可能有好几条字母相同的出边,也就是其实给的不是trie。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
const int maxn=500010;
int trans[maxn][26],fail[maxn],val[maxn],sg[maxn],trie[maxn][26],rt[2],
que[maxn],id[maxn],cnt[maxn],ord[2][maxn],vis[30],corr[maxn],
n[2],t1[2],tot;
LL size[maxn],sum[maxn],num[maxn][30],k;
int add(int x,int u,int fl)
{
    int nu,v,nv;
    val[nu=++tot]=val[u]+1;
    while (u&&!trans[u][x])
    {
        trans[u][x]=nu;
        u=fail[u];
    }
    if (!u) fail[nu]=rt[fl];
    else
    {
        v=trans[u][x];
        if (val[v]==val[u]+1) fail[nu]=v;
        else
        {
            val[nv=++tot]=val[u]+1;
            fail[nv]=fail[v];
            fail[nu]=fail[v]=nv;
            for (int i=1;i<=26;i++) trans[nv][i]=trans[v][i];
            while (u&&trans[u][x]==v)
            {
                trans[u][x]=nv;
                u=fail[u];
            }
        }
    }
    return nu;
}
void build(int fl)
{
    int x,hd,tl,u;
    char c[3];
    scanf("%d",&n[fl]);
    for (int i=0;i<=n[fl];i++)
        for (int j=1;j<=26;j++)
            trie[i][j]=0;
    for (int i=1;i<=n[fl];i++)
    {
        scanf("%d%s",&x,c);
        if (!trie[corr[x]][c[0]-'a'+1]) trie[corr[x]][c[0]-'a'+1]=i;
        corr[i]=trie[corr[x]][c[0]-'a'+1];
    }
    id[0]=rt[fl]=++tot;
    que[hd=tl=1]=0;
    while (hd<=tl)
    {
        u=que[hd++];
        for (int i=1;i<=26;i++)
            if (trie[u][i])
            {
                id[trie[u][i]]=add(i,id[u],fl);
                que[++tl]=trie[u][i];
            }
    }
    for (int i=0;i<=n[fl];i++) cnt[i]=0;
    for (int i=rt[fl];i<=tot;i++) cnt[val[i]]++;
    for (int i=1;i<=n[fl];i++) cnt[i]+=cnt[i-1];
    for (int i=rt[fl];i<=tot;i++) ord[fl][cnt[val[i]]--]=i;
    t1[fl]=tot-rt[fl]+1;
    for (int i=t1[fl];i>=1;i--)
    {
        u=ord[fl][i];
        memset(vis,0,sizeof(vis));
        for (int j=1;j<=26;j++)
            if (trans[ord[fl][i]][j])
                vis[sg[trans[u][j]]]=1;
        for (int j=0;;j++)
            if (!vis[j])
            {
                sg[u]=j;
                break;
            }
    }
}
void dfs(int fl,int u,LL k)
{
    int x;
    if (size[u]>=k)
    {
        putchar('\n');
        if (fl) return;
        for (int i=t1[1];i>=1;i--)
        {
            x=ord[1][i];
            size[x]=sum[x]=(sg[x]!=sg[u]);
            for (int j=1;j<=26;j++)
                sum[x]+=sum[trans[x][j]];
        }
        dfs(1,rt[1],k);
        return;
    }
    k-=size[u];
    for (int j=1;j<=26;j++)
        if (sum[trans[u][j]]>=k)
        {
            putchar('a'+j-1);
            dfs(fl,trans[u][j],k);
            return;
        }
        else k-=sum[trans[u][j]];
}
int main()
{
    //freopen("c.in","r",stdin);
    //freopen("c.out","w",stdout);
    int u;
    scanf("%lld",&k);
    build(0);
    build(1);
    for (int i=t1[1];i>=1;i--)
    {
        u=ord[1][i];
        num[u][sg[u]]=num[u][27]=1;
        for (int j=1;j<=26;j++)
            for (int k=0;k<=27;k++)
                num[u][k]+=num[trans[u][j]][k];
    }
    for (int i=t1[0];i>=1;i--)
    {
        u=ord[0][i];
        sum[u]=size[u]=num[rt[1]][27]-num[rt[1]][sg[u]];
        for (int j=1;j<=26;j++)
        {
            sum[u]+=sum[trans[u][j]];
            if (sum[u]>k+10) sum[u]=k+10;
        }
    }
    if (k>sum[1])
    {
        printf("K is too large!\n");
        return 0;
    }
    dfs(0,1,k);
}

猜你喜欢

转载自blog.csdn.net/sdfzyhx/article/details/74857586