Codeforces Round #146 (Div. 2) Cyclical Quest 后缀自动机

    比赛时居然没拍出来~~   

    首先,利用主串 构造一个后缀自动机。 然后利用拓扑序 计算出 从s(初态)出发到达每个节点所构成的子串在主串中出现过几次(就是将parent树中的,儿子状态出现次数加给他的parent ,因为parent树中parent的状态是他的儿子节点的状态并集,不懂看clj的ppt~~~)。

  构造出来以后,将要匹配的串 例如abc  改成 abcab ,将abcab拿去匹配。  如果匹配的长度为 strlen(abc)  那么答案就加上 当前匹配到的节点状态的出现次数。

#include<stdio.h>   
#include<string.h>   
#include <cmath>
#include<algorithm>   
#define fr(i,s,n) for(int i=s;i<n;i++)
#define fi freopen("in.txt","r",stdin)
#define fo freopen("output.txt","w",stdout)
#define cl(a) memset(a,0,sizeof(a))
using namespace std;
typedef long long ll;
const int  maxn=2000010;
const int kinds=26;

char ch[maxn];

struct Sam{
	Sam *son[kinds],*fa;
	int l ,cnt;
	bool vst;
}a[maxn],*head,*last;

int top=-1;
void add(int x){
	Sam *p=&a[++top],*bj=last;
	p->l=last->l+1;last=p;
	for(; bj && !bj->son[x] ; bj = bj->fa) bj->son[x] = p;
	if (!bj) p->fa = head;
	else if (bj->l+1 == bj->son[x]->l) p->fa = bj->son[x];
	else{
		Sam *r = &a[ ++ top],*q = bj->son[x];
		*r = *q ,r->l= bj->l+1, p->fa = q->fa = r;
		for( ; bj && bj->son[x] == q; bj = bj->fa) bj->son[x] = r;
	}
}

Sam *b[maxn];
int dws[maxn],len;
Sam *sta[maxn];

int main(){
	scanf("%s",ch);
	head = last = &a[++top];
	int n=strlen(ch);
	fr(i,0,n) add( ch[i] - 'a');
	int i;
	for (i = 0; i <= top; ++i) ++dws[a[i].l]; 
	for (i = 1; i <= n; ++i)	dws[i] += dws[i - 1];   
	for (i = 0; i <= top; ++i)   b[--dws[a[i].l]] = &a[i];
	for (last = head, i = 0; i < n; ++i)
		(last = last->son[ch[i] - 'a'])->cnt++;

	for (i = top; i > 0; --i){
		b[i]->fa->cnt += b[i]->cnt;
	}
	
	int q,l;
	scanf("%d",&q);
	fr(i,0,top+1) a[i].vst = 0;
	fr(k,0,q){
		int statop = 0;
		int ans=0;
		scanf("%s",ch);
		l = len = strlen(ch);
		fr(i , 0 ,len) ch[len + i] = ch[i];
		len<<=1;--len;ch[len] = '\0';
		int mid = 0, y;
		last = head;
		fr(i,0,len){
			if (last ->son[y = ch[i] - 'a' ]){
				++ mid;
				last = last->son[y];
				while(last->fa->l >= mid ) 
					last = last->fa;
				if (mid == l) {
					if (!last->vst) last->vst = 1,ans += last->cnt,sta[statop++] = last;
					--mid;
				}
			}
			else {
				for (; last && !last->son[y]; last = last->fa);
				if (!last) mid = 0,last = head;
				else{
					mid = last->l + 1;
					last = last->son[y];
					while(last->fa->l >= mid) 
						last = last->fa;
					if (mid == l) {
						if (!last->vst ) last->vst = 1,ans += last->cnt,sta[statop++] = last;
						--mid;
					}
				}
			}
		}
		fr(i,0,statop) sta[i]->vst = 0;
		printf("%d\n",ans);	
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值