[TJOI2018]碱基序列

[TJOI2018 碱基序列](https://www.luogu.com.cn/problem/P4591)

偷懒直接 SAM 上 DP。

设 dp(i,j) 表示用前 i 行氨基酸匹配到状态 j 的方案数。

由于这个题面比较玄学, 复杂度就挺玄学, 随手加点剪枝就可以很快。

```cpp
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;

const int mo = 1e9 + 7;

int siz[20113];
struct node {
	int f, len, t[26];
} t[20113];
int a[20113];

int tot = 1, las = 1;
void extnd (int c) {
	int np = ++ tot, p = las; las = np;
	t[np].len = t[p].len + 1;
	for (; p && !t[p].t[c]; p = t[p].f) t[p].t[c] = np;
	if (!p) t[np].f = 1;
	else {
		int v = t[p].t[c];
		if (t[v].len == t[p].len + 1) t[np].f = v;
		else {
			int nv = ++ tot; t[nv] = t[v];
			t[nv].len = t[p].len + 1;
			for (; p && t[p].t[c] == v; p = t[p].f) t[p].t[c] = nv;
			t[np].f = t[v].f = nv;
		}
	}
	siz[np] += 1;
}

int n;
char s[10003];

LL dp[103][20113];
bool cmp (int s1, int s2) { return t[s1].len < t[s2].len; }

int main()
{
	scanf ("%d%s", & n, s);
	int len = strlen (s);
	for (int i = 0; i < len; ++ i) extnd (s[i] - 'A');
	dp[0][1] = 1ll;
	for (int id = 1; id <= n; ++ id)
	{
		int k;
		scanf ("%d", &k);
		for (int i = 0; i < k; ++ i) {
			scanf ("%s", s); int sl = strlen (s); if (sl > len) continue;
			for (int sta = 1; sta <= tot; ++ sta) if(dp[id - 1][sta]) {
				int now = sta;
				// tran
				for (int j = 0; j < sl; ++ j) { now = t[now].t[s[j] - 'A']; if (!now) break; }
				if (now) dp[id][now] += dp[id - 1][sta], dp[id][now] %= mo;
			}
		}
	}
	for (int i = 1; i <= tot; ++ i) a[i] = i;
	sort (a + 1, a + 1 + tot, cmp);
	for (int i = tot; i >= 1; -- i) if (t[a[i]].f) siz[t[a[i]].f] += siz[a[i]];
	LL ans = 0ll;
	for (int i = 1; i <= tot; ++ i) ans += (LL)dp[n][i] * siz[i] % mo, ans = ans % mo;
	cout << ans;
	return 0;
}
上一篇:P1273 有线电视网 树上背包


下一篇:[BZOJ]最长道路