题意: 给你一个初始串 S,strlen(s) <= 3e5 然后给你 n 个单词。 n <= 4000, 每个单词的长度不超过 100 ;
问你这个初始串,分割成若干个单词的连接的方案;(这些单词必须是给定的n个单词中的任意一个,一个单词可以被使用多次。)
解: 将 n 个单词建个字典树;
dp[ i ] 表示,S的 0 ~ i - 1 切割成若干个 单词的方案数;
枚举S, 枚举到 当前位置 i; 然后就在字典树找,以 S 的 i + 1 开始的, 能不能找到一个单词与之匹配;
若能找到, 假设单词为 i + 1 ~ j; 那么就有 dp[ j + 1 ] = ( dp[ j + 1 ] + dp[ i ] ) % mod
这只是个简单dp; 和字典树的简单应用的结合。

#include <bits/stdc++.h>
#define LL long long
#define rep(i, j, k) for(int i = j; i <= k; i++)
#define dep(i, j, k) for(int i = k; i >= j; i--)
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f3f
#define mem(i, j) memset(i, j, sizeof(i))
#define pb push_back
using namespace std;
const int mod = 20071027;
const int N = 3e5 + 5, M = 4e5 + 5;
char s[N], b[105];
int a[M][40], tot, vis[N], dp[N];
int get(char x) {
return x - 'a';
}
void join() {
int now = 0;
rep(i, 0, (int)strlen(b) - 1) {
int id = get(b[i]);
if(!a[now][id]) {
mem(a[tot], 0); vis[tot] = 0;
a[now][id] = tot++;
}
now = a[now][id];
}
vis[now] = 1;
}
int main() {
int cas = 0;
while(~scanf("%s", s)) {
int n; scanf("%d", &n); tot = 1;
mem(a[0], 0);
rep(i, 1, n) {
scanf("%s", b); join();
}
mem(dp, 0); dp[0] = 1;
rep(i, 0, (int)strlen(s) - 1) {
int now = 0;
rep(j, i, (int)strlen(s) - 1) {
int id = get(s[j]);
if(!a[now][id]) break;
now = a[now][id];
if(vis[now]) {
dp[j + 1] = (dp[j + 1] + dp[i]) % mod;
}
}
}
printf("Case %d: ", ++cas);
printf("%d\n", dp[(int)strlen(s)]);
}
return 0;
}
来源:https://www.cnblogs.com/Willems/p/11980244.html
