题意: 给你一个初始串 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
时间: 2024-11-10 16:16:08