zoukankan      html  css  js  c++  java
  • [AC自动机]玄武密码

    题目描述

    一个长度为(N)的母串,有四个元素分别是:N,S,W,N.

    有M个长度为100的模式串.

    现在要求每个模式串的前缀与母串匹配最长长度.

    输入样例

    7 3
    SNNSSNS
    NNSS
    NNN
    WSEE

    输出样例

    4
    2
    0

    题解

    这是AC自动机的模板题

    关键是判断模式串的前缀是否被匹配

    因为AC自动机的一个结点代表一个前缀,通过fail指针的遍历可以标记所有匹配的前缀

    首先将模式串插入到trie树中,构造自动机

    再将母串输入到AC自动机中,把匹配到的前缀即结点都标记一下.这里有一个剪枝,如果当前结点已经标记过了,那就可以break了,因为这个结点到根节点一定标记了.

    最后再将每个模式串输入到AC自动机中,如果当前结点被标记就更新答案.当前结点被标记说明前缀已匹配.

    代码

    最开始有个小细节重要东西没注意导致RE

    数组开了400,当时理由是100*4

    果然是退役久了变傻了,状态肯定不止400,画一个四叉树就知道了,状态数应该选字符串总长比较合适

    #pragma GCC optimize(2)
    #pragma GCC optimize(3,"Ofast","inline")
    #include <bits/stdc++.h>
    
    using namespace std;
    const int N = 110;
    const int M = 1e5 + 7;
    namespace AC {
        int tr[10000000][4], tot;
        int fail[10000000];
        bool vis[10000000];
    
    
        inline int id(char ch) {
            if (ch == 'E') return 0;
            else if (ch == 'S') return 1;
            else if (ch == 'W') return 2;
            else if (ch == 'N') return 3;
            return 0;
        }
    
        void insert(char *s) {
            int u = 0, k = 0;
            for (int i = 1; s[i]; ++i) {
                k = id(s[i]);
                if (!tr[u][k]) tr[u][k] = ++tot;
                u = tr[u][k];
            }
        }
    
        queue<int> q;
    
        void build() {
            for (int i = 0; i < 4; ++i) {
                if (tr[0][i]) {
                    q.push(tr[0][i]);
                }
            }
            while (!q.empty()) {
                int u = q.front();
                q.pop();
                for (int i = 0; i < 4; ++i) {
                    if (tr[u][i]) {
                        fail[tr[u][i]] = tr[fail[u]][i];
                        q.push(tr[u][i]);
                    } else {
                        tr[u][i] = tr[fail[u]][i];
                    }
                }
            }
        }
    
        void query(char *t) {
            int u = 0;
            for (int i = 1; t[i]; ++i) {
                u = tr[u][id(t[i])];
                for (int j = u; j && vis[j] != 1; j = fail[j]) {
                    vis[j] = true;
                }
            }
        }
    
        int get_ans(char *s) {
            int u = 0, res = 0;
            for (int i = 1; s[i]; ++i) {
                u = tr[u][id(s[i])];
                if (vis[u]) res = i;
            }
            return res;
        }
    }
    char str[M][N];
    char p[10000005];
    
    int main() {
        int n, m;
        scanf("%d %d", &n, &m);
        scanf("%s", p + 1);
        for (int i = 1; i <= m; ++i) {
            scanf("%s", str[i] + 1);
            AC::insert(str[i]);
        }
        AC::build();
        AC::query(p);
        for (int i = 1; i <= m; ++i) {
            printf("%d
    ", AC::get_ans(str[i]));
        }
        return 0;
    }
    

     这个题虽然是以前做过的,但久了就忘了,当时也没有真正理解其精髓

  • 相关阅读:
    git注册和基本命令
    thinkphp概述2
    thinkphp概述
    PHP基础知识总结
    phpmyadmin教程
    开发环境wamp3.06 + Zend studio 12 调试配置
    PHP标记风格,编码规范
    PHP开发工具 zend studio
    php与ajax技术
    可变参模板template
  • 原文地址:https://www.cnblogs.com/smallocean/p/12240680.html
Copyright © 2011-2022 走看看