hdu6138 多校2017 ac自动机or后缀数组

来源:互联网 发布:网络综艺为什么这么火 编辑:程序博客网 时间:2024/06/16 08:13

题目链接
题目大意:给定n个字符串,m个询问,对于每一个询问,给出x,y 问第x个串和第y个串的相同连续子串当中,作为其他(包括自己)串的前缀的最长长度。


两种思路:第一种ac自动机
首先把所有的串建立一个fail树,那么对于询问把第一个串在树上跑一遍,把所有匹配的节点标记,然后让第二串跑一遍,如果遇到相同匹配的节点,记录当前的最大深度就可以了。
代码:

#include<iostream>#include<cstdio>#include<cmath>#include<algorithm>#include<string>#include<set>#include<map>#include<queue>#include<stack>#include<cstring>#define clr(x) memset(x,0,sizeof(x))using namespace std;#define LL long longconst int N = 100005;struct Trie{    int _next[30];    int fail;    int isStr;    int dep;    int id;}trie[N];int cnt;void Insert(char* s,int len){    int now = 0;    int dep = 0;    for(int i = 0;i<len;i++)    {        if(trie[now]._next[s[i]-'a']>0)        {            now = trie[now]._next[s[i]-'a'];        }        else        {            trie[now]._next[s[i]-'a'] = cnt;            now = cnt++;        }        trie[now].dep = ++dep;    }    trie[now].isStr++; // 字符串少一}void GetFail(){    queue<int>que;    que.push(0);    trie[0].fail = -1;    while(!que.empty())    {        int now = que.front();        que.pop();        for(int i =0;i<26;i++)        {            if(trie[now]._next[i]>0)            {                int son = trie[now]._next[i];                int pa = trie[now].fail;                while(pa>=0 && trie[pa]._next[i]<=0)pa = trie[pa].fail;                trie[son].fail = pa>=0?trie[pa]._next[i]:0;                que.push(son);            }        }    }}int acmachine(char *obj,int len,int bid,int fid){    int now = 0;    int maxlen = 0;    for(int i = 0;i<len;i++)    {        int p = now;        while(p>=0 && trie[p]._next[obj[i]-'a']<=0) p = trie[p].fail;        now = p>=0?trie[p]._next[obj[i]-'a']:0;        int temp = now;        while(temp>0)        {            if(trie[temp].id == fid && trie[temp].dep>maxlen)                maxlen = trie[temp].dep;            trie[temp].id = bid;            temp = trie[temp].fail;        }    }    return maxlen;}int pos[N];int slen[N];int main(){    int t;    scanf("%d",&t);    while(t--)    {        cnt = 1;        int n;        memset(trie,-1,sizeof(trie));        scanf("%d",&n);        int id = 0;        char str[100005];        for(int i = 0;i<n;i++)        {            scanf("%s",&str[id]);            pos[i] = id;            slen[i] = strlen(&str[id]);            id += slen[i]+1;            Insert(&str[pos[i]],slen[i]);        }        GetFail();        int m;        scanf("%d",&m);        int ans = 0;        for(int i = 0;i<m;i++)        {            int x,y;            scanf("%d%d",&x,&y);            x--;            y--;            ans = 0;            //cout << &str[pos[x]] << " " << &str[pos[y]] << endl;            acmachine(&str[pos[x]],slen[x],i+1,0);            //cout << trie[3].id << endl;            ans = max(ans,acmachine(&str[pos[y]],slen[y],-1,i+1));            printf("%d\n",ans);        }    }    return 0;}

第二种思路:用后缀数组
首先对n个串建立字典树,然后对于每一个询问,把两个串连接在一起,跑一遍后缀数组,然后枚举height数组,如果height[i]中字典序相邻的两个后缀,一个后缀在第一个串当中(即sa[i]<len1)另一个在第二串当中(即sa[i1]>len1 两个相反也可以)那么就以当前长度在字典树当中遍历找到这样的前缀的最长长度,更新答案就可以了
代码:

#include<iostream>#include<cstdio>#include<cmath>#include<algorithm>#include<string>#include<set>#include<map>#include<queue>#include<stack>#include<cstring>#define clr(x) memset(x,0,sizeof(x))using namespace std;#define LL long longconst int N = 500005;int sa[N],s[N],wa[N], wb[N], _ws[N], wv[N];int _rank[N], height[N];bool cmp(int r[], int a, int b, int l){    return r[a] == r[b] && r[a+l] == r[b+l];}void da(int r[], int sa[], int n, int m){    int i, j, p, *x = wa, *y = wb;    for (i = 0; i < m; ++i) _ws[i] = 0;    for (i = 0; i < n; ++i) _ws[x[i]=r[i]]++;    for (i = 1; i < m; ++i) _ws[i] += _ws[i-1];    for (i = n-1; i >= 0; --i) sa[--_ws[x[i]]] = i;    for (j = 1, p = 1; p < n; j *= 2, m = p)    {        for (p = 0, i = n - j; i < n; ++i) y[p++] = i;        for (i = 0; i < n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j;        for (i = 0; i < n; ++i) wv[i] = x[y[i]];        for (i = 0; i < m; ++i) _ws[i] = 0;        for (i = 0; i < n; ++i) _ws[wv[i]]++;        for (i = 1; i < m; ++i) _ws[i] += _ws[i-1];        for (i = n-1; i >= 0; --i) sa[--_ws[wv[i]]] = y[i];        for (std::swap(x, y), p = 1, x[sa[0]] = 0, i = 1; i < n; ++i)            x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p-1 : p++;    }}void calheight(int r[], int sa[], int n){    int i, j, k = 0;    for (i = 1; i <= n; ++i) _rank[sa[i]] = i;    for (i = 0; i < n; height[_rank[i++]] = k)        for (k?k--:0, j = sa[_rank[i]-1]; r[i+k] == r[j+k]; k++);}struct node{    char ch;    node* chil[30];    node()    {        _clear();    }    void _clear()    {        clr(chil);        ch = 0;    }}tree;void InsertTree(char le[],int len){    node* p = &tree;    for(int i = 0;i<len;i++)    {        if(p->chil[le[i]-'a']==NULL)        {            node* temp = new node();            temp->ch = le[i];            p->chil[le[i]-'a'] = temp;        }        p = p->chil[le[i]-'a'];    }}int Query(char le[],int len){    int cnt = 0;    node* p = &tree;    for(int i = 0;i<len;i++)    {        if(p->chil[le[i]-'a']!=NULL)        {            cnt++;            p = p->chil[le[i]-'a'];        }        else break;    }    return cnt;}char str[N];int pos[N];int main(){    int t;    scanf("%d",&t);    while(t--)    {        tree._clear();        int n;        scanf("%d",&n);        int cnt = 0;        for(int i = 0;i<n;i++)        {            scanf("%s",str+cnt);            pos[i] = cnt;            int len = strlen(str+cnt);            InsertTree(str+cnt,len);            cnt += len+1;        }        int m;        scanf("%d",&m);        while(m--)        {            int x,y;            scanf("%d%d",&x,&y);            char* chx = str+pos[x-1];            char* chy = str+pos[y-1];            int lenx = strlen(chx);            int leny = strlen(chy);            int s[100005];            for(int i = 0;i<lenx;i++)            {                s[i] = chx[i];            }            s[lenx] = 1;            for(int i = 0;i<leny;i++)            {                s[lenx+1+i] = chy[i];            }            s[lenx+leny+1] = 0;//cout << lenx << " " << leny << endl;            da(s,sa,lenx+leny+2,128);            calheight(s,sa,lenx+leny+1);            int mx = 0;            for(int i = 1;i<lenx+leny+1;i++)            {                if(height[i]<=mx)continue;                if(sa[i-1]<lenx&sa[i]>lenx)                {                    mx = max(mx,Query(str+pos[x-1]+sa[i-1],height[i]));                }                else if(sa[i-1]>lenx && sa[i]<lenx)                {                    mx = max(mx,Query(str+pos[x-1]+sa[i],height[i]));                }            }            printf("%d\n",mx);        }    }    return 0;}
阅读全文
0 0