[NOI2011]阿狸的打字机

题目描述

打字机上只有28个按键,分别印有26个小写英文字母和'B'、'P'两个字母。经阿狸研究发现,这个打字机是这样工作的:

·输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。

·按一下印有'B'的按键,打字机凹槽中最后一个字母会消失。

·按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。

例如,阿狸输入aPaPBbP,纸上被打印的字符如下:

a aa ab 我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。

阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~·

这道题是fail树的裸题。利用ac自动机或trie图能构建出trie树上的fail指针,代表与其拥有最长相同后缀的、比他短的位置。

重点是每个点只有唯一的fail指针

这就很像树了。(每个点只有唯一的父节点)

所以我们可以将fail指针反指,形成一棵树,叫fail树

fail树所具有的性质就是:当前子树中所有点代表的前缀都以当前树根代表的字符串为后缀。

人话翻译:

假设每个点对应一个字符串,那么若b点在a点的子树中,a一定是b的后缀。

现在再来看这道题,问题是求一个子串中有多少个另一个子串,就是问这个子串中的所有前缀以另一串为后缀的有多少个。

于是上fail树:

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100050
int n,m,len,tot,rt[N],hed[N],cnt;
char s[N];
struct Trie
{
    int ch[28];
    int fa,fl;
}tr[N];
struct EG
{
    int to,nxt;
}e[N];
void ae(int f,int t)
{
    e[++cnt].to = t;
    e[cnt].nxt = hed[f];
    hed[f] = cnt;
}
void trie_pic()
{
    queue<int>q;
    for(int i=1;i<=26;i++)
        if(tr[0].ch[i])
            q.push(tr[0].ch[i]),ae(0,tr[0].ch[i]);
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        for(int i=1;i<=26;i++)
        {
            int &v = tr[u].ch[i];
            if(!v)
            {
                v = tr[tr[u].fl].ch[i];
                continue;
            }
            tr[v].fl = tr[tr[u].fl].ch[i];
            ae(tr[v].fl,v);
            q.push(v);
        }
    }
}
int tin[N],tout[N],tim;
void dfs(int u)
{
    tin[u]=++tim;
    for(int j=hed[u];j;j=e[j].nxt)dfs(e[j].to);
    tout[u]=tim;
}
int f[2*N];
void up(int x,int d)
{
    if(!x)return ;
    while(x<=200000)
    {
        f[x]+=d;
        x+=(x&(-x));
    }
}
int down(int x)
{
    if(!x)return 0;
    int ret = 0;
    while(x)
    {
        ret+=f[x];
        x-=(x&(-x));
    }
    return ret;
}
struct node
{
    int u,v,id;
}p[N];
int ans[N];
bool cmp(node a,node b)
{
    return a.v<b.v;
}
int main()
{
    scanf("%s",s+1);
    len = strlen(s+1);
    int u = 0;
    for(int i=1;i<=len;i++)
    {
        if(s[i]>='a'&&s[i]<='z')
        {
            int c = s[i]-'a'+1;
            if(!tr[u].ch[c])tr[u].ch[c]=++tot,tr[tot].fa=u;
            u=tr[u].ch[c];
        }else if(s[i]=='B')
        {
            u = tr[u].fa;
        }else
        {
            rt[++n] = u;
        }
    }
    trie_pic();
    dfs(0);
    scanf("%d",&m);
    for(int i=1;i<=m;i++)scanf("%d%d",&p[i].u,&p[i].v),p[i].id=i;
    sort(p+1,p+1+m,cmp);
    int k = 1,c = 0;
    u = 0;
    for(int i=1;i<=len;i++)
    {
        if(s[i]>='a'&&s[i]<='z')
        {
            int c = s[i]-'a'+1;
            u = tr[u].ch[c];
            up(tin[u],1);
        }else if(s[i]=='B')
        {
            up(tin[u],-1);
            u = tr[u].fa;
        }else
        {
            c++;
            while(p[k].v==c)
            {
                ans[p[k].id] = down(tout[rt[p[k].u]])-down(tin[rt[p[k].u]]-1);
                k++;
            }
        }
    }
    for(int i=1;i<=m;i++)printf("%d
",ans[i]);
    return 0;
}
原文地址:https://www.cnblogs.com/LiGuanlin1124/p/9676086.html