1709:寻找好串
时间限制: 2000 ms 内存限制: 262144 KB
【题目描述】
有一个字符串集合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,均为小写字母组成。
【题解】
首先发现如果没有相同的好串,答案为cnt*cnt。
对于两个构造出来相同的串,则有一个串是前一个的前缀,第二个串是另一个的后缀。
那么我们可以找出每一个前缀和它最长的后缀的非公共部分,求出作为几个前缀的后缀(该串作为后缀有几个重复),减去该数即为答案。
代码如下:
#include<bits/stdc++.h> #define int long long using namespace std; const int N=3e5+5; int n,sum[N],cnt,ans,ren[N],ru[N]; char c[35]; struct trie { int er[26],fail,fa; }a[N]; inline void insert() { int len=strlen(c+1),now=0; for(int i=1;i<=len;i++) { if(!a[now].er[c[i]-'a']) a[now].er[c[i]-'a']=++cnt; a[a[now].er[c[i]-'a']].fa=now; now=a[now].er[c[i]-'a']; } } queue <int> que; inline void get_fail() { for(int i=0;i<=25;i++) if(a[0].er[i]) que.push(a[0].er[i]); while(!que.empty()) { int now=que.front();que.pop(); ru[a[now].fail]++; for(int i=0;i<=25;i++) { if(a[now].er[i]) { a[a[now].er[i]].fail=a[a[now].fail].er[i]; que.push(a[now].er[i]); } else a[now].er[i]=a[a[now].fail].er[i]; } } } inline void get_sum() { for(int i=0;i<=cnt;i++) if(ru[i]==0) que.push(i); for(int i=0;i<=cnt;i++) ren[i]=1; while(!que.empty()) { int now=que.front();que.pop(); ren[a[now].fail]+=ren[now]; sum[now]=ren[now]-1; ru[a[now].fail]--; if(ru[a[now].fail]==0) que.push(a[now].fail); } } signed main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%s",c+1); insert(); } get_fail(); get_sum(); ans=cnt*cnt; for(int i=0;i<=cnt;i++) if(a[i].fail) { int hu1=i,hu2=a[i].fail; while(hu2) { hu1=a[hu1].fa; hu2=a[hu2].fa; } // ans-=sum[hu1]; } cout<<ans; return 0; }