Educational Codeforces Round 97 (Rated for Div. 2) G. Death DBMS (AC自动机)

传送门
G. Death DBMS
time limit per test2 seconds
memory limit per test512 megabytes
inputstandard input
outputstandard output
For the simplicity, let’s say that the “Death Note” is a notebook that kills a person when their name is written in it.

It’s easy to kill with it, but it’s pretty hard to keep track of people you haven’t killed and still plan to. You decided to make a “Death Database Management System” — a computer program that provides the easy access to the database of possible victims. Let me describe its specifications to you.

Let’s define a victim entity: a victim has a name (not necessarily unique) that consists only of lowercase Latin letters and an integer suspicion value.

At the start of the program the user enters a list of n victim names into a database, each suspicion value is set to 0.

Then the user makes queries of two types:

1 i x — set the suspicion value of the i-th victim to x;
2 q — given a string q find the maximum suspicion value of a victim whose name is a contiguous substring of q.
Just to remind you, this program doesn’t kill people, it only helps to search for the names to write down in an actual notebook. Thus, the list of the victims in the database doesn’t change throughout the queries.

What are you waiting for? Write that program now!

Input
The first line contains two integers n and m (1≤n,m≤3⋅105) — the number of victims and the number of queries, respectively.

Each of the next n lines contains a single string si — the name of the i-th victim. Each name consists only of lowercase Latin letters.

Each of the next m lines contains a query of one of two types:

1 i x (1≤i≤n, 0≤x≤109) — change the suspicion value of the i-th victim to x;
2 q — given a string q consisting only of lowercase Latin letters find the maximum suspicion value of a victim whose name is a contiguous substring of q.
There is at least one query of the second type. The total length of the strings si doesn’t exceed 3⋅105. The total length of the strings q doesn’t exceed 3⋅105.

Output
For each query of the second type print an integer value. If there is no victim name that is a contiguous substring of q, then print −1. Otherwise, print the maximum suspicion value of a victim whose name is a contiguous substring of q.

Examples
5 8
kurou
takuo
takeshi
naomi
shingo
2 nakiraomi
2 abanaomicaba
1 3 943
2 takuotakeshishingo
1 5 135832
2 shingotakeshi
1 5 0
2 shingotakeshi

-1
0
943
135832
943

题意

给你n,q(n,q<=3 ⋅ \cdot 105),再给你n个只由小写字母组成的单词,再给你q次操作,每次操作有两种类型:
1 i x 将第i个单词的权值变为x (每个单词的权值初始为0)
2 s 查询所有为串s子串的单词中的最大权值。如果所有单词里没有这个串的子串,输出-1
所有单词的总长度不超过3 ⋅ \cdot 105,第二种查询的所有s总长度不超过3 ⋅ \cdot 105

思路

第一种做法: 如果对所有单词建立AC自动机,那么第二种查询相当于查询s的对应的所有节点到fail树根的路径上的权值最大值,第一种操作就是(fail树上点权的)单点修改,这种单点修改和区间查询可以用树链剖分来做。
第二种做法:
依然是对所有单词建立AC自动机,但是发现对答案有贡献的只有trie树上每个单词的结尾节点(用一个multiset存每个尾结点的所有权值,因为可能有重复的单词所以是multiset),因此我们在建立fail树的时候再开一个nxt数组,直接指向fail树上上一个结尾节点(有贡献的点),这样最坏情况下是查询 n \sqrt n n 个长度为 n \sqrt n n 的s串,每个串最多在fail树上跳 n \sqrt n n 次,所以总复杂度是 O ( n n ) O(n\sqrt n) O(nn ) n n n q q q同阶)

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dep(i,a,b) for(int i=(a);i>=(b);i--)
#define ls (rt<<1)
#define rs (rt<<1|1)
#define fi first
#define se second
#define pb push_back
using namespace std;
const int maxn=3e5+5;
//const double pi=acos(-1.0);
//const double eps=1e-9;
//const ll mo=1e9+7;
int n,m,k;
int a[maxn],c[maxn];
int ans,tmp,cnt;
int flag;
char s[maxn];
template <typename T>
inline void read(T &X){
    X=0;int w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    if(w) X=-X;
}
struct ACzdj{
    int ch[maxn][26];
    int fail[maxn];
    int nxt[maxn];
    multiset<int>st[maxn];
    int stk[maxn],ta;
    bool ok[maxn];
    int cnt;
    void init(){
        cnt=0;
        memset(ch[0],0,sizeof(ch[0]));
        st[0].clear();
        ok[0]=0;
    }
    int add(char *s,int val){
        int len=strlen(s),u=0;
        rep(i,0,len-1){
            int id=s[i]-'a';
            if(!ch[u][id]) {
                ch[u][id]=++cnt;
                memset(ch[cnt],0,sizeof(ch[cnt]));
                st[cnt].clear();
                ok[cnt]=0;
            }
            u=ch[u][id];
        }
        st[u].insert(val);
        return u;
    }
    void build(){
        queue<int>q;
        int u=0;
        rep(i,0,25) if(ch[u][i]){
            fail[ch[u][i]]=0;
            nxt[ch[u][i]]=0;
            q.push(ch[u][i]);
        }
        while(!q.empty()){
            int u=q.front();q.pop();
            rep(i,0,25){
                if(ch[u][i]) {
                    fail[ch[u][i]]=ch[fail[u]][i];
                    nxt[ch[u][i]]=(st[ch[fail[u]][i]].size()>0?ch[fail[u]][i]:nxt[ch[fail[u]][i]]);
                    q.push(ch[u][i]);
                }
                else ch[u][i]=ch[fail[u]][i];
            }
        }
    }
    int query(char *s){
        int ans = -1;
        int len=strlen(s),u=0;
        ta=0;
        rep(i,0,len-1){
            int id=s[i]-'a';
            u=ch[u][id];
            for(int j=u;j&&!ok[j];j=nxt[j]){
                //cout<<"!!: "<<nxt[j]<<endl;
                if(st[j].size()) ans=max(ans,*st[j].rbegin());
                ok[j]=1;
                stk[++ta]=j;
            }
        }
        while(ta) ok[stk[ta--]]=0;
        return ans;
    }
}ac;
void solve(){
    read(n); read(m);
    ac.init();
    rep(i,1,n){
        scanf("%s",s);
        a[i]=ac.add(s,0);
        c[i]=0;
    }
    ac.build();
    rep(i,1,m){
        int id;
        read(id);
        if(id==1){
            int x,val;
            read(x); read(val);
            ac.st[a[x]].erase(ac.st[a[x]].find(c[x]));
            ac.st[a[x]].insert(val);
            c[x]=val;
        }
        else {
            scanf("%s",s);
            int ans=ac.query(s);
            printf("%d\n",ans);
        }
    }
}
int main(){
/*
#ifdef ONLINE_JUDGE
#else
    freopen("D:/Temp/in.txt", "r", stdin);
#endif
*/
    // freopen("e://duipai//myout.txt","w",stdout);
    int T=1,cas=1;
    //read(T);
    while(T--){
        solve();
    }
    //system("pause");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值