子串计数
@(XSY)[后缀自动机]
题面
题目描述
给定n个仅有小写字母组成的字符串,求所有这些字符串中不同的子串的个数。
输入描述
第一行一个正整数n。
接下来n行每行一个字符串。
输出描述
一行一个整数,表示答案。
数据范围
共两个subtask。
所有字符串的总长不超过105。
Subtask 1
n=1
subtask 2
n≤10000
Soltion
广义后缀自动机乱搞
/*
线性构造
自带26巨大常数
绝望啊
*/
#include <cstdio>
#include <cctype>
#include <cstring>
const int LEN = (int)1e5;
namespace Zeonfai
{
inline int getInt()
{
int a = 0, sgn = 1;
char c;
while(! isdigit(c = getchar()))
if(c == '-')
sgn *= -1;
while(isdigit(c))
a = a * 10 + c - '0', c = getchar();
return a * sgn;
}
inline int getString(char *str)
{
char c;
while(! isgraph(c = getchar()));
int len = 0;
while(isgraph(c))
str[len ++] = c, c = getchar();
return len;
}
}
struct suffixAutomaton
{
struct state
{
int suc[26], pre, len;
inline state()
{
memset(suc, -1, sizeof(suc));
pre = -1;
len = 0;
}
}nd[LEN << 1];
int tp, s, lst;
inline void initialize()
{
s = lst = 0;
tp = 1;
}
inline void newString()
{
lst = s;
}
inline void insert(int c)
{
int u = tp ++, pre = lst;
nd[u].len = nd[pre].len + 1;
for(; ~ pre && nd[pre].suc[c] == -1; nd[pre].suc[c] = u, pre = nd[pre].pre);
if(pre == -1)
nd[u].pre = s;
else
{
int preSuc = nd[pre].suc[c];
if(nd[preSuc].len == nd[pre].len + 1)
nd[u].pre = preSuc;
else
{
int v = tp ++;
nd[v] = nd[preSuc];
nd[v].len = nd[pre].len + 1;
nd[u].pre = nd[preSuc].pre = v;
for(; ~ pre && nd[pre].suc[c] == preSuc; nd[pre].suc[c] = v, pre = nd[pre].pre);
}
}
lst = u;
}
inline void getAnswer()
{
long long ans = 0;
for(int i = 1; i < tp; ++ i)
ans += nd[i].len - nd[nd[i].pre].len;
printf("%lld\n", ans);
}
}SAM;
int main()
{
#ifndef ONLINE_JUDGE
freopen("SAM.in", "r", stdin);
freopen("SAM.out", "w", stdout);
#endif
using namespace Zeonfai;
int n = getInt();
SAM.initialize();
for(int i = 0; i < n; ++ i)
{
static char str[LEN];
// int len = getString(str);
gets(str);
int len = strlen(str);
SAM.newString();
for(int i = 0; i < len; ++ i)
SAM.insert(str[i] - 'a');
}
SAM.getAnswer();
}