CF 494 F. Abbreviation(动态规划)

题目链接:【http://codeforces.com/contest/1003/problem/F】

题意:给出一个n字符串,这些字符串按顺序组成一个文本,字符串之间用空格隔开,文本的大小是字母+空格的个数。在这个文本中找k(k>=2)个区间,使得这k个区间完全相同,字符串不能分开,然后把每段的字符串变成单个的字符,并去掉中间的空格。可能有多种方案,求文本的最小长度。【表达能力有限,望理解,具体可以看题目】

You are given a text consisting of nn space-separated words. There is exactly one space character between any pair of adjacent words. There are no spaces before the first word and no spaces after the last word. The length of text is the number of letters and spaces in it. wiwi is the ii-th word of text. All words consist only of lowercase Latin letters.

Let‘s denote a segment of words w[i..j]w[i..j] as a sequence of words wi,wi+1,…,wjwi,wi+1,…,wj. Two segments of words w[i1..j1]w[i1..j1] and w[i2..j2]w[i2..j2] are considered equal if j1?i1=j2?i2j1?i1=j2?i2, j1≥i1j1≥i1, j2≥i2j2≥i2, and for every t∈[0,j1?i1]t∈[0,j1?i1] wi1+t=wi2+twi1+t=wi2+t. For example, for the text "to be or not to be" the segments w[1..2]w[1..2] and w[5..6]w[5..6] are equal, they correspond to the words "to be".

An abbreviation is a replacement of some segments of words with their first uppercase letters. In order to perform an abbreviation, you have to choose at least two non-intersecting equal segments of words, and replace each chosen segment with the string consisting of first letters of the words in the segment (written in uppercase). For example, for the text "a ab a a b ab a a b c" you can replace segments of words w[2..4]w[2..4] and w[6..8]w[6..8] with an abbreviation "AAA" and obtain the text "a AAA b AAA b c", or you can replace segments of words w[2..5]w[2..5] and w[6..9]w[6..9] with an abbreviation "AAAB" and obtain the text "a AAAB AAAB c".

What is the minimum length of the text after at most one abbreviation?

题解:

  dp[i][j]表示从第i个字符出开始的串和从第j个字符串开始的串的有多少个公共前缀字符串。因为n不是很大,所以可以暴力枚举。从前到后枚举,从第i个位置开始,长度为x(x个字符串)的串,有几个重复的,若重复的个数大于等于二则统计答案。实现的时候,可以用string暴力比较,也可以用HASH的方法,把字符串HASH成一个数字,这道题HASH卡的比较严格,我用双值HASH才跑过全部数据。具体看代码。(思路是看了某个大神的代码才理解到的,共同学习)。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int M = 2;
const int mod[M] = { (int)1e9 + 7, (int)1e9 + 9 };
struct Hash
{
    int a[M];
    Hash(int x = 0)
    {
        for (int i = 0; i < M; i++)
            a[i] = x;
    }
    Hash(const vector<int> &v)
    {
        for (int i = 0; i < M; i++)
            a[i] = v[i];
    }
    Hash operator * (const Hash &x) const
    {
        Hash ret;
        for (int i = 0; i < M; i++)
            ret.a[i] = (LL)a[i] * x.a[i] % mod[i];
        return ret;
    }
    Hash operator - (const Hash &x) const
    {
        Hash ret;
        for (int i = 0; i < M; i++)
        {
            ret.a[i] = a[i] - x.a[i];
            if (ret.a[i] < 0)
                ret.a[i] += mod[i];
        }
        return ret;
    }
    Hash operator + (const Hash &x) const
    {
        Hash ret;
        for (int i = 0; i < M; i++)
        {
            ret.a[i] = a[i] + x.a[i];
            if (ret.a[i] >= mod[i])
                ret.a[i] -= mod[i];
        }
        return ret;
    }
    bool operator == (const Hash &x) const
    {
        for (int i = 0; i < M; i++)
            if (a[i] != x.a[i])
                return false;
        return true;
    }
};
const Hash seed = Hash({ 131, 137 });

const int maxn = 1e5 + 15;
const int maxm = 350;
Hash sum[maxm];
int n, len[maxm], dp[maxm][maxm];
char s[maxn];

Hash Hash_tab(int ln)
{
    Hash ret;
    for(int i = 0; i < ln; i++)
    {
        LL tmp = (LL)(s[i] - ‘a‘ + 1);
        ret = ret * seed + Hash({tmp, tmp});
    }
    return ret;
}

int main ()
{
    scanf("%d", &n);
    int sum_len = n - 1;
    for(int i = 0; i < n; i++)
    {
        scanf("%s", s);
        len[i] = strlen(s);
        sum_len += len[i];
        sum[i] = Hash_tab(len[i]);
    }
    for(int i = n - 2; i >= 0; i--)
        for(int j = n - 1; j >= 0 && j > i; j--)
            dp[i][j] = (len[i] == len[j] && sum[i] == sum[j]) ? 1 + dp[i + 1][j + 1] : 0;
    int ans = sum_len;
    for(int i = 0; i < n; i++)
    {
        int ret = -1;
        for(int j = 0; i + j < n; j++)
        {
            ret += len[i + j];
            int cnt = 1, k = i + j + 1;
            while(k < n)
            {
                if(dp[i][k] >= j + 1)
                {
                    k += j;
                    cnt++;
                }
                k++;
            }
            if(cnt >= 2)
                ans = min(ans, sum_len - ret * cnt);
        }
    }
    printf("%d\n", ans);
    return 0;
}

原文地址:https://www.cnblogs.com/pealicx/p/9270973.html

时间: 2024-11-09 10:34:01

CF 494 F. Abbreviation(动态规划)的相关文章

Codeforces Round #494 (Div. 3) F. Abbreviation

题目大意 在一些字符串中找到几个(大于等于2个)不相交的区间,且这些区间都相等,即区间内字符串都相等. 解题思路 我们可以用递推的方式求出f[i][j]表示以i,j为结束,已经匹配了多长的字符串. 求出f之后,枚举区间,计算匹配次数,得出结果. 代码 #include <bits/stdc++.h> using namespace std; vector<string> a; int n; int dp[350][350]; int main() { int totl = 0; /

Leetcode 494 Target Sum 动态规划 背包+滚动数据

这是一道水题,作为没有货的水货楼主如是说. 题意:已知一个数组nums {a1,a2,a3,.....,an}(其中0<ai <=1000(1<=k<=n, n<=20))和一个数S c1a1c2a2c3a3......cnan = S, 其中ci(1<=i<=n)可以在加号和减号之中任选. 求有多少种{c1,c2,c3,...,cn}的排列能使上述等式成立. 例如: 输入:nums is [1, 1, 1, 1, 1], S is 3. 输出 : 5符合要求5种

【Cf #502 F】The Neutral Zone

本题把$log$化简之后求得就是每个质数$f$前的系数,求系数并不难,难点在于求出所有的质数. 由于空间限制相当苛刻,$3e8$的$bitset$的内存超限,我们考虑所有的除了$2$和$3$以外的质数,他们可以被表示成$p \equiv \pm 1 (mod \; 6) $.(具体来讲就是把数表示成$6k, 6k+1, 6k+2, 6k+3, 6k+4, 6k+5$,显然只有余数为$1$或$5$才有成为质数的可能性).然后我们需要筛的个数就变成原来的三分之一了. 另一个做法:分块,一部分一部分筛

CF - 1117 F Crisp String

题目传送门 题解: 枚举非法对. 如果 ‘a'  和 ’b' 不能相邻的话,那么删除 'a' 'b'之间的字符就是非法操作了. 假设题目给定的字符串为 "acdbe",所以删除cd是非法操作, 因为cd是非法了,所以cde也是非法操作, 也就是说找到所有的非法操作之后往外推,比他多删的状态就一样是非法的了,当然对于上述的“acdbe"来说,不能确定 ”acd"是非法操作,因为在枚举非法对的时候,该非法对的字符并不能被删除. 然后把所有非法对的非法状态都存下来.然后从

树的直径| CF#615Div3 F. Three Paths on a Tree

F. Three Paths on a Tree 思路 两种方法: 1.两次bfs求树的直径,顺便求出一个直径端点到所有点的最短距离:再bfs一次,求另一个直径上的端点到其它所有点的最短距离:之后枚举第三个端点(不等于端点1和端点2),dis(a,b) + dis(b,c) + dis(a,c) 再除以 2 就是最终答案,因为每个路径走了两次所以除以2. 2.dfs求树的直径,记录直径上的所有点.从直径上的所有点去搜索它们到不在直径上的点的最远距离.最后直径+这个最远距离就是答案 代码1 bfs

CF 474/F, 线段树 + 一点数学

啊回家真是颓,一周了什么都没做 题目大意:给出一坨数,每次询问区间当中有多少个数能把其他区间内的所有数整除 解:由于我是知道这个是线段树专题,所以一开始就奔着gcd去了,想了一下还真是gcd,因为如果一个数a能整除另一个数b,那么gcd(a,b)一定为a,所以这说明可以做区间加法,直接上线段树就是,询问个数的时候还傻叉地想了一会,后面发现给每个线段记一下当前线段表示的gcd有几个就行,合并直接合并(可以证明现在这个线段的gcd一定是作为最优的取值,因为没有数等于这个gcd说明没有数能整除当前区间

CF 977 F. Consecutive Subsequence

题意: 第一场div3, 求的是一个序列中最长连续(a,a+1,a+2...)子序列. 分析: 设一个DP[i] 表示 序列以i结尾的最长长度, 一开始都设为0. 那么如果这个数是a, 他的最长长度就是 Dp[a-1] + 1, 最后找出最大那个值就是答案, 倒回去输出序列就可以了 #include <bits/stdc++.h> using namespace std; const int maxN = 2e5 + 7; int a[maxN]; map<int, int> dp

Codeforces Round #271 (Div. 2) F.Ant colony(线段树 + 统计区间内某数的个数)

F. Ant colony Mole is hungry again. He found one ant colony, consisting of n ants, ordered in a row. Each ant i (1 ≤ i ≤ n) has a strength si. In order to make his dinner more interesting, Mole organizes a version of «Hunger Games» for the ants. He c

cf Round 613

A.Peter and Snow Blower(计算几何) 给定一个点和一个多边形,求出这个多边形绕这个点旋转一圈后形成的面积.保证这个点不在多边形内. 画个图能明白 这个图形是一个圆环,那么就是这个点距离多边形边缘最远的距离形成的圆面积减去这个点距离多边形边缘最近的距离形成的圆面积.我们可以得出距离最远的点一定是多边形的顶点.而距离最近的点不一定是多边形的顶点,但是在多边形的边上.我们用勾股定理判断点与每条边形成的三角形的两边角.如果有一个边角是钝角,则表示距离最近的点是顶点.如果都是锐角,则