[置顶] hdu 5164 Matching on Array (奇葩版ac自动机)

题意:

给出一个a序列,m个b序列,a能通过倍数关系能出现多少b串。例如a{ 2 4 8 } b{ 1 2 } 那么a中的{ 2 4 } 和{ 4 8 } 是可以通过b序列通过倍数变成的。

题解:

我们可以把串进行缩放,然后缩放后的串进行匹配。

对于m==1的情况用kmp 其他情况用ac自动机。搞一个分数类进行处理。自动机的边用map,对奇葩的地方就是这里,用map做边,map<Node,int>next[SIZE] 这样就通过map映射Node类型(分数类)来解决节点的存储关系。弄完这个,就是无脑的自动机匹配。可惜哥弄了一个晚上的代码最终以wa结尾。。。找啊找,各种原因,最终发现可能是cnt初始化为0的缘故,因为我的代码写法,会导致冲突!应为map如果不存对应的边,那么会返回0,同样根编号也是0,这样很明显冲突啊,所以各种wa!!!最后参照了别人的代码解决了这个问题。由于g++类无法存太大的数组,因此没写成类的形式。这题好题啊,出题人脑洞太大了,ORZ!

#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define B(x) (1<<(x))
typedef __int64 ll;
const int oo=0x3f3f3f3f;
const ll OO=1LL<<61;
const int MOD=10007;
const int maxn=110000;
const int maxm=1000005;
const int SIZE=1000005;
int Next[maxm];
//const int type=4;

inline int gcd(int a,int b)
{
    return b==0 ? a : gcd(b,a%b);
}

struct Node
{
    int up,down;

    Node(){}

    Node(int a,int b){

        up=a;down=b;
        int temp;
        if(up>down)temp=gcd(up,down);
        else temp=gcd(down,up);
        up/=temp;
        down/=temp;
    }

    bool operator==(const Node& a)const{

        return up==a.up&&down==a.down;
    }

    bool operator!=(const Node& a)const{

        return up!=a.up||down!=a.down;
    }

    bool operator<(const Node& a)const{

        if(up!=a.up)
            return up<a.up;
        return down<a.down;
    }

}a[maxn],b[maxn];

void input(Node buff[],int len){

    int x,y;
    scanf("%d",&x);
    for(int i=1;i<len;i++){

        scanf("%d",&y);
        buff[i]=Node(y,x);
        x=y;
    }
}

map<Node,int>next[SIZE];
int fail[SIZE],flag[SIZE];
int cnt,root;

int newNode(){

    next[cnt].clear();
    flag[cnt++]=0;
    return cnt-1;
}

void Init(){

    cnt=1;
    root=newNode();
}

void Insert(Node buff[],int len){

    int now=root;
    Node k;
    for(int i=1;i<=len;i++){

        k=buff[i];
        if(next[now][k]==0)
            next[now][k]=newNode();
        now=next[now][k];
    }//for
    flag[now]++;
}

void build(){

    fail[root]=0;
    Node k;
    queue<int>Q;

    ///注:next[now][i] <=> it->second ; i <=>it->first;
    for(map<Node,int>::iterator it=next[root].begin();it!=next[root].end();++it){

        fail[it->second]=root;
        Q.push(it->second);
    }//for

    while(!Q.empty()){

        int now=Q.front();Q.pop();
        flag[now]+=flag[fail[now]];
        for(map<Node,int>::iterator it=next[now].begin();it!=next[now].end();++it){

            int temp=fail[now];
            int nxt=next[temp][it->first];
            while(temp&&!nxt){

                temp=fail[temp];
                nxt=next[temp][it->first];
            }
            if(temp) fail[it->second]=nxt;
            else fail[it->second]=root;
            Q.push(it->second);
        }//for
    }//while
}

ll Search(Node buff[],int len){

    int now=root;
    ll ans=0;
    for(int i=1;i<=len;i++){

        int nxt=next[now][buff[i]];
        while(now&&!nxt){

            now=fail[now];
            nxt=next[now][buff[i]];
        }
        if(now) now=nxt;
        else now=root;
        ans+=flag[now];
    }//for
    return ans;
}

void get_next(Node T[],int len)
{
    int i=0;
    Next[i]=-1;
    int j=-1;
    while(i<len)
    {
        if(j==-1||T[i]==T[j]){

            i++;j++;
            Next[i]=j;
        }else j=Next[j];
    }
}

ll kmp(Node S[],Node T[],int lenS,int lenT){

    ll ans=0;
    int i=0,j=0;
    while(i<lenS){

        if(j==-1||S[i]==T[j]){

            i++;
            j++;
        }else j=Next[j];
        if(j==lenT) ans++;
    }//for
    return ans;
}

int main(){

    int T,m,n,k;
    ll ans;
    scanf("%d",&T);
    while(T--){

        Init();
        scanf("%d %d",&n,&m);
        input(a,n);
        ans=0;
        if(m>1){

            for(int i=1;i<=m;i++){

                scanf("%d",&k);
                input(b,k);
                if(k>1) Insert(b,k-1);
                else ans+=n;
            }//for

            build();
            ans+=Search(a,n-1);

        }else{

            scanf("%d",&k);
            input(b,k);

            if(k>1){
                get_next(b+1,k-1);
                ans=kmp(a+1,b+1,n-1,k-1);
            }else ans+=n;
        }//else

        printf("%I64d\n",ans);
    }//while
    return 0;
}//main
/**
2
4 1
2 4 8 16
2 1 2
5 3
2 4 2 4 6
3 1 2 1
1 5
2 16 8
*/



你可能感兴趣的:([置顶] hdu 5164 Matching on Array (奇葩版ac自动机))