- 8.81%
- 3000ms
- 524288K
阿里巴巴的手机代理商正在研究 infra 输入法的新功能。他们需要分析单词频率以改进用户输入法的体验。于是需要你在系统内核里面写一个 API。 API 有如下功能:
添加操作
添加操作格式为
insert barty 8
,意思为插入barty
这个单词,这个单词词频为 88 次。注意如果再次添加insert barty 8
操作时,就会将词频增加为 1616 次。(不会出现词频 \le 0≤0 的情况)。删除操作
删除操作格式为
delete barty
,意思为删除所有barty
这个单词。如果当前没有删除的词汇,输出
"Empty"
。查询操作
查询操作格式为
query ty
,意思为查询当前版本以ty
结尾的单词词频总和。修改操作
修改操作格式为
update ty tied
,意思为将所有结尾是ty
的单词更新为tied
结尾,比如barty
会变为bartied
。如果不存在ty
结尾的单词,输出Empty
。如果已经存在tied
结尾的单词,那么说明存在 conflict。 不做合并,输出Conflict
。如果既不存在ty
结尾的单词,也已经存在以tied
结尾的单词,则输出Empty
。
输入格式
第一行读入一个整数 TT,代表数据组数。
每组数据的第一行读入一个整数 NN 代表操作数。
接下来 NN 行,每行形容一个操作。
保证数据满足 1 \le T \le 101≤T≤10,1 \le N \le 10^51≤N≤105,insert
和update
操作的字符串总长度之和 \le 10^6≤106,所有字符串长度 \le 10^6≤106,输入只有小写字母。
输出格式
输出题目中要求的结果。
样例输入
1 10 update y ty insert barty 8 delete shawn update ty tied query tied insert party 9 update y tied query ty delete barty query tied
样例输出
Empty Empty 8 Conflict 9 Empty 8
题解:把单词反转之后用Trie操作即可~~ 字典树每一个节点代表的字符信息由其在父节点的位置决定。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,a,n) for(int i=(a);i<(n);++i)
#define per(i,a,n) for(int i=(n-1);i>=(a);--i)
#define pb push_back
const int Inf=1e9;
#define N 1000010
#define M 26
int ch[N][M],tot;
ll sum[N];
void Insert(string str,int num){
int rt=0; //根节点
for(int i=0;i<str.length();++i){
int id=str[i]-'a';
if(!ch[rt][id]){ //添加新的子树
ch[rt][id]=++tot; //子树下标
for(int j=0;j<M;++j){ //初始化子树节点以及初始sum值
ch[tot][j]=0;
}
sum[tot]=0;
}
rt=ch[rt][id]; //进入子树
sum[rt]+=num; //更新子树值
}
}
ll Query(string str){ //以str为前缀的单词数目,(因为已经反转单词,其实是找后缀相同)
int rt=0;
for(int i=0;i<str.length();++i){
int id=str[i]-'a';
if(!ch[rt][id]){
return 0;
}
rt=ch[rt][id];
}
return sum[rt];
}
bool Delete(string str){
int rt=0;
for(int i=0;i<str.length();++i){
int id=str[i]-'a';
if(!ch[rt][id]){
return 0;
}
rt=ch[rt][id];
}
ll num=sum[rt];
for(int i=0;i<M;++i){ //去除以str为前缀的单词
if(ch[rt][i]){
num-=sum[ch[rt][i]];
}
}
if(!num) return false;
Insert(str,-1*num);
return true;
}
int Get(string str,ll num){ //把str删除,此处只把值更改
int rt=0,len=str.length();
for(int i=0;i<len-1;++i){
int id=str[i]-'a';
sum[ch[rt][id]]-=num;
rt=ch[rt][id];
}
int index=ch[rt][str[len-1]-'a']; //取出要删除字符串末尾位的下标,把这个节点连接作为到更改后的字符串末尾位
ch[rt][str[len-1]-'a']=0; //沿着str路径不再能访问末尾节点,(下标置为空)
return index;
}
void Link(string str,int index,ll num){
int rt=0,len=str.length();
for(int i=0;i<len-1;++i){
int id=str[i]-'a';
if(!ch[rt][id]){
ch[rt][id]=++tot;
for(int j=0;j<M;++j) ch[tot][j]=0;
sum[tot]=0;
}
rt=ch[rt][id];
sum[rt]+=num;
}
ch[rt][str[len-1]-'a']=index; //沿着新的路径,可以访问更改前的末尾位,以及后续子节点
}
int main(){
ios::sync_with_stdio(0);
int T;
cin>>T;
while(T--){
int n;
cin>>n;
string op;
string str1,str2;
ll num;
tot=0;
for(int i=0;i<M;++i) ch[tot][i]=0;
while(n--){
cin>>op;
if(op[0]=='i'){
cin>>str1>>num;
reverse(str1.begin(),str1.end());
Insert(str1,num);
}
else if(op[0]=='d'){
cin>>str1;
reverse(str1.begin(),str1.end());
bool flag=Delete(str1);
if(!flag) cout<<"Empty"<<endl;
}
else if(op[0]=='q'){
cin>>str1;
reverse(str1.begin(),str1.end());
cout<<Query(str1)<<endl;
}
else if(op[0]=='u'){
cin>>str1>>str2;
reverse(str1.begin(),str1.end());
reverse(str2.begin(),str2.end());
num=Query(str1);
if(!num){
cout<<"Empty"<<endl;
}
else{
if(Query(str2)){
cout<<"Conflict"<<endl;
}
else{
int index=Get(str1,num);
Link(str2,index,num);
}
}
}
}
}
return 0;
}