CF235C_Cyclical Quest

很好的一个自动机的题目。

给原串,和若干个询问串。求原串里有多少个不同子串可以通过询问串循环移动得到。

有点类似求两个串的lcs,但是灵活一点。

首先我们把询问串长度扩大一倍,去掉最后一个字符。因为最后那个字符结尾的情况已经有了。

然后把这个新串拿到SAM中跑一遍,跑的过程就像求lcs差不多,每次判断长度len是否大于询问串长度,以及节点有没有重复加入,来更新答案就好了。

前面自动机的构建以及拓扑排序处理就不说了。

 

召唤代码君:

 

#include <iostream>
#include <cstdio>
#include <cstring>
#define maxn 4002000
using namespace std;

char s[maxn];
int next[maxn][26],pre[maxn],step[maxn],g[maxn],tag[maxn];
int cnt[maxn],Q[maxn];
int N,last,ans,n,L;
int p,q,np,nq;

void insert(int x)
{
	np=++N,p=last,step[np]=step[p]+1,last=np,g[np]=1;
	while (p!=-1 && next[p][x]==0) next[p][x]=np,p=pre[p];
	if (p==-1) return;
	q=next[p][x];
	if (step[q]==step[p]+1) { pre[np]=q; return; }
	nq=++N,step[nq]=step[p]+1,pre[nq]=pre[q];
	for (int i=0; i<26; i++) next[nq][i]=next[q][i];
	pre[np]=pre[q]=nq;
	while (p!=-1 && next[p][x]==q) next[p][x]=nq,p=pre[p];  
}

void process()
{
	for (int i=1; i<=N; i++) cnt[step[i]]++;
	for (int i=2; i<=N; i++) cnt[i]+=cnt[i-1];
	for (int i=1; i<=N; i++) Q[--cnt[step[i]]]=i;
	for (int i=N-1; i>=0; i--) g[pre[Q[i]]]+=g[Q[i]];
}

int main()
{
	pre[0]=-1;
	scanf("%s",s);
	for (int i=0; s[i]; i++) insert(s[i]-'a');
	process();
	scanf("%d",&n);
	for (int T=1; T<=n; T++)
	{
		ans=0;
		scanf("%s",s+1);
		L=strlen(s+1);
		for (int i=1; i<L; i++) s[i+L]=s[i];
		s[L+L]='\0';
		int cur=0,len=0;
		for (int i=1; s[i]; i++)
		{
			
			int k=s[i]-'a';
			while (cur!=-1 && next[cur][k]==0) len=min(len,step[cur]),cur=pre[cur];
			if (cur==-1) { cur=0; continue; }
			len=min(len,step[cur])+1;
			cur=next[cur][k];
			for (; step[pre[cur]]>=L; len=min(len,step[cur])) cur=pre[cur];
			if (len>=L && tag[cur]!=T) ans+=g[cur],tag[cur]=T;
		}
		printf("%d\n",ans);
	}
	return 0;
}

  

posted @ 2014-06-23 11:23  092000  阅读(492)  评论(0编辑  收藏  举报