【HDU6096】String(AC自动机)

传送门


题解:

对于模式串 s t r str str,我们构造串 s t r + ∗ + s t r str+*+str str++str,对于询问串 p r e , s u f pre,suf pre,suf,我们构造串 s u f + ∗ + p r e suf+*+pre suf++pre

容易发现,对于所有的询问串建立AC自动机,然后把模式串放上去跑,匹配问题就解决了。至于串长限制,直接暴力跳fail即可。

对于离线的,这种做法已经足够了,也是就是我写的做法。

对于在线的,需要牺牲一点复杂度,我并不想写,这里口胡一下。

首先设原串集合为 A A A,反串集合为 B B B,将两个集合的串按照字典序排序,那么前缀等于询问前缀 p p p的是 A A A中的一个连续区间,后缀等于询问后缀 s s s的是 B B B中的一个连续区间,用主席树支持静态二维数点即可。对于前后缀重叠的情况,显然只有 O ( min ⁡ ( ∣ p ∣ , ∣ s ∣ ) ) O(\min(|p|,|s|)) O(min(p,s))种,暴力枚举然后哈希判断是否原串集合中存在这种串即可。

复杂度多一个log,常数难以形容。。。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::string;

cs int N=1e5+7;

namespace AC{
	cs int N=5e5+7;
	int son[N][27],fail[N],d[N],cnt[N],tot;
	
	inline void clear(){
		memset(son,0,sizeof(int)*(tot+1)*27);
		memset(fail,0,sizeof(int)*(tot+1));
		memset(d,0,sizeof(int)*(tot+1));
		memset(cnt,0,sizeof(int)*(tot+1));
		tot=0;
	}
	
	inline int ins(cs string &s){
		int u=0,len=s.size();
		for(int re i=0;i<len;++i){
			int c=s[i]-'a';
			if(!son[u][c]){
				son[u][c]=++tot;
				d[tot]=d[u]+1;
			}
			u=son[u][c];
		}
		return u;
	}
	
	int q[N],qn;
	inline void build(){
		qn=0;for(int re i=0;i<27;++i)if(son[0][i])q[++qn]=son[0][i];
		for(int re i=1,u=q[i];i<=qn;u=q[++i]){
			for(int re c=0;c<27;++c)
			son[u][c]?(q[++qn]=son[u][c],fail[son[u][c]]=son[fail[u]][c]):(son[u][c]=son[fail[u]][c]);
		}
	}
	
	inline void match(cs string &s,int h){
		int len=s.size(),u=0;
		for(int re i=0;i<len;++i){
			int c=s[i]-'a';
			u=son[u][c];while(d[u]>h)u=fail[u];
			++cnt[u];
		}
	}
	
	inline void get_ans(){for(int re i=qn;i;--i)cnt[fail[q[i]]]+=cnt[q[i]];}
}

int n,q;
string str[N];int h[N],ps[N];
inline void solve(){
	AC::clear();std::cin>>n>>q; 
	for(int re i=1;i<=n;++i){
		std::cin>>str[i];
		h[i]=str[i].size()+1;
		str[i]+=string("{")+str[i];
	}
	for(int re i=1;i<=q;++i){
		string p,s;
		std::cin>>p>>s;
		s=s+string("{")+p;
		ps[i]=AC::ins(s);
	}
	AC::build();
	for(int re i=1;i<=n;++i)AC::match(str[i],h[i]);
	AC::get_ans();
	for(int re i=1;i<=q;++i)std::cout<<AC::cnt[ps[i]]<<"\n";
}

signed main(){
#ifdef zxyoi
	freopen("string.in","r",stdin);
#endif
	std::ios::sync_with_stdio(false);
	int T;std::cin>>T;
	while(T--)solve();
	return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值