LA 3942 trie树 + dfs(dp)

传送门:LA 3942

题意

给定一个字符串, 在给出s个单词, 求字符串由这些单词组成的方案数(单词可重复)


题解

类似于dfs的深搜方案数求解, 区别在于组成单位是单词, 所以可以用trie数保存单词, 搜索时找到单词搜索, 记忆化搜索可以降低时间消耗
建完树之后用dp逆序也是可以做的, 和dfs的原理一样


AC code

dfs 树存储是以节点方式

/*
adrui's submission
Language : C++
Result : Accepted
Love : yy
Favorite : Dragon Balls

Standing in the Hall of Fame
*/


#include
#include
#include
#include
#include
#include
#include
using namespace std;

#define M(a, b) memset(a, b, sizeof(a))
#define mid ((l + r) >> 1)
#define ls rt << 1, l, mid
#define rs rt << 1|1, mid + 1, r
#define lowbit(x) (x & (-x))
#define LL long long
#define REP(n) for(int i = 0; i < n; i++)
#define debug 0

const int mod(20071027);
const int maxn(3e5 + 5);

int dp[maxn];

int idx(char p) {
    return p - 'a';
}


struct TrieTree {
    bool exist;
    TrieTree *next[26];

    TrieTree() {                                                
        M(next, 0);
        //cnt = 0;
        exist = false;
    }

    void insert_node(char *s) {                                 
        TrieTree *root = this;
        char *p = s;
        while (*p) {

            int tmp = idx(*p);

            if (root->next[tmp] == NULL) {
                root->next[tmp] = new TrieTree();
            }

            root = root->next[tmp];

            ++p;
        }

        root->exist = true;

    }

    int dfs(char *s, int tmp, int len) {

        if (tmp > len)  return 0;
        if (tmp == len) return 1;//找到串尾方案为1
        if (dp[tmp] != -1)  return dp[tmp];
        dp[tmp] = 0;

        TrieTree *root = this;

        char *p = s;

        int cnt = 1;
        while (*p) {
            int temp = idx(*p);

            if (root->next[temp] == NULL) {//树节点为空退出搜索
                break;
            }
            root = root->next[temp];
            if (root->exist) dp[tmp] += dfs(p + cnt, tmp + cnt, len);//如果单词存在, 深搜
            ++cnt;
            ++p;
        }

        return dp[tmp] %= mod;
    }
};

int main() {
#if debug
    freopen("in.txt", "r", stdin);
#endif //debug

    cin.tie(0);
    cin.sync_with_stdio(false);


    char s[maxn];
    char word[105];



    int flag = 0, cnt, kase = 0;
    while (cin >> s) {
        TrieTree *root = new TrieTree();//循环里
        cout << "Case " << ++kase << ": ";
        cin >> cnt;
        while (cnt--) {
            cin >> word;
            root->insert_node(word);
        }

        M(dp, -1);
        int ans = root->dfs(s, 0, strlen(s));

        cout << ans << endl;
    }
    return 0;
}

逆序dp

/*
adrui's submission
Language : C++
Result : Accepted
Love : yy
Favorite : Dragon Balls

Standing in the Hall of Fame
*/


#include
#include
#include
#include
#include
#include
#include
using namespace std;

#define M(a, b) memset(a, b, sizeof(a))
#define mid ((l + r) >> 1)
#define ls rt << 1, l, mid
#define rs rt << 1|1, mid + 1, r
#define lowbit(x) (x & (-x))
#define LL long long
#define REP(n) for(int i = 0; i < n; i++)
#define debug 0

const int mod(20071027);
const int maxn(5e5 + 5);

int dp[maxn];

int idx(char p) {
    return p - 'a';
}


struct TrieTree {
    bool exist;
    TrieTree *next[26];

    TrieTree() {                                                
        M(next, 0);
        //cnt = 0;
        exist = false;
    }

    void insert_node(char *s) {                                 
        TrieTree *root = this;
        char *p = s;
        while (*p) {

            int tmp = idx(*p);

            if (root->next[tmp] == NULL) {
                root->next[tmp] = new TrieTree();
            }

            root = root->next[tmp];

            ++p;
        }

        root->exist = true;

    }

    int Dp(char *s, int len) {

        dp[len] = 1;

        for (int i = len - 1; i >= 0; --i) {
            TrieTree *root = this;//根节点
            for (int j = i; j < len; ++j) {
                int temp = idx(s[j]);
                //cout << s[j] << endl;
                if (root->next[temp] == NULL) {//同dfs退出原理
                    break;
                }
                root = root->next[temp];
                if (root->exist == true) {
                    dp[i] = (dp[i] + dp[j + 1]) % mod;//更新方案数
                    //cout << j + 1 << " " << len << endl;
                }
            }
            dp[i] %= mod;
        }

        return dp[0];
    }
};

int main() {
#if debug
    freopen("in.txt", "r", stdin);
#endif //debug

    cin.tie(0);
    cin.sync_with_stdio(false);


    char s[maxn];
    char word[105];



    int flag = 0, cnt, kase = 0;
    while (cin >> s) {

        TrieTree *root = new TrieTree();
        cout << "Case " << ++kase << ": ";
        cin >> cnt;
        while (cnt--) {
            cin >> word;
            root->insert_node(word);
        }

        M(dp, 0);
        int ans = root->Dp(s,strlen(s));

        cout << ans << endl;
    }
    return 0;
}

总结

这题下午想思路半小时, 第一发代码10分钟, debug一小时…
把根节点开辟放在了循环外面, 好气, 还以为dfs写的搓了, 找出bud的时候表情那个精彩啊
果然结构体写得还是不熟练, 一定要上心了

你可能感兴趣的:(数据结构-Trie树,dfs)