hdu6138 多校2017 ac自動機or字尾陣列
阿新 • • 發佈:2019-01-27
題目連結
題目大意:給定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]中字典序相鄰的兩個字尾,一個字尾在第一個串當中(即
程式碼:
#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;
}