zoukankan      html  css  js  c++  java
  • 2018牛客网暑期ACM多校训练营(第九场)F(AC自动机)

    题目描述:

        有n(n<=4)个长度为len的字符串,以及一个长度为len2的操作串。每一次你将选取操作串中长度为i(0<=i<=len2)的前缀,问你最少在这个前缀后加多少个字符,使得新字符串的后缀中能够至少出现这n个字符串中的一个。

    题目分析:

        因为题目中设计多个串的匹配一个长串的问题,我们可以考虑使用AC自动机进行处理。

        再考虑题目中要求我们求出匹配串的后缀凑出模式串的最小长度,因此,我们可以将这样的一个问题转化成:在一个Trie图中,处于第i个结点的字符到达任意一个模式串终点j的最短路。

        因此,我们只需要利用AC自动机,将利用失配指针的性质,先将整张Trie图建立出来,并将每一个结点到达任意终点的最短路用bfs求出即可。

        具体细节请看代码。

    代码:

    #include <bits/stdc++.h>
    #define maxn 400005
    using namespace std;
    const int INF=0x3f3f3f3f;
    char tmp[maxn];
    vector<int>vec[maxn];
    struct Trie{//AC自动机
        int next[maxn][26],fail[maxn],root,id,ans[maxn];//ans[i]记录了某个结点是否处于字符串的末尾
        set<int>st;//set用来统计每个单词的结尾的坐标
        int newnode(){//新建立结点
            for(int i=0;i<26;i++){
                next[id][i]=-1;
            }
            ans[id]=INF;
            return id++;
        }
        void init(){
            st.clear();
            id=0;
            root=newnode();
        }
        void Insert(char *str){//将模式串插入Trie树中
            int len=strlen(str);
            int now=root;
            for(int i=0;i<len;i++){
                if(next[now][str[i]-'a']==-1){
                    next[now][str[i]-'a']=newnode();
                }
                now=next[now][str[i]-'a'];
            }
            st.insert(now);//将模式串结尾插入set中
            ans[now]=0;//ans[i]=0 记录了某个结点曾今
        }
        void build(){//建立AC自动机失配指针
            queue<int>que;
            fail[root]=root;
            for(int i=0;i<26;i++){
                if(next[root][i]==-1){
                    next[root][i]=root;
                }
                else{
                    fail[next[root][i]]=root;
                    que.push(next[root][i]);
                }
            }
            while(!que.empty()){
                int now=que.front();
                que.pop();
                //如果某个结点的失配指针正好等于某个模式串的终点,则证明该结点也必定跟失配指针所指的结点相同,也可以作为终点处理
                if(ans[fail[now]]==0){
                    st.insert(now);
                    ans[now]=0;
                }
                for(int i=0;i<26;i++){
                    if(next[now][i]==-1){
                        next[now][i]=next[fail[now]][i];
                    }
                    else{
                        fail[next[now][i]]=next[fail[now]][i];
                        que.push(next[now][i]);
                    }
                }
            }
        }
    
        void bfs(){//bfs处理最短路
            for(int i=0;i<id;i++){
                vec[i].clear();
            }
            for(int i=0;i<id;i++){
                for(int j=0;j<26;j++){
                    vec[next[i][j]].push_back(i);//建立Trie图的过程
                }
            }
            queue<int>que;
            for(auto it:st){//首先将可以作为终点的结点压入队列
                que.push(it);
            }
            while(!que.empty()){//扩展距离
                int now=que.front();
                que.pop();
                for(auto to:vec[now]){
                    if(ans[to]!=INF) continue;
                    ans[to]=ans[now]+1;
                    que.push(to);
                }
            }
        }
        void query(char *str){//查询答案
            int len=strlen(str);
            stack<int>sta;//用栈去维护模板串
            int now=root;
            cout<<ans[now]<<endl;
            for(int i=0;i<len;i++){
                if(str[i]=='-'&&sta.empty()){//如果当前的栈顶为空,且当前字符还是'-',则直接将now变为根节点
                    now=root;
                }
                else if(str[i]=='-'){
                    now=sta.top();
                    sta.pop();
                }
                else{
                    sta.push(now);
                    now=next[now][str[i]-'a'] ;
                }
                cout<<ans[now]<<endl;
            }
        }
    }ac;
    int main()
    {
        int n;
        scanf("%d",&n);
        ac.init();
        while(n--){
            scanf("%s",tmp);
            ac.Insert(tmp);
        }
        ac.build();
        ac.bfs();
        scanf("%s",tmp);
        ac.query(tmp);
        return 0;
    }
    
  • 相关阅读:
    shopping car 1.0
    文件分类
    求1-100的所有数的和
    输出 1-100 内的所有奇数和
    求1-2+3-4+5 ... 99的所有数的和
    关闭提示的下拉框
    h5页面乱码-设置编码
    常用的css
    渲染后新元素没有绑定事件
    爬虫日记-关于一些动态爬取
  • 原文地址:https://www.cnblogs.com/Chen-Jr/p/11007235.html
Copyright © 2011-2022 走看看