Hdu 4117 GRE Words (后缀数组+dp)

题目大意:

求出最多能记住的单词的权值和,要求最大。

记住的规则就是上一个单词是这个单词的子串。

思路分析:

首先得声明这题是数据水了才能用sa做的。

sa的复杂度最多可以达到 Orz(sumlen * sumlen) ...

所以我们sa处理的就是这个串是否是下一个串的子串,如果是就转移方程。

dp[i] = max (dp[i] , dp[j] + val[i])...

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define maxn 350005
#define inf 0x3f3f3f3f
using namespace std;

int str[maxn];
int sa[maxn],t1[maxn],t2[maxn],c[maxn],n;
int bel[maxn];
int st[maxn],sumlen[maxn],val[maxn];
int dp[maxn];
void suffix(int m)
{
    int *x=t1,*y=t2;
    for(int i=0;i<m;i++)c[i]=0;
    for(int i=0;i<n;i++)c[x[i]=str[i]]++;
    for(int i=1;i<m;i++)c[i]+=c[i-1];
    for(int i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
    for(int k=1;k<=n;k<<=1)
    {
        int p=0;
        for(int i=n-k;i<n;i++)y[p++]=i;
        for(int i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k;
        for(int i=0;i<m;i++)c[i]=0;
        for(int i=0;i<n;i++)c[x[y[i]]]++;
        for(int i=0;i<m;i++)c[i]+=c[i-1];
        for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1;x[sa[0]]=0;
        for(int i=1;i<n;i++)
        x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
        if(p>=n)break;
        m=p;
    }
}
int rank[maxn],height[maxn];
void getheight()
{
    int k=0;
    for(int i=0;i<n;i++)rank[sa[i]]=i;
    for(int i=0;i<n;i++)
    {
        if(k)k--;
        if(!rank[i])continue;
        int j=sa[rank[i]-1];
        while(str[i+k]==str[j+k])k++;
        height[rank[i]]=k;
    }
}
char tmp[maxn];
int main()
{
    int T,cas=1;
    for(scanf("%d",&T);T--;)
    {
        int N;
        scanf("%d",&N);
        n=0;
        for(int i=1;i<=N;i++)
        {
            scanf("%s%d",tmp,&val[i]);
            dp[i]=val[i];
            int len=strlen(tmp);
            st[i]=n;
            sumlen[i]=len;
            for(int j=0;j<len;j++)
            {
                str[n]=tmp[j];
                bel[n++]=i;
            }
            bel[n]=i;
            str[n++]=i+128;
        }
        str[n-1]=0;
        suffix(30000);
        getheight();

        for(int i=1;i<=N;i++)
        {
            int mlen=inf;
            for(int j=rank[st[i]]+1;j<n;j++)
            {
                mlen=min(mlen,height[j]);
                if(mlen<sumlen[i])break;
                if(bel[sa[j]]>i)
                {
                    dp[bel[sa[j]]]=max(dp[bel[sa[j]]],val[bel[sa[j]]]+dp[i]);
                }
            }

            mlen=inf;
            for(int j=rank[st[i]]-1;j>0;j--)
            {
                mlen=min(mlen,height[j+1]);
                if(mlen<sumlen[i])break;
                if(bel[sa[j]]>i)
                {
                    dp[bel[sa[j]]]=max(dp[bel[sa[j]]],val[bel[sa[j]]]+dp[i]);
                }
            }
        }
        printf("Case #%d: %d\n",cas++,max(0,*max_element(dp+1,dp+1+N)));
    }
    return 0;
}
/*
1
6
abbab 800
a 1
ab 2
abb 3
baba 5
abbab 8
*/
时间: 2024-11-05 05:58:09

Hdu 4117 GRE Words (后缀数组+dp)的相关文章

[后缀数组+dp/AC自动机+dp+线段树] hdu 4117 GRE Words

题意: 给你N个字符串, N(1 <= N <= 2w), 所有串的长度加一起不超过30w.每个串有个值.这个值[-1000, 1000]. 问不打乱字符串顺序,从中取若干个字符串,使得前一个串是后一个串的子串,求满足前面调条件的字符串值得和最大,求这个值. 思路: 其实就是一个很明显的dp. dp[i]代表以第i个字符串结尾的最大权值. 但是就是子串这个问题怎么处理. 由于这题数据比较水可以用后缀数组处理这个问题. 将所有字符串拼接,做sa. 每次在height数组里往上和往下寻找公共前缀等

hdu 4029 Distinct Sub-matrix (后缀数组)

题目大意: n*m的矩阵中,有多少个子矩阵不是同的. 思路分析: 假设这题题目只是一维的求一个串中有多少个子串是不同的. 那么也就是直接扫描height,然后减去前缀. 现在变成二维,如何降低维度. 知道hash 的作用就是将一个串映射到一个数字. 那我们就将这个矩阵hash,考虑到不同的长度和宽度都会导致不同, 所以就要枚举子矩阵的宽度. hash [i][j] 就表示在当前宽度W 下,从 第 i 行 第 j 个开始往后W长度的串的hash值. 然后将列上相同起点的hash值 子串. 然后将所

HDU 5769 Substring(后缀数组)

[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5769 [题目大意] 在一个串中求出包含字母的子串个数, 只要存在一个字符不相等的子串即可视为不同的子串. [题解] 因为要求存在字符不相等因此我们可以利用后缀数组统计, 后缀数组的h数组可以记录前后两个后缀的最长公共前缀这样子相同的前缀就不会被多次计算, 保存每个位置之后出现的最近的要求的字母的位置, 从该后缀的包含该字母的位置往后且不在最长公共前缀的范围内的位置都可以作为子串的右端点, 统计左

hdu 5442 Favorite Donut 后缀数组

Favorite Donut Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=5442 Description Lulu has a sweet tooth. Her favorite food is ring donut. Everyday she buys a ring donut from the same bakery. A ring donut is consis

hdu 3518 Boring counting 后缀数组LCP

题目链接 题意:给定长度为n(n <= 1000)的只含小写字母的字符串,问字符串子串不重叠出现最少两次的不同子串个数; input: aaaa ababcabb aaaaaa # output 2 3 3 思路:套用后缀数组求解出sa数组和height数组,之后枚举后缀的公共前缀长度i,由于不能重叠,所以计数的是相邻height不满足LCP >= i的. 写写对后缀数组倍增算法的理解: 1.如果要sa数组对应的值也是1~n就需要在最后加上一个最小的且不出现的字符'#',里面y[]是利用sa数

HDU - 6704 K-th occurrence (后缀数组+主席树/后缀自动机+线段树合并+倍增)

题意:给你一个长度为n的字符串和m组询问,每组询问给出l,r,k,求s[l,r]的第k次出现的左端点. 解法一: 求出后缀数组,按照排名建主席树,对于每组询问二分或倍增找出主席树上所对应的的左右端点,求第k大的下标即可. 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=1e5+10,mod=998244353; 5 char buf[N]; 6 int s[N],sa[

HDU 4576 简单概率 + 滚动数组DP(大坑)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4576 坑大发了,居然加 % 也会超时: 1 #include <cstdio> 2 #include <iostream> 3 #include <sstream> 4 #include <cmath> 5 #include <cstring> 6 #include <cstdlib> 7 #include <string>

hdu 3518 Boring counting 后缀数组 height分组

题目链接 题意 对于给定的字符串,求有多少个 不重叠的子串 出现次数 \(\geq 2\). 思路 枚举子串长度 \(len\),以此作为分界值来对 \(height\) 值进行划分. 显然,对于每一组,组内子串具有一个长度为 \(len\) 的公共前缀. 至于是否重叠,只需判断 \(sa_{max}-sa_{min}\geq len\). 对于组间,显然它们的公共前缀互不相同,所以答案即为\(\sum_{len}\sum_{group}\) Code #include <bits/stdc++

hdu 3948 后缀数组

The Number of Palindromes Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Total Submission(s): 2465    Accepted Submission(s): 841 Problem Description Now, you are given a string S. We want to know how many distin