[HDOJ 4416] Good Article Good sentence [后缀自动机]

69 篇文章 0 订阅
1 篇文章 0 订阅

给一个串S和一些串s1,s2,...,sn,问是S的子串且不是s1,s2,...,sn中任何一个的子串的字符串有多少个?出现在S的不同位置的相同子串算一个。

后缀自动机,先用S跑一个后缀自动机,然后在这个自动机上跑s1,s2,...,sn,记录每个节点被s1,s2,...,sn访问到的最大长度。最后用原来的最大长度减去后来被访问到的最大长度,得到的就是不重复的子串个数。

#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

const int K=26;
const int N=100000;
inline int toint(char c) {
	return c-'a';
}
struct SANode {
	SANode *f;
	SANode *son[K];
	int maxl,l;
	SANode *clear(int ll=0) {
		f=NULL;
		for (int i=0;i<K;i++) son[i]=NULL;
		maxl=ll;
		return this;
	}
};
SANode b[2*N+2];
SANode *bp,*tail,*init;
void clear() {
	bp=b;
	init=tail=(bp++)->clear();
}
void push_back(char c) {
	int x=toint(c);
	SANode *i=tail;
	tail=(bp++)->clear(i->maxl+1);
	for (;i&&!i->son[x];i=i->f) i->son[x]=tail;
	if (!i) tail->f=init;
	else if (i->maxl+1==i->son[x]->maxl) tail->f=i->son[x];
	else {
		SANode *p=(bp++)->clear(),*q=i->son[x];
		*p=*q;
		q->f=tail->f=p;
		p->maxl=i->maxl+1;
		for (;i&&i->son[x]==q;i=i->f) i->son[x]=p;
	}
}
void repair() {
	for (SANode *it=b+1;it!=bp;it++) it->l=it->f->maxl;
}
void add(char *s) {
	int x,l=0;
	SANode *i=init;
	while (*s!='\0') {
		x=toint(*s);
		if (i->son[x]) {
			i=i->son[x];
			l++;
			i->l=max(i->l,l);
		} else {
			while (i&&!i->son[x]) i=i->f;
			if (!i) {
				i=init;
				l=0;
			} else {
				l=i->maxl+1;
				i=i->son[x];
				i->l=max(i->l,l);
			}
		}
		for (SANode *j=i->f;j&&j->l!=j->maxl;j=j->f) j->l=j->maxl;
		s++;
	}
}
long long getans() {
	long long ans=0;
	for (SANode *it=b+1;it!=bp;it++) ans+=it->maxl-it->l;
	return ans;
}

char s[100001];

int main() {
	int t,tt,i,n;
	scanf("%d",&t);
	for (tt=1;tt<=t;tt++) {
		scanf("%d",&n);
		scanf("%s",s);
		clear();
		for (i=0;s[i]!='\0';i++) push_back(s[i]);
		repair();
		while (n--) {
			scanf("%s",s);
			add(s);
		}
		printf("Case %d: %lld\n",tt,getans());
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值