CF963D Frequency of String【多串在母串的出现位置】

题目描述:

CF963D Frequency of String【多串在母串的出现位置】_第1张图片
保证 m i m_i mi 互不相同。

题目分析:

m i m_i mi s s s 中的匹配次数是 O ( ∣ S ∣ ∗ ∑ m i ) O(|S|*\sqrt {\sum {m_i}}) O(Smi ) 级别的。
证明可以用AC自动机来理解,设fail树上每个点的祖先中结束节点的个数为 x x x,因为长度各不相同,所以总长度至少为 x ( x + 1 ) 2 \frac {x(x+1)}2 2x(x+1),所以 x ≤ ∑ m i x\le \sqrt {\sum {m_i}} xmi 。总共会遍历 ∣ S ∣ |S| S 个点的祖先。

原问题相当于是求所有匹配位置中相隔 k k k 个的最近距离,求出所有匹配位置之后扫一遍即可。复杂度有保证。

求匹配位置可以 bitset。 O ( n ∑ m i w ) O(\frac {n\sum m_i}w) O(wnmi)
也可以AC自动机,预处理fail树上第一个有结束节点的祖先 F a i l Fail Fail,用 s s s 在自动机上走,每次跳 F a i l Fail Fail,把遇到的结束节点压进对应的栈中,顺便就可以求答案。 O ( n + n ∑ m i ) O(n+n\sqrt{\sum m_i}) O(n+nmi )

Code:

#include
#define maxn 100005
using namespace std;
int n,m,Q;
char s[maxn],t[maxn];
bitset<maxn>a[26],ans;
int main()
{
	scanf("%s%d",s,&Q),n=strlen(s);
	for(int i=0;i<n;i++) a[s[i]-'a'].set(i);
	for(int k,sz;Q--;){
		scanf("%d%s",&k,t),m=strlen(t);
		ans.set();
		for(int i=0;i<m;i++) ans&=a[t[i]-'a']>>i;
		if((sz=ans.count())<k) {puts("-1");continue;}
		int len=maxn,l=ans._Find_first(),r=ans._Find_first();
		for(int i=1;i<k;i++) r=ans._Find_next(r);
		for(int i=k;i<=sz;i++) len=min(len,r-l+m),l=ans._Find_next(l),r=ans._Find_next(r);
		printf("%d\n",len);
	}
}

你可能感兴趣的:(字符串)