luogu P2353 背单词

二次联通门 : luogu P2353 背单词

一眼看过去, 卧槽,AC自动机板子题

写完后T成SB

卧槽10^6 做个篮子啊

重构思路。。。

恩。。Hash + 莫队。。。

恶心啊。。

找xxy dalao, AC自动机 + 前缀和

码完WA成SB

去群里找dalao

大佬告诉了我前缀和的正确使用姿势。。。

然后就依然WA成SB

 做个毛线

贴一个AC自动机的30代码

#include <cstdio>
#include <cstring>
#include <queue>

#define Max 3000090

void read (int &now)
{
    now = 0;
    register char word = getchar ();
    while (word < '0' || word > '9')
        word = getchar ();
    while (word >= '0' && word <= '9')
    {
        now = now * 10 + word - '0';
        word = getchar ();
    }
}

char __txt[Max];

struct T_D
{
    T_D *child[26];

    T_D *Fail;
    int Count;
    
    int number;
    T_D ()
    {
        for (int i = 0; i < 26; i ++)
            this->child[i] = NULL;
        
        Count = 0;
        Fail = NULL;
        number = 0;
    }
};

int List[Max << 1];
int List_Cur;

bool visit[Max];

class AC_Type
{

    private :

        T_D *Root;
        int Trie_Count;

    
    public :

        void Insert (char *key)
        {
            T_D *now = Root;

            int Len = strlen (key);
            int Id;

            for (int i = 0; i < Len; i ++)
            {
                Id = key[i] - 'a';
                if (now->child[Id] == NULL)
                {
                    now->child[Id] = new T_D;
                    now->child[Id]->number = ++ Trie_Count;
                }

                now = now->child[Id];
            }
            now->Count ++;
        }

        AC_Type ()
        {
            Trie_Count = 0;
            Root = new T_D ;
            Root->number = ++ Trie_Count;
        }

        void Build_AC ()
        {
            std :: queue <T_D *> Queue;

            Queue.push (Root);
            
            T_D *now, *pos;
            while (!Queue.empty ())
            {
                now = Queue.front ();
                Queue.pop ();

                pos = NULL;

                for (int i = 0; i < 26; i ++)
                {
                    if (now->child[i] == NULL)
                        continue;
                    if (now == Root)
                        now->child[i]->Fail = Root;
                    else
                    {
                        for (pos = now->Fail; pos; pos = pos->Fail)
                            if (pos->child[i])
                            {
                                now->child[i]->Fail = pos->child[i];
                                break;
                            }
                        if (pos == NULL)
                            now->child[i]->Fail = Root;
                    }
                    Queue.push (now->child[i]);
                }
            }
        }

        int Query (int x, int y)
        {
            T_D *now, *pos;
            
            int Id ;
            now = Root;
            int res = 0;
            
            for (int i = x; i <= y; i ++)
            {
                Id = __txt[i] - 'a';
                for (; now != Root && now->child[Id] == NULL; now = now->Fail);
                
                now = now->child[Id];
                if (now == NULL)
                    now = Root;

                for (pos = now; pos != Root && !visit[pos->number]; pos = pos->Fail)
                {
                    res += pos->Count;
                    visit[pos->number] = true;
                    List[++ List_Cur] = pos->number;
                }

                for (int j = 1; j <= List_Cur; j ++)
                    visit[List[j]] = false;

                List_Cur = 0;
        
            }
        
            return res;
        }

};

AC_Type Make;

int N, Q;
char line[Max];

int main (int argc, char *argv[])
{
    read (N);
    read (Q);
    scanf ("%s", __txt);
    
    for (int i = 1; i <= N; i ++)
    {
        scanf ("%s", line);
        Make.Insert (line);
    }
    
    Make.Build_AC ();
    
    for (int x, y; Q --; )
    {
        read (x);
        read (y);
                    
        printf ("%d
", Make.Query (-- x, -- y));
    }

    return 0;
}

 再贴个AC自动机思路正确但由于细节问题WA成dog的代码

#include <cstdio>
#include <cstring>
#include <queue>

#define Max 1000090

#define DEBUG for (int i = 1; i <= strlen (__txt); i ++)
                printf ("%d  ", __sum[i]);
                putchar ('
');
                
void read (int &now)
{
    now = 0;
    register char word = getchar ();
    while (word < '0' || word > '9')
        word = getchar ();
    while (word >= '0' && word <= '9')
    {
        now = now * 10 + word - '0';
        word = getchar ();
    }
}

char __txt[Max];

struct T_D
{
    T_D *child[26];

    T_D *Fail;
    int Count;
    
    int number;
    T_D ()
    {
        for (int i = 0; i < 26; i ++)
            this->child[i] = NULL;
        
        Count = 0;
        Fail = NULL;
        number = 0;
    }
};

int __sum[Max];

class AC_Type
{

    private :

        T_D *Root;
        int Trie_Count;

    
    public :

        void Insert (char *key)
        {
            T_D *now = Root;

            int Len = strlen (key);
            int Id;

            for (register int i = 0; i < Len; i ++)
            {
                Id = key[i] - 'a';
                if (now->child[Id] == NULL)
                {
                    now->child[Id] = new T_D;
                    now->child[Id]->number = ++ Trie_Count;
                }

                now = now->child[Id];
            }
            now->Count ++;
        }

        AC_Type ()
        {
            Trie_Count = 0;
            Root = new T_D ;
            Root->number = ++ Trie_Count;
        }

        void Build_AC ()
        {
            std :: queue <T_D *> Queue;

            Queue.push (Root);
            
            T_D *now, *pos;
            while (!Queue.empty ())
            {
                now = Queue.front ();
                Queue.pop ();

                pos = NULL;

                for (register int i = 0; i < 26; i ++)
                {
                    if (now->child[i] == NULL)
                        continue;
                    if (now == Root)
                        now->child[i]->Fail = Root;
                    else
                    {
                        for (pos = now->Fail; pos; pos = pos->Fail)
                            if (pos->child[i])
                            {
                                now->child[i]->Fail = pos->child[i];
                                break;
                            }
                        if (pos == NULL)
                            now->child[i]->Fail = Root;
                    }
                    Queue.push (now->child[i]);
                }
            }
        }

        int Query ()
        {
            T_D *now, *pos;
            
            int Id ;
            now = Root;
            int res = 0;
            int Len = strlen (__txt);
            
            for (register int i = 0; i < Len; i ++)
            {
                Id = __txt[i] - 'a';
                for ( ; now != Root && now->child[Id] == NULL; now = now->Fail);
                
                now = now->child[Id];
                if (now == NULL)
                    now = Root;

                for (pos = now; pos != Root && pos->Count >= 0; pos = pos->Fail)
                {
                    __sum[i + 1] += pos->Count;
                    pos->Count = -1;
                }
                __sum[i + 1] += __sum[i];
        
            }
        
            return res;
        }

};

AC_Type Make;

int N, Q;
char line[Max];

int length[Max];

int main (int argc, char *argv[])
{

    read (N);
    read (Q);
    scanf ("%s", __txt);
    
    for (int i = 1; i <= N; i ++)
    {
        scanf ("%s", line);
        Make.Insert (line);
        length[i] = strlen (line);
    }
    
    Make.Build_AC ();
    Make.Query ();
       register int Answer, now;
       
    for (int x, y; Q --; )
    {
        Answer = 0;
        
        read (x);
        read (y);
        
        for (int i = 1; i <= N; i ++)
            Answer += (__sum[y - length[i]] - __sum[x - 1]);
        
        printf ("%d
", Answer);
    }

    return 0;
}

最后再贴个正解。。。。是我想麻烦了。。kmp或者hash都可以。。

/*
    luogu P2353 背单词
    
    由于M很小
    可以进行M次kmp
    
    统计出M个前缀和
    
    每次输出时把 M 个前缀和扫一遍 
    注意区间的开闭问题
    
    由于r端点的串不包含在所查询的区间内
    所以要减去当前模式串的长度 

*/
#include <cstdio>
#include <cstring>

#define Max 1000090

void read (int &now)
{
    now = 0;
    register char word = getchar ();
    while (word > '9' || word < '0')
        word = getchar ();
    while (word >= '0' && word <= '9')
    {
        now = now * 10 + word - '0';
        word = getchar ();
    }
}

int __next[Max];

void Get_Next (char *line)
{
    __next[0] = -1;

    for (int pos_1 = 0, pos_2 = -1, Len = strlen (line); pos_1 < Len; )
        if (pos_2 == -1 || line[pos_1] == line[pos_2])
        {
            pos_1 ++;
            pos_2 ++;
            __next[pos_1] = pos_2;
        }
        else
            pos_2 = __next[pos_2];
    
}

int __sum[Max][Max / 100000 + 1];

void Kmp (char *line, char *__txt, int number)
{
    for (int Len_txt = strlen (__txt), Len = strlen (line), pos_1 = 0, pos_2 = 0; pos_1 <= Len_txt; )
    {
        if (pos_2 == -1 || __txt[pos_1] == line[pos_2])
        {
            pos_1 ++;
            pos_2 ++;
        }
        else
            pos_2 = __next[pos_2];
        if (pos_2 == Len)
        {
            __sum[pos_1][number] ++;
            pos_2 = __next[pos_2];
        }
    }
}

char __txt[Max];

int length[Max];
char line[Max];

int main (int argc, char *argv[])
{
    int N, M;
    
    read (N);
    read (M);
    
    scanf ("%s", __txt);
    
    int Len_txt = strlen (__txt);
    
    for (int i = 1; i <= N; i ++)
    {
        scanf ("%s", line);
        
        Get_Next (line);
        Kmp (line, __txt, i);
        
        length[i] = strlen (line);
    }
    
    for (int i = 1; i <= Len_txt; i ++) // 把每个模式串的前缀和分开存 
        for (int j = 1; j <= N; j ++)
            __sum[i][j] += __sum[i - 1][j];
    
    for (int i = 1, x, y, Answer; i <= M; i ++)
    {
        read (x);
        read (y);
        Answer = 0;
        
        for (int j = 1; j <= N; j ++)
            if (x - 1 <= y - length[j])
                Answer += __sum[y][j] - __sum[x + length[j] - 2][j];
    
        printf ("%d
", Answer);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/ZlycerQan/p/7043850.html