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

猜你喜欢

转载自www.cnblogs.com/Wolfycz/p/10040854.html