2018 计蒜之道 初赛 第二场(字典树)

转载请注明出处https://blog.csdn.net/the_Little_Penguin/article/details/80339641

第一次写博客,如有不足,还请多多指教。

赛后过的题…..
这里写图片描述
题目是字典树,只不过是后缀的。

**

C题 阿里巴巴的手机代理商(中等)

**

记原后缀为s1,新后缀为s2,要修改的情况下,分成两种情况:

一、s1不包含(见注释①)在s2中,可直接处理,即增加新后缀,然后将s1后面的内容接到s2后面。

二、s1包含在s2中,在字典树中沿着s1往下走,结束后,直接开新枝记录s2,s2结束后,然后将s1后面的内容接到s2后面。

这里要注意,要防止成环,在处理过程中记录一下s1结束后与s2对应位置的下一个字母(如s1=”ty”,s2=”arty”,那么这个字母指的是r)在字典树中s1这条枝下的标号(因为如果s1这条枝的下一个字母上存在与s2相同的字母,则会被覆盖,而到时候将s1的后续接到s2时则会成环)。

这里举一个例子:字典树中存在rty这个单词,现在要将ty改为rrty,会发现,如果不记录r,就会出现旧的rty丢失,那么,以rty为后缀的单词也都会丢失,而rrty在字典树中,由于将t的子孙都搬了过来,那么ttrr这条路下最后的r的下个一子孙r指向了前一个r,就出现了环。那么,只要记录下原来r在字典树中的位置,再对其进行更新就可以了。
这里写图片描述 这里写图片描述 这里写图片描述
注释:①包含:指的是,lens1小于lens2(因为lens1==lens2的情况就算是“包含”也是conflict),且在s1的长度内,该后缀从后往前每一个字母都与s2匹配 如:ty –> rty 则记为包含,而 ty –> tyr 不记为包含

注意最终的数据是有可能超过int的。

更新部分的代码

bool contains(char s1[], char s2[]) {
    int len1 = strlen(s1);
    int len2 = strlen(s2);
    int i = len1 - 1, j = len2 - 1;
    if (len1 >= len2)
        return false;
    while (j >= 0) {
        if (i < 0)
            return true;
        if (s1[i] != s2[j])
            return false;
        i--;
        j--;
    }
}

int update_insert(char s2[], int len1, int endd, bool contain) {
    int len2 = strlen(s2);
    ll val = num[endd];
    int now = 0;
    int start = len2 - 1;
    int save = ztree[endd][s2[len2 - len1 - 1] - 'a'];
    for (int i = start; i >= 0; i--) {
        if (contain && i <= len2 - len1 - 1 || ztree[now][s2[i] - 'a'] == 0)
            ztree[now][s2[i] - 'a'] = cnt++;
        now = ztree[now][s2[i] - 'a'];
        num[now] += val;
    }
    for (int i = 0; i < 26; i++) {
        if (ztree[endd][i]) {
            ztree[now][i] = ztree[endd][i];
            num[ztree[now][i]] = num[ztree[endd][i]];
        }
    }
    if (contain) {
        ztree[now][s2[len2 - len1 - 1] - 'a'] = save;
        num[ztree[now][s2[len2 - len1 - 1] - 'a']] = num[save];
    }
    return ztree[endd][s2[len2 - len1 - 1] - 'a'];
}

void update_del(char s1[], ll val) {
    int len1 = strlen(s1);
    int now = 0;
    int aa = 0;
    for (int i = len1 - 1; i >= 0; i--) {
        de[aa][0] = now;
        de[aa++][1] = s1[i] - 'a';
        now = ztree[now][s1[i] - 'a'];
        num[now] -= val;
    }
    de[aa++][0] = now;
    for (int i = 0; i < aa - 1; i++) {
        if (num[de[i + 1][0]] == 0)
            ztree[de[i][0]][de[i][1]] = 0;
    }
    for (int i = 0; i < 26; i++) {
        ztree[now][i] = 0;
    }
}

void update(char s1[], char s2[]) {
    bool con = contains(s1, s2);
    int endd = query(s1);
    ll val = num[endd];
    int connect = update_insert(s2, strlen(s1), endd, con);
    update_del(s1, val);
    if (con)
        ztree[endd][s2[strlen(s2) - strlen(s1) - 1] - 'a'] = connect;
}

**

D题 阿里巴巴的手机代理商(困难)

**

暴力一下,跑二次字典树。

第一遍,先把各种操作保存起来,离线跑一下,记录一下各个询问的版本号。

第二遍,按照版本排下序,过一遍(过程中会经历0~x个版本号,就在其中对排序好的vquery进行处理),到达指定id(第几个操作)时再输出。因为dirV<=id,可一遍完成。

struct VQ {
    int dirV;
    int id;
    string qstring;
}vquery[1000005];

注意题意那个修改操作empty的时候也是不记版本号的,题意没说。

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭