hdu 5164 Matching on Array AC自动机

来源:互联网 发布:linux stdin 编辑:程序博客网 时间:2024/05/01 17:09

Matching on Array

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 257    Accepted Submission(s): 61


Problem Description
Alice has a sequence {a1,a2,,an} with n positive integers. Bob has some positive integer sequences with different size. Alice wants to know the total occurrences of every sequence Bob has in Alice's sequence (the occurrences are allowed to overlap).

We say one sequence B occurs in another sequence A if there is a contiguous subsequence of A that is the same as B after scaled by a positive real factor.

For example A={2,4,8,16},B={1,2} then B occurs three times in A. The occurrences are {2,4},{4,8} and {8,16}. And the factor is 0.5, 0.25 and 0.125.
 

Input
There are multiple test cases. The first line of input contains an integerT (1T30), indicating the number of test cases. For each test case:

The first line contains two integer n and m(1n,m100000), indicating the size of Alice's sequence and the number of sequences Bob has. In the next line, there aren integers, a1,a2,,an, indicating Alice's sequence. In the following m lines, each starts with an integer ki(1ki300000) - the size of the sequence. Then ki space separated positive integers follow, indicating the sequence.

The total sum of ki is less than or equal to 1000000. Other integers are between 1 and 10000, inclusive.
 

Output
For each test case, output a single line with a single integer, indicating the total number of occurrences.
 

Sample Input
24 12 4 8 162 1 25 32 4 2 4 63 1 2 11 52 16 8
 

Sample Output
37
Hint
For sample 1, please refer to the problem description. For sample 2, {1, 2, 1} occurs only once, {5} occurs five times and {16, 8} occurs only once.思路: 如果是单纯的在文本串中找模式串,可以用KMP或者BM,算法讲解 如果是多个模式串,就需要用AC自动机。AC自动机:点击打开链接 大神讲解的很是清楚。乘以任意的factor,使俩个串相等,那么前提条件肯定是每个串之间的比值肯定是一样的。A{x1, x2, x3……} B{y1, y2, y3……}如果存在 B* p = A 必然, x2/ x1 = y2 / y1 ; x3/x2 = y3/y2;先把数据转换一下,利用AC自动机就可以了。需要注意 ans 的范围会爆 int { n = 100000; m = 100000 ; Bob序列每行只有一个数字,那么ans = n * m;}一开始,我用map<double, Tree *> 去记录字典树中每个节点的子节点;不过用double 多少害怕会有误差。 解决方法是用 node()结构体储存 被除数和除数约分后的值。不过验证过了,用double是可以AC的,
#include<stdio.h>#include<string.h>#include<math.h>#include<string>#include<iostream>#include<algorithm>#include<vector>#include<queue>#include<list>#include<map>#include<set>using namespace std;int n, m;int a[100010], b[300010];int kind;long long ans;struct Tree{    map<int, Tree* >next;    vector<int>v;    Tree *fail;    int cnt;    Tree(){        next.clear();        v.clear();        cnt = 0;        fail = NULL;    }};vector<int>e;Tree *root;struct node{    int x,y;    bool operator < (const node &p)const{        if(x == p.x) return y < p.y;        return x < p.x;    }    bool operator == (const node &p)const{        return x == p.x && y == p.y;    }    node(int x = 0, int y = 0):x(x), y(y) {}};node alice[100010];vector<node>idx;int gcd(int x, int y){    if(y == 0) return x;    else return gcd(y, x % y);}node deal_gcd(int x, int y){    int r = gcd(x, y);    x /= r;    y /= r;    return node(x, y);}void tree_add(){    Tree *p = root;    for(int i = 0; i < e.size(); i++){        if(p->next[e[i]] == NULL){            p->v.push_back(e[i]);            p->next[e[i]] = new Tree();        }        p = p->next[e[i]];    }    p->cnt ++;}bool march(node p, node q){    if(p == q) return 1;    else return 0;}void deal_init_bob(){    int len;    scanf("%d", &len);    for(int i = 0; i < len; i++)        scanf("%d", &b[i]);    if(len > n) return;    if(len == 1){        ans += n;        return ;    }    e.clear();    for(int i = 1; i < len; i++){        node tp =deal_gcd(b[i], b[i - 1]);        int id = lower_bound(idx.begin(), idx.end(), tp) - idx.begin();        if(id >=idx.size() || march(tp, idx[id]) == 0) return ;        e.push_back(id);    }    tree_add();}void deal_init(){    scanf("%d%d", &n, &m);    for(int i = 0; i < n; i++)        scanf("%d", &a[i]);    idx.clear();    for(int i = 1; i < n; i++){        alice[i] = deal_gcd(a[i], a[i - 1]);//        printf("alice[%d] = %d %d\n",i, alice[i].x, alice[i].y);        idx.push_back(alice[i]);    }    sort(idx.begin(), idx.end());    idx.erase(unique(idx.begin(), idx.end()), idx.end());//    for(int i = 0; i < idx.size(); i++)//        printf("i = %d, x=%d, y= %d\n", i, idx[i].x, idx[i].y);    for(int i = 1; i < n; i++)        a[i] = lower_bound(idx.begin(), idx.end(), alice[i]) - idx.begin();    while(m--){        deal_init_bob();    }}void tree_AC(){    root->fail = NULL;    queue<Tree *>que;    for(int i = 0; i < root->v.size(); i++){        int x = root->v[i];        Tree *tp = root->next[x];        tp->fail = root;        que.push(tp);    }    while(!que.empty()){        Tree *res = que.front(); que.pop();        for(int i = 0; i < res->v.size(); i++){            int x = res->v[i];            Tree *p = res;            while(p->fail != NULL){                p = p->fail;                if(p->next[x]){                    p = p->next[x];                    break;                }            }            res->next[x]->fail = p;            que.push(res->next[x]);        }    }}void work(){    Tree *p = root;    for(int i = 1; i < n; i++){        while(p!= root && p->next[a[i]] == NULL) p = p->fail;        Tree *res = p->next[a[i]];        if(res == NULL) continue;        p = res;        while(res != root){            ans += res->cnt;            res = res->fail;        }    }    cout << ans << endl;}void debug(){    queue<Tree *>que;    que.push(root);    while(!que.empty()){        Tree *res = que.front(); que.pop();        for(int i = 0; i < res->v.size(); i++){            int x = res->v[i];            que.push(res->next[x]);        }    }}int main(){//freopen("in.txt","r",stdin);    int T;    scanf("%d", &T);    while(T--){        root = new Tree();        ans = 0;        deal_init();        tree_AC();        work();//        debug();    }return 0;}


0 0
原创粉丝点击