洛谷 - P3966 - 单词 - AC自动机

https://www.luogu.org/problemnew/show/P3966
因为文本串就是字典本身,所以这个和平时的AC自动机不太一样。平时的query要沿着fail树把子树的出现次数依次统计。但是这个的query就是对每个字典里的字符串搞一次。所以就直接按广搜的顺序反过来树形dp统计出子树中的出现次数,直接回答。

#include
using namespace std;
typedef long long ll;

const int MAXN = 1e6;
const int MAXM = 2e2;

int idx[MAXM + 5];

struct Trie {
    int trie[MAXN + 5][26], fail[MAXN + 5], cnt[MAXN + 5], que[MAXN + 5];
    int root, top;
    int newnode() {
        for(int i = 0; i < 26; i++)
            trie[top][i] = -1;
        cnt[top++] = 0;
        return top - 1;
    }

    void init() {
        top = 0;
        root = newnode();
    }

    void insert(char buf[], int id) {
        int len = strlen(buf);
        int cur = root;
        for(int i = 0; i < len; i++) {
            int u = buf[i] - 'a';
            if(trie[cur][u] == -1)
                trie[cur][u] = newnode();
            cur = trie[cur][u];
            cnt[cur]++;
        }
        idx[id] = cur;
    }

    void build() {
        int head = 0, tail = 0;
        fail[root] = root;
        for(int i = 0; i < 26; i++) {
            if(trie[root][i] == -1)
                trie[root][i] = root;
            else {
                fail[trie[root][i]] = root;
                que[tail++] = trie[root][i];
            }
        }
        while(head < tail) {
            int cur = que[head++];
            for(int i = 0; i < 26; i++) {
                if(trie[cur][i] == -1)
                    trie[cur][i] = trie[fail[cur]][i];
                else {
                    fail[trie[cur][i]] = trie[fail[cur]][i];
                    que[tail++] = trie[cur][i];
                }
            }
        }
        for(int i = tail - 1; i >= 0; i--) {
            cnt[fail[que[i]]] += cnt[que[i]];
        }
    }
};

char buf[MAXN + 5];

Trie ac;

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
    //freopen("Yinku.out","w",stdout);
#endif // Yinku
    int n;
    scanf("%d", &n);
    ac.init();
    for(int i = 0; i < n; i++) {
        scanf("%s", buf);
        ac.insert(buf, i);
    }
    ac.build();
    for(int i = 0; i < n; i++)
        printf("%d\n", ac.cnt[idx[i]]);
}

这种写法是,每次文本串遇到一个模式串,就会给这个模式串+1次出现次数。问题是怎么优化重复的文本串呢?把文本串插入另一个trie里面就可以知道他们的重复数。然后每次就给每个模式串都贡献一个重数就等价了。这样最好理解。优化的地方在于同样的文本串不用多次匹配。

#include
using namespace std;
typedef long long ll;

const int MAXN = 1e6;
const int MAXM = 2e2;

int ans[MAXM + 5];
int aidx[MAXN + 5][MAXM + 5];

struct Trie {
    int trie[MAXN + 5][26], fail[MAXN + 5], cnt[MAXN + 5], que[MAXN + 5];
    //本题多使用的一个是文本串重复计数器tcnt,但是在这里文本一定和模式一样就可以合并到模式重复计数器cnt里,一个文本防重vis
    int tcnt[MAXN + 5];
    bool vis[MAXN + 5];

    int root, top;
    int newnode() {
        for(int i = 0; i < 26; i++)
            trie[top][i] = -1;
        cnt[top++] = 0;
        return top - 1;
    }

    void init() {
        top = 0;
        root = newnode();
    }

    void insert(char buf[], int id) {
        int len = strlen(buf);
        int cur = root;
        for(int i = 0; i < len; i++) {
            int u = buf[i] - 'a';
            if(trie[cur][u] == -1)
                trie[cur][u] = newnode();
            cur = trie[cur][u];
        }
        cnt[cur]++;
        //本题中文本可以一并插入,干脆合并成一个cnt,可以在同一个trie中,否则应该分开插入两个trie
        aidx[cur][cnt[cur]-1] = id;
    }

    void build() {
        int head = 0, tail = 0;
        fail[root] = root;
        for(int i = 0; i < 26; i++) {
            if(trie[root][i] == -1)
                trie[root][i] = root;
            else {
                fail[trie[root][i]] = root;
                que[tail++] = trie[root][i];
            }
        }
        while(head < tail) {
            int cur = que[head++];
            for(int i = 0; i < 26; i++) {
                if(trie[cur][i] == -1)
                    trie[cur][i] = trie[fail[cur]][i];
                else {
                    fail[trie[cur][i]] = trie[fail[cur]][i];
                    que[tail++] = trie[cur][i];
                }
            }
        }
    }

    void query(char buf[]) {
        //该文本串匹配了多少次模式串
        /*int len=strlen(buf);
        int cur=root;
        int res=0;
        for(int i=0;i

转载于:https://www.cnblogs.com/Yinku/p/11084288.html

你可能感兴趣的:(洛谷 - P3966 - 单词 - AC自动机)