【问题描述】
给定一个由n个小写字母组成的字符串s,需要使用最少数量的钱币来压缩它。
压缩该字符串,必须将s表示为多个相互连接的非空字符串: s=t1t2...tk,其中第 i 个字符串按照下列两种方法之一编码:
-
-
如果|ti|=1,也就是说 ti为单个字符组成的字符串,编码时需要支付a个钱币
-
如果ti是t1t2...ti-1的子串,编码时需要支付b个钱币
-
你的任务是计算压缩给定的字符串需要花费的最小钱币数。
【输入形式】
输入的第一行包含3个用空格分隔的正整数:n、a和b(1≤n、a、b≤5000),第二行为一个长度为n的小写字符串。
【输出形式】
输出一个整数,表示你需要为压缩s所需要支付的最小钱币数。
【样例输入1】
3 3 1 aba
【样例输出1】
7
【样例输入2】
4 1 1 abcd
【样例输出2】
4
【样例输入3】
4 10 1 aaaa
【样例输出3】
12
【样例输入4】
8 1 3 aaaaaaaa
【样例输出4】
7
【思路解析】
读题就很容易感觉到可以使用动态规划:本状态下的结果与前状态相关。虽然这一题可以不按照动态规划的方法来写,但是思路仍然是一个状态的转移。
- 以字符串按照一个字符一个字符的增长设立状态(好像大部分的字符串动态规划都是按照这一方式设置状态的)
- 如果把该状态下的新字符设立为单独字符存在,则dp[i]=dp[i-1]+a
- 如果把新字符视为与前面字符串组合形成子串存在,则若存在符合要求的子串有dp[i]=dp[i-j+1]+b,我们取这俩种情况出现的最小值作为dp[i]的结果即可。
【AC代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5010;
int n,a,b;
string s;
int dp[N];
int main()
{
cin>>n>>a>>b>>s;
dp[0]=a;
for(int i=1;i<n;i++)
{
/* 以独立字符存在 */
dp[i]=dp[i-1]+a;
/* 以子串存在 */
for(int j=(i+1)/2;j<=i;j++)
{
bool flag=false;
string s1=s.substr(0,j);
string s2=s.substr(j,i-j+1);
if(s1.find(s2)!=string::npos)
{
//cout<<s1<<" "<<s2<<endl;
dp[i]=min(dp[i],dp[j-1]+b);
flag=true;
break;
}
if(flag) break;
}
}
cout<<dp[n-1]<<endl;
return 0;
}