【NOIP2012模拟11.7】秘密文件

3 篇文章 0 订阅

Description
某天,情报局得到了一份秘密文件。文件的内容是加密后的全部由大写字母组成字符串。情报局局长小明想将其发送给远在东方神秘的XX大陆上的老朋友小刘来解密。然而若字符串太长,则需要很长的发送时间,太不安全了,因此小明想尽量将其缩短。于是小明制定了这样一个缩短规则:若一个字符串t连续出现k次,则可以用k(t)进行说明。如ABABAB可以缩成3(AB)。当然,重复缩短是允许的,如ABABABAAAAAAABABABAAAAAA可以缩成2(3(AB)6(A))
现在,小明想知道,对于给定的字符串,最短可以缩成什么样子。
Input
输入仅一行,为给定的字符串。
Output
输出仅一行,为经过缩短操作后的字符串。
若有多解,输出任意解即可。
Sample Input
AAAAAAAAAABABABCCD
Sample Output
9(A)3(AB)CCD
**Data Constraint
Hint**
对于100%的数据,字符串的长度L<=100。

The Solution

此题为经典DP题
用f[i][j]表示原串中第i位至第j位经过处理后能压缩成的最短字符串。
对于f[i][j]的处理,显然是可以直接使用原串的。
然后我们可以枚举断点,由f[i][k]和f[k+1][j]拼接之后来更新f[i][j]。
最后,检测原串中i~j这一段是否由重复的子串构成。
如果是,也能更新f[i][j]。
注意到对于状态f[i][j],若i=j,此状态必然是废状态,可以直接去掉。所以我们可以计算出所有情况的排位。对于状态f[i][j],若i>j,则此状态不存在,否则该状态的排位为j*(j-1)/2+i。这样就能优化掉一半的空间了。
最后答案ans即为f[1][len]。

Code

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define fd(i,a,b) for (int i=a;i>=b;i--)
#define INF 2147483647
#define N 105
using namespace std;
char s[N];
int F[N][N];
string ans[N][N];
bool bz[N][N];
inline int read()
{
    int x=0,w=1;
    char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*w;
}
bool Check(int l,int r,int ll,int rr)
{
    if ((r-l+1)%(rr-ll+1)!=0) return false;
    fo(i,l,r)
        if (s[i]!=s[(i-l)%(rr-ll+1)+ll]) return false;
    return true;
}

int Ask(int x){int y=0;for (;x;x/=10) y++;return y;}
string Get(int x){string t;for (;x;x/=10) t=char(x%10+'0')+t;return t;}
int Dp(int l,int r)
{
    if (bz[l][r]) return F[l][r];
    if (l==r) 
    {
        ans[l][r]=s[l];
        return 1;
    }
    bz[l][r]=true;
    int mn=r-l+1;
    fo(i,l,r) ans[l][r]+=s[i];
    fo(i,l,r-1)
    {
        int temp=Dp(l,i)+Dp(i+1,r);
        if (temp<mn)
        {
            mn=temp;
            ans[l][r]=ans[l][i]+ans[i+1][r];
        }
        if (Check(i+1,r,l,i))
        {
            int temp=Dp(l,i)+Ask((r-i)/(i-l+1)+1)+2;
            if (temp<mn)
            {
                mn=temp;
                ans[l][r]=Get((r-i)/(i-l+1)+1)+"("+ans[l][i]+")";
            }
        }
    }
    return F[l][r]=mn;
}
int main()
{
    scanf("%s",s+1);
    int l=strlen(s+1);
    Dp(1,l);
    cout<<ans[1][l]<<endl;
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值