【2018计蒜之道】第二场 阿里巴巴的手机代理商(中等)

题目:https://nanti.jisuanke.com/t/26986
解题思路:前面增删查都是很基础的东西,改的话确实有不少需要注意的地方。
官方题解给出的是将单词逆序存储,因为改的是后缀。
基本的思路是查询到需要替换的后缀s1,然后查询被替换的后缀s2,根据题意判断非法条件。在合法的情况下,将s1的后续节点连到s2上,然后将s1的后续节点删除。之后将源点到s1和s2的路径上的数据分别更新(s1删除、s2增加)。

思路并不难,但实现起来有点绝望。。。还是太菜了,写出了各种错误QAQ
想看代码请拉到最底部。
因为接下来我会贴一堆错误代码。

错误代码1.0

void Update(string s1,string s2){
    int u1=0;
    for(int i=s1.size()-1;i>=0;i--){
        int tp=s1[i]-'a';
        if(!dex[u1][tp]){
            cout<<"Empty"<<endl;
            return;
        }
        u1=dex[u1][tp];
    }
    if(!num[u1]){
        cout<<"Empty"<<endl;
        return;
    }
    int u2=0;
    for(int i=s2.size()-1;i>=0;i--){
        int tp=s2[i]-'a';
        if(!dex[u2][tp])
            dex[u2][tp]=++cnt;
        u2=dex[u2][tp];
    }
    if(num[u2]){
        cout<<"Conflict"<<endl;
        return;
    }
    for(int i=0;i<27;i++){
        if(dex[u1][i]){
            dex[u2][i]=dex[u1][i];
            dex[u1][i]=0;
        }
    }
    LL k=num[u1],t=sign[u1];
    num[u1]=0,sign[u1]=0;
    num[u2]+=k,sign[u2]+=t;    
    u1=0;
    for(int i=s1.size()-1;i>=0;i--){
        int tp=s1[i]-'a';
        num[u1]-=k;
        u1=dex[u1][tp];
    }
    u2=0;
    for(int i=s2.size()-1;i>=0;i--){
        int tp=s2[i]-'a';
        num[u2]+=k;
        u2=dex[u2][tp];
    }
}

因为前面查询s2的时候已经建了树了,所以理所当然地觉得后面直接用就好了。然而忽略了一个问题就是前面s1删除的时候有可能将已经建好的节点之间的指向删掉。
eg:insert a 4
        update a aaa
所以更新s2的时候一定要重新建树(这个重新未必是全新的,可能部分被删的点之间的联系需要重新建立)
于是就出现了如下2.0版的错误
哭唧唧~怎么那么多错误

错误代码2.0

void Update(string s1,string s2){
    int u1=0;
    for(int i=s1.size()-1;i>=0;i--){
        int tp=s1[i]-'a';
        if(!dex[u1][tp]){
            cout<<"Empty"<<endl;
            return;
        }
        u1=dex[u1][tp];
    }
    if(!num[u1]){
        cout<<"Empty"<<endl;
        return;
    }
    int u2=0;
    for(int i=s2.size()-1;i>=0;i--){
        int tp=s2[i]-'a';
        if(!dex[u2][tp])
            dex[u2][tp]=++cnt;
        u2=dex[u2][tp];
    }
    if(num[u2]){
        cout<<"Conflict"<<endl;
        return;
    }
    for(int i=0;i<27;i++){
        if(dex[u1][i]){
            dex[u2][i]=dex[u1][i];
            dex[u1][i]=0;
        }
    }
    LL k=num[u1],t=sign[u1];
    num[u1]=0,sign[u1]=0;
    num[u2]+=k,sign[u2]+=t;    
    u1=0;
    for(int i=s1.size()-1;i>=0;i--){
        int tp=s1[i]-'a';
        num[u1]-=k;
        u1=dex[u1][tp];
    }
    u2=0;
    for(int i=s2.size()-1;i>=0;i--){
        int tp=s2[i]-'a';
        if(!dex[u2][tp])
            dex[u2][tp]=++cnt;
        num[u2]+=k;
        u2=dex[u2][tp];
    }
}

其实这个也是后续测试的时候改的,基本写法与后期代码大同小异。
交上去一直wa。然后随手一改AC了,于是对着AC的代码找这个为啥不对。
之前说了,s2要重建,所以怎么可以在重新建树之前就更新s2节点存储的数据呢
显然是错的嘛,因为此时的u2非最后的u2啊QAQ
然后所谓的AC代码就是接下来的伪AC代码1.0了。

伪AC代码1.0

void Update(string s1,string s2){
    int u1=0;
    for(int i=s1.size()-1;i>=0;i--){
        int tp=s1[i]-'a';
        if(!dex[u1][tp]){
            cout<<"Empty"<<endl;
            return;
        }
        u1=dex[u1][tp];
    }
    if(!num[u1]){
        cout<<"Empty"<<endl;
        return;
    }
    LL k=num[u1],t=sign[u1];
    int u2=0;
    for(int i=s2.size()-1;i>=0;i--){
        int tp=s2[i]-'a';
        if(!dex[u2][tp])
            dex[u2][tp]=++cnt;
        u2=dex[u2][tp];
    }
    if(num[u2]){
        cout<<"Conflict"<<endl;
        return;
    }
    for(int i=0;i<27;i++){
        if(dex[u1][i]){
            dex[u2][i]=dex[u1][i];
            dex[u1][i]=0;
        }
    }
    u1=0;
    for(int i=s1.size()-1;i>=0;i--){
        int tp=s1[i]-'a';
        num[u1]-=k;
        u1=dex[u1][tp];
    }
    num[u1]-=k,sign[u1]-=t;
    u2=0;
    for(int i=s2.size()-1;i>=0;i--){
        int tp=s2[i]-'a';
        if(!dex[u2][tp])
            dex[u2][tp]=++cnt;
        num[u2]+=k;
        u2=dex[u2][tp];
    }
    num[u2]+=k,sign[u2]+=t;
}

改掉了前面的错误之后,就AC了。然后拖了两天准备写题解的时候想起来之前看的一位大佬的博客,当时还在下面提了个问题,然后很感动的被回答了。本来想着都AC了,就无所谓了。哦,其实是因为那里面说先删再建,而我当时特地试了先建后删,所以想反驳一下。然后华丽丽地打脸。
发现计蒜客的数据并不严谨。。
eg:insert ba 4
        update a aaa
        query baaa
这样,我的代码输出结果是0.。肉眼可见的错误。。
也就是前面那个博客大佬的说法了。
既然前面新建了节点,那为什么转移的时候是挂到了原来的节点上。。
可能我是个智障吧。。
于是重新改了一下。好了,接下来是AC代码了,不用长篇大论记录这些无聊(其实困扰了很久)的错误了。。

AC代码

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1000010;
int dex[N][30];
LL num[N],sign[N];
int cnt=0,mx=0;

void Insert(string s,int x){
    int u=0;
    for(int i=s.size()-1;i>=0;i--){
        int tp=s[i]-'a';
        if(!dex[u][tp])
            dex[u][tp]=++cnt;
        num[u]+=x;
        u=dex[u][tp];
    }
    mx=max(mx,u);
    num[u]+=x;
    sign[u]+=x;
}
void Delete(string s){
    int u=0,x=0;
    for(int i=s.size()-1;i>=0;i--){
        int tp=s[i]-'a';
        if(!dex[u][tp]){
            cout<<"Empty"<<endl;
            return;
        }
        u=dex[u][tp];
    }
    if(!sign[u]) {
        cout << "Empty" << endl;
        return;
    }
    else
        x=sign[u];
    u=0;
    for(int i=s.size()-1;i>=0;i--){
        int tp=s[i]-'a';
        num[u]-=x;
        u=dex[u][tp];
    }
    num[u]-=x;
    sign[u]=0;
}
LL Query(string s){
    int u=0;
    for(int i=s.size()-1;i>=0;i--){
        int tp=s[i]-'a';
        if(!dex[u][tp])
            return 0;
        u=dex[u][tp];
    }
    return num[u];
}
void Update(string s1,string s2){
    int u1=0;
    for(int i=s1.size()-1;i>=0;i--){
        int tp=s1[i]-'a';
        if(!dex[u1][tp]){
            cout<<"Empty"<<endl;
            return;
        }
        u1=dex[u1][tp];
    }
    if(!num[u1]){
        cout<<"Empty"<<endl;
        return;
    }
    LL k=num[u1],t=sign[u1];
    int u2=0;
    for(int i=s2.size()-1;i>=0;i--){
        int tp=s2[i]-'a';
        if(!dex[u2][tp])
            dex[u2][tp]=++cnt;
        u2=dex[u2][tp];
    }
    if(num[u2]){
        cout<<"Conflict"<<endl;
        return;
    }
    int tmp[30]={0};//非全局变量不赋初值是不会自动变0的QAQ
    for(int i=0;i<27;i++){
        if(dex[u1][i]) {
            tmp[i] = dex[u1][i];
            dex[u1][i] = 0;
        }
    }
    u1=0;
    for(int i=s1.size()-1;i>=0;i--){
        int tp=s1[i]-'a';
        num[u1]-=k;
        u1=dex[u1][tp];
    }
    num[u1]-=k,sign[u1]-=t;
    u2=0;
    for(int i=s2.size()-1;i>=0;i--){
        int tp=s2[i]-'a';
        if(!dex[u2][tp])
            dex[u2][tp]=++cnt;
        num[u2]+=k;
        u2=dex[u2][tp];
    }
    num[u2]+=k,sign[u2]+=t;
    for(int i=0;i<27;i++){
        if(tmp[i])
            dex[u2][i] = tmp[i];
    }
}
void init(){
    cnt=0;
    memset(num,0, sizeof(num));
    memset(sign,0, sizeof(sign));
    for(int i=0;i<=mx;i++)
        memset(dex[i],0, sizeof(dex[i]));
}
int main(){
    int casen;
    cin>>casen;
    while(casen--){
        init();
        int n;
        cin>>n;
        string s1,s2,s3;
        LL x;
        for(int i=0;i<n;i++){
            cin>>s1;
            if(s1=="insert"){
                cin>>s2>>x;
                Insert(s2,x);
            }else if(s1=="delete"){
                cin>>s2;
                Delete(s2);
            }else if(s1=="query"){
                cin>>s2;
                LL ans=Query(s2);
                cout<<ans<<endl;
            }else {
                cin>>s2>>s3;
                Update(s2,s3);
            }
        }
    }
    return 0;
}

这个故事告诉我们,眼瞎的人是会在一个地方一而再再而三地跌倒的。。(捂脸.jpg)内心戏真足2333
所以需要用的值记得提前存下来,要改的值在这个东西出现的最后一个不会被更改的地方更新。不要想当然!!!
嘤嘤嘤,谁晓得重新建点会有这么多影响。。好吧,怪我自己写程序不规范。。

重点鸣谢我参考的博客的作者,附一下大佬博客链接:https://blog.csdn.net/acterminate/article/details/80305963#comments
你看人家就不会遇到如此多问题,果然还是我太菜(唉)

猜你喜欢

转载自blog.csdn.net/vermouth_x/article/details/80391863
今日推荐