1. 程式人生 > >hdu6138 多校2017 ac自動機or字尾陣列

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;
}