[Noip模拟题]PermRLE

本文介绍了一种名为PermRLE的文本压缩算法,该算法通过特定的字符排列和重复字符合并来减少文本长度。文章详细阐述了算法的工作原理、输入输出格式、示例解析及实现思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description
文本压缩的算法有很多种,这里给出一种叫做PermRLE的压缩算法。定义一个整数k, PermRLE算法依赖于一种压缩顺序。所谓的压缩顺序就是一种1~k的排列。例如当k=4的时候,其中一种排列方式是{1,2,4,3},对于字符串”abdb”,按照这种排列方式进行排列之后就变成了”abbd”。对于一段长度为Len的文本,其中k能整除Len,那么PermRLE算法就是把整个文本分成Len/k段,然后每一段按照一种1~k的排列方式进行重新排列,重新排列完之后,就把这Len/k段进行合并。对于合并之后得到一个新字符串,PermRLE算法就是把字符串中连续相同的字符合并成一个字符,例如aabccaabb合并后就变成了abcab。给出一段长度Len(1Len50000)的文本以及一个整数k(1k16)其中k能整除Len,你的任务就是找出一种1~k的排列,使得PermRLE算法压缩之后的文本的长度最小。当然,为了降低难度,你只需输出文本压缩之后最小的长度,而不需要输出这种排列。

Input
输入第一行有一个整数k(1k16),意义如上所述。
接下来一行有一个字符串,保证字符串的长度能被k整除,且字符串里仅含有小写字母。
1k161Len50000

Output
输出一行,仅包含文本压缩之后的最小长度。

Sample Input
4
abcabcabcabc

Sample Output
7

HINT
k的排列是1 4 3 2,然后原字符串变成aacbbbacccba,压缩之后变成acbacba,长度为7,可以证明,这是最小答案

思路
对于一个字符串,它总是能够分成Len/k段。记两个数组insidei,jbetweeni,j,前者表示在Len/k个块中每个块的第i个和第j个字符总共有多少个相同的,后者表示每个块的第i个字符和下一个块的第j个字符总共有多少个相同的。如样例:

abcabcabcabc

分成块就是:

abca
bcab
cabc

那么inside1,4=3inside1,3=0between4,3=2
对于k的一个排列,显然只需要关心那些数出现过,那些没有,设fS,i,j表示在前x个数中(不关心x的值),使用过的数是S的二进制状态,第一个数字为i,最后一个数字为j所能压缩的最大长度。 那么状态转移方程为:

fS,j,k=fS(1<<k1),j,x+insidek,x(j,k,xS,j!=k)
最后考虑块与块之间的压缩,对于每个f(1<<n)1,i,j,都加上betweeni,j,最终nmax(f(1<<n)1,i,j)就是压缩后答案的最小值。

代码

#include <cstdio>
#include <cstring>
#include <algorithm>

const int maxn=16;
const int maxlen=60000;

int f[1<<maxn][maxn+1][maxn+1],ans;
int n,len,inside[maxn+1][maxn+1],between[maxn+1][maxn+1];
char s[maxlen+10];

int main()
{
    scanf("%d%s",&n,s+1);
    len=strlen(s+1);
    for(int i=0; i<len/n; i++)
    {
        for(int j=1; j<=n; j++)
        {
            for(int k=1; k<=n; k++)
            {
                if(j!=k)
                {
                    inside[j][k]+=s[i*n+j]==s[i*n+k];
                }
            }
        }
    }
    for(int i=0; i<len/n-1; i++)
    {
        for(int j=1; j<=n; j++)
        {
            for(int k=1; k<=n; k++)
            {
                between[j][k]+=s[i*n+j]==s[(i+1)*n+k];
            }
        }
    }
    for(int i=0; i<1<<n; i++)
    {
        for(int j=1; j<=n; j++)
        {
            if(i&(1<<(j-1)))
            {
                for(int k=1; k<=n; k++)
                {
                    if((j!=k)&&(i&(1<<(k-1))))
                    {
                        for(int l=1; l<=n; l++)
                        {
                            if(i&(1<<(l-1)))
                            {
                                if(k!=l)
                                {
                                    f[i][j][k]=std::max(f[i][j][k],f[i^(1<<(k-1))][j][l]+inside[k][l]);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++)
        {
            ans=std::max(ans,f[(1<<n)-1][i][j]+between[i][j]);
        }
    }
    printf("%d\n",len-ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值