[hiho一下 第130周] 后缀自动机

题目来源:https://hihocoder.com/problemset/problem/1457


原命题等价于求后缀自动机里面的路径数量


在任意两个字符串之间插入一个符号“:",把所有的字符串连接成一个长串,直接建立后缀自动机。

然后删除所有的“:”边,dp即可。

正确性显然


#include 
#include 
#include 
#include 

#define mod 1000000007LL
#define N 2000050

using namespace std;
typedef long long LL;

char s[N];
int ch[N][11],len[N],link[N],cnt,last,rt,n;
int rd[N];
LL F[N],siz[N];

void inc(LL &x,LL y) { x = ( x + y ) % mod; }

void add(int pos) {
	int x = s[pos] - '0' , np = ++cnt , p = last;
	last = np; len[np] = len[p] + 1;
	
	while (p && !ch[p][x]) ch[p][x] = np , p = link[p];
	if (!p) 
		link[np] = rt;
	else {
		int q = ch[p][x];
		if (len[q] == len[p] + 1)
			link[np] = q;
		else {
			int nq = ++cnt;
			len[nq] = len[p] + 1;
			memcpy(ch[nq],ch[q],sizeof(ch[q]));
			link[nq] = link[q];
			link[q] = link[np] = nq;
			while (p && ch[p][x] == q) ch[p][x] = nq , p = link[p];
		}
	}
	return ;
}

int main() {
	last = rt = cnt = 1;
	LL ans = 0LL;
	int T = 0; scanf("%d",&T);
	for (int _=1;_<=T;_++) {
		scanf("%s",s+1); n = strlen(s+1);
		s[n+1] = ':';
		for (int i=1;i<=n;i++) add(i);
		if (_!=T) add(n+1);
	}

	for (int i=1;i<=cnt;i++)
		for (int j=0;j<=9;j++) if (ch[i][j]) rd[ ch[i][j] ]++;
	siz[rt] = 1;
	
	queue q;
	for (int i=1;i<=cnt;i++) if (!rd[i]) q.push(i);
	while (!q.empty()) {
		int u = q.front(); q.pop();
		#define t ch[u][i]
		for (LL i=0;i<=9;i++) if (t) {
			inc( F[t] , ( F[u] * 10LL + i * siz[u] ) % mod );
			siz[t] += siz[u];
		}
		for (int i=0;i<=9;i++) 
			if (--rd[t] == 0) q.push(t);
	}
	
	for (int i=1;i<=cnt;i++) inc( ans , F[i] );
	printf("%d\n",(int)ans);
	return 0;
}


你可能感兴趣的:(后缀自动机)