给出 n n 个文本串 |s| | s | 和1个模板串 |S| | S | ,问对于每个文本串能与模板串匹配的最长前缀长度。
∑|s|≤107,|S|≤107,n≤105 ∑ | s | ≤ 10 7 , | S | ≤ 10 7 , n ≤ 10 5
对 n n 个文本串建立AC自动机,然后将模板串放到AC自动机上匹配,对于匹配到的点就沿着它的fail指针向上走,将经过的点标记成可以匹配,碰到已标记的点就退出,这样每个点最多只会被标记一次。统计答案的时候从每个单词的最后一个点向上走到第一个被标记的节点就是答案。神奇读优AC
BZOJ 4327
LibreOJ10058
#include
#include
using namespace std;
const int maxn=10000005;
const char flg[4]={'E','S','W','N'};
int n,len,ch[maxn][4],w[maxn],lst[maxn],nxt[maxn],que[maxn],dep[maxn],id[100005],fa[maxn],L[maxn];
char st[maxn],s[maxn];
bool vs[maxn];
int geti(char ch){for (int i=0;i<4;i++) if (flg[i]==ch) return i; return -1;}
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
struct AC{
void clear(int x){memset(ch[x],0,sizeof(ch[x])); w[x]=lst[x]=nxt[x]=vs[x]=0;}
void insist(int x){
int now=0;
for (int i=0;iif (!ch[now][geti(s[i])]) {ch[now][geti(s[i])]=++len; dep[len]=dep[now]+1; fa[len]=now; clear(len);}
now=ch[now][geti(s[i])];
}
w[now]++; id[x]=now;
}
void makeF(){
int hed=0,til=0;
for (int i=0;i<4;i++)
if (ch[0][i]){
que[++til]=ch[0][i];
nxt[ch[0][i]]=lst[ch[0][i]]=0;
}
while (hed!=til){
int x=que[++hed];
for (int i=0;i<4;i++)
if (ch[x][i]){
int now=ch[x][i]; nxt[now]=ch[nxt[x]][i];
lst[now]=(w[nxt[now]])?nxt[now]:lst[nxt[now]];
que[++til]=now;
}else ch[x][i]=ch[nxt[x]][i];
}
}
void get_num(int x){for (;x&&!vs[x];x=nxt[x]) vs[x]=1;}
void find(){
int now=0;
for (int i=0;i0];i++){
now=ch[now][geti(st[i])];
get_num(now);
}
}
int print(int x){for (;x&&!vs[x];x=fa[x]); return dep[x];}
}tr;
void reads(int x){
char cha=nc();
while (geti(cha)==-1) cha=nc();
for (L[x]=0;geti(cha)!=-1;L[x]++) {s[L[x]]=cha; cha=nc();}
}
int main()
{
freopen("symbol.in","r",stdin);
freopen("symbol.out","w",stdout);
scanf("%d%d",&len,&n); tr.clear(0);
reads(0); for (int i=0;i0]=len=0;
for (int i=1;i<=n;i++){reads(i); tr.insist(i);}
tr.makeF(); tr.find();
for (int i=1;i<=n;i++) printf("%d\n",tr.print(id[i]));
return 0;
}