HDU 4117 GRE Words

  建立所有单词的AC自动机,对于每个节点的转移,都是从parent[]或者从fail[],fail[fail[]],...得到的。可以看出fail[]的关系形成一棵树,于是问题转化成,不断在节点处插入,询问点到根路径上的最大值,可以利用dfs序列转化用线段树维护。

基本想法是建好AC自动机后,顺便建立fail树,然后用线段树维护操作。

基本原理:从某个节点沿着fail指针走直到根节点过程中遇到的节点都是改串的后缀相同的串,而fail指针构成了一棵fail树,所以一个串可能是哪些串的后缀字串呢?答案是节点对应fail树里的一颗子树。

当我们用AC自动机解决DP 或者 统计问题的时候,如果要支持更新操作,就需要数据结构的帮忙了

比如codeforces 163E,背景是最简单的多串匹配,但是有一个特殊的地方是会删除一些字符串和重新恢复一些字符串,注意到我们在统计的时候其实就是沿着fail指针走,把所有的标记叠加起来,而fail指针构成了一棵fail树,所以我们在求当前节点的fail指针方向有多少个标记的时候不必一层层的fail上去了,对于每个点维护其到根的有效节点的个数即可,

当更新某个点的时候,就相当于这个点的子树到根的有效节点的个数都发生了变化,将树形结构变成线性结构,在线段树中更新即可


GRE Words

Time Limit: 30000/15000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3005    Accepted Submission(s): 173


Problem Description
Recently George is preparing for the Graduate Record Examinations (GRE for short). Obviously the most important thing is reciting the words.
Now George is working on a word list containing N words.
He has so poor a memory that it is too hard for him to remember all of the words on the list. But he does find a way to help him to remember. He finds that if a sequence of words has a property that for all pairs of neighboring words, the previous one is a substring of the next one, then the sequence of words is easy to remember.
So he decides to eliminate some words from the word list first to make the list easier for him. Meantime, he doesn't want to miss the important words. He gives each word an importance, which is represented by an integer ranging from -1000 to 1000, then he wants to know which words to eliminate to maximize the sum of the importance of remaining words. Negative importance just means that George thought it useless and is a waste of time to recite the word.
Note that although he can eliminate any number of words from the word list, he can never change the order between words. In another word, the order of words appeared on the word list is consistent with the order in the input. In addition, a word may have different meanings, so it can appear on the list more than once, and it may have different importance in each occurrence.
HDU 4117 GRE Words_第1张图片
 

Input
The first line contains an integer T(1 <= T <= 50), indicating the number of test cases.
Each test case contains several lines.
The first line contains an integer N(1 <= N <= 2 * 10 4), indicating the number of words.
Then N lines follows, each contains a string S i and an integer W i, representing the word and its importance. S i contains only lowercase letters.
You can assume that the total length of all words will not exceeded 3 * 10 5.
 

Output
For each test case in the input, print one line: "Case #X: Y", where X is the test case number (starting with 1) and Y is the largest importance of the remaining sequence of words.
 

Sample Input
   
   
   
   
1 5 a 1 ab 2 abb 3 baba 5 abbab 8
 

Sample Output
   
   
   
   
Case #1: 14
 

Source
2011 Asia ChengDu Regional Contest
 

Recommend

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
#define ll long long
#define prt(k) ;//cout<<#k"="<<k<<" "
inline void Max(int& a,int b) { if(a<b) a=b; }
const int N=300012;
/************AC Automaton***********/
int ch[N][26],fail[N];
int node[N];
int root,AC_size,L;
const int Char=26;
int newnode()
{ memset(ch[L],-1,sizeof ch[L]); L++; return L-1; }
void init() {  L=0; root=newnode(); }
int idx(char a) { return a-'a'; }
void insert(char s[],int id)
{
    int n=strlen(s),u=root;
    for(int i=0;i<n;i++)
    {
        int& tmp=ch[u][idx(s[i])];
        if(tmp==-1) tmp=newnode();
        u=tmp;
    }
    node[id]=u;
}
void BUILD()
{
    queue<int> q;
    for(int i=0;i<Char;i++)
    {
        int &tmp=ch[root][i];
        if(tmp==-1) tmp=root;
        else { fail[tmp]=root; q.push(tmp) ; }
    }
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        for(int i=0;i<Char;i++)
        {
            int& tmp=ch[u][i];
            if(tmp==-1) tmp=ch[fail[u]][i];
            else { fail[tmp]=ch[fail[u]][i]; q.push(tmp); }
        }
    }
}
/*************End AC Automaton*************/
/*********Segment Tree**************/
int tree[N<<2],lazy[N<<2];
#define lson l,m,rt*2
#define rson m+1,r,rt*2+1
void build(int l,int r,int rt)
{
    tree[rt]=lazy[rt]=0;
    if(l==r) return;
    int m=(l+r)/2;
    build(lson) ; build(rson);
}
void pushup(int rt) { tree[rt]=max(tree[rt*2],tree[rt*2+1]); }
void pushdown(int rt)
{
    if(lazy[rt]) {
        Max(lazy[rt*2],lazy[rt]);
        Max(lazy[rt*2+1],lazy[rt]);
        Max(tree[rt*2],lazy[rt*2]);
        Max(tree[rt*2+1],lazy[rt*2+1]);
        lazy[rt]=0;
    }
}
void update(int L,int R,int v,int l,int r,int rt)
{
    if(L<=l&&r<=R) {
        Max(lazy[rt],v);
        Max(tree[rt],v);
        return;
    }
    pushdown(rt);
    int m=(l+r)/2;
    if(L<=m) update(L,R,v,lson);
    if(R>m) update(L,R,v,rson);
    pushup(rt);
}
int query(int L,int l,int r,int rt)
{
    if(l==r) return tree[rt];
    pushdown(rt);
    int m=(l+r)/2;
    if(L<=m) return query(L,lson);
    return (query(L,rson));
}
/*********End Segment Tree**********/
vector<int> g[N];
int tot,cnt;
int Left[N],Right[N];
void dfs(int u)
{
    Left[u]=++tot;  
    for(int i=0;i<g[u].size();i++)
    {
        dfs(g[u][i]);
    }
    Right[u]=tot;
}
int dp[N];
char str[N];
int pos[N/10];
int n;
void AC(int id,int l,int r)
{
    int u=0,v=0;
    for(int i=l;i<=r;i++)
    {
        u=ch[u][str[i]-'a'];
        Max(v,query(Left[u],1,tot,1));
    }
    dp[id]+=v;
    update(Left[node[id]],Right[node[id]],dp[id],1,tot,1);
}

int main()
{
    int re,ca=1; cin>>re;
    while(re--)
    {
        cin>>n;
        init();
        pos[0]=0;
        memset(dp,0,sizeof dp);
        for(int i=1;i<=n;i++)
        {
            scanf("%s%d",str+pos[i-1],&dp[i]);
            insert(str+pos[i-1],i);
            pos[i]=strlen(str);
        }

        BUILD();
        for(int i=0;i<L;i++) g[i].clear();
        for(int i=1;i<L;i++)
        {
            g[fail[i]].push_back(i);
        }
        tot=0;
        dfs(0);
        build(1,tot,1);
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            if(dp[i]>0) AC(i,pos[i-1],pos[i]-1);
            Max(ans,dp[i]);
        }
        printf("Case #%d: %d\n",ca++,ans);
    }
}


你可能感兴趣的:(数据结构,算法,字符串,ACM,AC自动机)