[BZOJ4502]串

Description
兔子们在玩字符串的游戏。首先,它们拿出了一个字符串集合S,然后它们定义一个字
符串为“好”的,当且仅当它可以被分成非空的两段,其中每一段都是字符串集合S中某个字符串的前缀。
比如对于字符串集合{"abc","bca"},字符串"abb","abab"是“好”的("abb"="ab"+"b",abab="ab"+"ab"),而字符串“bc”不是“好”的。
兔子们想知道,一共有多少不同的“好”的字符串。

Input
第一行一个整数n,表示字符串集合中字符串的个数
接下来每行一个字符串

Output
一个整数,表示有多少不同的“好”的字符串

Sample Input
2
ab
ac

Sample Output
9

HINT
1<=n<=10000,每个字符串非空且长度不超过30,均为小写字母组成。


考虑总共组成的方案有前缀的平方种,然后考虑去重

我们规定最右边的划分方案一定是合法的。考虑图中绿色的串,红色串显然为其后缀,又因为红绿串都是字符集的前缀,红串又是满足条件的最长的一个,这就是“最长前缀匹配后缀”,也就是AC自动机的fail指针,因此每当存在一个蓝串结尾的前缀时,红串就会给绿串一个-1的贡献

于是问题就转化为:对于每一个串和它fail指针指向的串,求有多少个以两串相差部分为后缀的前缀。

AC自动机上,根到每个节点的路径都对应一个前缀。而以一个串为后缀的串的个数,就是它fail树上子树大小减一(本身不算)。于是暴枚每个串即可。注意一个串的fail指针如果指向根,则不存在一个串是它的后缀,那它一定是所在答案串中最靠右的划分方式,就不应减去了。

/*problem from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline char gc(){
	static char buf[1000000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int frd(){
	int x=0,f=1; char ch=gc();
	for (;ch<'0'||ch>'9';ch=gc())	if (ch=='-')	f=-1;
	for (;ch>='0'&&ch<='9';ch=gc())	x=(x<<3)+(x<<1)+ch-'0';
	return x*f;
}
inline int read(){
	int x=0,f=1; char ch=getchar();
	for (;ch<'0'||ch>'9';ch=getchar())	if (ch=='-')	f=-1;
	for (;ch>='0'&&ch<='9';ch=getchar())	x=(x<<3)+(x<<1)+ch-'0';
	return x*f;
}
inline void print(int x){
	if (x<0)	putchar('-');
	if (x>9)	print(x/10);
	putchar(x%10+'0');
}
const int N=1e4;
struct S1{
	int tot,root;
	struct node{
		int fa,fail,size,son[26];
		node(){
			fa=fail=size=0;
			memset(son,0,sizeof(son));
		}
	}tree[N*30+10];
	int h[N*30+10];
	S1(){tot=root=0;}
	void insert(char *s){
		int len=strlen(s),p=root;
		for (int i=0;i<len;i++){
			if (!tree[p].son[s[i]-'a']){
				tree[p].son[s[i]-'a']=++tot;
				tree[tot].fa=p;
			}
			p=tree[p].son[s[i]-'a'];
		}
	}
	void Get_Fail(){
		int head=1,tail=0;
		for (int i=0;i<26;i++){
			if (tree[root].son[i]){
				tree[tree[root].son[i]].fail=root;
				h[++tail]=tree[root].son[i];
			}
		}
		for (;head<=tail;head++){
			int Now=h[head];
			for (int i=0;i<26;i++){
				if (tree[Now].son[i]){
					tree[tree[Now].son[i]].fail=tree[tree[Now].fail].son[i];
					h[++tail]=tree[Now].son[i];
				}
				else	tree[Now].son[i]=tree[tree[Now].fail].son[i];
			}
		}
	}
	void solve(){
		for (int i=1;i<=tot;i++)
			for (int j=tree[i].fail;j;j=tree[j].fail)
				tree[j].size++;
		ll Ans=1ll*tot*tot;
		for (int i=1;i<=tot;i++){
			int j=i,k=tree[i].fail;
			if (!k)	continue;
			while (k)	j=tree[j].fa,k=tree[k].fa;
			Ans-=tree[j].size;
		}
		printf("%lld\n",Ans);
	}
}AC;//Aho-Corasick automaton
char s[40];
int main(){
	int n=read();
	for (int i=1;i<=n;i++){
		scanf("%s",s);
		AC.insert(s);
	}
	AC.Get_Fail();
	AC.solve();
	return 0;
}
posted @ 2018-11-29 21:06  Wolfycz  阅读(204)  评论(0编辑  收藏  举报