HDU4117 GRE WORDS(AC自动机+线段树维护fail树的dfs序)

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.

InputThe 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.OutputFor 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

题解:

给定n个字符串,要求按顺序取一些字符串,满足后一个字符串是前一个字符串的子串,要求使得取出的权值和最大。

题目类似一般的DP问题,项最长上升子序列,只不过把上升的要求改成了是前一个的子串,权重也发生了改变 。

判断子串关系,我们用AC 自动机。

我们把AC自动机的fail指针拿出来建立一棵树,然后在求这棵树的dfs序,对于每个点,我所能得到的最大权值一定是该点到root的所有权值。然后我们对每个点的in[x],和out[x]之间的dfs序去更新。

每次取最大值即可

参考代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
typedef long long LL;
const int maxn=3e5+5;
int cnt,root,n,kase,w[20000+5],pos[20000+5];
int INDEX,in[maxn],out[maxn];
char s[maxn];
int fail[maxn];
int S[maxn*4],tag[maxn*4],MAX,L,R;
int st[maxn],tot;
struct edge {
    int v,nxt;
}v[maxn*2];
inline void addedge(int x,int y)
{
    v[tot]=(edge){y,st[x]};
    st[x]=tot++;
}
struct node {
int nxt[26],cnt;
}T[maxn];
inline int newnode()
{
    cnt++; memset(T[cnt].nxt,0,sizeof(T[cnt].nxt));
    T[cnt].cnt=0; fail[cnt]=0;
    return cnt;
}
inline void insert(char *s)
{
    int now=root; int i=0;
    while(s[i])
    {
        if(!T[now].nxt[s[i]-‘a‘])T[now].nxt[s[i]-‘a‘]=newnode();
        now=T[now].nxt[s[i]-‘a‘]; i++;
    }
    T[now].cnt++;
}
queue<int>Q;
inline void build()
{
    Q.push(root);
    while(!Q.empty())
    {
        int k=Q.front(); Q.pop();
        if(k!=root)addedge(fail[k],k);
        for(int i=0;i<26;++i)
        {
            if(!T[k].nxt[i]){T[k].nxt[i]=T[fail[k]].nxt[i];continue;}
            if(k!=root)
                fail[T[k].nxt[i]]=T[fail[k]].nxt[i];
            Q.push(T[k].nxt[i]);
        }
    }
}
inline void DFS(int u,int fa)
{
    in[u]=++INDEX;
    for(int i=st[u];~i;i=v[i].nxt)
        if(v[i].v!=fa) DFS(v[i].v,u);
    out[u]=INDEX;
}
inline void update(int k)
{
    S[k]=max(S[k<<1],S[k<<1|1]);
}
inline void down(int k)
{
    if(!tag[k])return ;
    tag[k<<1]=max(tag[k<<1],tag[k]);
    tag[k<<1|1]=max(tag[k<<1|1],tag[k]);
    S[k<<1]=max(S[k<<1],tag[k]);
    S[k<<1|1]=max(S[k<<1|1],tag[k]); tag[k]=0;
}
inline int ask(int l,int r,int k)
{
    if(L<=l&&r<=R)return S[k];
    int mid=(l+r)>>1; down(k);
    int res=0;
    if(L<=mid)res=max(res,ask(l,mid,k<<1));
    if(R>mid)res=max(res,ask(mid+1,r,k<<1|1)); return res;
}
inline void add(int l,int r,int k)
{
    if(L<=l&&r<=R)
    {
        S[k]=max(S[k],MAX); tag[k]=max(tag[k],MAX);
        return ;
    }
    int mid=(l+r)>>1; down(k);
    if(L<=mid)add(l,mid,k<<1);
    if(R>mid)add(mid+1,r,k<<1|1);
    update(k);
}
int main()
{
    int t; scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n); cnt=-1;
        root=newnode(); tot=0;
        memset(st,-1,sizeof(st));
        pos[0]=0;
        for(int i=1;i<=n;++i)
        {
            scanf("%s%d",s+pos[i-1],w+i);
            pos[i]=pos[i-1]+strlen(s+pos[i-1]);
            insert(s+pos[i-1]);
        }
        build();
        INDEX=0;
        DFS(root,-1);
        int ans=0;
        memset(S,0,sizeof(S));
        memset(tag,0,sizeof(tag));
        for(int i=1;i<=n;++i)
        {
            MAX=0; int p=root;
            for(int j=pos[i-1];j<pos[i];++j)
            {
                p=T[p].nxt[s[j]-‘a‘]; L=in[p];R=in[p];
                int res=ask(1,INDEX,1);
                MAX=max(res,MAX);
            }
            MAX=MAX+w[i];
            ans=max(ans,MAX);
            L=in[p];R=out[p];
            add(1,INDEX,1);
        }
        printf("Case #%d: %d\n",++kase,ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/songorz/p/11455717.html

时间: 2024-10-03 21:54:52

HDU4117 GRE WORDS(AC自动机+线段树维护fail树的dfs序)的相关文章

HDU 5069 Harry And Biological Teacher(AC自动机+线段树)

题意 给定 \(n\) 个字符串,\(m\) 个询问,每次询问 \(a\) 字符串的后缀和 \(b\) 字符串的前缀最多能匹配多长. \(1\leq n,m \leq 10^5\) 思路 多串匹配,考虑 \(\text{AC}\)自动机,对 \(n\) 个串建自动机,观察这个结构,不难发现 \(Trie\) 树的结构和前缀有关,\(fail\) 树的结构和后缀有关. 考虑离线,对于每个 \(b\) ,存储它对应的 \(a\) ,我们通过在自动机上扫 \(b\) 来回答.由于扫到某节点 \(u\)

CoderForces 163E e-Government(AC自动机+树状数组维护fail树的dfs序)

E. e-Government time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output The best programmers of Embezzland compete to develop a part of the project called "e-Government" — the system of automa

BZOJ.4817.[SDOI2017]树点涂色(LCT DFS序 线段树)

题目链接 1.2裸树剖,但是3.每个点的答案val很不好维护.. 如果我们把同种颜色的点划分到同一连通块中,那么向根染色的过程就是Access()! 最初所有点间都是虚边,相同颜色点用实边相连.一条边由实边变为虚边时,深度大的点所在子树所有点val+1(Access()中原先x的右儿子答案+1,因为x颜色变了): 由虚边变为实边时,深度大的点所在子树所有点val-1(fa[x]颜色与x相同导致fa[x]的贡献没了).(其实是因为 实链数量(贡献)就等于虚边数量+1?无所谓了) 于是2.就是val

【树链剖分】【dfs序】【线段树】bzoj2836 魔法树

这道题告诉我们:树链剖分的重标号就是dfs序. #include<cstdio> #include<algorithm> using namespace std; #define N 100001 #define lson rt<<1,l,m #define rson rt<<1|1,m+1,r typedef long long ll; ll delta[N<<2],sumv[N<<2]; int n,m; int en,v[N],

[BZOJ4817][SDOI2017]树点涂色(LCT+DFS序线段树)

4817: [Sdoi2017]树点涂色 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 692  Solved: 408[Submit][Status][Discuss] Description Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路 径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色.Bob可能会进行这几种操作: 1 x: 把点x到根节点的路径上所有的点染上一种

【树链剖分】【dfs序】【LCA】【分类讨论】Codeforces Round #425 (Div. 2) D. Misha, Grisha and Underground

一棵树,q次询问,每次给你三个点a b c,让你把它们选做s f t,问你把s到f +1后,询问f到t的和,然后可能的最大值是多少. 最无脑的想法是链剖线段树--但是会TLE. LCT一样无脑,但是少一个log,可以过. 正解是分类讨论, 如果t不在lca(s,f)的子树内,答案是dis(lca(s,f),f). 如果t在lca(s,f)的子树内,并且dep(lca(s,t))>dep(lca(f,t)),答案是dis(lca(s,t),f): 否则答案是dis(lca(f,t),f). #in

Trie图和Fail树

Trie图和AC自动机的区别 Trie图是AC自动机的确定化形式,即把每个结点不存在字符的next指针都补全了.这样做的好处是使得构造fail指针时不需要next指针为空而需要不断回溯. 比如构造next[cur][i]的fail指针,cur为父节点,next[cur][i]为cur的儿子结点,如果是AC自动机,如果父亲结点tmp(tmp是cur的一份拷贝)的next[fail[tmp]][i]不存在时,需要让tmp不断回溯(即tmp = fail[tmp]),直到next[fail[tmp]]

【uva1502/hdu4117-GRE Words】DP+线段树优化+AC自动机

这题我的代码在hdu上AC,在uva上WA. 题意:按顺序输入n个串以及它的权值di,要求在其中选取一些串,前一个必须是后一个的子串.问d值的和最大是多少. (1≤n≤2×10^4 ,串的总长度<=3*10^5) 题解: 这题一开始我的方向就错了,想了很久d[x][y]表示在AC自动机上的节点x.下一个串要大于y的dp.然而这样做数组要10^4*10^5=10^9级别,开都开不了,妥妥超时. 后来看了一眼题解...觉得自己智商真是感人... 用f[i]表示以第i个串为结尾的时候最大的d值,这样做

[NOI2011][bzoj2434] 阿狸的打字机 [AC自动机+dfs序+fail树+树状数组]

题面 传送门 正文 最暴力的 最暴力的方法:把所有询问代表的字符串跑一遍kmp然后输出 稍微优化一下:把所有询问保存起来,把模板串相同的合并,求出next然后匹配 但是这两种方法本质没有区别,都是暴力 不那么暴力的 我们对于所有的串建立一个AC自动机,把询问按照$y$排序,然后在AC自动机上面跑,每次跳fail更新答案 这样可以拿到70分,但是时间上限还是会$O\left(n^2\right)$左右 巧妙的优化 这道题里面,所有的模板串和文本串都在AC自动机里 那么,题目中实际是在要求什么呢?