uva 1401 - Remember the Word(DP+Tire树,5级)

Neal is very curious about combinatorial problems, and now here comes a problem about words. Knowing that Ray has a photographic memory and this may not trouble him, Neal gives it to Jiejie.

Since Jiejie can't remember numbers clearly, he just uses sticks to help himself. Allowing for Jiejie's only 20071027 sticks, he can only record the remainders of the numbers divided by total amount of sticks.

The problem is as follows: a word needs to be divided into small pieces in such a way that each piece is from some given set of words. Given a word and the set of words, Jiejie should calculate the number of ways the given word can be divided, using the words in the set.

Input 

The input file contains multiple test cases. For each test case: the first line contains the given word whose length is no more than 300 000.

The second line contains an integer S , 1S4000 .

Each of the following S lines contains one word from the set. Each word will be at most 100 characters long. There will be no two identical words and all letters in the words will be lowercase.

There is a blank line between consecutive test cases.

You should proceed to the end of file.

Output 

For each test case, output the number, as described above, from the task description modulo 20071027.

Sample Input 

abcd 
4 
a 
b 
cd 
ab

Sample Output 

Case 1: 2

题意:给出一个由S个不同的字符串组成的字典和一个长字符串,问:把这个长字符串分解成若干个字典中出现的字符串,共有几种分法。单词可以重复使用。
最开始的想法是记忆化搜索,对于一个字符串s[i, j]枚举中间位置k(i<k<j)将其分成两个子串,递归求解,但是这样时间和空间都不允许。然后就想到递推:开始是从左往右推:dp(i)表示子串s[0, i]的拆分方案数,然后枚举它的每个后缀判断其是否在字典树中,转移方程:dp(i) = { sum(dp(i - len(x))) | x为s[0, i]的后缀且出现在字典树中 };但是这样时间依然高达O(n^2 * 100),效率还是太低。
上面的方法的瓶颈在于:对于每个子串s[0, i],枚举每一个后缀没有利用到串的连续性,而进行了大量重复的操作。如果是枚举每一个前缀的话就可以在线性时间完成所有的枚举了,那么怎样才能用枚举前缀的方法递推呢。可以想到把从左往右递推改成从右往左,这样就变成了枚举子串s[i, len(s)]的每个前缀了,可以通过在字典树中查找s[i, len(s)]每遇到一个单词节点就对dp(i)进行相应的操作。时间就降为O(n * 100)。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int ch_size=26;
const int mm=4e5;
const int mod=20071027;
int dp[mm];
class Tire
{ public:
  int ch[mm][ch_size];
  int val[mm];
  int sz;
  Tire(){sz=1;memset(ch[0],0,sizeof(ch[0]));}
  void reset()
  {
    memset(val,0,sizeof(val));memset(ch,0,sizeof(ch));sz=1;
  }
  void insert(char*s,int v)
  {
    int u=0,c,len=strlen(s);
    for(int i=0;i<len;++i)
    {
      c=s[i]-'a';
      if(!ch[u][c])
      {
        memset(ch[sz],0,sizeof(ch[sz]));
        val[sz]=0;
        ch[u][c]=sz++;
      }
      u=ch[u][c];
    }
    val[u]=1;
  }
  int query(char*s,int x)
  { int ret=0,u=0,c;
    int len=strlen(s);
    for(int i=x;i<len;++i)
    { c=s[i]-'a';
      if(!ch[u][c])return ret;
      u=ch[u][c];
      if(val[u])
        {ret=(ret+dp[i+1])%mod;
        }
    }
    return ret;
  }
}T;
const int mn=3e5+9;
char s[mn],ss[mn];
int main()
{ int n;int cas=0;
  while(~scanf("%s",s))
  {
    scanf("%d",&n);
    T.reset();
    for(int i=0;i<n;++i)
    {
      scanf("%s",ss);
      T.insert(ss,1);
    }
    int len=strlen(s);dp[len]=1;
    for(int i=len-1;i>=0;--i)
    {
      dp[i]=T.query(s,i);
    }
    printf("Case %d: %d\n",++cas,dp[0]);
  }
  return 0;
}



你可能感兴趣的:(uva 1401 - Remember the Word(DP+Tire树,5级))