[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;
}