题解直接看大佬博客
诶,可能是能力不够,还是不是很懂,下次再战
建议模拟一遍,
来点图,结合代码,加深印象
样例一:
两个模式串:ACCGT 和 TTT
建立所有节点的fail指针
样例二:
图不画了,参考样例一
根据fail树,建立时间戳
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
const int INF=0x3f3f3f3f;
int n,m,k;
struct Segment_Tree{
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
int val[N<<2];
bool flag[N<<2];//懒标记
void pushup(int rt){
val[rt]=max(val[rt<<1],val[rt<<1|1]);
flag[rt]=0;//清空懒标记
}
void build(int l,int r,int rt){
if(l==r){
val[rt]=flag[rt]=0;
return;
}
int mid=l+r>>1;
build(lson);
build(rson);
pushup(rt);
}
void pushdown(int rt){
if(flag[rt]){
flag[rt<<1]=flag[rt<<1|1]=1;//向下传递
flag[rt]=0;
val[rt<<1]=max(val[rt<<1],val[rt]);
val[rt<<1|1]=max(val[rt<<1|1],val[rt]);
//因为时间戳 父区间不可能再更新 //val[rt]的更新在pushup里
}
}
void update(int l,int r,int rt,int L,int R,int value){
if(L<=l && r<=R){
val[rt]=max(val[rt],value);
flag[rt]=1;
return;
}
pushdown(rt);
int mid=l+r>>1;
if(L<=mid)update(lson,L,R,value);
if(R>mid)update(rson,L,R,value);
//pushup(rt);不可以更新 会影响到前面的时间戳的
}
int query(int l,int r,int rt,int pos){
if(l==r){
return val[rt];
}
pushdown(rt);
int mid=l+r>>1;
if(pos<=mid)return query(lson,pos);
if(pos>mid)return query(rson,pos);
return -INF;//应对一个不存在的?? 会用到吗?
}
}seg;
struct query{
int a,b,id;
}que[N];
bool cmp(query x,query y){//自动机上由a走到b
if(x.b==y.b) return x.a<y.a;
return x.b<y.b;
}
struct Tire{
int next[N][4];
int fail[N];
int pos[N];//记录每个字符串在AC自动机上的结尾的位置
int L,root;
vector<string> v;//记录所有模式串
int NewNode(){//init
for (int i = 0; i <4; ++i) {
next[L][i]=-1;
}
L++;
return L-1;
}
void init(){
L=0;
root=NewNode();
v.clear();
}
int encode(char c){
if(c=='A')return 0;
if(c=='G')return 1;
if(c=='C')return 2;
if(c=='T')return 3;
return -1;
}
void insert(int id){//build 将字符串添加到树上
int now=root;
for (int i = 0,len=v[id].size();i<len; ++i) {
int x=encode(v[id][i]);
if(next[now][x]==-1)
next[now][x]=NewNode();
now=next[now][x];
}
pos[id]=now;
}
void build(){//get_fail
fail[root]=root;
queue<int>q;
q.push(root);
while(!q.empty()){
int now=q.front();
q.pop();
for (int i = 0; i <4; ++i) {
if(next[now][i]==-1){//子节点不存在
next[now][i]=(now==root?root:next[fail[now]][i]);
}else{//子节点存在
fail[next[now][i]]=(now==root?root:next[fail[now]][i]);
q.push(next[now][i]);
}
}
}
}
int time;//时间戳
vector<int> g[N];//存树
int left[N],right[N];//每个节点对应的线段树的时间戳 [ left[i], right[i] ]
void dfs(int now){//更新每个节点的时间戳
left[now]=++time;
for (int i = 0, sz = g[now].size(); i < sz; ++i) {
dfs(g[now][i]);
}
right[now]=time;
}
void failTree(){
for (int i = 0; i < L; ++i) {
g[i].clear();
}
for (int i = 1; i <L; ++i) {
g[fail[i]].push_back(i);//建 fail8 tree
}
time=0;
dfs(root);//获取时间戳
}
int ans[N];
void solve(){
for (int i = 1; i <= m; ++i) {//离线
cin>>que[i].a>>que[i].b;
que[i].a--;//与vector的存储顺序一一对应
que[i].b--;
que[i].id=i;
}
sort(que+1,que+1+m,cmp);
failTree();//建 fail tree
seg.build(1,time,1);//以时间戳为轴更新线段树
int base=0;//基准
for (int i = 1; i <= m; ++i) {
int nowb=que[i].b;
int now=root;//每次都从根节点出发
for (int j = 0, sz = v[nowb].size(); j < sz; ++j) {
now=next[now][encode(v[nowb][j])];
seg.update(1,time,1,left[now],right[now],j+1+base);//val和j有关
}
while(i<=m && que[i].b==nowb){//相同结尾的 直接统计 因为第一遍已经处理到结尾了
ans[que[i].id]=max(0,seg.query(1,time,1,left[pos[que[i].a]])-base);
//从结尾(后缀)开始查询匹配
//如果没有相同部分 返回的是个非正数 答案就是0了
i++;
}
i--;
base+=v[nowb].size()+1;//对于下一组相同的b, base提升一个基值, 这样就不用初始化线段树了
}
//输出答案
for (int i = 1; i <= m; ++i) {
cout <<ans[i] << endl;
}
}
}AC;
int main(){
ios::sync_with_stdio(0);//不开必T
while(cin>>n>>m){
AC.init();
string s;
for (int i = 0; i <n; ++i) {
cin>>s;
AC.v.push_back(s);//保存字符串
AC.insert(i);//build(s)
}
AC.build();//get_fail
AC.solve();
}
return 0;
}