阿狸的打字机 Trie fail树+树状数组+dfs序

https://ac.nowcoder.com/acm/problem/17633

题意就不说了,那上面说得很清楚了;

这道题最暴力的办法就是,每个字符串都跑next数组,然后kmp匹配,数据很小的时候,可以这样,但不过这道题就不能了,太暴力了。

所以我们还是想想Trie(AC自动机吧),这道题用到了一个很有用的结论,我觉得这个结论在字符串配中还是很重要的

T串为S串的子串,当且仅当T是S的某一个前缀的后缀,这样的前缀的数目的多少,就是T在S中出现的次数。

而fail的指针跳的就是一个前缀的最长匹配后缀

我们这样做,从y字符串方向走fail,有多少个x的结束点就出现了多少次,直接在自动机上条跳fail绝对超时,不管你用什么优化,(dalao除外。。),首先建一颗fail树,有的人可能不会建fail树,其实也不难,就是fail指向跳到他的点,其实就是把fail指针反向。然后把y上的所有结点变成1,然后求fail的树中x的结点的子树中有多个数1,这个东西就可以用dfs序,加上树状数组维护了,这种方法在AC自动机的字符串题目中比较常见。对于一个P操作,就是打印一个串,我们直接把串的最后一个字符的记录下来,用一个pos数组表示字符串的编号,p数组表示,他在Trie上的位置,B操作就是返回他的父亲结点。

在求解答案的时候,当加入一个点时,就对in全部加1,然后查找与他的有关的x串,这个的ans就是ou[x的位置]-in[x的位置-1];这样遍历Trie就可以了。

#include
using namespace std;
const int N=100010;
int tree[N];
int lowbits(int x){return x&(-x);}
void add(int x,int val)
{
    for(;xson[N];
int nxt[N][26],fail[N];
int tot=0;
void Insert(char *s)
{
    int len=strlen(s),now=0;
    num=0;
    for(int i=0;i='a'&&s[i]<='z')
        {
            if(!nxt[now][x]) nxt[now][x]=++tot,fa[nxt[now][x]]=now;
            now=nxt[now][x];
        }
        else if(s[i]=='P')
            pos[now]=++num,p[num]=now;
        else
            now=fa[now];
    }
}
void build()
{
    queuequ;
    for(int i=0;i<26;i++)
        if(nxt[0][i]) qu.push(nxt[0][i]);
    while(!qu.empty())
    {
        int u=qu.front();qu.pop();
        for(int i=0;i<26;i++)
        {
            if(nxt[u][i]!=0) fail[nxt[u][i]]=nxt[fail[u]][i],qu.push(nxt[u][i]);
            else nxt[u][i]=nxt[fail[u]][i];
        }
    }
}
int in[N],ou[N],dfn=0;
vectorvec[N];
void dfs(int u,int fa)
{
    in[u]=++dfn;
    for(int i=0;i >pa[N];
int x[N],y[N],m,ans[N];
void solve(int u)
{
    add(in[u],1);
    for(int i=0;i

 

你可能感兴趣的:(ACM题解,数据结构)