1. 程式人生 > >HDU 4787 GRE Words Revenge(線上AC自動機)

HDU 4787 GRE Words Revenge(線上AC自動機)

題意:Coach Pang學習英語單詞,總共有n個操作,2種操作。每行讀入一個字串。

  1. 如果字串以+開頭,此為單詞(即模式串,不考慮重複)
  2. 如果字串以?開頭,此為文章(即文字串,查詢在此之前的單詞在文字串中出現的次數)

需要注意的是,文章是被加密過的,加密的方法就是將文章看作一個環,每次旋轉上一次詢問的答案次數。具體看輸入即可。

思路:如果只建一個AC自動機的話,每次插入單詞後的新查詢之前都需要重新對整個AC自動機重新求一遍失配邊,這樣的複雜度略高,最壞的情況就是一次插入一次查詢。

作出的優化是:因為考慮到會新加入單詞和較為頻繁的getfail(),那麼對於已有的單詞和新加入的單詞建2個AC自動機(多個也可以?),已有的單詞所在的AC自動機ac即可以不需要getfail(),只對新的加入單詞所在的AC自動機buf呼叫getfail()函式,然後將2個ac自動機的find()函式的結果相加即得到最終答案。這樣做好處在於,每次getfail()的時候避免了大量重複的求一些已知的(不能完全說是已知把,合併後的某些結點的失配邊會改變,但並不會影響最終答案)失配邊。當buf的節點數超過一定量(如何控制?)時,即將buf的字典樹合併到ac的字典樹中,再對ac求一次getfail(),然後清空buf,繼續新增。據說複雜度O(L * sqrt(L))

程式碼

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <vector>
#include <map>
#include <queue>
#include <stack>

using namespace std;

#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1 #define ceil(x, y) (((x) + (y) - 1) / (y)) const int SIZE = 2; const int N = 1e5 + 10; const int M = 1e3 + 10; const int INF = 0x7f7f7f7f; const int MAX_WORD = 5e6 + 10; const double EPS = 1e-9; const int MOD = 2015; const int TH = 2e3; struct AC { int sz; int
ch[N][SIZE]; bool ed[N]; int f[N]; int newnode() { memset(ch[sz], 0, sizeof(ch[sz])); ed[sz] = false; f[sz] = 0; return sz++; } void init() { sz = 0; newnode(); } void insert(char *s) { int u = 0; for (int i = 1; s[i]; i++) { int v = s[i] - '0'; if (!ch[u][v]) ch[u][v] = newnode(); u = ch[u][v]; } ed[u] = true; } void getfail() { queue<int> q; for (int i = 0; i < SIZE; i++) if (ch[0][i]) q.push(ch[0][i]); while (!q.empty()) { int r = q.front(); q.pop(); for (int i = 0; i < SIZE; i++) { int v = ch[r][i]; if (v) { q.push(v); int u = f[r]; while (u && !ch[u][i]) u = f[u]; f[v] = ch[u][i]; } } } } int find(char *s) { int t = 0; int u = 0; for (int i = 1; s[i]; i++) { int v = s[i] - '0'; while (u && !ch[u][v]) u = f[u]; u = ch[u][v]; int p = u; while (p) { if (ed[p]) t++; p = f[p]; } } return t; } }ac, buf;//建2個ac自動機,buf用來存少量的資料 int ans; char str[MAX_WORD]; void init() { ac.init(); buf.init(); } void dfs(int r1, int r2) {//將buf中以r2為根結點的樹合併到ac中以r1為根結點的樹中 for (int i = 0; i < SIZE; i++) { if (buf.ch[r2][i]) { if (!ac.ch[r1][i]) ac.ch[r1][i] = ac.newnode(); int t = ac.ch[r1][i]; ac.ed[t] |= buf.ed[buf.ch[r2][i]]; dfs(ac.ch[r1][i], buf.ch[r2][i]); } } } void join() { dfs(0, 0); //暴力把buf的字典樹合併到ac的字典樹中去 buf.init(); //清空buf ac.getfail(); //求新的ac自動機的失配邊 } void _swap(int is, int ie, int js, int je) { int leni = ie - is + 1, lenj = je - js + 1; if (leni > 0 && lenj > 0) { for (int i = 0; i < min(leni, lenj); i++) swap(str[is + i], str[js + i]); if (leni > lenj) _swap(is + lenj, ie, js, je); else _swap(js, js + leni - 1, js + leni, je); } } int main() { int t_case; scanf("%d", &t_case); for (int i_case = 1; i_case <= t_case; i_case++) { int n; scanf("%d", &n); init(); ans = 0; printf("Case #%d:\n", i_case); for (int i = 0; i < n; i++) { scanf("%s", str); int len = strlen(str); int k = ans % (len - 1); _swap(1, k, k + 1, len - 1); if (str[0] == '+') { buf.insert(str); buf.getfail(); if (buf.sz > TH)//超出閾值,則合併 join(); } else { ans = ac.find(str) + buf.find(str); printf("%d\n", ans); } } } return 0; }