二次联通门 : 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; }