串 2016Vijos省选集训 day3[AC自动机]

1、串(string.c/.cpp/.pas)

限时1s,内存限制256MB,20个测试点

 

【题目描述】

兔子们在玩字符串的游戏。首先,它们拿出了一个字符串集合S,然后它们定义一个字符串为“好”的,当且仅当它可以被分成非空的两段,其中每一段都是字符串集合S中某个字符串的前缀。

比如对于字符串集合{"abc", "bca"},字符串"abb","abab"是“好”的("abb" = "ab"+"b", abab = "ab" + "ab"),而字符串“bc”不是“好”的。

兔子们想知道,一共有多少不同的“好”的字符串。

【输入格式】

第一行一个整数n,表示字符串集合中字符串的个数

接下来每行一个字符串

【输出格式】

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

【样例输入】

2

ab

ac

【样例输出】

9

【数据规模】

对于20%的数据,1 <= n <= 200

对于50%的数据,1 <= n <= 2000

对于100%的数据,1 <= n <= 10000,每个字符串非空且长度不超过30,均为小写字母组成。

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;

#ifdef WIN32
#define LL "%I64d"
#else
#define LL "%lld"
#endif 
#define FRE(name) freopen(#name".in","r",stdin);freopen(#name".out","w",stdout);
#define FC fclose(stdin);fclose(stdout);

typedef long long ll;
const int N=3e5+5;

int n,L,len,cnt,tr[N][26],d[N][26],num[N],fail[N],q[N];
char s[32];
ll ans,f[32][N];
void insert(){
    int now=1;
    for(int i=0;i<len;i++){
        int t=s[i]-'a';
        if(!tr[now][t]) tr[now][t]=++cnt,num[cnt]=num[now]+1;
        now=tr[now][t];
    }
}
void AC_mach(){
    int h=0,t=1;int p;
    q[1]=1;fail[1]=0;
    while(h!=t){
        int now=q[++h];
        for(int i=0;i<26;i++){
            if(tr[now][i]){
                p=fail[now];
                while(!tr[p][i]) p=fail[p];
                fail[tr[now][i]]=tr[p][i];
                q[++t]=tr[now][i];
            }
            else{
                p=fail[now];
                while(!tr[p][i]) p=fail[p];
                tr[now][i]=tr[p][i];
                d[now][i]=1;
            }
        }
    }
}
void first(){
    cnt=1;ans=0;
    for(int i=0;i<26;i++) tr[0][i]=1;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%s",s);
        len=strlen(s);
        L=max(L,len);
        insert();
    }
}
void solve(){
    AC_mach();
    for(int i=2;i<=cnt;i++){
        if(fail[i]!=1) ans++;
        for(int j=0;j<26;j++){
            if(tr[i][j]!=1&&d[i][j]){
                f[1][tr[i][j]]++;
            }
        }
    }
    for(int i=1;i<=L;i++){
        for(int j=2;j<=cnt;j++){
            if(f[i][j]){
                ans+=f[i][j];
                for(int k=0;k<26;k++){
                    if(num[tr[j][k]]>=i+1){
                        f[i+1][tr[j][k]]+=f[i][j];
                    }
                }
            }
        }
    }
    printf(LL,ans);
}
int main(){
    FRE(string)
    first();
    solve();
    FC
    return 0;
}
原文地址:https://www.cnblogs.com/shenben/p/6398010.html