1. 程式人生 > 實用技巧 >UVA1401 Remember the Word 字典樹維護dp

UVA1401 Remember the Word 字典樹維護dp

題目連結:https://vjudge.net/problem/UVA-1401

題目:

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, 1 ≤ S ≤ 4000. 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

題意:
多組輸入,首先給你一個長度最大為3e5的字串s
然後給你一個整數n,後面給你n個長度最大為100的字串str[i]
問你使用str組成s字串有多少種方式

這裡講解一下樣例:
abcd
4
a
b
cd
ab

那麼abcd可以通過a+b+cd 或者 ab+cd 兩種方式構成

題解:
dp方程很容易找到
dp[i]=(dp[i]+dp[j]) (i<j)
dp[i]表示構成s字串的[i,len](這裡我們把s字串下標看作從1開始)這一個子串有多少種方式
那麼我們就是需要找到有多少個j可以滿足i的需求,因為如果dp[i]+=dp[j],那麼s的子串[i,j-1]就需要是str字串
中的一個才可以

那麼暴力判斷的話肯定就會TLE,這個時候我們使用字典樹來維護
字典樹建樹的複雜度是O(n),n就是所有字串的長度,在這裡就是所有str字串的長度,大致建樹複雜度就是O(1e5)
另外在字典樹上查詢滿足要求的j的時候,因為str最長為100,所以查詢的複雜度最大也是100
那麼所有複雜度就是O(1e5)+O(1e5*1e2)

程式碼:

/*
題意:
多組輸入,首先給你一個長度最大為3e5的字串s
然後給你一個整數n,後面給你n個長度最大為100的字串str[i]
問你使用str組成s字串有多少種方式

這裡講解一下樣例:
abcd
4
a
b
cd
ab

那麼abcd可以通過a+b+cd  或者  ab+cd  兩種方式構成

題解:
dp方程很容易找到
dp[i]=(dp[i]+dp[j]) (i<j)  
dp[i]表示構成s字串的[i,len](這裡我們把s字串下標看作從1開始)這一個子串有多少種方式
那麼我們就是需要找到有多少個j可以滿足i的需求,因為如果dp[i]+=dp[j],那麼s的子串[i,j-1]就需要是str字串
中的一個才可以

那麼暴力判斷的話肯定就會TLE,這個時候我們使用字典樹來維護
字典樹建樹的複雜度是O(n),n就是所有字串的長度,在這裡就是所有str字串的長度,大致建樹複雜度就是O(1e5)
另外在字典樹上查詢滿足要求的j的時候,因為str最長為100,所以查詢的複雜度最大也是100
那麼所有複雜度就是O(1e5)+O(1e5*1e2)


*/

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=3e5+10;
const int mod=20071027;
typedef struct Trie* TrieNode;
int dp[maxn],flag;
struct Trie
{
    int sum;
    TrieNode next[30];
    Trie()
    {
        sum=0;
        memset(next,NULL,sizeof(next));
    }
};
void inserts(TrieNode root,char s[105])
{
    TrieNode p = root;
    int len=strlen(s);
    for(int i=0; i<len; ++i)
    {
        int temp=s[i]-'a';
        if(p->next[temp]==NULL) p->next[temp]=new struct Trie();
        p=p->next[temp];
    }
    p->sum+=1;
}
void Del(TrieNode root)
{
    for(int i=0 ; i<2 ; ++i)
    {
        if(root->next[i])Del(root->next[i]);
    }
    delete(root);
}
void query(TrieNode root,char s[105],int pos)
{
    TrieNode p = root;
    int len=strlen(s+1),ci=0;
    for(int i=pos;i<=len;++i)
    {
        int temp=s[i]-'a';
        if(p->next[temp]==NULL)
        {
            return;
        }
        else
        {
            p=p->next[temp];
        }
        ci++;
        if(p->sum>0)
        {
            //printf("%d %d %d\n",pos,dp[pos],dp[pos+ci]);
            dp[pos]+=dp[pos+ci];
            dp[pos]%=mod;
        }
    }
}
char ss[maxn],str[105];
int main()
{
    int n,p=0;
    while(~scanf("%s",ss+1))
    {
        flag=0;
        memset(dp,0,sizeof(dp));
        TrieNode root = new struct Trie();
        scanf("%d",&n);
        for(int i=0 ; i<n; ++i)
        {
            scanf("%s",str);
            inserts(root,str);
        }
        int len=strlen(ss+1);
        dp[len+1]=1;
        for(int i=len;i>=1;--i)
        {
            //printf("------------%d\n",i);
            query(root,ss,i);
        }
        printf("Case %d: %d\n",++p,dp[1]);
        Del(root);
    }
    return 0;
}