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

题目链接
题目大意:给定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 long
const 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 long

const 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值