UVALive - 3942(字典树+DP)

题目大意:题目链接

        给出一个字符串s和若干个单词,统计出有多少种方法把字符串拆分成这些单词。

题目思路:

用 dp [ i ] 表示从s的第i位开始的字符串的分解方案数目。初始化dp [ len + 1 ] = 1,用于末尾的dp[ len ] 状态转移;

我们从字符串的第 i 位出发,一直往后跑字典树;

如果跑到第j位时找到了一个单词,说明从i到j 可以划分成一个单词,加上后缀的j+1的方案数:dp [ i ] + = dp [ j + 1 ];

题目代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#define maxn 500005
#define mod 20071027

using namespace std;
char word[maxn];
bool val[maxn];
char str[1005];
int trie[maxn][26];
int dp[maxn];
int tot;
int wlen;

void inserts(char a[])
{
     int root=0;
     int alen=strlen(a);
     for(int i=0;i<alen;++i)
     {
          int id=a[i]-'a';
          if(!trie[root][id])
          {
               trie[root][id]=++tot;
          }
          root=trie[root][id];
     }
     val[root]=true;
     //printf("%d---\n",root);
}
int searchs(int x)
{
     int sum=0;
     int root=0;
     for(int i=x;i<=wlen;++i)
     {
          int id=word[i]-'a';
          if(!trie[root][id])//说明根本就没
               return sum;
          root=trie[root][id];
          if(val[root])//找到了一个结点
               sum=(sum+dp[i+1])%mod;
     }
     return sum;
}
int main(void)
{
     int n;
     int kase=0;
     while(~scanf("%s",word+1))
     {
          tot=0;
          wlen=strlen(word+1);
          memset(trie,0,sizeof(trie));//忘记清空了
          memset(dp,0,sizeof(dp));
          memset(val,false,sizeof(val));
          //
          scanf("%d",&n);
          for(int i=0;i<n;i++)
          {
               scanf("%s",str);
               inserts(str);
          }
          dp[wlen+1]=1;
          for(int i=wlen;i>=1;i--)
          {
               dp[i]=searchs(i);
          }
          printf("Case %d: %d\n",++kase,dp[1]);
     }
     return 0;
}

呼呼

猜你喜欢

转载自blog.csdn.net/destiny1507/article/details/81544378