2016 UESTC Training for Search Algorithm & String H - 中二少女与字符串 Trie 字典树

H – 中二少女与字符串

Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 262140/262140KB (Java/Others)

Submit  Status

Minakami Yuki 很喜欢字符串,作为一个犯中二病的熊孩子,她只喜欢某些特定的小写字母,如果一个字符串包含了多于K个她不喜欢的字母,

她就认为这是一个“坏串”。有一天她看到了一个只由小写字母组成的字符串,她想知道这个字符串的哪些子串不是“坏串”。因为她还要努力研究奇怪

的哲学知识,这个问题就由你来计算了。

注意注意如果一个相同的子串出现在了原串的多个位置,只能算作一次喵~

Input

本题有多组测试数据,Input的第一行是一个整数T(T <= 100),是测试数据的组数。

接下来T组数据,每组由三行构成。

第一行是一个字符串S,长度不超过1500,是Yuki看到的字符串。

第一行是一个长度为26的01串,代表Yuki喜不喜欢每个字母,从az0代表不喜欢,1代表喜欢。

第三行是一个整数K,0 <= K(S的长度),表示子串中最多所能包含的不喜欢的字母个数

Output

输出T行,每行一个整数,即满足条件子串的个数

Sample input and output

Sample Input Sample Output
2
ababab
01000000000000000000000000
1
acbacbacaa
00000000000000000000000000
2
5
8

Hint

子串是字符串中连续的一段,也可以包含原串本身。

    

  Source

      
2016 UESTC Training for Search Algorithm & String

  My Solution



Trie 字典树

分别求以l为起始点的字符串有多少个前缀符合条件,答案即为和。

从后往前,每次s[l..n-1]都加入字典树中。
不能从前往后的,从前往后漏了很多,或者说要在里面加一个for 把j <= k <= i 把s[k…i]插到Trie
里,这样显然有大量的重复,而且O(n^2)也必定超时
而从后往前插, 则左端点在不断左移,故每次把s[l..r]插入是相当于也插入了s[l+1..r],s[l+2..r]等
这样就变成O(n)了

产生的新节点就是符合条件的新子串。

一边扫一边控制最长可到的r,即每次加入字典树的其实是s[l..r]。

对于01串可以用map<char, bool> _map; 来处理, 从char 映射到 0 or 1

复杂度 O(n*n)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
using namespace std;

//CHARSE 为字符集大小
//BASE 为字符集ASCII 最小字符
//MAX_NODE 为最大点数
const int CHARSE = 26, BASE = 'a', MAX_NODE = 1e7 + 8;          //!!!!!!前面MAX_NODE开的太小, WA1 怎么不RE呢
//!!!!!!                           确实是这里  看样子当不能确定MAX_NODE多大的时候还是在不超内存的情况下尽可能大好了
//struct Trie{
    int tot, root, child[MAX_NODE][CHARSE];
    //bool flag[MAX_NODE];
    inline void Trie()
    {
        memset(child[1], 0, sizeof child[1]);
        //flag[1] = false;
        root = tot = 1;
    }
/*
    void _insert(const char* str)
    {
        int *cur = &root;
        for(const char *p = str; *p; p++){
            cur = &child[*cur][*p - BASE];
            if(*cur == 0){
                *cur = ++tot;
                memset(child[tot], 0, sizeof child[tot]);
                flag[tot] = false;
            }
        }
        flag[*cur] = true;
    }
*/
    inline void _insert(const char* str, const int& n)
    {
        int *cur = &root;
        const char *endptr = str + n;                 //
        for(const char *p = str; p != endptr; p++){
            cur = &child[*cur][*p - BASE];
            if(*cur == 0){
                *cur = ++tot;
                memset(child[tot], 0, sizeof child[tot]);
                //flag[tot] = false;
            }
        }
        //flag[*cur] = true;
    }
/*
    bool query(const char *str)
    {
        int *cur = &root;
        for(const char *p = str; *p && *cur; p++)
            cur = &child[*cur][*p - BASE];
        return (*cur && flag[*cur]);
    }

    bool query(const char *str, const int& n)
    {
        int *cur = &root;
        const char* endptr = str + n;
        for(const char *p = str; p != endptr && *cur; p++)   //这个是根据上面改成插入 str[i,....i+n] 这一段, 可能有错误
            cur = &child[*cur][*p - BASE];
        return (*cur && flag[*cur]);
    }
*/
//};

const int maxn = 1500 + 8;
map<char, bool> _map;

char line[maxn], or01[27];

int main()
{
    #ifdef LOCAL
    freopen("a.txt", "r", stdin);
    #endif // LOCAL
    int T, n, k, cnt;
    scanf("%d", &T);
    while(T--){
        Trie();
        //_map.clear(); //not necessary
        scanf("%s", line);
        scanf("%s", or01);
        scanf("%d", &k);

        for(int i = 0; i < 26; i++){
            _map[i + 'a'] = or01[i] - '0';
        }
        n = strlen(line);
        cnt = 0;

        for(int i = n-1, j = n-1; i >= 0; i--){
            if(!_map[line[i]]) cnt++;
            while(cnt > k){
                if(!_map[line[j--]]) cnt--;
            }
            //cout<<"here"<<endl;
            //从左到右要加上这个for(int l = j; l <= i; l++)    //!前面这里用了 k = j
                _insert(line + i, j - i + 1);  //line + j, 不是 line + i 拜托
        }
        printf("%d\n", tot - 1);  //边数 == 借的书 - 1 即为Trie中子串总数
    }
    return 0;
}

Thank you!

                                                                                                                                               ——from ProLights

    原文作者:Trie树
    原文地址: https://blog.csdn.net/ProLightsfxjh/article/details/51611079
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞