题目链接:https://vjudge.net/problem/UVA-1401
参考自:①https://www.cnblogs.com/xuqiulin/p/4034143.html
②《算法竞赛入门经典 训练指南》 刘汝佳
解题思路:
①
首先想到dp的思路,dp[i]表示str[0]~str[i-1]的字符串可以分解成的单词的方法数目
那么dp[0] 等于 1 (这个感觉比较玄学,理解为给出每次字典上的单词存在一个为空的串吧)
如果【a,b】出现了一个完整的单词,那么证明 dp[b+1] += dp[a];
这个dp[a]有两种可能:
①0,意味着【0,a-1】不能拆成字典中的单词,那么现在就算【a,b】能拆也没用,不能全部拆开,所以dp=0
②>0,意味着【0,a-1】可以拆,当前【0,b】可以拆的方法就增加上【0,a-1】可以拆的方法,相当于前面【0,a-1】
有那么多种方法,最后把【a,b】这一块放在最后罢了
那么一个dp[i]怎么样才是准确的呢?
以i-1为后缀,【0,i-1】是单词吗?是那么dp[i]+=dp[0],不是就不管
【1,i-1】是单词吗? 同上。
.......
直到【i-1,i-1】
最后dp【strlen(str)】就是答案啦。
②dp思路分析清楚了,那么就要解决怎么判断【a,b】是否是字典中的单词了
暴力判断就是一个个单词遍历过去,和每个单词作比较。用py想都知道要超时。
所以用字典树来查单词。
现打的板子(应该没问题,不放心可以对照着这题的代码看看)
int trie[maxnode][sigma_size];///maxnode表示节点数,maxnode=单词数*每个单词最大长度(最坏情况每个单词组合都不一样)
///sigma_size 表示一个节点最多延伸出多少各节点,也就是字符种类
/// trie[i][j] 的值表示 i节点指向j这个数字对应字母 的节点的位置
bool val[maxnode]; ///为真表示当前节点是一个单词的结束
int sz;
int idx(char ch) { return ch-'a';}//将字母转换成对应数字,如果还有大写,标点的话要更麻烦些
void init(int x){
val[x] = false;
memset(trie[x],0,sizeof trie[x]);
}
void insert(char *s){///插入
int u = 0;
for (int i=0;s[i];i++){
int v = idx(s[i]);
if (!trie[u][v]){
init(sz);///初始化多组输入时上一次残留的数据
trie[u][v] = sz++;
}
u = trie[u][v];
}
val[u] = true;
}
bool query(char *s){
int u = 0;
for (int i=0;s[i];i++){
int v = idx(s[i]);
if (!trie[u][v]) return false; ///这个节点还没有开辟
u = trie[u][v];
}
return true;
}
本题代码:
#include<cstdio>
#include<cstring>
#define mod 20071027
using namespace std;
const int maxnode = 4e5+5;
const int N = 3e5+5;
const int sigma_size = 26;
char str[N];
char words[105];
int ch[maxnode][sigma_size],dp[N];
bool val[maxnode];
int sz;
void init(int i) { val[i] = false; memset(ch[i],0,sizeof ch[i]);}
int idx(char c) { return c-'a';}
void insert(char *s){
int u = 0;
for (int i=0;s[i];i++){
int v = idx(s[i]);
if (!ch[u][v]){
init(sz); ///将新节点初始化,清除上一组测试数据
ch[u][v] = sz++;
}
u = ch[u][v];
}
val[u] = true;
}
/*
bool query(char *s){
int u = 0;
for (int i=0;s[i];i++){
int v = idx(s[i]);
if (!ch[u][v]) return false;
u = ch[u][v];
}
return true;
}
*/
void DP(){
memset(dp,0,sizeof dp);
dp[0] = 1;
int i;
for (i=0;str[i];i++){
int u = 0;
for (int j = i;str[j];j++){
int v = idx(str[j]);
if (!ch[u][v]) break;
u = ch[u][v];
if (val[u]) dp[j+1] = (dp[j+1]+dp[i])%mod;
}
}
printf("%d\n",dp[i]);
}
int main()
{
for (int kca=1;~scanf("%s",str);kca++){
sz = 1;
init(0);
int n;
scanf("%d",&n);
while (n--){
scanf("%s",words);
insert(words);
}
printf("Case %d: ",kca);
DP();
}
return 0;
}
这应该只是字典树入入入入门级操作,更高深的慢慢学吧~~~