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
",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;
}