这是AC自动机系列的第一篇
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)0
大意
给出N个仅由小写字母组成的字符串S[1]...S[N],它们的总长度为L。有Q组询问,询问分两类:
1.S[x]是否是S[y]的子序列;
2.S[x]是否是S[y]的子串。
数据范围:
N,L,Q<=100000,有60%的数据满足 L<=100, Q<=1000
Solution
先贴代码,留坑待填。
开始填坑。
首先由于这题的数据范围比较坑,不大可能开二维数组来存字符串,故采用了将各字符串连续存起来的办法,另开一个数组记录各串首字母位置。
void input(int n){ for(int i=0; i<n; i++){ scanf("%s", s+beg[i]); beg[i+1]=beg[i]+strlen(s+beg[i]); } }
判断子序列询问比较简单,可以O(N)解决。
nt[i][j]表示位置第j个字母在位置i之后(包括位置i)首次出现的位置。从后往前扫一遍就可以打出这个表了。
int nt[N][26]; void calc_nt(int n){ memset(nt, -1, 26*beg[n]<<2); for(int i=0, j; i<n; i++){ j=beg[i+1]-1; nt[j][s[j]-'a']=j; for(j--; j>=beg[i]; j--) for(int k=0; k<26; k++) if(k==s[j]-'a') nt[j][k]=j; else nt[j][k]=nt[j+1][k]; } }
查询时只要在串S[j]对应的nt表中不断往后匹配就可以了:
int subseq(int x, int y){ if(len(x)>len(y)) return 0; for(int i=beg[x], j=beg[y]; i<beg[x+1]; i++){ if(j>=beg[y+1]) return 0; if(nt[j][s[i]-'a']==-1) return 0; j=nt[j][s[i]-'a']+1; } return 1; }
判断子串的询问可用AC自动机离线处理。
AC代码
#include <bits/stdc++.h> using namespace std; const int N(1e5+5); int beg[N], id[N]; char s[N]; void input(int n){ for(int i=0; i<n; i++){ scanf("%s", s+beg[i]); beg[i+1]=beg[i]+strlen(s+beg[i]); } } int x[N], y[N]; void preprocess(int q, map<pair<int,int>,int> &mp){ mp.clear(); for(int i=0; i<q; i++){ scanf("%d%d", x+i, y+i), x[i]--, y[i]--; mp[{id[x[i]], id[y[i]]}]; } } int nt[N][26]; void calc_nt(int n){ memset(nt, -1, 26*beg[n]<<2); for(int i=0, j; i<n; i++){ j=beg[i+1]-1; nt[j][s[j]-'a']=j; for(j--; j>=beg[i]; j--) for(int k=0; k<26; k++) if(k==s[j]-'a') nt[j][k]=j; else nt[j][k]=nt[j+1][k]; } } int len(int x){return beg[x+1]-beg[x];} int subseq(int x, int y){ if(len(x)>len(y)) return 0; for(int i=beg[x], j=beg[y]; i<beg[x+1]; i++){ if(j>=beg[y+1]) return 0; if(nt[j][s[i]-'a']==-1) return 0; j=nt[j][s[i]-'a']+1; } return 1; } void solve1(int q, map<pair<int,int>,int> &mp, int *ans){ mp.clear(); for(int i=0; i<q; i++){ if(mp.find({x[i], y[i]})==mp.end()) mp[{x[i], y[i]}]=subseq(x[i], y[i]); ans[i]=mp[{x[i], y[i]}]; } } int ch[N][26], f[N], last[N], val[N]; void init(int i){ memset(ch[i], 0, sizeof(ch[i])); f[i]=last[i]=val[i]=0; } void build_trie(int n){ int tot=0; init(tot++); for(int i=0, ID=0, u; i<n; i++){ for(int j=(u=0,beg[i]); j<beg[i+1]; j++){ int &v=ch[u][s[j]-'a']; if(!v) v=tot++, init(v); u=v; } if(!val[u]) val[u]=++ID; id[i]=val[u]; } } int que[N]; int build_ac(){ int head=0, tail=0; for(int i=0; i<26; i++){ if(ch[0][i]) que[tail++]=ch[0][i]; } for(int u; head!=tail;){ u=que[head++]; for(int i=0; i<26; i++){ int &v=ch[u][i]; if(v){ f[v]=ch[f[u]][i]; last[v] = val[f[v]] ? f[v] : last[f[v]]; que[tail++]=v; } else v=ch[f[u]][i]; } } } void solve2(int n, int q, map<pair<int,int>,int> &mp, int *ans){ for(int i=0, x, y; i<n; i++){ y=id[i]; for(int j=beg[i], k=0; j<beg[i+1]; j++){ k=ch[k][s[j]-'a']; x=val[k]; if(x&& mp.find({x, y})!=mp.end()) mp[{x,y}]=1; for(int l=last[k]; l; l=last[l]){ x=val[l]; if(mp.find({x, y})!=mp.end()) mp[{x,y}]=1; } } } for(int i=0; i<q; i++) ans[i]=mp[{id[x[i]],id[y[i]]}]; } int ans[2][N]; map<pair<int,int>,int> mp[2]; int main(){ int T; scanf("%d", &T); for(int n, q; T--; puts("")){ scanf("%d%d", &n, &q); input(n); calc_nt(n); build_trie(n); build_ac(); preprocess(q, mp[1]); solve1(q, mp[0], ans[0]); solve2(n, q, mp[1], ans[1]); for(int i=0; i<q; i++) printf("%d%d", ans[0][i], ans[1][i]); } }